[Refactor] unexport config.Config var & move login tasks to task module (#288)
Unexport generateToken() move CreateLogin into task Create func config.SetDefaultLogin() Unexport loadConfig() & saveConfig unexport config var make SetDefaultLogin() case insensitive update func descriptions move FindSSHKey to task module Reviewed-on: https://gitea.com/gitea/tea/pulls/288 Reviewed-by: Norwin <noerw@noreply.gitea.io> Reviewed-by: Andrew Thornton <art27@cantab.net> Co-Authored-By: 6543 <6543@obermui.de> Co-Committed-By: 6543 <6543@obermui.de>
This commit is contained in:
parent
eeb9cbafe7
commit
c063329e9a
|
@ -39,10 +39,6 @@ func runLogins(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLoginDetail(name string) error {
|
func runLoginDetail(name string) error {
|
||||||
if err := config.LoadConfig(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
l := config.GetLoginByName(name)
|
l := config.GetLoginByName(name)
|
||||||
if l == nil {
|
if l == nil {
|
||||||
fmt.Printf("Login '%s' do not exist\n\n", name)
|
fmt.Printf("Login '%s' do not exist\n\n", name)
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/tea/modules/config"
|
|
||||||
"code.gitea.io/tea/modules/interact"
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,7 @@ func runLoginAdd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// else use args to add login
|
// else use args to add login
|
||||||
return config.AddLogin(
|
return task.CreateLogin(
|
||||||
ctx.String("name"),
|
ctx.String("name"),
|
||||||
ctx.String("token"),
|
ctx.String("token"),
|
||||||
ctx.String("user"),
|
ctx.String("user"),
|
||||||
|
|
|
@ -24,9 +24,6 @@ var CmdLoginSetDefault = cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLoginSetDefault(ctx *cli.Context) error {
|
func runLoginSetDefault(ctx *cli.Context) error {
|
||||||
if err := config.LoadConfig(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ctx.Args().Len() == 0 {
|
if ctx.Args().Len() == 0 {
|
||||||
l, err := config.GetDefaultLogin()
|
l, err := config.GetDefaultLogin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,18 +32,7 @@ func runLoginSetDefault(ctx *cli.Context) error {
|
||||||
fmt.Printf("Default Login: %s\n", l.Name)
|
fmt.Printf("Default Login: %s\n", l.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
loginExist := false
|
|
||||||
for i := range config.Config.Logins {
|
|
||||||
config.Config.Logins[i].Default = false
|
|
||||||
if config.Config.Logins[i].Name == ctx.Args().First() {
|
|
||||||
config.Config.Logins[i].Default = true
|
|
||||||
loginExist = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !loginExist {
|
name := ctx.Args().First()
|
||||||
return fmt.Errorf("login '%s' not found", ctx.Args().First())
|
return config.SetDefaultLogin(name)
|
||||||
}
|
|
||||||
|
|
||||||
return config.SaveConfig()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,6 @@ var CmdLoginEdit = cli.Command{
|
||||||
Flags: []cli.Flag{&flags.OutputFlag},
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLoginEdit(ctx *cli.Context) error {
|
func runLoginEdit(_ *cli.Context) error {
|
||||||
return open.Start(config.GetConfigPath())
|
return open.Start(config.GetConfigPath())
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,12 @@ var CmdLoginList = cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunLoginList list all logins
|
// RunLoginList list all logins
|
||||||
func RunLoginList(ctx *cli.Context) error {
|
func RunLoginList(_ *cli.Context) error {
|
||||||
err := config.LoadConfig()
|
logins, err := config.GetLogins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print.LoginsList(config.Config.Logins, flags.GlobalOutputValue)
|
print.LoginsList(logins, flags.GlobalOutputValue)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ var CmdLogout = cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLogout(ctx *cli.Context) error {
|
func runLogout(ctx *cli.Context) error {
|
||||||
err := config.LoadConfig()
|
logins, err := config.GetLogins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@ func runLogout(ctx *cli.Context) error {
|
||||||
name = ctx.String("name")
|
name = ctx.String("name")
|
||||||
} else if len(ctx.Args().First()) != 0 {
|
} else if len(ctx.Args().First()) != 0 {
|
||||||
name = ctx.Args().First()
|
name = ctx.Args().First()
|
||||||
} else if len(config.Config.Logins) == 1 {
|
} else if len(logins) == 1 {
|
||||||
name = config.Config.Logins[0].Name
|
name = logins[0].Name
|
||||||
} else {
|
} else {
|
||||||
return errors.New("Please specify a login name")
|
return errors.New("Please specify a login name")
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
// the remotes of the .git repo specified in repoFlag or $PWD, and using overrides from
|
// the remotes of the .git repo specified in repoFlag or $PWD, and using overrides from
|
||||||
// command flags. If a local git repo can't be found, repo slug values are unset.
|
// command flags. If a local git repo can't be found, repo slug values are unset.
|
||||||
func InitCommand(repoFlag, loginFlag, remoteFlag string) (login *Login, owner string, reponame string) {
|
func InitCommand(repoFlag, loginFlag, remoteFlag string) (login *Login, owner string, reponame string) {
|
||||||
err := LoadConfig()
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func InitCommand(repoFlag, loginFlag, remoteFlag string) (login *Login, owner st
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// discovers login & repo slug from the default branch remote of the given local repo
|
// contextFromLocalRepo discovers login & repo slug from the default branch remote of the given local repo
|
||||||
func contextFromLocalRepo(repoValue, remoteValue string) (*Login, string, error) {
|
func contextFromLocalRepo(repoValue, remoteValue string) (*Login, string, error) {
|
||||||
repo, err := git.RepoFromPath(repoValue)
|
repo, err := git.RepoFromPath(repoValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -106,7 +106,7 @@ func contextFromLocalRepo(repoValue, remoteValue string) (*Login, string, error)
|
||||||
return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository")
|
return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, l := range Config.Logins {
|
for _, l := range config.Logins {
|
||||||
for _, u := range remoteConfig.URLs {
|
for _, u := range remoteConfig.URLs {
|
||||||
p, err := git.ParseURL(strings.TrimSpace(u))
|
p, err := git.ParseURL(strings.TrimSpace(u))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"code.gitea.io/tea/modules/utils"
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
@ -22,8 +23,9 @@ type LocalConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Config contain if loaded local tea config
|
// config contain if loaded local tea config
|
||||||
Config LocalConfig
|
config LocalConfig
|
||||||
|
loadConfigOnce sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetConfigPath return path to tea config file
|
// GetConfigPath return path to tea config file
|
||||||
|
@ -53,29 +55,30 @@ func GetConfigPath() string {
|
||||||
return configFilePath
|
return configFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig load config into global Config var
|
// loadConfig load config from file
|
||||||
func LoadConfig() error {
|
func loadConfig() (err error) {
|
||||||
|
loadConfigOnce.Do(func() {
|
||||||
ymlPath := GetConfigPath()
|
ymlPath := GetConfigPath()
|
||||||
exist, _ := utils.FileExist(ymlPath)
|
exist, _ := utils.FileExist(ymlPath)
|
||||||
if exist {
|
if exist {
|
||||||
bs, err := ioutil.ReadFile(ymlPath)
|
bs, err := ioutil.ReadFile(ymlPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to read config file: %s", ymlPath)
|
err = fmt.Errorf("Failed to read config file: %s", ymlPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = yaml.Unmarshal(bs, &Config)
|
err = yaml.Unmarshal(bs, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse contents of config file: %s", ymlPath)
|
err = fmt.Errorf("Failed to parse contents of config file: %s", ymlPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveConfig save config from global Config var into config file
|
// saveConfig save config to file
|
||||||
func SaveConfig() error {
|
func saveConfig() error {
|
||||||
ymlPath := GetConfigPath()
|
ymlPath := GetConfigPath()
|
||||||
bs, err := yaml.Marshal(Config)
|
bs, err := yaml.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,15 @@ package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/tea/modules/utils"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Login represents a login to a gitea server, you even could add multiple logins for one gitea server
|
// Login represents a login to a gitea server, you even could add multiple logins for one gitea server
|
||||||
|
@ -39,55 +33,88 @@ type Login struct {
|
||||||
Created int64 `yaml:"created"`
|
Created int64 `yaml:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLogins return all login available by config
|
||||||
|
func GetLogins() ([]Login, error) {
|
||||||
|
if err := loadConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config.Logins, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetDefaultLogin return the default login
|
// GetDefaultLogin return the default login
|
||||||
func GetDefaultLogin() (*Login, error) {
|
func GetDefaultLogin() (*Login, error) {
|
||||||
if len(Config.Logins) == 0 {
|
if err := loadConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Logins) == 0 {
|
||||||
return nil, errors.New("No available login")
|
return nil, errors.New("No available login")
|
||||||
}
|
}
|
||||||
for _, l := range Config.Logins {
|
for _, l := range config.Logins {
|
||||||
if l.Default {
|
if l.Default {
|
||||||
return &l, nil
|
return &l, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Config.Logins[0], nil
|
return &config.Logins[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLoginByName get login by name
|
// SetDefaultLogin set the default login by name (case insensitive)
|
||||||
|
func SetDefaultLogin(name string) error {
|
||||||
|
if err := loadConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
loginExist := false
|
||||||
|
for i := range config.Logins {
|
||||||
|
config.Logins[i].Default = false
|
||||||
|
if strings.ToLower(config.Logins[i].Name) == strings.ToLower(name) {
|
||||||
|
config.Logins[i].Default = true
|
||||||
|
loginExist = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !loginExist {
|
||||||
|
return fmt.Errorf("login '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return saveConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoginByName get login by name (case insensitive)
|
||||||
func GetLoginByName(name string) *Login {
|
func GetLoginByName(name string) *Login {
|
||||||
for _, l := range Config.Logins {
|
err := loadConfig()
|
||||||
if l.Name == name {
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range config.Logins {
|
||||||
|
if strings.ToLower(l.Name) == strings.ToLower(name) {
|
||||||
return &l
|
return &l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateLoginName generates a name string based on instance URL & adds username if the result is not unique
|
// GetLoginByToken get login by token
|
||||||
func GenerateLoginName(url, user string) (string, error) {
|
func GetLoginByToken(token string) *Login {
|
||||||
parsedURL, err := utils.NormalizeURL(url)
|
err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
log.Fatal(err)
|
||||||
}
|
|
||||||
name := parsedURL.Host
|
|
||||||
|
|
||||||
// append user name if login name already exists
|
|
||||||
if len(user) != 0 {
|
|
||||||
for _, l := range Config.Logins {
|
|
||||||
if l.Name == name {
|
|
||||||
name += "_" + user
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return name, nil
|
for _, l := range config.Logins {
|
||||||
|
if l.Token == token {
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLogin delete a login by name
|
// DeleteLogin delete a login by name from config
|
||||||
func DeleteLogin(name string) error {
|
func DeleteLogin(name string) error {
|
||||||
var idx = -1
|
var idx = -1
|
||||||
for i, l := range Config.Logins {
|
for i, l := range config.Logins {
|
||||||
if l.Name == name {
|
if l.Name == name {
|
||||||
idx = i
|
idx = i
|
||||||
break
|
break
|
||||||
|
@ -97,9 +124,22 @@ func DeleteLogin(name string) error {
|
||||||
return fmt.Errorf("can not delete login '%s', does not exist", name)
|
return fmt.Errorf("can not delete login '%s', does not exist", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
Config.Logins = append(Config.Logins[:idx], Config.Logins[idx+1:]...)
|
config.Logins = append(config.Logins[:idx], config.Logins[idx+1:]...)
|
||||||
|
|
||||||
return SaveConfig()
|
return saveConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLogin save a login to config
|
||||||
|
func AddLogin(login *Login) error {
|
||||||
|
if err := loadConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// save login to global var
|
||||||
|
config.Logins = append(config.Logins, *login)
|
||||||
|
|
||||||
|
// save login to config file
|
||||||
|
return saveConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client returns a client to operate Gitea API
|
// Client returns a client to operate Gitea API
|
||||||
|
@ -138,65 +178,3 @@ func (l *Login) GetSSHHost() string {
|
||||||
|
|
||||||
return u.Hostname()
|
return u.Hostname()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindSSHKey retrieves the ssh keys registered in gitea, and tries to find
|
|
||||||
// a matching private key in ~/.ssh/. If no match is found, path is empty.
|
|
||||||
func (l *Login) FindSSHKey() (string, error) {
|
|
||||||
// get keys registered on gitea instance
|
|
||||||
keys, _, err := l.Client().ListMyPublicKeys(gitea.ListPublicKeysOptions{})
|
|
||||||
if err != nil || len(keys) == 0 {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// enumerate ~/.ssh/*.pub files
|
|
||||||
glob, err := utils.AbsPathWithExpansion("~/.ssh/*.pub")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
localPubkeyPaths, err := filepath.Glob(glob)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse each local key with present privkey & compare fingerprints to online keys
|
|
||||||
for _, pubkeyPath := range localPubkeyPaths {
|
|
||||||
var pubkeyFile []byte
|
|
||||||
pubkeyFile, err = ioutil.ReadFile(pubkeyPath)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields := strings.Split(string(pubkeyFile), " ")
|
|
||||||
if len(fields) < 2 { // first word is key type, second word is key material
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var keymaterial []byte
|
|
||||||
keymaterial, err = base64.StdEncoding.DecodeString(fields[1])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubkey ssh.PublicKey
|
|
||||||
pubkey, err = ssh.ParsePublicKey(keymaterial)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
privkeyPath := strings.TrimSuffix(pubkeyPath, ".pub")
|
|
||||||
var exists bool
|
|
||||||
exists, err = utils.FileExist(privkeyPath)
|
|
||||||
if err != nil || !exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// if pubkey fingerprints match, return path to corresponding privkey.
|
|
||||||
fingerprint := ssh.FingerprintSHA256(pubkey)
|
|
||||||
for _, key := range keys {
|
|
||||||
if fingerprint == key.Fingerprint {
|
|
||||||
return privkeyPath, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/tea/modules/config"
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +28,7 @@ func CreateLogin() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err := config.GenerateLoginName(giteaURL, "")
|
name, err := task.GenerateLoginName(giteaURL, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -87,5 +87,5 @@ func CreateLogin() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.AddLogin(name, token, user, passwd, sshKey, giteaURL, insecure)
|
return task.CreateLogin(name, token, user, passwd, sshKey, giteaURL, insecure)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,35 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package config
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
"code.gitea.io/tea/modules/utils"
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddLogin add login to config ( global var & file)
|
// CreateLogin create a login to be stored in config
|
||||||
func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool) error {
|
func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool) error {
|
||||||
// checks ...
|
// checks ...
|
||||||
// ... if we have a url
|
// ... if we have a url
|
||||||
if len(giteaURL) == 0 {
|
if len(giteaURL) == 0 {
|
||||||
log.Fatal("You have to input Gitea server URL")
|
log.Fatal("You have to input Gitea server URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := LoadConfig()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, l := range Config.Logins {
|
|
||||||
// ... if there already exist a login with same name
|
// ... if there already exist a login with same name
|
||||||
if strings.ToLower(l.Name) == strings.ToLower(name) {
|
if login := config.GetLoginByName(name); login != nil {
|
||||||
return fmt.Errorf("login name '%s' has already been used", l.Name)
|
return fmt.Errorf("login name '%s' has already been used", login.Name)
|
||||||
}
|
}
|
||||||
// ... if we already use this token
|
// ... if we already use this token
|
||||||
if l.Token == token {
|
if login := config.GetLoginByToken(token); login != nil {
|
||||||
return fmt.Errorf("token already been used, delete login '%s' first", l.Name)
|
return fmt.Errorf("token already been used, delete login '%s' first", login.Name)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// .. if we have enough information to authenticate
|
// .. if we have enough information to authenticate
|
||||||
|
@ -55,7 +48,7 @@ func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool)
|
||||||
log.Fatal("Unable to parse URL", err)
|
log.Fatal("Unable to parse URL", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
login := Login{
|
login := config.Login{
|
||||||
Name: name,
|
Name: name,
|
||||||
URL: serverURL.String(),
|
URL: serverURL.String(),
|
||||||
Token: token,
|
Token: token,
|
||||||
|
@ -64,15 +57,17 @@ func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool)
|
||||||
Created: time.Now().Unix(),
|
Created: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := login.Client()
|
||||||
|
|
||||||
if len(token) == 0 {
|
if len(token) == 0 {
|
||||||
login.Token, err = GenerateToken(login.Client(), user, passwd)
|
login.Token, err = generateToken(client, user, passwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if authentication works and get user info
|
// Verify if authentication works and get user info
|
||||||
u, _, err := login.Client().GetMyUserInfo()
|
u, _, err := client.GetMyUserInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -90,17 +85,13 @@ func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool)
|
||||||
login.SSHHost = serverURL.Hostname()
|
login.SSHHost = serverURL.Hostname()
|
||||||
|
|
||||||
if len(sshKey) == 0 {
|
if len(sshKey) == 0 {
|
||||||
login.SSHKey, err = login.FindSSHKey()
|
login.SSHKey, err = findSSHKey(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Warning: problem while finding a SSH key: %s\n", err)
|
fmt.Printf("Warning: problem while finding a SSH key: %s\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// save login to global var
|
err = config.AddLogin(&login)
|
||||||
Config.Logins = append(Config.Logins, login)
|
|
||||||
|
|
||||||
// save login to config file
|
|
||||||
err = SaveConfig()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -110,8 +101,8 @@ func AddLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken creates a new token when given BasicAuth credentials
|
// generateToken creates a new token when given BasicAuth credentials
|
||||||
func GenerateToken(client *gitea.Client, user, pass string) (string, error) {
|
func generateToken(client *gitea.Client, user, pass string) (string, error) {
|
||||||
gitea.SetBasicAuth(user, pass)(client)
|
gitea.SetBasicAuth(user, pass)(client)
|
||||||
|
|
||||||
host, _ := os.Hostname()
|
host, _ := os.Hostname()
|
||||||
|
@ -131,3 +122,21 @@ func GenerateToken(client *gitea.Client, user, pass string) (string, error) {
|
||||||
t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{Name: tokenName})
|
t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{Name: tokenName})
|
||||||
return t.Token, err
|
return t.Token, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateLoginName generates a name string based on instance URL & adds username if the result is not unique
|
||||||
|
func GenerateLoginName(url, user string) (string, error) {
|
||||||
|
parsedURL, err := utils.NormalizeURL(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name := parsedURL.Host
|
||||||
|
|
||||||
|
// append user name if login name already exists
|
||||||
|
if len(user) != 0 {
|
||||||
|
if login := config.GetLoginByName(name); login != nil {
|
||||||
|
return name + "_" + user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, nil
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// findSSHKey retrieves the ssh keys registered in gitea, and tries to find
|
||||||
|
// a matching private key in ~/.ssh/. If no match is found, path is empty.
|
||||||
|
func findSSHKey(client *gitea.Client) (string, error) {
|
||||||
|
// get keys registered on gitea instance
|
||||||
|
keys, _, err := client.ListMyPublicKeys(gitea.ListPublicKeysOptions{})
|
||||||
|
if err != nil || len(keys) == 0 {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// enumerate ~/.ssh/*.pub files
|
||||||
|
glob, err := utils.AbsPathWithExpansion("~/.ssh/*.pub")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
localPubkeyPaths, err := filepath.Glob(glob)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse each local key with present privkey & compare fingerprints to online keys
|
||||||
|
for _, pubkeyPath := range localPubkeyPaths {
|
||||||
|
var pubkeyFile []byte
|
||||||
|
pubkeyFile, err = ioutil.ReadFile(pubkeyPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Split(string(pubkeyFile), " ")
|
||||||
|
if len(fields) < 2 { // first word is key type, second word is key material
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var keymaterial []byte
|
||||||
|
keymaterial, err = base64.StdEncoding.DecodeString(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubkey ssh.PublicKey
|
||||||
|
pubkey, err = ssh.ParsePublicKey(keymaterial)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
privkeyPath := strings.TrimSuffix(pubkeyPath, ".pub")
|
||||||
|
var exists bool
|
||||||
|
exists, err = utils.FileExist(privkeyPath)
|
||||||
|
if err != nil || !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// if pubkey fingerprints match, return path to corresponding privkey.
|
||||||
|
fingerprint := ssh.FingerprintSHA256(pubkey)
|
||||||
|
for _, key := range keys {
|
||||||
|
if fingerprint == key.Fingerprint {
|
||||||
|
return privkeyPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
Loading…
Reference in New Issue