git.iko.soy/typeable/oauth2-proxy
on custom-timeout help sign in
typeable/oauth2-proxy/validator.go
103 lines 2.6 kB history raw
1package main
3import (
4 "encoding/csv"
5 "fmt"
6 "os"
7 "strings"
8 "sync/atomic"
9 "unsafe"
11 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
14// UserMap holds information from the authenticated emails file
15type UserMap struct {
16 usersFile string
17 m unsafe.Pointer
20// NewUserMap parses the authenticated emails file into a new UserMap
21func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap {
22 um := &UserMap{usersFile: usersFile}
23 m := make(map[string]bool)
24 atomic.StorePointer(&um.m, unsafe.Pointer(&m))
25 if usersFile != "" {
26 logger.Printf("using authenticated emails file %s", usersFile)
27 WatchForUpdates(usersFile, done, func() {
28 um.LoadAuthenticatedEmailsFile()
29 onUpdate()
30 })
31 um.LoadAuthenticatedEmailsFile()
32 }
33 return um
36// IsValid checks if an email is allowed
37func (um *UserMap) IsValid(email string) (result bool) {
38 m := *(*map[string]bool)(atomic.LoadPointer(&um.m))
39 _, result = m[email]
40 return
43// LoadAuthenticatedEmailsFile loads the authenticated emails file from disk
44// and parses the contents as CSV
45func (um *UserMap) LoadAuthenticatedEmailsFile() {
46 r, err := os.Open(um.usersFile)
47 if err != nil {
48 logger.Fatalf("failed opening authenticated-emails-file=%q, %s", um.usersFile, err)
49 }
50 defer r.Close()
51 csvReader := csv.NewReader(r)
52 csvReader.Comma = ','
53 csvReader.Comment = '#'
54 csvReader.TrimLeadingSpace = true
55 records, err := csvReader.ReadAll()
56 if err != nil {
57 logger.Printf("error reading authenticated-emails-file=%q, %s", um.usersFile, err)
58 return
59 }
60 updated := make(map[string]bool)
61 for _, r := range records {
62 address := strings.ToLower(strings.TrimSpace(r[0]))
63 updated[address] = true
64 }
65 atomic.StorePointer(&um.m, unsafe.Pointer(&updated))
68func newValidatorImpl(domains []string, usersFile string,
69 done <-chan bool, onUpdate func()) func(string) bool {
70 validUsers := NewUserMap(usersFile, done, onUpdate)
72 var allowAll bool
73 for i, domain := range domains {
74 if domain == "*" {
75 allowAll = true
76 continue
77 }
78 domains[i] = fmt.Sprintf("@%s", strings.ToLower(domain))
79 }
81 validator := func(email string) (valid bool) {
82 if email == "" {
83 return
84 }
85 email = strings.ToLower(email)
86 for _, domain := range domains {
87 valid = valid || strings.HasSuffix(email, domain)
88 }
89 if !valid {
90 valid = validUsers.IsValid(email)
91 }
92 if allowAll {
93 valid = true
94 }
95 return valid
96 }
97 return validator
100// NewValidator constructs a function to validate email addresses
101func NewValidator(domains []string, usersFile string) func(string) bool {
102 return newValidatorImpl(domains, usersFile, nil, func() {})