From 5c230cb81e399f12cc7a1c1688b73f549b12a5f0 Mon Sep 17 00:00:00 2001 From: Michael Muré Date: Sat, 15 Feb 2020 16:01:15 +0100 Subject: jira: rework to use the credential system + adapt to refactors --- bridge/jira/config.go | 166 +++++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 76 deletions(-) (limited to 'bridge/jira/config.go') diff --git a/bridge/jira/config.go b/bridge/jira/config.go index 077f258a..c4743448 100644 --- a/bridge/jira/config.go +++ b/bridge/jira/config.go @@ -1,15 +1,13 @@ package jira import ( - "encoding/json" "fmt" - "io/ioutil" - - "github.com/pkg/errors" "github.com/MichaelMure/git-bug/bridge/core" + "github.com/MichaelMure/git-bug/bridge/core/auth" "github.com/MichaelMure/git-bug/cache" "github.com/MichaelMure/git-bug/input" + "github.com/MichaelMure/git-bug/repository" ) const moreConfigText = ` @@ -28,32 +26,27 @@ after October 1st 2019 must use "TOKEN" authentication. You must create a user API token and the client will provide this along with your username with each request.` -// Configure sets up the bridge configuration -func (g *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) { - conf := make(core.Configuration) - conf[core.ConfigKeyTarget] = target +func (*Jira) ValidParams() map[string]interface{} { + return map[string]interface{}{ + "BaseURL": nil, + "Login": nil, + "CredPrefix": nil, + "Project": nil, + } +} +// Configure sets up the bridge configuration +func (j *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) { var err error - // if params.Token != "" || params.TokenStdin { - // return nil, fmt.Errorf( - // "JIRA session tokens are extremely short lived. We don't store them " + - // "in the configuration, so they are not valid for this bridge.") - // } - - if params.Owner != "" { - fmt.Println("warning: --owner is ineffective for a Jira bridge") - } - - serverURL := params.URL - if serverURL == "" { + baseURL := params.BaseURL + if baseURL == "" { // terminal prompt - serverURL, err = input.Prompt("JIRA server URL", "URL", input.Required) + baseURL, err = input.Prompt("JIRA server URL", "URL", input.Required, input.IsURL) if err != nil { return nil, err } } - conf[keyServer] = serverURL project := params.Project if project == "" { @@ -62,77 +55,56 @@ func (g *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core. return nil, err } } - conf[keyProject] = project fmt.Println(credTypeText) - credType, err := input.PromptChoice("Authentication mechanism", []string{"SESSION", "TOKEN"}) - if err != nil { - return nil, err - } - - switch credType { - case 1: - conf[keyCredentialsType] = "SESSION" - case 2: - conf[keyCredentialsType] = "TOKEN" - } - - fmt.Println("How would you like to store your JIRA login credentials?") - credTargetChoice, err := input.PromptChoice("Credential storage", []string{ - "sidecar JSON file: Your credentials will be stored in a JSON sidecar next" + - "to your git config. Note that it will contain your JIRA password in clear" + - "text.", - "git-config: Your credentials will be stored in the git config. Note that" + - "it will contain your JIRA password in clear text.", - "username in config, askpass: Your username will be stored in the git" + - "config. We will ask you for your password each time you execute the bridge.", - }) - if err != nil { - return nil, err - } - - username, err := input.Prompt("JIRA username", "username", input.Required) + credTypeInput, err := input.PromptChoice("Authentication mechanism", []string{"SESSION", "TOKEN"}) if err != nil { return nil, err } + credType := []string{"SESSION", "TOKEN"}[credTypeInput] - password, err := input.PromptPassword("Password", "password", input.Required) - if err != nil { - return nil, err - } + var login string + var cred auth.Credential - switch credTargetChoice { - case 1: - // TODO: a validator to see if the path is writable ? - credentialsFile, err := input.Prompt("Credentials file path", "path", input.Required) + switch { + case params.CredPrefix != "": + cred, err = auth.LoadWithPrefix(repo, params.CredPrefix) if err != nil { return nil, err } - conf[keyCredentialsFile] = credentialsFile - jsonData, err := json.Marshal(&SessionQuery{Username: username, Password: password}) - if err != nil { - return nil, err + l, ok := cred.GetMetadata(auth.MetaKeyLogin) + if !ok { + return nil, fmt.Errorf("credential doesn't have a login") + } + login = l + default: + login := params.Login + if login == "" { + // TODO: validate username + login, err = input.Prompt("JIRA login", "login", input.Required) + if err != nil { + return nil, err + } } - err = ioutil.WriteFile(credentialsFile, jsonData, 0644) + cred, err = promptCredOptions(repo, login, baseURL) if err != nil { - return nil, errors.Wrap( - err, fmt.Sprintf("Unable to write credentials to %s", credentialsFile)) + return nil, err } - case 2: - conf[keyUsername] = username - conf[keyPassword] = password - case 3: - conf[keyUsername] = username } - err = g.ValidateConfig(conf) + conf := make(core.Configuration) + conf[core.ConfigKeyTarget] = target + conf[confKeyBaseUrl] = baseURL + conf[confKeyProject] = project + conf[confKeyCredentialType] = credType + + err = j.ValidateConfig(conf) if err != nil { return nil, err } fmt.Printf("Attempting to login with credentials...\n") - client := NewClient(serverURL, nil) - err = client.Login(conf) + client, err := buildClient(nil, baseURL, credType, cred) if err != nil { return nil, err } @@ -144,7 +116,12 @@ func (g *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core. return nil, fmt.Errorf( "Project %s doesn't exist on %s, or authentication credentials for (%s)"+ " are invalid", - project, serverURL, username) + project, baseURL, login) + } + + err = core.FinishConfig(repo, metaKeyJiraLogin, login) + if err != nil { + return nil, err } fmt.Print(moreConfigText) @@ -159,9 +136,46 @@ func (*Jira) ValidateConfig(conf core.Configuration) error { return fmt.Errorf("unexpected target name: %v", v) } - if _, ok := conf[keyProject]; !ok { - return fmt.Errorf("missing %s key", keyProject) + if _, ok := conf[confKeyProject]; !ok { + return fmt.Errorf("missing %s key", confKeyProject) } return nil } + +func promptCredOptions(repo repository.RepoConfig, login, baseUrl string) (auth.Credential, error) { + creds, err := auth.List(repo, + auth.WithTarget(target), + auth.WithKind(auth.KindToken), + auth.WithMeta(auth.MetaKeyLogin, login), + auth.WithMeta(auth.MetaKeyBaseURL, baseUrl), + ) + if err != nil { + return nil, err + } + + cred, index, err := input.PromptCredential(target, "password", creds, []string{ + "enter my password", + "ask my password each time", + }) + switch { + case err != nil: + return nil, err + case cred != nil: + return cred, nil + case index == 0: + password, err := input.PromptPassword("Password", "password", input.Required) + if err != nil { + return nil, err + } + lp := auth.NewLoginPassword(target, login, password) + lp.SetMetadata(auth.MetaKeyLogin, login) + return lp, nil + case index == 1: + l := auth.NewLogin(target, login) + l.SetMetadata(auth.MetaKeyLogin, login) + return l, nil + default: + panic("missed case") + } +} -- cgit