Skip to content
Snippets Groups Projects
Commit d26e23c1 authored by Ed Tan's avatar Ed Tan
Browse files

Make suggested code changes

parent 2c024d8c
No related branches found
No related tags found
No related merge requests found
...@@ -14,7 +14,7 @@ The following is an example of a configuration for `examples/config-dev.yaml`: ...@@ -14,7 +14,7 @@ The following is an example of a configuration for `examples/config-dev.yaml`:
```yaml ```yaml
connectors: connectors:
- type: bitbucket - type: bitbucket-cloud
# Required field for connector id. # Required field for connector id.
id: bitbucket id: bitbucket
# Required field for connector name. # Required field for connector name.
...@@ -22,9 +22,11 @@ connectors: ...@@ -22,9 +22,11 @@ connectors:
config: config:
# Credentials can be string literals or pulled from the environment. # Credentials can be string literals or pulled from the environment.
clientID: $BITBUCKET_CLIENT_ID clientID: $BITBUCKET_CLIENT_ID
clientSecret: BITBUCKET_CLIENT_SECRET clientSecret: $BITBUCKET_CLIENT_SECRET
redirectURI: http://127.0.0.1:5556/dex/callback redirectURI: http://127.0.0.1:5556/dex/callback
# Optional teams, communicated through the "groups" scope. # Optional teams whitelist, communicated through the "groups" scope.
# If `teams` is omitted, all of the user's Bitbucket teams are returned when the groups scope is present.
# If `teams` is provided, this acts as a whitelist - only the user's Bitbucket teams that are in the configured `teams` below will go into the groups claim. Conversely, if the user is not in any of the configured `teams`, the user will not be authenticated.
teams: teams:
- my-team - my-team
``` ```
// Package bitbucket provides authentication strategies using Bitbucket. // Package bitbucketcloud provides authentication strategies using Bitbucket Cloud.
package bitbucket package bitbucketcloud
import ( import (
"context" "context"
...@@ -71,8 +71,7 @@ type bitbucketConnector struct { ...@@ -71,8 +71,7 @@ type bitbucketConnector struct {
clientID string clientID string
clientSecret string clientSecret string
logger logrus.FieldLogger logger logrus.FieldLogger
// apiURL defaults to "https://api.bitbucket.org/2.0" apiURL string
apiURL string
// the following are used only for tests // the following are used only for tests
hostName string hostName string
...@@ -294,11 +293,11 @@ func (b *bitbucketConnector) user(ctx context.Context, client *http.Client) (use ...@@ -294,11 +293,11 @@ func (b *bitbucketConnector) user(ctx context.Context, client *http.Client) (use
) )
if err = get(ctx, client, b.apiURL+"/user", &u); err != nil { if err = get(ctx, client, b.apiURL+"/user", &u); err != nil {
return u, err return user{}, err
} }
if u.Email, err = b.userEmail(ctx, client); err != nil { if u.Email, err = b.userEmail(ctx, client); err != nil {
return u, err return user{}, err
} }
return u, nil return u, nil
...@@ -309,7 +308,6 @@ func (b *bitbucketConnector) user(ctx context.Context, client *http.Client) (use ...@@ -309,7 +308,6 @@ func (b *bitbucketConnector) user(ctx context.Context, client *http.Client) (use
type userEmail struct { type userEmail struct {
IsPrimary bool `json:"is_primary"` IsPrimary bool `json:"is_primary"`
IsConfirmed bool `json:"is_confirmed"` IsConfirmed bool `json:"is_confirmed"`
Type string `json:"type"`
Email string `json:"email"` Email string `json:"email"`
} }
...@@ -356,7 +354,7 @@ func (b *bitbucketConnector) getGroups(ctx context.Context, client *http.Client, ...@@ -356,7 +354,7 @@ func (b *bitbucketConnector) getGroups(ctx context.Context, client *http.Client,
if len(b.teams) > 0 { if len(b.teams) > 0 {
filteredTeams := filterTeams(bitbucketTeams, b.teams) filteredTeams := filterTeams(bitbucketTeams, b.teams)
if len(filteredTeams) == 0 { if len(filteredTeams) == 0 {
return nil, fmt.Errorf("bitbucket: user %q not in required teams", userLogin) return nil, fmt.Errorf("bitbucket: user %q is not in any of the required teams", userLogin)
} }
return filteredTeams, nil return filteredTeams, nil
} else if groupScope { } else if groupScope {
...@@ -367,23 +365,22 @@ func (b *bitbucketConnector) getGroups(ctx context.Context, client *http.Client, ...@@ -367,23 +365,22 @@ func (b *bitbucketConnector) getGroups(ctx context.Context, client *http.Client,
} }
// Filter the users' team memberships by 'teams' from config. // Filter the users' team memberships by 'teams' from config.
func filterTeams(userTeams, configTeams []string) (teams []string) { func filterTeams(userTeams, configTeams []string) []string {
teams := []string{}
teamFilter := make(map[string]struct{}) teamFilter := make(map[string]struct{})
for _, team := range configTeams { for _, team := range configTeams {
if _, ok := teamFilter[team]; !ok { teamFilter[team] = struct{}{}
teamFilter[team] = struct{}{}
}
} }
for _, team := range userTeams { for _, team := range userTeams {
if _, ok := teamFilter[team]; ok { if _, ok := teamFilter[team]; ok {
teams = append(teams, team) teams = append(teams, team)
} }
} }
return return teams
} }
type team struct { type team struct {
Username string `json:"username"` // Username is actually the team name Name string `json:"username"` // The "username" from Bitbucket Cloud is actually the team name here
} }
type userTeamsResponse struct { type userTeamsResponse struct {
...@@ -392,7 +389,10 @@ type userTeamsResponse struct { ...@@ -392,7 +389,10 @@ type userTeamsResponse struct {
} }
func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client) ([]string, error) { func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client) ([]string, error) {
apiURL, teams := b.apiURL+"/teams?role=member", []string{}
var teams []string
apiURL := b.apiURL + "/teams?role=member"
for { for {
// https://developer.atlassian.com/bitbucket/api/2/reference/resource/teams // https://developer.atlassian.com/bitbucket/api/2/reference/resource/teams
var response userTeamsResponse var response userTeamsResponse
...@@ -402,7 +402,7 @@ func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client) ...@@ -402,7 +402,7 @@ func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client)
} }
for _, team := range response.Values { for _, team := range response.Values {
teams = append(teams, team.Username) teams = append(teams, team.Name)
} }
if response.Next == nil { if response.Next == nil {
...@@ -432,13 +432,13 @@ func get(ctx context.Context, client *http.Client, apiURL string, v interface{}) ...@@ -432,13 +432,13 @@ func get(ctx context.Context, client *http.Client, apiURL string, v interface{})
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return fmt.Errorf("bitbucket: read body: %v", err) return fmt.Errorf("bitbucket: read body: %s: %v", resp.Status, err)
} }
return fmt.Errorf("%s: %s", resp.Status, body) return fmt.Errorf("%s: %s", resp.Status, body)
} }
if err := json.NewDecoder(resp.Body).Decode(v); err != nil { if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("failed to decode response: %v", err) return fmt.Errorf("bitbucket: failed to decode response: %v", err)
} }
return nil return nil
......
package bitbucket package bitbucketcloud
import ( import (
"context" "context"
...@@ -22,9 +22,9 @@ func TestUserGroups(t *testing.T) { ...@@ -22,9 +22,9 @@ func TestUserGroups(t *testing.T) {
PageLen: 10, PageLen: 10,
}, },
Values: []team{ Values: []team{
{Username: "team-1"}, {Name: "team-1"},
{Username: "team-2"}, {Name: "team-2"},
{Username: "team-3"}, {Name: "team-3"},
}, },
} }
...@@ -113,12 +113,12 @@ func newClient() *http.Client { ...@@ -113,12 +113,12 @@ func newClient() *http.Client {
func expectNil(t *testing.T, a interface{}) { func expectNil(t *testing.T, a interface{}) {
if a != nil { if a != nil {
t.Errorf("Expected %+v to equal nil", a) t.Fatalf("Expected %+v to equal nil", a)
} }
} }
func expectEquals(t *testing.T, a interface{}, b interface{}) { func expectEquals(t *testing.T, a interface{}, b interface{}) {
if !reflect.DeepEqual(a, b) { if !reflect.DeepEqual(a, b) {
t.Errorf("Expected %+v to equal %+v", a, b) t.Fatalf("Expected %+v to equal %+v", a, b)
} }
} }
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"github.com/dexidp/dex/connector" "github.com/dexidp/dex/connector"
"github.com/dexidp/dex/connector/authproxy" "github.com/dexidp/dex/connector/authproxy"
"github.com/dexidp/dex/connector/bitbucket" "github.com/dexidp/dex/connector/bitbucketcloud"
"github.com/dexidp/dex/connector/github" "github.com/dexidp/dex/connector/github"
"github.com/dexidp/dex/connector/gitlab" "github.com/dexidp/dex/connector/gitlab"
"github.com/dexidp/dex/connector/ldap" "github.com/dexidp/dex/connector/ldap"
...@@ -430,17 +430,17 @@ type ConnectorConfig interface { ...@@ -430,17 +430,17 @@ type ConnectorConfig interface {
// ConnectorsConfig variable provides an easy way to return a config struct // ConnectorsConfig variable provides an easy way to return a config struct
// depending on the connector type. // depending on the connector type.
var ConnectorsConfig = map[string]func() ConnectorConfig{ var ConnectorsConfig = map[string]func() ConnectorConfig{
"mockCallback": func() ConnectorConfig { return new(mock.CallbackConfig) }, "mockCallback": func() ConnectorConfig { return new(mock.CallbackConfig) },
"mockPassword": func() ConnectorConfig { return new(mock.PasswordConfig) }, "mockPassword": func() ConnectorConfig { return new(mock.PasswordConfig) },
"ldap": func() ConnectorConfig { return new(ldap.Config) }, "ldap": func() ConnectorConfig { return new(ldap.Config) },
"github": func() ConnectorConfig { return new(github.Config) }, "github": func() ConnectorConfig { return new(github.Config) },
"gitlab": func() ConnectorConfig { return new(gitlab.Config) }, "gitlab": func() ConnectorConfig { return new(gitlab.Config) },
"oidc": func() ConnectorConfig { return new(oidc.Config) }, "oidc": func() ConnectorConfig { return new(oidc.Config) },
"saml": func() ConnectorConfig { return new(saml.Config) }, "saml": func() ConnectorConfig { return new(saml.Config) },
"authproxy": func() ConnectorConfig { return new(authproxy.Config) }, "authproxy": func() ConnectorConfig { return new(authproxy.Config) },
"linkedin": func() ConnectorConfig { return new(linkedin.Config) }, "linkedin": func() ConnectorConfig { return new(linkedin.Config) },
"microsoft": func() ConnectorConfig { return new(microsoft.Config) }, "microsoft": func() ConnectorConfig { return new(microsoft.Config) },
"bitbucket": func() ConnectorConfig { return new(bitbucket.Config) }, "bitbucket-cloud": func() ConnectorConfig { return new(bitbucketcloud.Config) },
// Keep around for backwards compatibility. // Keep around for backwards compatibility.
"samlExperimental": func() ConnectorConfig { return new(saml.Config) }, "samlExperimental": func() ConnectorConfig { return new(saml.Config) },
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment