aboutsummaryrefslogtreecommitdiffstats
path: root/bridge/jira/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'bridge/jira/config.go')
-rw-r--r--bridge/jira/config.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/bridge/jira/config.go b/bridge/jira/config.go
new file mode 100644
index 00000000..406bed31
--- /dev/null
+++ b/bridge/jira/config.go
@@ -0,0 +1,232 @@
+package jira
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/bridge/core"
+ "github.com/MichaelMure/git-bug/cache"
+ "github.com/MichaelMure/git-bug/input"
+)
+
+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
+`
+
+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
+request.
+
+Which authentication mechanism should this bridge use?
+[1]: SESSION
+[2]: TOKEN
+`
+const credentialsText = `
+How would you like to store your JIRA login credentials?
+[1]: 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.
+[2]: git-config: Your credentials will be stored in the git config. Note that
+ it will contain your JIRA password in clear text.
+[3]: 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.
+`
+
+// Configure sets up the bridge configuration
+func (g *Jira) Configure(repo *cache.RepoCache, params core.BridgeParams) (core.Configuration, error) {
+ conf := make(core.Configuration)
+ var err error
+ var url string
+ var project string
+ var credentialsFile string
+ var username string
+ var password string
+ var serverURL string
+
+ // 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 != "" {
+ return nil, fmt.Errorf("owner doesn't make sense for jira")
+ }
+
+ serverURL = params.URL
+ if url == "" {
+ // terminal prompt
+ serverURL, err = prompt("JIRA server URL", "URL")
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ project = params.Project
+ if project == "" {
+ project, err = prompt("JIRA project key", "project")
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ credType, err := promptOptions(credTypeText, 1, 2)
+ if err != nil {
+ return nil, err
+ }
+
+ choice, err := promptOptions(credentialsText, 1, 3)
+ if err != nil {
+ return nil, err
+ }
+
+ if choice == 1 {
+ credentialsFile, err = prompt("Credentials file path", "path")
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ username, err = prompt("JIRA username", "username")
+ if err != nil {
+ return nil, err
+ }
+
+ password, err = input.PromptPassword("Password", "password", input.Required)
+ if err != nil {
+ return nil, err
+ }
+
+ jsonData, err := json.Marshal(
+ &SessionQuery{Username: username, Password: password})
+ if err != nil {
+ return nil, err
+ }
+
+ conf[core.ConfigKeyTarget] = target
+ conf[keyServer] = serverURL
+ conf[keyProject] = project
+
+ switch credType {
+ case 1:
+ conf[keyCredentialsType] = "SESSION"
+ case 2:
+ conf[keyCredentialsType] = "TOKEN"
+ }
+
+ switch choice {
+ case 1:
+ conf[keyCredentialsFile] = credentialsFile
+ err = ioutil.WriteFile(credentialsFile, jsonData, 0644)
+ if err != nil {
+ return nil, errors.Wrap(
+ err, fmt.Sprintf("Unable to write credentials to %s", credentialsFile))
+ }
+ case 2:
+ conf[keyUsername] = username
+ conf[keyPassword] = password
+ case 3:
+ conf[keyUsername] = username
+ }
+
+ err = g.ValidateConfig(conf)
+ if err != nil {
+ return nil, err
+ }
+
+ 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)
+ }
+
+ fmt.Print(moreConfigText)
+ return conf, nil
+}
+
+// ValidateConfig returns true if all required keys are present
+func (*Jira) ValidateConfig(conf core.Configuration) error {
+ if v, ok := conf[core.ConfigKeyTarget]; !ok {
+ return fmt.Errorf("missing %s key", core.ConfigKeyTarget)
+ } 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
+}
+
+func promptOptions(description string, minVal, maxVal int) (int, error) {
+ fmt.Print(description)
+ for {
+ fmt.Print("Select option: ")
+
+ line, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ fmt.Println()
+ if err != nil {
+ return -1, err
+ }
+
+ line = strings.TrimRight(line, "\n")
+
+ index, err := strconv.Atoi(line)
+ if err != nil {
+ fmt.Println("invalid input")
+ continue
+ }
+ if index < minVal || index > maxVal {
+ fmt.Println("invalid choice")
+ continue
+ }
+
+ return index, nil
+ }
+}
+
+func prompt(description, name string) (string, error) {
+ for {
+ fmt.Printf("%s: ", description)
+
+ line, err := bufio.NewReader(os.Stdin).ReadString('\n')
+ if err != nil {
+ return "", err
+ }
+
+ line = strings.TrimRight(line, "\n")
+ if line == "" {
+ fmt.Printf("%s is empty\n", name)
+ continue
+ }
+
+ return line, nil
+ }
+}