mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-30 16:55:25 +01:00 
			
		
		
		
	[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:
		| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								modules/task/login_ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								modules/task/login_ssh.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 6543
					6543