git.iko.soy/typeable/oauth2-proxy
on remove-gap-headers help sign in
180 lines 4.4 kB history raw
1package providers
3import (
4 "context"
5 "net/url"
6 "strings"
8 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
13// BitbucketProvider represents an Bitbucket based Identity Provider
14type BitbucketProvider struct {
15 *ProviderData
16 Team string
17 Repository string
20var _ Provider = (*BitbucketProvider)(nil)
22const (
23 bitbucketProviderName = "Bitbucket"
24 bitbucketDefaultScope = "email"
27var (
28 // Default Login URL for Bitbucket.
29 // Pre-parsed URL of https://bitbucket.org/site/oauth2/authorize.
30 bitbucketDefaultLoginURL = &url.URL{
31 Scheme: "https",
32 Host: "bitbucket.org",
33 Path: "/site/oauth2/authorize",
34 }
36 // Default Redeem URL for Bitbucket.
37 // Pre-parsed URL of https://bitbucket.org/site/oauth2/access_token.
38 bitbucketDefaultRedeemURL = &url.URL{
39 Scheme: "https",
40 Host: "bitbucket.org",
41 Path: "/site/oauth2/access_token",
42 }
44 // Default Validation URL for Bitbucket.
45 // This simply returns the email of the authenticated user.
46 // Bitbucket does not have a Profile URL to use.
47 // Pre-parsed URL of https://api.bitbucket.org/2.0/user/emails.
48 bitbucketDefaultValidateURL = &url.URL{
49 Scheme: "https",
50 Host: "api.bitbucket.org",
51 Path: "/2.0/user/emails",
52 }
55// NewBitbucketProvider initiates a new BitbucketProvider
56func NewBitbucketProvider(p *ProviderData) *BitbucketProvider {
57 p.setProviderDefaults(providerDefaults{
58 name: bitbucketProviderName,
59 loginURL: bitbucketDefaultLoginURL,
60 redeemURL: bitbucketDefaultRedeemURL,
61 profileURL: nil,
62 validateURL: bitbucketDefaultValidateURL,
63 scope: bitbucketDefaultScope,
64 })
65 return &BitbucketProvider{ProviderData: p}
68// SetTeam defines the Bitbucket team the user must be part of
69func (p *BitbucketProvider) SetTeam(team string) {
70 p.Team = team
71 if !strings.Contains(p.Scope, "team") {
72 p.Scope += " team"
73 }
76// SetRepository defines the repository the user must have access to
77func (p *BitbucketProvider) SetRepository(repository string) {
78 p.Repository = repository
79 if !strings.Contains(p.Scope, "repository") {
80 p.Scope += " repository"
81 }
84// GetEmailAddress returns the email of the authenticated user
85func (p *BitbucketProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
87 var emails struct {
88 Values []struct {
89 Email string `json:"email"`
90 Primary bool `json:"is_primary"`
91 }
92 }
93 var teams struct {
94 Values []struct {
95 Name string `json:"username"`
96 }
97 }
98 var repositories struct {
99 Values []struct {
100 FullName string `json:"full_name"`
101 }
102 }
104 requestURL := p.ValidateURL.String() + "?access_token=" + s.AccessToken
105 err := requests.New(requestURL).
106 WithContext(ctx).
107 Do().
108 UnmarshalInto(&emails)
109 if err != nil {
110 logger.Errorf("failed making request: %v", err)
111 return "", err
112 }
114 if p.Team != "" {
115 teamURL := &url.URL{}
116 *teamURL = *p.ValidateURL
117 teamURL.Path = "/2.0/teams"
119 requestURL := teamURL.String() + "?role=member&access_token=" + s.AccessToken
121 err := requests.New(requestURL).
122 WithContext(ctx).
123 Do().
124 UnmarshalInto(&teams)
125 if err != nil {
126 logger.Errorf("failed requesting teams membership: %v", err)
127 return "", err
128 }
129 var found = false
130 for _, team := range teams.Values {
131 if p.Team == team.Name {
132 found = true
133 break
134 }
135 }
136 if !found {
137 logger.Error("team membership test failed, access denied")
138 return "", nil
139 }
140 }
142 if p.Repository != "" {
143 repositoriesURL := &url.URL{}
144 *repositoriesURL = *p.ValidateURL
145 repositoriesURL.Path = "/2.0/repositories/" + strings.Split(p.Repository, "/")[0]
147 requestURL := repositoriesURL.String() + "?role=contributor" +
148 "&q=full_name=" + url.QueryEscape("\""+p.Repository+"\"") +
149 "&access_token=" + s.AccessToken
151 err := requests.New(requestURL).
152 WithContext(ctx).
153 Do().
154 UnmarshalInto(&repositories)
155 if err != nil {
156 logger.Errorf("failed checking repository access: %v", err)
157 return "", err
158 }
160 var found = false
161 for _, repository := range repositories.Values {
162 if p.Repository == repository.FullName {
163 found = true
164 break
165 }
166 }
167 if !found {
168 logger.Error("repository access test failed, access denied")
169 return "", nil
170 }
171 }
173 for _, email := range emails.Values {
174 if email.Primary {
175 return email.Email, nil
176 }
177 }
179 return "", nil