@ -21,7 +21,8 @@ import (
const (
apiURL = "https://api.bitbucket.org/2.0"
// Switch to API v2.0 when the Atlassian platform services are fully available in Bitbucket
legacyAPIURL = "https://api.bitbucket.org/1.0"
// Bitbucket requires this scope to access '/user' API endpoints.
scopeAccount = "account"
// Bitbucket requires this scope to access '/user/emails' API endpoints.
@ -33,21 +34,24 @@ const (
// Config holds configuration options for Bitbucket logins.
type Config struct {
ClientID string ` json:"clientID" `
ClientSecret string ` json:"clientSecret" `
RedirectURI string ` json:"redirectURI" `
Teams [ ] string ` json:"teams" `
ClientID string ` json:"clientID" `
ClientSecret string ` json:"clientSecret" `
RedirectURI string ` json:"redirectURI" `
Teams [ ] string ` json:"teams" `
IncludeTeamGroups bool ` json:"includeTeamGroups,omitempty" `
}
// Open returns a strategy for logging in through Bitbucket.
func ( c * Config ) Open ( id string , logger log . Logger ) ( connector . Connector , error ) {
func ( c * Config ) Open ( _ string , logger log . Logger ) ( connector . Connector , error ) {
b := bitbucketConnector {
redirectURI : c . RedirectURI ,
teams : c . Teams ,
clientID : c . ClientID ,
clientSecret : c . ClientSecret ,
apiURL : apiURL ,
logger : logger ,
redirectURI : c . RedirectURI ,
teams : c . Teams ,
clientID : c . ClientID ,
clientSecret : c . ClientSecret ,
includeTeamGroups : c . IncludeTeamGroups ,
apiURL : apiURL ,
legacyAPIURL : legacyAPIURL ,
logger : logger ,
}
return & b , nil
@ -71,10 +75,13 @@ type bitbucketConnector struct {
clientSecret string
logger log . Logger
apiURL string
legacyAPIURL string
// the following are used only for tests
hostName string
httpClient * http . Client
includeTeamGroups bool
}
// groupsRequired returns whether dex requires Bitbucket's 'team' scope.
@ -396,9 +403,39 @@ func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client)
}
}
if b . includeTeamGroups {
for _ , team := range teams {
teamGroups , err := b . userTeamGroups ( ctx , client , team )
if err != nil {
return nil , fmt . Errorf ( "bitbucket: %v" , err )
}
teams = append ( teams , teamGroups ... )
}
}
return teams , nil
}
type group struct {
Slug string ` json:"slug" `
}
func ( b * bitbucketConnector ) userTeamGroups ( ctx context . Context , client * http . Client , teamName string ) ( [ ] string , error ) {
var teamGroups [ ] string
apiURL := b . legacyAPIURL + "/groups/" + teamName
var response [ ] group
if err := get ( ctx , client , apiURL , & response ) ; err != nil {
return nil , fmt . Errorf ( "get user team %q groups: %v" , teamName , err )
}
for _ , group := range response {
teamGroups = append ( teamGroups , teamName + "/" + group . Slug )
}
return teamGroups , nil
}
// get creates a "GET `apiURL`" request with context, sends the request using
// the client, and decodes the resulting response body into v.
// Any errors encountered when building requests, sending requests, and