Make suggested code changes
This commit is contained in:
parent
2c024d8caf
commit
d26e23c16f
4 changed files with 41 additions and 39 deletions
|
@ -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:
|
||||||
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
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// 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 {
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
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,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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{})
|
||||||
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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package bitbucket
|
package bitbucketcloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
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 (
|
||||||
|
|
||||||
"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 {
|
||||||
// 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) },
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue