diff --git a/authentication_test.go b/authentication_test.go index 431a7fdc..9235d080 100644 --- a/authentication_test.go +++ b/authentication_test.go @@ -133,7 +133,7 @@ func TestAuthenticationService_Authenticated_WithBasicAuthButNoUsername(t *testi } } -func TestAithenticationService_GetUserInfo_AccessForbidden_Fail(t *testing.T) { +func TestAuthenticationService_GetUserInfo_AccessForbidden_Fail(t *testing.T) { setup() defer teardown() testMux.HandleFunc("/rest/auth/1/session", func(w http.ResponseWriter, r *http.Request) { diff --git a/jira.go b/jira.go index 66046a45..a6e6b2e6 100644 --- a/jira.go +++ b/jira.go @@ -382,6 +382,42 @@ func (t *BasicAuthTransport) transport() http.RoundTripper { return http.DefaultTransport } +// PATAuthTransport is an http.RoundTripper that authenticates all requests +// using the Personal Access Token specified. +// See here for more info: https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html +type PATAuthTransport struct { + // Token is the key that was provided by Jira when creating the Personal Access Token. + Token string + + // Transport is the underlying HTTP transport to use when making requests. + // It will default to http.DefaultTransport if nil. + Transport http.RoundTripper +} + +// RoundTrip implements the RoundTripper interface. We just add the +// basic auth and return the RoundTripper for this transport type. +func (t *PATAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req2 := cloneRequest(req) // per RoundTripper contract + req2.Header.Set("Authorization", "Bearer "+t.Token) + return t.transport().RoundTrip(req2) +} + +// Client returns an *http.Client that makes requests that are authenticated +// using HTTP Basic Authentication. This is a nice little bit of sugar +// so we can just get the client instead of creating the client in the calling code. +// If it's necessary to send more information on client init, the calling code can +// always skip this and set the transport itself. +func (t *PATAuthTransport) Client() *http.Client { + return &http.Client{Transport: t} +} + +func (t *PATAuthTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} + // CookieAuthTransport is an http.RoundTripper that authenticates all requests // using Jira's cookie-based authentication. // diff --git a/jira_test.go b/jira_test.go index 9f34dd78..3aedbd64 100644 --- a/jira_test.go +++ b/jira_test.go @@ -631,3 +631,26 @@ func TestJWTAuthTransport_HeaderContainsJWT(t *testing.T) { jwtClient, _ := NewClient(jwtTransport.Client(), testServer.URL) jwtClient.Issue.Get("TEST-1", nil) } + +func TestPATAuthTransport_HeaderContainsAuth(t *testing.T) { + setup() + defer teardown() + + token := "shhh, it's a token" + + patTransport := &PATAuthTransport{ + Token: token, + } + + testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + val := r.Header.Get("Authorization") + expected := "Bearer " + token + if val != expected { + t.Errorf("request does not contain bearer token in the Authorization header.") + } + }) + + client, _ := NewClient(patTransport.Client(), testServer.URL) + client.User.GetSelf() + +}