blob: 333ee8f3299106d8a6dc0e76fca8abc246120571 [file] [log] [blame] [edit]
package allowed
import (
"sort"
"strings"
"go.skia.org/infra/go/util"
)
// AnyDomain is the value to use if any domain is allowed.
const AnyDomain = "*"
// Allow is used to enforce additional restrictions on who has access to a site,
// eg. members of a group.
type Allow interface {
// Member returns true if the given email address has access.
Member(email string) bool
Emails() []string
}
// AllowedFromList controls access by checking an email address
// against a list of approved domain names and email addresses.
//
// It implements Allow.
type AllowedFromList struct {
domains map[string]bool
emails map[string]bool
}
// NewAllowedFromList creates a new *AllowedFromList from the list of domain names
// and email addresses.
//
// Example:
//
// a := NewAllowedFromList([]string{"google.com", "chromium.org", "someone@example.org"})
func NewAllowedFromList(emailsAndDomains []string) *AllowedFromList {
domains := map[string]bool{}
emails := map[string]bool{}
for _, entry := range emailsAndDomains {
trimmed := strings.ToLower(strings.TrimSpace(entry))
if trimmed == "" {
continue
}
if strings.Contains(trimmed, "@") {
emails[trimmed] = true
} else {
domains[trimmed] = true
}
}
return &AllowedFromList{
domains: domains,
emails: emails,
}
}
// Member returns true if the given email address is AllowedFromList.
func (a *AllowedFromList) Member(email string) bool {
parts := strings.Split(email, "@")
if len(parts) != 2 {
return false
}
if parts[1] == "" {
return false
}
if a.domains[AnyDomain] {
return true
}
if a.domains[parts[1]] || a.emails[email] {
return true
}
return false
}
func (a *AllowedFromList) Emails() []string {
ret := make([]string, 0, len(a.emails))
for k := range a.emails {
ret = append(ret, k)
}
return ret
}
// Googlers creates a new AllowedFromList which restricts to only users logged
// in with an @google.com account.
func Googlers() *AllowedFromList {
return NewAllowedFromList([]string{"google.com"})
}
// Union is an Allow which includes members of multiple other Allows.
type Union []Allow
// UnionOf combines multiple Allows together in an "or" fashion.
func UnionOf(allows ...Allow) Allow {
return Union(allows)
}
// Member returns true if email is a member of any of the Allow in this union.
func (allows Union) Member(email string) bool {
for _, a := range allows {
if a.Member(email) {
return true
}
}
return false
}
// Emails returns a slice of unique emails from the Union.
func (allows Union) Emails() []string {
emails := util.StringSet{}
for _, a := range allows {
emails.AddLists(a.Emails())
}
rv := emails.Keys()
sort.Strings(rv)
return rv
}