diff --git a/internal/auth.go b/internal/auth.go index a28b973a..647976e8 100644 --- a/internal/auth.go +++ b/internal/auth.go @@ -59,53 +59,57 @@ 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 +// if an email or domain is added by a rule, it is merged with the config before being evaluated +// in order to respect the match whitelist or domain parameter func ValidateEmail(email string) bool { + // Do we have any validation to perform? if len(config.Whitelist) == 0 && len(config.Domains) == 0 && len(config.Rules) == 0 { return true } + var whitelists []string + var domains []string -// Email rule validation -if len(config.Rules) > 0 { + // rule validation for _, rule := range config.Rules { - if rule.Action == "whitelist" { - for _, whitelisted := range rule.Whitelist { - if whitelisted == email { - return true - } - } + for _, whitelisted := range rule.Whitelist { + whitelists = append(whitelists, whitelisted) + } + for _, domain := range rule.Domains { + domains = append(domains, domain) } } -} - + domains = append(domains, config.Domains...) + whitelists = append(whitelists, config.Whitelist...) // Email whitelist validation - if len(config.Whitelist) > 0 { - for _, whitelist := range config.Whitelist { - if email == whitelist { - return true - } - } - - // If we're not matching *either*, stop here - if !config.MatchWhitelistOrDomain { - return false + for _, whitelist := range whitelists { + if email == whitelist { + return true } - } + + // If there's a whitelist and we're not matching *either*, stop here + if len(whitelists) > 0 && !config.MatchWhitelistOrDomain { + return false + } + // 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 - } + for _, domain := range domains { + if emailIsInDomain(email, domain) { + return true } } - + return false +} +func emailIsInDomain(email string, domain string) bool { + parts := strings.Split(email, "@") + if len(parts) < 2 { + return false + } + if parts[1] == domain { + return true + } return false } diff --git a/internal/auth_test.go b/internal/auth_test.go index 3147abc9..d4e3c8b0 100644 --- a/internal/auth_test.go +++ b/internal/auth_test.go @@ -72,17 +72,72 @@ func TestAuthValidateEmail(t *testing.T) { v = ValidateEmail("one@two.com") assert.True(v, "should allow any domain if email domain is not defined") - // Should allow email whitelisted in rule + // Should allow email specified in a whitelist rule config.Rules = map[string]*Rule{ "whitelisted-from-rule": { - Action: "whitelist", - Rule: "multiple-ppl", + Rule: "test-whitelist-rule", Whitelist: []string{"whitelisted-from-rule@example.com"}, }, } v = ValidateEmail("whitelisted-from-rule@example.com") - assert.True(v, "should allow email in whitelist rule") + assert.True(v, "should allow email from whitelist rule") + + // Should allow multiple emails specified in a whitelist rule + config.Rules = map[string]*Rule{ + "whitelist-multiple-emails": { + Rule: "whitelist-rule", + Whitelist: []string{"allowed-from-whitelist-multiple-emails1@example.com", + "allowed-from-whitelist-multiple-emails2@example.com"}, + }, + } + + v1 := ValidateEmail("allowed-from-whitelist-multiple-emails@example.com") + v2 := ValidateEmail("allowed-from-whitelist-multiple-emails@example.com") + assert.True((v1 == v2), "should allow emails from a whitelist rule") + + // Should allow multiple whitelist rules + config.Rules = map[string]*Rule{ + "allowed-from-whitelist-rule1": { + Rule: "whitelist-rule1", + Whitelist: []string{"allowed-from-whitelist-rule1@example.com"}, + }, + "allowed-from-whitelist-rule2": { + Rule: "whitelist-rule2", + Whitelist: []string{"allowed-from-whitelist-rule2@example.com"}, + }, + } + v1 = ValidateEmail("allowed-from-whitelist-rule1@example.com") + v2 = ValidateEmail("allowed-from-whitelist-rule2@example.com") + assert.True((v1 == v2), "should allow emails from multiple whitelist rules") + + // Should allow domain to be specified in a rule + config.Rules = map[string]*Rule{ + "allowed-from-domain-rule": { + Rule: "domain-rule1", + Domains: []string{"example.com"}, + }, + } + + v = ValidateEmail("whitelisted-from-domain@example.com") + assert.True(v, "should allow email from domain rule") + + // Should allow multiple domains to be specified in a rule + config.Rules = map[string]*Rule{ + "allowed-from-domain-rule-1": { + Rule: "domain-rule1", + Domains: []string{"example.com"}, + }, + "allowed-from-domain-rule2": { + Rule: "domain-rule2", + Domains: []string{"example.org"}, + }, + } + + v1 = ValidateEmail("allowed-from-domain-rule1@example.org") + v2 = ValidateEmail("allowed-from-domain-rule2@example.com") + assert.True((v1 == v2), "Should allow emails from multiple domain rules") + // Should block non matching domain config.Domains = []string{"test.com"} v = ValidateEmail("one@two.com") @@ -110,26 +165,59 @@ 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") + assert.True(v, "should allow user from whitelist config if MatchWhitelistOrDomain is disabled") + v = ValidateEmail("test@example.com") - assert.False(v, "should not allow user from valid domain") + assert.False(v, "should not allow user from domain config if a whitelist is specified and MatchWhitelistOrDomain is disabled") + + // Should allow only matching email address when + // MatchWhitelistOrDomain is disabled + config.Rules = map[string]*Rule{ + "allowed-from-whitelist-rule": { + Rule: "whitelist-rule", + Whitelist: []string{"allowed-from-whitelist-rule@example.com"}, + }, + "allowed-from-domain-rule": { + Rule: "domain-rule", + Domains: []string{"example.org"}, + }, + } + config.MatchWhitelistOrDomain = false + + v = ValidateEmail("allowed-from-whitelist-rule@example.com") + assert.True(v, "should allow user from whitelist rule if MatchWhitelistOrDomain is disabled") + + v = ValidateEmail("not-allowed-from-domain-rule@example.org") + assert.False(v, "should not allow user from domain rule if MatchWhitelistOrDomain is disabled") + v = ValidateEmail("one@two.com") - assert.False(v, "should not allow user not in either") + assert.False(v, "should not allow user not in whitelist or domain") - // Should allow either matching domain or email address when - // MatchWhitelistOrDomain is enabled - config.Domains = []string{"example.com"} - config.Whitelist = []string{"test@test.com"} + // Should allow matching email address or domain from a rule + // when MatchWhitelistOrDomain is enabled + config.Rules = map[string]*Rule{ + "allowed-from-whitelist-rule": { + Rule: "whitelist-rule", + Whitelist: []string{"allowed-from-whitelist-rule@example.com"}, + }, + "allowed-from-domain-rule": { + Rule: "domain-rule", + Domains: []string{"example.org"}, + }, + } 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("allowed-from-whitelist-rule@example.com") + assert.True(v, "should allow user from whitelist rule if MatchWhitelistOrDomain is true") + + v = ValidateEmail("not-allowed-from-domain-rule@example.org") + assert.True(v, "should allow user from domain rule if MatchWhitelistOrDomain is enabled") + v = ValidateEmail("one@two.com") - assert.False(v, "should not allow user not in either") + assert.False(v, "should not allow user not in whitelist or domain rule") } - func TestRedirectUri(t *testing.T) { assert := assert.New(t) diff --git a/internal/config.go b/internal/config.go index 67aca0fc..32d89038 100644 --- a/internal/config.go +++ b/internal/config.go @@ -331,11 +331,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 []string - Domains []string + Domains []string } // NewRule creates a new rule object