Add support for a simple group filter

This commit is contained in:
Brandon Matthews 2017-01-12 12:15:00 -08:00
parent 5e5717ded7
commit 2fd6f02cd8
6 changed files with 54 additions and 33 deletions

View File

@ -34,7 +34,8 @@ func main() {
flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint or file:// paths for static files. Routing is based on the path")
flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
flagSet.Bool("pass-groups", false, "pass user group information along with basic auth to upstream")
flagSet.Bool("pass-groups", false, "pass user group information in the X-Forwarded-Groups header to upstream (Azure only)")
flagSet.String("filter-groups", "", "exclude groups that do not contain this value in its 'displayName' (Azure only)")
flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header")
flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream")

View File

@ -64,6 +64,7 @@ type OAuthProxy struct {
SetXAuthRequest bool
PassBasicAuth bool
PassGroups bool
FilterGroups string
SkipProviderButton bool
PassUserHeaders bool
BasicAuthPassword string
@ -208,6 +209,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
PassBasicAuth: opts.PassBasicAuth,
PassUserHeaders: opts.PassUserHeaders,
PassGroups: opts.PassGroups,
FilterGroups: opts.FilterGroups,
BasicAuthPassword: opts.BasicAuthPassword,
PassAccessToken: opts.PassAccessToken,
SkipProviderButton: opts.SkipProviderButton,
@ -551,7 +553,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
session.IDToken = req.Form.Get("id_token")
if p.PassGroups && session.IDToken != "" {
session.Groups, err = p.provider.GetGroups(session)
session.Groups, err = p.provider.GetGroups(session, p.FilterGroups)
if err != nil {
p.ErrorPage(rw, 500, "Internal Error", "Internal Error")
return

View File

@ -52,6 +52,7 @@ type Options struct {
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
PassGroups bool `flag:"pass-groups" cfg:"pass_groups"`
FilterGroups string `flag:"filter-groups" cfg:"filter_groups"`
BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"`
PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"`
PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"`
@ -103,6 +104,7 @@ func NewOptions() *Options {
PassBasicAuth: true,
PassUserHeaders: true,
PassGroups: false,
FilterGroups: "",
PassAccessToken: false,
PassHostHeader: true,
ApprovalPrompt: "force",

View File

@ -127,7 +127,8 @@ func (p *AzureProvider) GetEmailAddress(s *SessionState) (string, error) {
return email, err
}
func (p *AzureProvider) GetGroups(s *SessionState) (string, error) {
// Get list of groups user belong to. Filter the desired names of groups (in case of huge group set)
func (p *AzureProvider) GetGroups(s *SessionState, f string) (string, error) {
if s.AccessToken == "" {
return "", errors.New("missing access token")
}
@ -136,36 +137,51 @@ func (p *AzureProvider) GetGroups(s *SessionState) (string, error) {
return "", errors.New("missing id token")
}
// parse token, check if it has groups
// if groups, return "|".join(groups_list)
// else
// look for claim source
// GET source
// parse response
// return "|".join(response.groups_list)
// For future use. Right now microsoft graph don't support filter
// http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part2-url-conventions/odata-v4.0-errata02-os-part2-url-conventions-complete.html#_Toc406398116
req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/me/memberOf/", nil)
//req, err := http.NewRequest("POST", "https://graph.microsoft.com/v1.0/me/getMemberGroups", strings.NewReader("{\"securityEnabledOnly\":true}"))
if err != nil {
return "", err
}
req.Header = getAzureHeader(s.AccessToken)
req.Header.Add("Content-Type", "application/json")
groupData, err := api.Request(req)
groups := make([]string, 0)
for _, groupInfo := range groupData.Get("value").MustArray() {
//v, ok := groupInfo.(string)
v, ok := groupInfo.(map[string]interface{})
if !ok {
continue
/*
var request string = "https://graph.microsoft.com/v1.0/me/memberOf?$select=id,displayName,groupTypes,securityEnabled,description,mailEnabled&$top=999"
if f != "" {
request += "?$filter=contains(displayName, '"+f+"')"
}
dname := v["displayName"].(string)
secen := v["securityEnabled"].(bool)
mailen := v["mailEnabled"].(bool)
if secen == true && mailen == false {
groups = append(groups, dname)
*/
//
// Filters that will be possible to use:
// contains - unknown function | "https://graph.microsoft.com/v1.0/me/memberOf?$filter=contains(displayName,%27olm%27)"
// startswith - not supported | "https://graph.microsoft.com/v1.0/me/memberOf?$filter=startswith(displayName,%27olm%27)"
// substring - not supported | "https://graph.microsoft.com/v1.0/me/memberOf?$filter=substring(displayName,0,2)%20eq%20%27olm%27"
requestUrl := "https://graph.microsoft.com/v1.0/me/memberOf?$select=displayName"
groups := make([]string, 0)
for {
req, err := http.NewRequest("GET", requestUrl, nil)
if err != nil {
return "", err
}
req.Header = getAzureHeader(s.AccessToken)
req.Header.Add("Content-Type", "application/json")
groupData, err := api.Request(req)
for _, groupInfo := range groupData.Get("value").MustArray() {
v, ok := groupInfo.(map[string]interface{})
if !ok {
continue
}
dname := v["displayName"].(string)
if strings.Contains(dname, f) {
groups = append(groups, dname)
}
}
if nextlink := groupData.Get("@odata.nextLink").MustString(); nextlink != "" {
requestUrl = nextlink
} else {
break
}
}

View File

@ -111,7 +111,7 @@ func (p *ProviderData) GetEmailAddress(s *SessionState) (string, error) {
return "", errors.New("not implemented")
}
func (p *ProviderData) GetGroups(s *SessionState) (string, error) {
func (p *ProviderData) GetGroups(s *SessionState, f string) (string, error) {
return "", errors.New("not implemented")
}

View File

@ -7,7 +7,7 @@ import (
type Provider interface {
Data() *ProviderData
GetEmailAddress(*SessionState) (string, error)
GetGroups(*SessionState) (string, error)
GetGroups(*SessionState, string) (string, error)
Redeem(string, string) (*SessionState, error)
ValidateGroup(string) bool
ValidateSessionState(*SessionState) bool