1package providers 2 3import ( 4 "context" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "testing" 9 10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" 11 . "github.com/onsi/gomega" 12 "github.com/stretchr/testify/assert" 13) 14 15func testGitHubProvider(hostname string) *GitHubProvider { 16 p := NewGitHubProvider( 17 &ProviderData{ 18 ProviderName: "", 19 LoginURL: &url.URL{}, 20 RedeemURL: &url.URL{}, 21 ProfileURL: &url.URL{}, 22 ValidateURL: &url.URL{}, 23 Scope: ""}) 24 if hostname != "" { 25 updateURL(p.Data().LoginURL, hostname) 26 updateURL(p.Data().RedeemURL, hostname) 27 updateURL(p.Data().ProfileURL, hostname) 28 updateURL(p.Data().ValidateURL, hostname) 29 } 30 return p 31} 32 33func testGitHubBackend(payloads map[string][]string) *httptest.Server { 34 pathToQueryMap := map[string][]string{ 35 "/repo/oauth2-proxy/oauth2-proxy": {""}, 36 "/repos/oauth2-proxy/oauth2-proxy/collaborators/mbland": {""}, 37 "/user": {""}, 38 "/user/emails": {""}, 39 "/user/orgs": {"page=1&per_page=100", "page=2&per_page=100", "page=3&per_page=100"}, 40 } 41 42 return httptest.NewServer(http.HandlerFunc( 43 func(w http.ResponseWriter, r *http.Request) { 44 query, ok := pathToQueryMap[r.URL.Path] 45 validQuery := false 46 index := 0 47 for i, q := range query { 48 if q == r.URL.RawQuery { 49 validQuery = true 50 index = i 51 } 52 } 53 payload := []string{} 54 if ok && validQuery { 55 payload, ok = payloads[r.URL.Path] 56 } 57 if !ok { 58 w.WriteHeader(404) 59 } else if !validQuery { 60 w.WriteHeader(404) 61 } else if payload[index] == "" { 62 w.WriteHeader(204) 63 } else { 64 w.WriteHeader(200) 65 w.Write([]byte(payload[index])) 66 } 67 })) 68} 69 70func TestNewGitHubProvider(t *testing.T) { 71 g := NewWithT(t) 72 73 // Test that defaults are set when calling for a new provider with nothing set 74 providerData := NewGitHubProvider(&ProviderData{}).Data() 75 g.Expect(providerData.ProviderName).To(Equal("GitHub")) 76 g.Expect(providerData.LoginURL.String()).To(Equal("https://github.com/login/oauth/authorize")) 77 g.Expect(providerData.RedeemURL.String()).To(Equal("https://github.com/login/oauth/access_token")) 78 g.Expect(providerData.ProfileURL.String()).To(Equal("")) 79 g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.github.com/")) 80 g.Expect(providerData.Scope).To(Equal("user:email")) 81} 82 83func TestGitHubProviderOverrides(t *testing.T) { 84 p := NewGitHubProvider( 85 &ProviderData{ 86 LoginURL: &url.URL{ 87 Scheme: "https", 88 Host: "example.com", 89 Path: "/login/oauth/authorize"}, 90 RedeemURL: &url.URL{ 91 Scheme: "https", 92 Host: "example.com", 93 Path: "/login/oauth/access_token"}, 94 ValidateURL: &url.URL{ 95 Scheme: "https", 96 Host: "api.example.com", 97 Path: "/"}, 98 Scope: "profile"}) 99 assert.NotEqual(t, nil, p) 100 assert.Equal(t, "GitHub", p.Data().ProviderName) 101 assert.Equal(t, "https://example.com/login/oauth/authorize", 102 p.Data().LoginURL.String()) 103 assert.Equal(t, "https://example.com/login/oauth/access_token", 104 p.Data().RedeemURL.String()) 105 assert.Equal(t, "https://api.example.com/", 106 p.Data().ValidateURL.String()) 107 assert.Equal(t, "profile", p.Data().Scope) 108} 109 110func TestGitHubProvider_getEmail(t *testing.T) { 111 b := testGitHubBackend(map[string][]string{ 112 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 113 }) 114 defer b.Close() 115 116 bURL, _ := url.Parse(b.URL) 117 p := testGitHubProvider(bURL.Host) 118 119 session := CreateAuthorizedSession() 120 err := p.getEmail(context.Background(), session) 121 assert.NoError(t, err) 122 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 123} 124 125func TestGitHubProvider_getEmailNotVerified(t *testing.T) { 126 b := testGitHubBackend(map[string][]string{ 127 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": false, "primary": true} ]`}, 128 }) 129 defer b.Close() 130 131 bURL, _ := url.Parse(b.URL) 132 p := testGitHubProvider(bURL.Host) 133 134 session := CreateAuthorizedSession() 135 err := p.getEmail(context.Background(), session) 136 assert.NoError(t, err) 137 assert.Empty(t, session.Email) 138} 139 140func TestGitHubProvider_getEmailWithOrg(t *testing.T) { 141 b := testGitHubBackend(map[string][]string{ 142 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 143 "/user/orgs": { 144 `[ {"login":"testorg"} ]`, 145 `[ {"login":"testorg1"} ]`, 146 `[ ]`, 147 }, 148 }) 149 defer b.Close() 150 151 bURL, _ := url.Parse(b.URL) 152 p := testGitHubProvider(bURL.Host) 153 p.Org = "testorg1" 154 155 session := CreateAuthorizedSession() 156 err := p.getEmail(context.Background(), session) 157 assert.NoError(t, err) 158 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 159} 160 161func TestGitHubProvider_getEmailWithWriteAccessToPublicRepo(t *testing.T) { 162 b := testGitHubBackend(map[string][]string{ 163 "/repo/oauth2-proxy/oauth2-proxy": {`{"permissions": {"pull": true, "push": true}, "private": false}`}, 164 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 165 }) 166 defer b.Close() 167 168 bURL, _ := url.Parse(b.URL) 169 p := testGitHubProvider(bURL.Host) 170 p.SetRepo("oauth2-proxy/oauth2-proxy", "") 171 172 session := CreateAuthorizedSession() 173 err := p.getEmail(context.Background(), session) 174 assert.NoError(t, err) 175 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 176} 177 178func TestGitHubProvider_getEmailWithReadOnlyAccessToPrivateRepo(t *testing.T) { 179 b := testGitHubBackend(map[string][]string{ 180 "/repo/oauth2-proxy/oauth2-proxy": {`{"permissions": {"pull": true, "push": false}, "private": true}`}, 181 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 182 }) 183 defer b.Close() 184 185 bURL, _ := url.Parse(b.URL) 186 p := testGitHubProvider(bURL.Host) 187 p.SetRepo("oauth2-proxy/oauth2-proxy", "") 188 189 session := CreateAuthorizedSession() 190 err := p.getEmail(context.Background(), session) 191 assert.NoError(t, err) 192 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 193} 194 195func TestGitHubProvider_getEmailWithWriteAccessToPrivateRepo(t *testing.T) { 196 b := testGitHubBackend(map[string][]string{ 197 "/repo/oauth2-proxy/oauth2-proxy": {`{"permissions": {"pull": true, "push": true}, "private": true}`}, 198 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 199 }) 200 defer b.Close() 201 202 bURL, _ := url.Parse(b.URL) 203 p := testGitHubProvider(bURL.Host) 204 p.SetRepo("oauth2-proxy/oauth2-proxy", "") 205 206 session := CreateAuthorizedSession() 207 err := p.getEmail(context.Background(), session) 208 assert.NoError(t, err) 209 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 210} 211 212func TestGitHubProvider_getEmailWithNoAccessToPrivateRepo(t *testing.T) { 213 b := testGitHubBackend(map[string][]string{ 214 "/repo/oauth2-proxy/oauth2-proxy": {`{}`}, 215 }) 216 defer b.Close() 217 218 bURL, _ := url.Parse(b.URL) 219 p := testGitHubProvider(bURL.Host) 220 p.SetRepo("oauth2-proxy/oauth2-proxy", "") 221 222 session := CreateAuthorizedSession() 223 err := p.getEmail(context.Background(), session) 224 assert.NoError(t, err) 225 assert.Empty(t, session.Email) 226} 227 228func TestGitHubProvider_getEmailWithToken(t *testing.T) { 229 b := testGitHubBackend(map[string][]string{ 230 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 231 }) 232 defer b.Close() 233 234 bURL, _ := url.Parse(b.URL) 235 p := testGitHubProvider(bURL.Host) 236 p.SetRepo("oauth2-proxy/oauth2-proxy", "token") 237 238 session := CreateAuthorizedSession() 239 err := p.getEmail(context.Background(), session) 240 assert.NoError(t, err) 241 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 242} 243 244// Note that trying to trigger the "failed building request" case is not 245// practical, since the only way it can fail is if the URL fails to parse. 246func TestGitHubProvider_getEmailFailedRequest(t *testing.T) { 247 b := testGitHubBackend(map[string][]string{}) 248 defer b.Close() 249 250 bURL, _ := url.Parse(b.URL) 251 p := testGitHubProvider(bURL.Host) 252 253 // We'll trigger a request failure by using an unexpected access 254 // token. Alternatively, we could allow the parsing of the payload as 255 // JSON to fail. 256 session := &sessions.SessionState{AccessToken: "unexpected_access_token"} 257 err := p.getEmail(context.Background(), session) 258 assert.Error(t, err) 259 assert.Empty(t, session.Email) 260} 261 262func TestGitHubProvider_getEmailNotPresentInPayload(t *testing.T) { 263 b := testGitHubBackend(map[string][]string{ 264 "/user/emails": {`{"foo": "bar"}`}, 265 }) 266 defer b.Close() 267 268 bURL, _ := url.Parse(b.URL) 269 p := testGitHubProvider(bURL.Host) 270 271 session := CreateAuthorizedSession() 272 err := p.getEmail(context.Background(), session) 273 assert.Error(t, err) 274 assert.Empty(t, session.Email) 275} 276 277func TestGitHubProvider_getUser(t *testing.T) { 278 b := testGitHubBackend(map[string][]string{ 279 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 280 }) 281 defer b.Close() 282 283 bURL, _ := url.Parse(b.URL) 284 p := testGitHubProvider(bURL.Host) 285 286 session := CreateAuthorizedSession() 287 err := p.getUser(context.Background(), session) 288 assert.NoError(t, err) 289 assert.Equal(t, "mbland", session.User) 290} 291 292func TestGitHubProvider_getUserWithRepoAndToken(t *testing.T) { 293 b := testGitHubBackend(map[string][]string{ 294 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 295 "/repos/oauth2-proxy/oauth2-proxy/collaborators/mbland": {""}, 296 }) 297 defer b.Close() 298 299 bURL, _ := url.Parse(b.URL) 300 p := testGitHubProvider(bURL.Host) 301 p.SetRepo("oauth2-proxy/oauth2-proxy", "token") 302 303 session := CreateAuthorizedSession() 304 err := p.getUser(context.Background(), session) 305 assert.NoError(t, err) 306 assert.Equal(t, "mbland", session.User) 307} 308 309func TestGitHubProvider_getUserWithRepoAndTokenWithoutPushAccess(t *testing.T) { 310 b := testGitHubBackend(map[string][]string{}) 311 defer b.Close() 312 313 bURL, _ := url.Parse(b.URL) 314 p := testGitHubProvider(bURL.Host) 315 p.SetRepo("oauth2-proxy/oauth2-proxy", "token") 316 317 session := CreateAuthorizedSession() 318 err := p.getUser(context.Background(), session) 319 assert.Error(t, err) 320 assert.Empty(t, session.User) 321} 322 323func TestGitHubProvider_getEmailWithUsername(t *testing.T) { 324 b := testGitHubBackend(map[string][]string{ 325 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 326 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 327 }) 328 defer b.Close() 329 330 bURL, _ := url.Parse(b.URL) 331 p := testGitHubProvider(bURL.Host) 332 p.SetUsers([]string{"mbland", "octocat"}) 333 334 session := CreateAuthorizedSession() 335 err := p.getEmail(context.Background(), session) 336 assert.NoError(t, err) 337 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 338} 339 340func TestGitHubProvider_getEmailWithNotAllowedUsername(t *testing.T) { 341 b := testGitHubBackend(map[string][]string{ 342 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 343 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 344 }) 345 defer b.Close() 346 347 bURL, _ := url.Parse(b.URL) 348 p := testGitHubProvider(bURL.Host) 349 p.SetUsers([]string{"octocat"}) 350 351 session := CreateAuthorizedSession() 352 err := p.getEmail(context.Background(), session) 353 assert.Error(t, err) 354 assert.Empty(t, session.Email) 355} 356 357func TestGitHubProvider_getEmailWithUsernameAndNotBelongToOrg(t *testing.T) { 358 b := testGitHubBackend(map[string][]string{ 359 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 360 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 361 "/user/orgs": { 362 `[ {"login":"testorg"} ]`, 363 `[ ]`, 364 }, 365 }) 366 defer b.Close() 367 368 bURL, _ := url.Parse(b.URL) 369 p := testGitHubProvider(bURL.Host) 370 p.SetOrgTeam("not_belong_to", "") 371 p.SetUsers([]string{"mbland"}) 372 373 session := CreateAuthorizedSession() 374 err := p.getEmail(context.Background(), session) 375 assert.NoError(t, err) 376 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 377} 378 379func TestGitHubProvider_getEmailWithUsernameAndNoAccessToPrivateRepo(t *testing.T) { 380 b := testGitHubBackend(map[string][]string{ 381 "/user": {`{"email": "michael.bland@gsa.gov", "login": "mbland"}`}, 382 "/user/emails": {`[ {"email": "michael.bland@gsa.gov", "verified": true, "primary": true} ]`}, 383 "/repo/oauth2-proxy/oauth2-proxy": {`{}`}, 384 }) 385 defer b.Close() 386 387 bURL, _ := url.Parse(b.URL) 388 p := testGitHubProvider(bURL.Host) 389 p.SetRepo("oauth2-proxy/oauth2-proxy", "") 390 p.SetUsers([]string{"mbland"}) 391 392 session := CreateAuthorizedSession() 393 err := p.getEmail(context.Background(), session) 394 assert.NoError(t, err) 395 assert.Equal(t, "michael.bland@gsa.gov", session.Email) 396} 397