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