2019-11-07 19:01:08 +03:00
|
|
|
package jira
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
|
|
|
"github.com/MichaelMure/git-bug/bridge/core"
|
2019-12-18 18:49:49 +03:00
|
|
|
"github.com/MichaelMure/git-bug/cache"
|
2019-11-23 09:34:19 +03:00
|
|
|
"github.com/MichaelMure/git-bug/input"
|
2019-11-07 19:01:08 +03:00
|
|
|
)
|
|
|
|
|
2019-11-23 09:34:19 +03:00
|
|
|
const moreConfigText = `
|
|
|
|
NOTE: There are a few optional configuration values that you can additionally
|
|
|
|
set in your git configuration to influence the behavior of the bridge. Please
|
|
|
|
see the notes at:
|
|
|
|
https://github.com/MichaelMure/git-bug/blob/master/doc/jira_bridge.md
|
|
|
|
`
|
|
|
|
|
2019-12-05 09:50:35 +03:00
|
|
|
const credTypeText = `
|
|
|
|
JIRA has recently altered it's authentication strategies. Servers deployed
|
|
|
|
prior to October 1st 2019 must use "SESSION" authentication, whereby the REST
|
|
|
|
client logs in with an actual username and password, is assigned a session, and
|
|
|
|
passes the session cookie with each request. JIRA Cloud and servers deployed
|
|
|
|
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
|
2020-02-09 22:52:19 +03:00
|
|
|
request.`
|
2019-12-05 09:50:35 +03:00
|
|
|
|
2019-11-07 19:01:08 +03:00
|
|
|
// Configure sets up the bridge configuration
|
2020-02-09 22:08:12 +03:00
|
|
|
func (g *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) {
|
2019-11-07 19:01:08 +03:00
|
|
|
conf := make(core.Configuration)
|
2020-02-09 22:52:19 +03:00
|
|
|
conf[core.ConfigKeyTarget] = target
|
|
|
|
|
2019-11-07 19:01:08 +03:00
|
|
|
var err error
|
|
|
|
|
2019-12-18 18:49:49 +03:00
|
|
|
// 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.")
|
|
|
|
// }
|
2019-11-07 19:01:08 +03:00
|
|
|
|
|
|
|
if params.Owner != "" {
|
2020-02-09 22:52:19 +03:00
|
|
|
fmt.Println("warning: --owner is ineffective for a Jira bridge")
|
2019-11-07 19:01:08 +03:00
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
serverURL := params.URL
|
|
|
|
if serverURL == "" {
|
2019-11-07 19:01:08 +03:00
|
|
|
// terminal prompt
|
2020-02-09 22:52:19 +03:00
|
|
|
serverURL, err = input.Prompt("JIRA server URL", "URL", input.Required)
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2020-02-09 22:52:19 +03:00
|
|
|
conf[keyServer] = serverURL
|
2019-11-07 19:01:08 +03:00
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
project := params.Project
|
2019-11-07 19:01:08 +03:00
|
|
|
if project == "" {
|
2020-02-09 22:52:19 +03:00
|
|
|
project, err = input.Prompt("JIRA project key", "project", input.Required)
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2020-02-09 22:52:19 +03:00
|
|
|
conf[keyProject] = project
|
2019-11-07 19:01:08 +03:00
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
fmt.Println(credTypeText)
|
|
|
|
credType, err := input.PromptChoice("Authentication mechanism", []string{"SESSION", "TOKEN"})
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
switch credType {
|
|
|
|
case 1:
|
|
|
|
conf[keyCredentialsType] = "SESSION"
|
|
|
|
case 2:
|
|
|
|
conf[keyCredentialsType] = "TOKEN"
|
2019-11-07 19:01:08 +03:00
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
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.",
|
|
|
|
})
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
username, err := input.Prompt("JIRA username", "username", input.Required)
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
password, err := input.PromptPassword("Password", "password", input.Required)
|
2019-11-07 19:01:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-02-09 22:52:19 +03:00
|
|
|
switch credTargetChoice {
|
2019-11-23 09:34:19 +03:00
|
|
|
case 1:
|
2020-02-09 22:52:19 +03:00
|
|
|
// TODO: a validator to see if the path is writable ?
|
|
|
|
credentialsFile, err := input.Prompt("Credentials file path", "path", input.Required)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-11-07 19:01:08 +03:00
|
|
|
conf[keyCredentialsFile] = credentialsFile
|
2020-02-09 22:52:19 +03:00
|
|
|
jsonData, err := json.Marshal(&SessionQuery{Username: username, Password: password})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-11-07 19:01:08 +03:00
|
|
|
err = ioutil.WriteFile(credentialsFile, jsonData, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(
|
|
|
|
err, fmt.Sprintf("Unable to write credentials to %s", credentialsFile))
|
|
|
|
}
|
2019-11-23 09:34:19 +03:00
|
|
|
case 2:
|
2019-11-07 19:01:08 +03:00
|
|
|
conf[keyUsername] = username
|
|
|
|
conf[keyPassword] = password
|
2019-11-23 09:34:19 +03:00
|
|
|
case 3:
|
2019-11-07 19:01:08 +03:00
|
|
|
conf[keyUsername] = username
|
|
|
|
}
|
2019-11-23 09:34:19 +03:00
|
|
|
|
2019-11-07 19:01:08 +03:00
|
|
|
err = g.ValidateConfig(conf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-12-05 09:50:35 +03:00
|
|
|
fmt.Printf("Attempting to login with credentials...\n")
|
|
|
|
client := NewClient(serverURL, nil)
|
|
|
|
err = client.Login(conf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify access to the project with credentials
|
|
|
|
fmt.Printf("Checking project ...\n")
|
|
|
|
_, err = client.GetProject(project)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Project %s doesn't exist on %s, or authentication credentials for (%s)"+
|
|
|
|
" are invalid",
|
|
|
|
project, serverURL, username)
|
|
|
|
}
|
|
|
|
|
2019-11-23 09:34:19 +03:00
|
|
|
fmt.Print(moreConfigText)
|
2019-11-07 19:01:08 +03:00
|
|
|
return conf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateConfig returns true if all required keys are present
|
|
|
|
func (*Jira) ValidateConfig(conf core.Configuration) error {
|
2019-12-18 18:49:49 +03:00
|
|
|
if v, ok := conf[core.ConfigKeyTarget]; !ok {
|
|
|
|
return fmt.Errorf("missing %s key", core.ConfigKeyTarget)
|
2019-11-07 19:01:08 +03:00
|
|
|
} else if v != target {
|
|
|
|
return fmt.Errorf("unexpected target name: %v", v)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := conf[keyProject]; !ok {
|
|
|
|
return fmt.Errorf("missing %s key", keyProject)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|