mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2024-11-28 09:08:44 +02:00
e642daef4e
Co-authored-by: Henry Jenkins <henry@henryjenkins.name>
252 lines
8.3 KiB
Go
252 lines
8.3 KiB
Go
package providers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
admin "google.golang.org/api/admin/directory/v1"
|
|
option "google.golang.org/api/option"
|
|
)
|
|
|
|
func newRedeemServer(body []byte) (*url.URL, *httptest.Server) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
rw.Write(body)
|
|
}))
|
|
u, _ := url.Parse(s.URL)
|
|
return u, s
|
|
}
|
|
|
|
func newGoogleProvider() *GoogleProvider {
|
|
return NewGoogleProvider(
|
|
&ProviderData{
|
|
ProviderName: "",
|
|
LoginURL: &url.URL{},
|
|
RedeemURL: &url.URL{},
|
|
ProfileURL: &url.URL{},
|
|
ValidateURL: &url.URL{},
|
|
Scope: ""})
|
|
}
|
|
|
|
func TestGoogleProviderDefaults(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
assert.NotEqual(t, nil, p)
|
|
assert.Equal(t, "Google", p.Data().ProviderName)
|
|
assert.Equal(t, "https://accounts.google.com/o/oauth2/auth?access_type=offline",
|
|
p.Data().LoginURL.String())
|
|
assert.Equal(t, "https://www.googleapis.com/oauth2/v3/token",
|
|
p.Data().RedeemURL.String())
|
|
assert.Equal(t, "https://www.googleapis.com/oauth2/v1/tokeninfo",
|
|
p.Data().ValidateURL.String())
|
|
assert.Equal(t, "", p.Data().ProfileURL.String())
|
|
assert.Equal(t, "profile email", p.Data().Scope)
|
|
}
|
|
|
|
func TestGoogleProviderOverrides(t *testing.T) {
|
|
p := NewGoogleProvider(
|
|
&ProviderData{
|
|
LoginURL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/oauth/auth"},
|
|
RedeemURL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/oauth/token"},
|
|
ProfileURL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/oauth/profile"},
|
|
ValidateURL: &url.URL{
|
|
Scheme: "https",
|
|
Host: "example.com",
|
|
Path: "/oauth/tokeninfo"},
|
|
Scope: "profile"})
|
|
assert.NotEqual(t, nil, p)
|
|
assert.Equal(t, "Google", p.Data().ProviderName)
|
|
assert.Equal(t, "https://example.com/oauth/auth",
|
|
p.Data().LoginURL.String())
|
|
assert.Equal(t, "https://example.com/oauth/token",
|
|
p.Data().RedeemURL.String())
|
|
assert.Equal(t, "https://example.com/oauth/profile",
|
|
p.Data().ProfileURL.String())
|
|
assert.Equal(t, "https://example.com/oauth/tokeninfo",
|
|
p.Data().ValidateURL.String())
|
|
assert.Equal(t, "profile", p.Data().Scope)
|
|
}
|
|
|
|
type redeemResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
ExpiresIn int64 `json:"expires_in"`
|
|
IDToken string `json:"id_token"`
|
|
}
|
|
|
|
func TestGoogleProviderGetEmailAddress(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
body, err := json.Marshal(redeemResponse{
|
|
AccessToken: "a1234",
|
|
ExpiresIn: 10,
|
|
RefreshToken: "refresh12345",
|
|
IDToken: "ignored prefix." + base64.URLEncoding.EncodeToString([]byte(`{"email": "michael.bland@gsa.gov", "email_verified":true}`)),
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
var server *httptest.Server
|
|
p.RedeemURL, server = newRedeemServer(body)
|
|
defer server.Close()
|
|
|
|
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, session, nil)
|
|
assert.Equal(t, "michael.bland@gsa.gov", session.Email)
|
|
assert.Equal(t, "a1234", session.AccessToken)
|
|
assert.Equal(t, "refresh12345", session.RefreshToken)
|
|
}
|
|
|
|
func TestGoogleProviderValidateGroup(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
p.GroupValidator = func(email string) bool {
|
|
return email == "michael.bland@gsa.gov"
|
|
}
|
|
assert.Equal(t, true, p.ValidateGroup("michael.bland@gsa.gov"))
|
|
p.GroupValidator = func(email string) bool {
|
|
return email != "michael.bland@gsa.gov"
|
|
}
|
|
assert.Equal(t, false, p.ValidateGroup("michael.bland@gsa.gov"))
|
|
}
|
|
|
|
func TestGoogleProviderWithoutValidateGroup(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
assert.Equal(t, true, p.ValidateGroup("michael.bland@gsa.gov"))
|
|
}
|
|
|
|
//
|
|
func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
body, err := json.Marshal(redeemResponse{
|
|
AccessToken: "a1234",
|
|
IDToken: "ignored prefix." + `{"email": "michael.bland@gsa.gov"}`,
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
var server *httptest.Server
|
|
p.RedeemURL, server = newRedeemServer(body)
|
|
defer server.Close()
|
|
|
|
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
|
assert.NotEqual(t, nil, err)
|
|
if session != nil {
|
|
t.Errorf("expect nill session %#v", session)
|
|
}
|
|
}
|
|
|
|
func TestGoogleProviderRedeemFailsNoCLientSecret(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
p.ProviderData.ClientSecretFile = "srvnoerre"
|
|
|
|
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
|
assert.NotEqual(t, nil, err)
|
|
if session != nil {
|
|
t.Errorf("expect nill session %#v", session)
|
|
}
|
|
assert.Equal(t, "could not read client secret file", err.Error())
|
|
}
|
|
|
|
func TestGoogleProviderGetEmailAddressInvalidJson(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
|
|
body, err := json.Marshal(redeemResponse{
|
|
AccessToken: "a1234",
|
|
IDToken: "ignored prefix." + base64.URLEncoding.EncodeToString([]byte(`{"email": michael.bland@gsa.gov}`)),
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
var server *httptest.Server
|
|
p.RedeemURL, server = newRedeemServer(body)
|
|
defer server.Close()
|
|
|
|
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
|
assert.NotEqual(t, nil, err)
|
|
if session != nil {
|
|
t.Errorf("expect nill session %#v", session)
|
|
}
|
|
|
|
}
|
|
|
|
func TestGoogleProviderGetEmailAddressEmailMissing(t *testing.T) {
|
|
p := newGoogleProvider()
|
|
body, err := json.Marshal(redeemResponse{
|
|
AccessToken: "a1234",
|
|
IDToken: "ignored prefix." + base64.URLEncoding.EncodeToString([]byte(`{"not_email": "missing"}`)),
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
var server *httptest.Server
|
|
p.RedeemURL, server = newRedeemServer(body)
|
|
defer server.Close()
|
|
|
|
session, err := p.Redeem(context.Background(), "http://redirect/", "code1234")
|
|
assert.NotEqual(t, nil, err)
|
|
if session != nil {
|
|
t.Errorf("expect nill session %#v", session)
|
|
}
|
|
|
|
}
|
|
|
|
func TestGoogleProviderUserInGroup(t *testing.T) {
|
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/groups/group@example.com/hasMember/member-in-domain@example.com" {
|
|
fmt.Fprintln(w, `{"isMember": true}`)
|
|
} else if r.URL.Path == "/groups/group@example.com/hasMember/non-member-in-domain@example.com" {
|
|
fmt.Fprintln(w, `{"isMember": false}`)
|
|
} else if r.URL.Path == "/groups/group@example.com/hasMember/member-out-of-domain@otherexample.com" {
|
|
http.Error(
|
|
w,
|
|
`{"error": {"errors": [{"domain": "global","reason": "invalid","message": "Invalid Input: memberKey"}],"code": 400,"message": "Invalid Input: memberKey"}}`,
|
|
http.StatusBadRequest,
|
|
)
|
|
} else if r.URL.Path == "/groups/group@example.com/hasMember/non-member-out-of-domain@otherexample.com" {
|
|
http.Error(
|
|
w,
|
|
`{"error": {"errors": [{"domain": "global","reason": "invalid","message": "Invalid Input: memberKey"}],"code": 400,"message": "Invalid Input: memberKey"}}`,
|
|
http.StatusBadRequest,
|
|
)
|
|
} else if r.URL.Path == "/groups/group@example.com/members/non-member-out-of-domain@otherexample.com" {
|
|
// note that the client currently doesn't care what this response text or code is - any error here results in failure to match the group
|
|
http.Error(
|
|
w,
|
|
`{"error": {"errors": [{"domain": "global","reason": "notFound","message": "Resource Not Found: memberKey"}],"code": 404,"message": "Resource Not Found: memberKey"}}`,
|
|
http.StatusNotFound,
|
|
)
|
|
} else if r.URL.Path == "/groups/group@example.com/members/member-out-of-domain@otherexample.com" {
|
|
fmt.Fprintln(w,
|
|
`{"kind": "admin#directory#member","etag":"12345","id":"1234567890","email": "member-out-of-domain@otherexample.com","role": "MEMBER","type": "USER","status": "ACTIVE","delivery_settings": "ALL_MAIL"}}`,
|
|
)
|
|
}
|
|
}))
|
|
defer ts.Close()
|
|
|
|
client := ts.Client()
|
|
ctx := context.Background()
|
|
|
|
service, err := admin.NewService(ctx, option.WithHTTPClient(client))
|
|
service.BasePath = ts.URL
|
|
assert.Equal(t, nil, err)
|
|
|
|
result := userInGroup(service, []string{"group@example.com"}, "member-in-domain@example.com")
|
|
assert.True(t, result)
|
|
|
|
result = userInGroup(service, []string{"group@example.com"}, "member-out-of-domain@otherexample.com")
|
|
assert.True(t, result)
|
|
|
|
result = userInGroup(service, []string{"group@example.com"}, "non-member-in-domain@example.com")
|
|
assert.False(t, result)
|
|
|
|
result = userInGroup(service, []string{"group@example.com"}, "non-member-out-of-domain@otherexample.com")
|
|
assert.False(t, result)
|
|
}
|