|
|
@ -6,8 +6,12 @@ import ( |
|
|
|
"encoding/json" |
|
|
|
"encoding/pem" |
|
|
|
"errors" |
|
|
|
log "github.com/Sirupsen/logrus" |
|
|
|
"github.com/andygrunwald/go-jira" |
|
|
|
"github.com/dghubble/oauth1" |
|
|
|
"github.com/matrix-org/go-neb/realms/jira/urls" |
|
|
|
"github.com/matrix-org/go-neb/types" |
|
|
|
"golang.org/x/net/context" |
|
|
|
"net/http" |
|
|
|
) |
|
|
|
|
|
|
@ -49,12 +53,22 @@ func (r *jiraRealm) Register() error { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
r.JIRAEndpoint = ju.Base |
|
|
|
|
|
|
|
// Check to see if JIRA endpoint is valid by pinging an endpoint
|
|
|
|
err = jiraClient(ju, "") |
|
|
|
cli, err := r.jiraClient(ju, "", true) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
info, err := jiraServerInfo(cli) |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
log.WithFields(log.Fields{ |
|
|
|
"jira_url": ju.Base, |
|
|
|
"title": info.ServerTitle, |
|
|
|
"version": info.Version, |
|
|
|
}).Print("Found JIRA endpoint") |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
@ -71,6 +85,47 @@ func (r *jiraRealm) AuthSession(id, userID, realmID string) types.AuthSession { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// jiraClient returns an authenticated jira.Client for the given userID. Returns an unauthenticated
|
|
|
|
// client if allowUnauth is true and no authenticated session is found, else returns an error.
|
|
|
|
func (r *jiraRealm) jiraClient(u urls.JIRAURL, userID string, allowUnauth bool) (*jira.Client, error) { |
|
|
|
// TODO: Check if user has an auth session. Requires access token+secret
|
|
|
|
hasAuthSession := false |
|
|
|
|
|
|
|
if hasAuthSession { |
|
|
|
// make an authenticated client
|
|
|
|
var cli *jira.Client |
|
|
|
|
|
|
|
auth := &oauth1.Config{ |
|
|
|
ConsumerKey: r.ConsumerKey, |
|
|
|
ConsumerSecret: r.ConsumerSecret, |
|
|
|
CallbackURL: u.Base + "realms/redirect/" + r.id, |
|
|
|
// TODO: In JIRA Cloud, the Authorization URL is only the Instance BASE_URL:
|
|
|
|
// https://BASE_URL.atlassian.net.
|
|
|
|
// It also does not require the + "/plugins/servlet/oauth/authorize"
|
|
|
|
// We should probably check the provided JIRA base URL to see if it is a cloud one
|
|
|
|
// then adjust accordingly.
|
|
|
|
Endpoint: oauth1.Endpoint{ |
|
|
|
RequestTokenURL: u.Base + "plugins/servlet/oauth/request-token", |
|
|
|
AuthorizeURL: u.Base + "plugins/servlet/oauth/authorize", |
|
|
|
AccessTokenURL: u.Base + "plugins/servlet/oauth/access-token", |
|
|
|
}, |
|
|
|
Signer: &oauth1.RSASigner{ |
|
|
|
PrivateKey: r.privateKey, |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
httpClient := auth.Client(context.TODO(), oauth1.NewToken("access_tokenTODO", "access_secretTODO")) |
|
|
|
cli, err := jira.NewClient(httpClient, u.Base) |
|
|
|
return cli, err |
|
|
|
} else if allowUnauth { |
|
|
|
// make an unauthenticated client
|
|
|
|
cli, err := jira.NewClient(nil, u.Base) |
|
|
|
return cli, err |
|
|
|
} else { |
|
|
|
return nil, errors.New("No authenticated session found for " + userID) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (r *jiraRealm) parsePrivateKey() error { |
|
|
|
pk, err := loadPrivateKey(r.PrivateKeyPEM) |
|
|
|
if err != nil { |
|
|
@ -113,8 +168,22 @@ func publicKeyAsPEM(pkey *rsa.PrivateKey) (string, error) { |
|
|
|
return string(pem.EncodeToMemory(&block)), nil |
|
|
|
} |
|
|
|
|
|
|
|
func jiraClient(u urls.JIRAURL, userID string) error { |
|
|
|
return nil |
|
|
|
// jiraServiceInfo is the HTTP response to JIRA_ENDPOINT/rest/api/2/serverInfo
|
|
|
|
type jiraServiceInfo struct { |
|
|
|
ServerTitle string `json:"serverTitle"` |
|
|
|
Version string `json:"version"` |
|
|
|
VersionNumbers []int `json:"versionNumbers"` |
|
|
|
BaseURL string `json:"baseUrl"` |
|
|
|
} |
|
|
|
|
|
|
|
func jiraServerInfo(cli *jira.Client) (*jiraServiceInfo, error) { |
|
|
|
var jsi jiraServiceInfo |
|
|
|
req, _ := cli.NewRequest("GET", "rest/api/2/serverInfo", nil) |
|
|
|
_, err := cli.Do(req, &jsi) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return &jsi, nil |
|
|
|
} |
|
|
|
|
|
|
|
func init() { |
|
|
|