Merge pull request #139 from jburnham/google_group_auth
[RDY] google: Support restricting access to a specific group(s)
This commit is contained in:
commit
2a784ae0d0
13
Godeps
13
Godeps
@ -1,5 +1,8 @@
|
|||||||
github.com/BurntSushi/toml 3883ac1ce943878302255f538fce319d23226223
|
github.com/BurntSushi/toml 3883ac1ce943878302255f538fce319d23226223
|
||||||
github.com/bitly/go-simplejson 3378bdcb5cebedcbf8b5750edee28010f128fe24
|
github.com/bitly/go-simplejson 3378bdcb5cebedcbf8b5750edee28010f128fe24
|
||||||
github.com/mreiferson/go-options ee94b57f2fbf116075426f853e5abbcdfeca8b3d
|
github.com/mreiferson/go-options ee94b57f2fbf116075426f853e5abbcdfeca8b3d
|
||||||
github.com/bmizerany/assert e17e99893cb6509f428e1728281c2ad60a6b31e3
|
github.com/bmizerany/assert e17e99893cb6509f428e1728281c2ad60a6b31e3
|
||||||
gopkg.in/fsnotify.v1 v1.2.0
|
gopkg.in/fsnotify.v1 v1.2.0
|
||||||
|
golang.org/x/oauth2 397fe7649477ff2e8ced8fc0b2696f781e53745a
|
||||||
|
golang.org/x/oauth2/google 397fe7649477ff2e8ced8fc0b2696f781e53745a
|
||||||
|
google.golang.org/api/admin/directory/v1 a5c3e2a4792aff40e59840d9ecdff0542a202a80
|
||||||
|
25
README.md
25
README.md
@ -51,6 +51,26 @@ For Google, the registration steps are:
|
|||||||
|
|
||||||
It's recommended to refresh sessions on a short interval (1h) with `cookie-refresh` setting which validates that the account is still authorized.
|
It's recommended to refresh sessions on a short interval (1h) with `cookie-refresh` setting which validates that the account is still authorized.
|
||||||
|
|
||||||
|
#### Restrict auth to specific Google groups on your domain. (optional)
|
||||||
|
|
||||||
|
1. Create a service account: https://developers.google.com/identity/protocols/OAuth2ServiceAccount and make sure to download the json file.
|
||||||
|
2. Make note of the Client ID for a future step.
|
||||||
|
3. Under "APIs & Auth", choose APIs.
|
||||||
|
4. Click on Admin SDK and then Enable API.
|
||||||
|
5. Follow the steps on https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account and give the client id from step 2 the following oauth scopes:
|
||||||
|
```
|
||||||
|
https://www.googleapis.com/auth/admin.directory.group.readonly
|
||||||
|
https://www.googleapis.com/auth/admin.directory.user.readonly
|
||||||
|
```
|
||||||
|
6. Follow the steps on https://support.google.com/a/answer/60757 to enable Admin API access.
|
||||||
|
7. Create or choose an existing administrative email address on the Gmail domain to assign to the ```google-admin-email``` flag. This email will be impersonated by this client to make calls to the Admin SDK. See the note on the link from step 5 for the reason why.
|
||||||
|
8. Create or choose an existing email group and set that email to the ```google-group``` flag. You can pass multiple instances of this flag with different groups
|
||||||
|
and the user will be checked against all the provided groups.
|
||||||
|
9. Lock down the permissions on the json file downloaded from step 1 so only oauth2_proxy is able to read the file and set the path to the file in the ```google-service-account-json``` flag.
|
||||||
|
10. Restart oauth2_proxy.
|
||||||
|
|
||||||
|
Note: The user is checked against the group members list on initial authentication and every time the token is refreshed ( about once an hour ).
|
||||||
|
|
||||||
### GitHub Auth Provider
|
### GitHub Auth Provider
|
||||||
|
|
||||||
1. Create a new project: https://github.com/settings/developers
|
1. Create a new project: https://github.com/settings/developers
|
||||||
@ -110,6 +130,10 @@ Usage of oauth2_proxy:
|
|||||||
-email-domain=: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email
|
-email-domain=: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email
|
||||||
-github-org="": restrict logins to members of this organisation
|
-github-org="": restrict logins to members of this organisation
|
||||||
-github-team="": restrict logins to members of this team
|
-github-team="": restrict logins to members of this team
|
||||||
|
-google-group="": restrict logins to members of this google group
|
||||||
|
-google-admin-email="": the google admin to impersonate for api calls
|
||||||
|
-google-service-account-json="": the path to the service account json credentials
|
||||||
|
|
||||||
-htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
|
-htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
|
||||||
-http-address="127.0.0.1:4180": [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients
|
-http-address="127.0.0.1:4180": [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients
|
||||||
-https-address=":443": <addr>:<port> to listen on for HTTPS clients
|
-https-address=":443": <addr>:<port> to listen on for HTTPS clients
|
||||||
@ -233,4 +257,3 @@ Follow the examples in the [`providers` package](providers/) to define a new
|
|||||||
`Provider` instance. Add a new `case` to
|
`Provider` instance. Add a new `case` to
|
||||||
[`providers.New()`](providers/providers.go) to allow `oauth2_proxy` to use the
|
[`providers.New()`](providers/providers.go) to allow `oauth2_proxy` to use the
|
||||||
new `Provider`.
|
new `Provider`.
|
||||||
|
|
||||||
|
4
main.go
4
main.go
@ -20,6 +20,7 @@ func main() {
|
|||||||
emailDomains := StringArray{}
|
emailDomains := StringArray{}
|
||||||
upstreams := StringArray{}
|
upstreams := StringArray{}
|
||||||
skipAuthRegex := StringArray{}
|
skipAuthRegex := StringArray{}
|
||||||
|
googleGroups := StringArray{}
|
||||||
|
|
||||||
config := flagSet.String("config", "", "path to config file")
|
config := flagSet.String("config", "", "path to config file")
|
||||||
showVersion := flagSet.Bool("version", false, "print version string")
|
showVersion := flagSet.Bool("version", false, "print version string")
|
||||||
@ -39,6 +40,9 @@ func main() {
|
|||||||
flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||||
flagSet.String("github-org", "", "restrict logins to members of this organisation")
|
flagSet.String("github-org", "", "restrict logins to members of this organisation")
|
||||||
flagSet.String("github-team", "", "restrict logins to members of this team")
|
flagSet.String("github-team", "", "restrict logins to members of this team")
|
||||||
|
flagSet.Var(&googleGroups, "google-group", "restrict logins to members of this google group (may be given multiple times).")
|
||||||
|
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
|
||||||
|
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
|
||||||
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
|
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
|
||||||
flagSet.String("client-secret", "", "the OAuth Client Secret")
|
flagSet.String("client-secret", "", "the OAuth Client Secret")
|
||||||
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
|
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
|
||||||
|
@ -433,7 +433,7 @@ func (p *OauthProxy) OauthCallback(rw http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set cookie, or deny
|
// set cookie, or deny
|
||||||
if p.Validator(session.Email) {
|
if p.Validator(session.Email) && p.provider.ValidateGroup(session.Email) {
|
||||||
log.Printf("%s authentication complete %s", remoteAddr, session)
|
log.Printf("%s authentication complete %s", remoteAddr, session)
|
||||||
err := p.SaveSession(rw, req, session)
|
err := p.SaveSession(rw, req, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -477,7 +477,7 @@ func (p *OauthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||||||
clearSession = true
|
clearSession = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveSession && !revalidated && session.AccessToken != "" {
|
if saveSession && !revalidated && session != nil && session.AccessToken != "" {
|
||||||
if !p.provider.ValidateSessionState(session) {
|
if !p.provider.ValidateSessionState(session) {
|
||||||
log.Printf("%s removing session. error validating %s", remoteAddr, session)
|
log.Printf("%s removing session. error validating %s", remoteAddr, session)
|
||||||
saveSession = false
|
saveSession = false
|
||||||
@ -493,7 +493,7 @@ func (p *OauthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||||||
clearSession = true
|
clearSession = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveSession {
|
if saveSession && session != nil {
|
||||||
err := p.SaveSession(rw, req, session)
|
err := p.SaveSession(rw, req, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s %s", remoteAddr, err)
|
log.Printf("%s %s", remoteAddr, err)
|
||||||
|
39
options.go
39
options.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -21,13 +22,16 @@ type Options struct {
|
|||||||
TLSCertFile string `flag:"tls-cert" cfg:"tls_cert_file"`
|
TLSCertFile string `flag:"tls-cert" cfg:"tls_cert_file"`
|
||||||
TLSKeyFile string `flag:"tls-key" cfg:"tls_key_file"`
|
TLSKeyFile string `flag:"tls-key" cfg:"tls_key_file"`
|
||||||
|
|
||||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||||
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
|
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
|
||||||
GitHubOrg string `flag:"github-org" cfg:"github_org"`
|
GitHubOrg string `flag:"github-org" cfg:"github_org"`
|
||||||
GitHubTeam string `flag:"github-team" cfg:"github_team"`
|
GitHubTeam string `flag:"github-team" cfg:"github_team"`
|
||||||
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
|
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
|
||||||
DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`
|
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
|
||||||
CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`
|
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
|
||||||
|
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
|
||||||
|
DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`
|
||||||
|
CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`
|
||||||
|
|
||||||
CookieName string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
CookieName string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
||||||
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
||||||
@ -159,6 +163,18 @@ func (o *Options) Validate() error {
|
|||||||
o.CookieExpire.String()))
|
o.CookieExpire.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
|
||||||
|
if len(o.GoogleGroups) < 1 {
|
||||||
|
msgs = append(msgs, "missing setting: google-group")
|
||||||
|
}
|
||||||
|
if o.GoogleAdminEmail == "" {
|
||||||
|
msgs = append(msgs, "missing setting: google-admin-email")
|
||||||
|
}
|
||||||
|
if o.GoogleServiceAccountJSON == "" {
|
||||||
|
msgs = append(msgs, "missing setting: google-service-account-json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(msgs) != 0 {
|
if len(msgs) != 0 {
|
||||||
return fmt.Errorf("Invalid configuration:\n %s",
|
return fmt.Errorf("Invalid configuration:\n %s",
|
||||||
strings.Join(msgs, "\n "))
|
strings.Join(msgs, "\n "))
|
||||||
@ -182,6 +198,15 @@ func parseProviderInfo(o *Options, msgs []string) []string {
|
|||||||
switch p := o.provider.(type) {
|
switch p := o.provider.(type) {
|
||||||
case *providers.GitHubProvider:
|
case *providers.GitHubProvider:
|
||||||
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
|
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
|
||||||
|
case *providers.GoogleProvider:
|
||||||
|
if o.GoogleServiceAccountJSON != "" {
|
||||||
|
file, err := os.Open(o.GoogleServiceAccountJSON)
|
||||||
|
if err != nil {
|
||||||
|
msgs = append(msgs, "invalid Google credentials file: "+o.GoogleServiceAccountJSON)
|
||||||
|
} else {
|
||||||
|
p.SetGroupRestriction(o.GoogleGroups, o.GoogleAdminEmail, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,32 @@ func TestNewOptions(t *testing.T) {
|
|||||||
assert.Equal(t, expected, err.Error())
|
assert.Equal(t, expected, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGoogleGroupOptions(t *testing.T) {
|
||||||
|
o := testOptions()
|
||||||
|
o.GoogleGroups = []string{"googlegroup"}
|
||||||
|
err := o.Validate()
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
|
||||||
|
expected := errorMsg([]string{
|
||||||
|
"missing setting: google-admin-email",
|
||||||
|
"missing setting: google-service-account-json"})
|
||||||
|
assert.Equal(t, expected, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoogleGroupInvalidFile(t *testing.T) {
|
||||||
|
o := testOptions()
|
||||||
|
o.GoogleGroups = []string{"test_group"}
|
||||||
|
o.GoogleAdminEmail = "admin@example.com"
|
||||||
|
o.GoogleServiceAccountJSON = "file_doesnt_exist.json"
|
||||||
|
err := o.Validate()
|
||||||
|
assert.NotEqual(t, nil, err)
|
||||||
|
|
||||||
|
expected := errorMsg([]string{
|
||||||
|
"invalid Google credentials file: file_doesnt_exist.json",
|
||||||
|
})
|
||||||
|
assert.Equal(t, expected, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
func TestInitializedOptions(t *testing.T) {
|
func TestInitializedOptions(t *testing.T) {
|
||||||
o := testOptions()
|
o := testOptions()
|
||||||
assert.Equal(t, nil, o.Validate())
|
assert.Equal(t, nil, o.Validate())
|
||||||
|
@ -6,17 +6,25 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
"google.golang.org/api/admin/directory/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GoogleProvider struct {
|
type GoogleProvider struct {
|
||||||
*ProviderData
|
*ProviderData
|
||||||
RedeemRefreshUrl *url.URL
|
RedeemRefreshUrl *url.URL
|
||||||
|
// GroupValidator is a function that determines if the passed email is in
|
||||||
|
// the configured Google group.
|
||||||
|
GroupValidator func(string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
||||||
@ -42,7 +50,15 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
|
|||||||
if p.Scope == "" {
|
if p.Scope == "" {
|
||||||
p.Scope = "profile email"
|
p.Scope = "profile email"
|
||||||
}
|
}
|
||||||
return &GoogleProvider{ProviderData: p}
|
|
||||||
|
return &GoogleProvider{
|
||||||
|
ProviderData: p,
|
||||||
|
// Set a default GroupValidator to just always return valid (true), it will
|
||||||
|
// be overwritten if we configured a Google group restriction.
|
||||||
|
GroupValidator: func(email string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func emailFromIdToken(idToken string) (string, error) {
|
func emailFromIdToken(idToken string) (string, error) {
|
||||||
@ -139,6 +155,102 @@ func (p *GoogleProvider) Redeem(redirectUrl, code string) (s *SessionState, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetGroupRestriction configures the GoogleProvider to restrict access to the
|
||||||
|
// specified group(s). AdminEmail has to be an administrative email on the domain that is
|
||||||
|
// checked. CredentialsFile is the path to a json file containing a Google service
|
||||||
|
// account credentials.
|
||||||
|
func (p *GoogleProvider) SetGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) {
|
||||||
|
adminService := getAdminService(adminEmail, credentialsReader)
|
||||||
|
p.GroupValidator = func(email string) bool {
|
||||||
|
return userInGroup(adminService, groups, email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAdminService(adminEmail string, credentialsReader io.Reader) *admin.Service {
|
||||||
|
data, err := ioutil.ReadAll(credentialsReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("can't read Google credentials file:", err)
|
||||||
|
}
|
||||||
|
conf, err := google.JWTConfigFromJSON(data, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("can't load Google credentials file:", err)
|
||||||
|
}
|
||||||
|
conf.Subject = adminEmail
|
||||||
|
|
||||||
|
client := conf.Client(oauth2.NoContext)
|
||||||
|
adminService, err := admin.New(client)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return adminService
|
||||||
|
}
|
||||||
|
|
||||||
|
func userInGroup(service *admin.Service, groups []string, email string) bool {
|
||||||
|
user, err := fetchUser(service, email)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error fetching user: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
id := user.Id
|
||||||
|
custID := user.CustomerId
|
||||||
|
|
||||||
|
for _, group := range groups {
|
||||||
|
members, err := fetchGroupMembers(service, group)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error fetching group members: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, member := range members {
|
||||||
|
switch member.Type {
|
||||||
|
case "CUSTOMER":
|
||||||
|
if member.Id == custID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case "USER":
|
||||||
|
if member.Id == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchUser(service *admin.Service, email string) (*admin.User, error) {
|
||||||
|
user, err := service.Users.Get(email).Do()
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchGroupMembers(service *admin.Service, group string) ([]*admin.Member, error) {
|
||||||
|
members := []*admin.Member{}
|
||||||
|
pageToken := ""
|
||||||
|
for {
|
||||||
|
req := service.Members.List(group)
|
||||||
|
if pageToken != "" {
|
||||||
|
req.PageToken(pageToken)
|
||||||
|
}
|
||||||
|
r, err := req.Do()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, member := range r.Members {
|
||||||
|
members = append(members, member)
|
||||||
|
}
|
||||||
|
if r.NextPageToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pageToken = r.NextPageToken
|
||||||
|
}
|
||||||
|
return members, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateGroup validates that the provided email exists in the configured Google
|
||||||
|
// group(s).
|
||||||
|
func (p *GoogleProvider) ValidateGroup(email string) bool {
|
||||||
|
return p.GroupValidator(email)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *GoogleProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
|
func (p *GoogleProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
|
||||||
if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
|
if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" {
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -148,6 +260,12 @@ func (p *GoogleProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// re-check that the user is in the proper google group(s)
|
||||||
|
if !p.ValidateGroup(s.Email) {
|
||||||
|
return false, fmt.Errorf("%s is no longer in the group(s)", s.Email)
|
||||||
|
}
|
||||||
|
|
||||||
origExpiration := s.ExpiresOn
|
origExpiration := s.ExpiresOn
|
||||||
s.AccessToken = newToken
|
s.AccessToken = newToken
|
||||||
s.ExpiresOn = time.Now().Add(duration).Truncate(time.Second)
|
s.ExpiresOn = time.Now().Add(duration).Truncate(time.Second)
|
||||||
|
@ -105,6 +105,23 @@ func TestGoogleProviderGetEmailAddress(t *testing.T) {
|
|||||||
assert.Equal(t, "refresh12345", session.RefreshToken)
|
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) {
|
func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) {
|
||||||
p := newGoogleProvider()
|
p := newGoogleProvider()
|
||||||
|
@ -105,6 +105,12 @@ func (p *ProviderData) GetEmailAddress(s *SessionState) (string, error) {
|
|||||||
return "", errors.New("not implemented")
|
return "", errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateGroup validates that the provided email exists in the configured provider
|
||||||
|
// email group(s).
|
||||||
|
func (p *ProviderData) ValidateGroup(email string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ProviderData) ValidateSessionState(s *SessionState) bool {
|
func (p *ProviderData) ValidateSessionState(s *SessionState) bool {
|
||||||
return validateToken(p, s.AccessToken, nil)
|
return validateToken(p, s.AccessToken, nil)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ type Provider interface {
|
|||||||
Data() *ProviderData
|
Data() *ProviderData
|
||||||
GetEmailAddress(*SessionState) (string, error)
|
GetEmailAddress(*SessionState) (string, error)
|
||||||
Redeem(string, string) (*SessionState, error)
|
Redeem(string, string) (*SessionState, error)
|
||||||
|
ValidateGroup(string) bool
|
||||||
ValidateSessionState(*SessionState) bool
|
ValidateSessionState(*SessionState) bool
|
||||||
GetLoginURL(redirectURI, finalRedirect string) string
|
GetLoginURL(redirectURI, finalRedirect string) string
|
||||||
RefreshSessionIfNeeded(*SessionState) (bool, error)
|
RefreshSessionIfNeeded(*SessionState) (bool, error)
|
||||||
|
Loading…
Reference in New Issue
Block a user