Allow override of domains and whitelist in rules (#169)
Co-authored-by: Mathieu Cantin <mcantin@petalmd.com> Co-authored-by: Pete Shaw <lozlow@users.noreply.github.com>
This commit is contained in:
@@ -59,18 +59,28 @@ func ValidateCookie(r *http.Request, c *http.Cookie) (string, error) {
|
||||
// ValidateEmail checks if the given email address matches either a whitelisted
|
||||
// email address, as defined by the "whitelist" config parameter. Or is part of
|
||||
// a permitted domain, as defined by the "domains" config parameter
|
||||
func ValidateEmail(email string) bool {
|
||||
func ValidateEmail(email, ruleName string) bool {
|
||||
// Use global config by default
|
||||
whitelist := config.Whitelist
|
||||
domains := config.Domains
|
||||
|
||||
if rule, ok := config.Rules[ruleName]; ok {
|
||||
// Override with rule config if found
|
||||
if len(rule.Whitelist) > 0 || len(rule.Domains) > 0 {
|
||||
whitelist = rule.Whitelist
|
||||
domains = rule.Domains
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have any validation to perform?
|
||||
if len(config.Whitelist) == 0 && len(config.Domains) == 0 {
|
||||
if len(whitelist) == 0 && len(domains) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Email whitelist validation
|
||||
if len(config.Whitelist) > 0 {
|
||||
for _, whitelist := range config.Whitelist {
|
||||
if email == whitelist {
|
||||
return true
|
||||
}
|
||||
if len(whitelist) > 0 {
|
||||
if ValidateWhitelist(email, whitelist) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If we're not matching *either*, stop here
|
||||
@@ -80,21 +90,37 @@ func ValidateEmail(email string) bool {
|
||||
}
|
||||
|
||||
// Domain validation
|
||||
if len(config.Domains) > 0 {
|
||||
parts := strings.Split(email, "@")
|
||||
if len(parts) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, domain := range config.Domains {
|
||||
if domain == parts[1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if len(domains) > 0 && ValidateDomains(email, domains) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateWhitelist checks if the email is in whitelist
|
||||
func ValidateWhitelist(email string, whitelist CommaSeparatedList) bool {
|
||||
for _, whitelist := range whitelist {
|
||||
if email == whitelist {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateDomains checks if the email matches a whitelisted domain
|
||||
func ValidateDomains(email string, domains CommaSeparatedList) bool {
|
||||
parts := strings.Split(email, "@")
|
||||
if len(parts) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, domain := range domains {
|
||||
if domain == parts[1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
// Get the redirect base
|
||||
|
||||
@@ -65,32 +65,25 @@ func TestAuthValidateEmail(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
config, _ = NewConfig([]string{})
|
||||
|
||||
// Should allow any
|
||||
v := ValidateEmail("test@test.com")
|
||||
// Should allow any with no whitelist/domain is specified
|
||||
v := ValidateEmail("test@test.com", "default")
|
||||
assert.True(v, "should allow any domain if email domain is not defined")
|
||||
v = ValidateEmail("one@two.com")
|
||||
v = ValidateEmail("one@two.com", "default")
|
||||
assert.True(v, "should allow any domain if email domain is not defined")
|
||||
|
||||
// Should block non matching domain
|
||||
config.Domains = []string{"test.com"}
|
||||
v = ValidateEmail("one@two.com")
|
||||
assert.False(v, "should not allow user from another domain")
|
||||
|
||||
// Should allow matching domain
|
||||
config.Domains = []string{"test.com"}
|
||||
v = ValidateEmail("test@test.com")
|
||||
v = ValidateEmail("one@two.com", "default")
|
||||
assert.False(v, "should not allow user from another domain")
|
||||
v = ValidateEmail("test@test.com", "default")
|
||||
assert.True(v, "should allow user from allowed domain")
|
||||
|
||||
// Should block non whitelisted email address
|
||||
config.Domains = []string{}
|
||||
config.Whitelist = []string{"test@test.com"}
|
||||
v = ValidateEmail("one@two.com")
|
||||
assert.False(v, "should not allow user not in whitelist")
|
||||
|
||||
// Should allow matching whitelisted email address
|
||||
config.Domains = []string{}
|
||||
config.Whitelist = []string{"test@test.com"}
|
||||
v = ValidateEmail("test@test.com")
|
||||
v = ValidateEmail("one@two.com", "default")
|
||||
assert.False(v, "should not allow user not in whitelist")
|
||||
v = ValidateEmail("test@test.com", "default")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
|
||||
// Should allow only matching email address when
|
||||
@@ -98,24 +91,106 @@ func TestAuthValidateEmail(t *testing.T) {
|
||||
config.Domains = []string{"example.com"}
|
||||
config.Whitelist = []string{"test@test.com"}
|
||||
config.MatchWhitelistOrDomain = false
|
||||
v = ValidateEmail("test@test.com")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
v = ValidateEmail("test@example.com")
|
||||
assert.False(v, "should not allow user from valid domain")
|
||||
v = ValidateEmail("one@two.com")
|
||||
v = ValidateEmail("one@two.com", "default")
|
||||
assert.False(v, "should not allow user not in either")
|
||||
v = ValidateEmail("test@example.com", "default")
|
||||
assert.False(v, "should not allow user from allowed domain")
|
||||
v = ValidateEmail("test@test.com", "default")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
|
||||
// Should allow either matching domain or email address when
|
||||
// MatchWhitelistOrDomain is enabled
|
||||
config.Domains = []string{"example.com"}
|
||||
config.Whitelist = []string{"test@test.com"}
|
||||
config.MatchWhitelistOrDomain = true
|
||||
v = ValidateEmail("test@test.com")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
v = ValidateEmail("test@example.com")
|
||||
assert.True(v, "should allow user from valid domain")
|
||||
v = ValidateEmail("one@two.com")
|
||||
v = ValidateEmail("one@two.com", "default")
|
||||
assert.False(v, "should not allow user not in either")
|
||||
v = ValidateEmail("test@example.com", "default")
|
||||
assert.True(v, "should allow user from allowed domain")
|
||||
v = ValidateEmail("test@test.com", "default")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
|
||||
// Rule testing
|
||||
|
||||
// Should use global whitelist/domain when not specified on rule
|
||||
config.Domains = []string{"example.com"}
|
||||
config.Whitelist = []string{"test@test.com"}
|
||||
config.Rules = map[string]*Rule{"test": NewRule()}
|
||||
config.MatchWhitelistOrDomain = true
|
||||
v = ValidateEmail("one@two.com", "test")
|
||||
assert.False(v, "should not allow user not in either")
|
||||
v = ValidateEmail("test@example.com", "test")
|
||||
assert.True(v, "should allow user from allowed global domain")
|
||||
v = ValidateEmail("test@test.com", "test")
|
||||
assert.True(v, "should allow user in global whitelist")
|
||||
|
||||
// Should allow matching domain in rule
|
||||
config.Domains = []string{"testglobal.com"}
|
||||
config.Whitelist = []string{}
|
||||
rule := NewRule()
|
||||
config.Rules = map[string]*Rule{"test": rule}
|
||||
rule.Domains = []string{"testrule.com"}
|
||||
config.MatchWhitelistOrDomain = false
|
||||
v = ValidateEmail("one@two.com", "test")
|
||||
assert.False(v, "should not allow user from another domain")
|
||||
v = ValidateEmail("one@testglobal.com", "test")
|
||||
assert.False(v, "should not allow user from global domain")
|
||||
v = ValidateEmail("test@testrule.com", "test")
|
||||
assert.True(v, "should allow user from allowed domain")
|
||||
|
||||
// Should allow matching whitelist in rule
|
||||
config.Domains = []string{}
|
||||
config.Whitelist = []string{"test@testglobal.com"}
|
||||
rule = NewRule()
|
||||
config.Rules = map[string]*Rule{"test": rule}
|
||||
rule.Whitelist = []string{"test@testrule.com"}
|
||||
config.MatchWhitelistOrDomain = false
|
||||
v = ValidateEmail("one@two.com", "test")
|
||||
assert.False(v, "should not allow user from another domain")
|
||||
v = ValidateEmail("test@testglobal.com", "test")
|
||||
assert.False(v, "should not allow user from global domain")
|
||||
v = ValidateEmail("test@testrule.com", "test")
|
||||
assert.True(v, "should allow user from allowed domain")
|
||||
|
||||
// Should allow only matching email address when
|
||||
// MatchWhitelistOrDomain is disabled
|
||||
config.Domains = []string{"exampleglobal.com"}
|
||||
config.Whitelist = []string{"test@testglobal.com"}
|
||||
rule = NewRule()
|
||||
config.Rules = map[string]*Rule{"test": rule}
|
||||
rule.Domains = []string{"examplerule.com"}
|
||||
rule.Whitelist = []string{"test@testrule.com"}
|
||||
config.MatchWhitelistOrDomain = false
|
||||
v = ValidateEmail("one@two.com", "test")
|
||||
assert.False(v, "should not allow user not in either")
|
||||
v = ValidateEmail("test@testglobal.com", "test")
|
||||
assert.False(v, "should not allow user in global whitelist")
|
||||
v = ValidateEmail("test@exampleglobal.com", "test")
|
||||
assert.False(v, "should not allow user from global domain")
|
||||
v = ValidateEmail("test@examplerule.com", "test")
|
||||
assert.False(v, "should not allow user from allowed domain")
|
||||
v = ValidateEmail("test@testrule.com", "test")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
|
||||
// Should allow either matching domain or email address when
|
||||
// MatchWhitelistOrDomain is enabled
|
||||
config.Domains = []string{"exampleglobal.com"}
|
||||
config.Whitelist = []string{"test@testglobal.com"}
|
||||
rule = NewRule()
|
||||
config.Rules = map[string]*Rule{"test": rule}
|
||||
rule.Domains = []string{"examplerule.com"}
|
||||
rule.Whitelist = []string{"test@testrule.com"}
|
||||
config.MatchWhitelistOrDomain = true
|
||||
v = ValidateEmail("one@two.com", "test")
|
||||
assert.False(v, "should not allow user not in either")
|
||||
v = ValidateEmail("test@testglobal.com", "test")
|
||||
assert.False(v, "should not allow user in global whitelist")
|
||||
v = ValidateEmail("test@exampleglobal.com", "test")
|
||||
assert.False(v, "should not allow user from global domain")
|
||||
v = ValidateEmail("test@examplerule.com", "test")
|
||||
assert.True(v, "should allow user from allowed domain")
|
||||
v = ValidateEmail("test@testrule.com", "test")
|
||||
assert.True(v, "should allow user in whitelist")
|
||||
}
|
||||
|
||||
func TestRedirectUri(t *testing.T) {
|
||||
|
||||
@@ -210,6 +210,14 @@ func (c *Config) parseUnknownFlag(option string, arg flags.SplitArgument, args [
|
||||
rule.Rule = val
|
||||
case "provider":
|
||||
rule.Provider = val
|
||||
case "whitelist":
|
||||
list := CommaSeparatedList{}
|
||||
list.UnmarshalFlag(val)
|
||||
rule.Whitelist = list
|
||||
case "domains":
|
||||
list := CommaSeparatedList{}
|
||||
list.UnmarshalFlag(val)
|
||||
rule.Domains = list
|
||||
default:
|
||||
return args, fmt.Errorf("invalid route param: %v", option)
|
||||
}
|
||||
@@ -327,9 +335,11 @@ func (c *Config) setupProvider(name string) error {
|
||||
|
||||
// Rule holds defined rules
|
||||
type Rule struct {
|
||||
Action string
|
||||
Rule string
|
||||
Provider string
|
||||
Action string
|
||||
Rule string
|
||||
Provider string
|
||||
Whitelist CommaSeparatedList
|
||||
Domains CommaSeparatedList
|
||||
}
|
||||
|
||||
// NewRule creates a new rule object
|
||||
|
||||
@@ -101,7 +101,7 @@ func (s *Server) AuthHandler(providerName, rule string) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// Validate user
|
||||
valid := ValidateEmail(email)
|
||||
valid := ValidateEmail(email, rule)
|
||||
if !valid {
|
||||
logger.WithField("email", email).Warn("Invalid email")
|
||||
http.Error(w, "Not authorized", 401)
|
||||
|
||||
Reference in New Issue
Block a user