Add comments to exported methods for root package
This commit is contained in:
		
							parent
							
								
									8ee802d4e5
								
							
						
					
					
						commit
						ee913fb788
					
				|  | @ -6,8 +6,14 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // EnvOptions holds program options loaded from the process environment
 | ||||
| type EnvOptions map[string]interface{} | ||||
| 
 | ||||
| // LoadEnvForStruct loads environment variables for each field in an options
 | ||||
| // struct passed into it.
 | ||||
| //
 | ||||
| // Fields in the options struct must have an `env` and `cfg` tag to be read
 | ||||
| // from the environment
 | ||||
| func (cfg EnvOptions) LoadEnvForStruct(options interface{}) { | ||||
| 	val := reflect.ValueOf(options).Elem() | ||||
| 	typ := val.Type() | ||||
|  |  | |||
|  | @ -14,10 +14,12 @@ import ( | |||
| // Lookup passwords in a htpasswd file
 | ||||
| // Passwords must be generated with -B for bcrypt or -s for SHA1.
 | ||||
| 
 | ||||
| // HtpasswdFile represents the structure of an htpasswd file
 | ||||
| type HtpasswdFile struct { | ||||
| 	Users map[string]string | ||||
| } | ||||
| 
 | ||||
| // NewHtpasswdFromFile constructs an HtpasswdFile from the file at the path given
 | ||||
| func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { | ||||
| 	r, err := os.Open(path) | ||||
| 	if err != nil { | ||||
|  | @ -27,6 +29,7 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { | |||
| 	return NewHtpasswd(r) | ||||
| } | ||||
| 
 | ||||
| // NewHtpasswd  consctructs an HtpasswdFile from an io.Reader (opened file)
 | ||||
| func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) { | ||||
| 	csvReader := csv.NewReader(file) | ||||
| 	csvReader.Comma = ':' | ||||
|  | @ -44,6 +47,7 @@ func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) { | |||
| 	return h, nil | ||||
| } | ||||
| 
 | ||||
| // Validate checks a users password against the HtpasswdFile entries
 | ||||
| func (h *HtpasswdFile) Validate(user string, password string) bool { | ||||
| 	realPassword, exists := h.Users[user] | ||||
| 	if !exists { | ||||
|  |  | |||
							
								
								
									
										6
									
								
								http.go
								
								
								
								
							
							
						
						
									
										6
									
								
								http.go
								
								
								
								
							|  | @ -9,11 +9,13 @@ import ( | |||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Server represents an HTTP server
 | ||||
| type Server struct { | ||||
| 	Handler http.Handler | ||||
| 	Opts    *Options | ||||
| } | ||||
| 
 | ||||
| // ListenAndServe will serve traffic on HTTP or HTTPS depending on TLS options
 | ||||
| func (s *Server) ListenAndServe() { | ||||
| 	if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" { | ||||
| 		s.ServeHTTPS() | ||||
|  | @ -22,9 +24,10 @@ func (s *Server) ListenAndServe() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ServeHTTP constructs a net.Listener and starts handling HTTP requests
 | ||||
| func (s *Server) ServeHTTP() { | ||||
| 	HTTPAddress := s.Opts.HTTPAddress | ||||
| 	scheme := "" | ||||
| 	var scheme string | ||||
| 
 | ||||
| 	i := strings.Index(HTTPAddress, "://") | ||||
| 	if i > -1 { | ||||
|  | @ -57,6 +60,7 @@ func (s *Server) ServeHTTP() { | |||
| 	log.Printf("HTTP: closing %s", listener.Addr()) | ||||
| } | ||||
| 
 | ||||
| // ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
 | ||||
| func (s *Server) ServeHTTPS() { | ||||
| 	addr := s.Opts.HTTPSAddress | ||||
| 	config := &tls.Config{ | ||||
|  |  | |||
|  | @ -27,10 +27,13 @@ type responseLogger struct { | |||
| 	authInfo string | ||||
| } | ||||
| 
 | ||||
| // Header returns the ResponseWriter's Header
 | ||||
| func (l *responseLogger) Header() http.Header { | ||||
| 	return l.w.Header() | ||||
| } | ||||
| 
 | ||||
| // ExtractGAPMetadata extracts and removes GAP headers from the ResponseWriter's
 | ||||
| // Header
 | ||||
| func (l *responseLogger) ExtractGAPMetadata() { | ||||
| 	upstream := l.w.Header().Get("GAP-Upstream-Address") | ||||
| 	if upstream != "" { | ||||
|  | @ -44,6 +47,7 @@ func (l *responseLogger) ExtractGAPMetadata() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Write writes the response using the ResponseWriter
 | ||||
| func (l *responseLogger) Write(b []byte) (int, error) { | ||||
| 	if l.status == 0 { | ||||
| 		// The status will be StatusOK if WriteHeader has not been called yet
 | ||||
|  | @ -55,16 +59,19 @@ func (l *responseLogger) Write(b []byte) (int, error) { | |||
| 	return size, err | ||||
| } | ||||
| 
 | ||||
| // WriteHeader writes the status code for the Response
 | ||||
| func (l *responseLogger) WriteHeader(s int) { | ||||
| 	l.ExtractGAPMetadata() | ||||
| 	l.w.WriteHeader(s) | ||||
| 	l.status = s | ||||
| } | ||||
| 
 | ||||
| // Status returns the response status code
 | ||||
| func (l *responseLogger) Status() int { | ||||
| 	return l.status | ||||
| } | ||||
| 
 | ||||
| // Size returns teh response size
 | ||||
| func (l *responseLogger) Size() int { | ||||
| 	return l.size | ||||
| } | ||||
|  | @ -94,6 +101,7 @@ type loggingHandler struct { | |||
| 	logTemplate *template.Template | ||||
| } | ||||
| 
 | ||||
| // LoggingHandler provides an http.Handler which logs requests to the HTTP server
 | ||||
| func LoggingHandler(out io.Writer, h http.Handler, v bool, requestLoggingTpl string) http.Handler { | ||||
| 	return loggingHandler{ | ||||
| 		writer:      out, | ||||
|  |  | |||
|  | @ -20,12 +20,16 @@ import ( | |||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// SignatureHeader is the name of the request header containing the GAP Signature
 | ||||
| 	// Part of hmacauth
 | ||||
| 	SignatureHeader = "GAP-Signature" | ||||
| 
 | ||||
| 	httpScheme  = "http" | ||||
| 	httpsScheme = "https" | ||||
| ) | ||||
| 
 | ||||
| // SignatureHeaders contains the headers to be signed by the hmac algorithm
 | ||||
| // Part of hmacauth
 | ||||
| var SignatureHeaders = []string{ | ||||
| 	"Content-Length", | ||||
| 	"Content-Md5", | ||||
|  | @ -39,6 +43,7 @@ var SignatureHeaders = []string{ | |||
| 	"Gap-Auth", | ||||
| } | ||||
| 
 | ||||
| // OAuthProxy is the main authentication proxy
 | ||||
| type OAuthProxy struct { | ||||
| 	CookieSeed     string | ||||
| 	CookieName     string | ||||
|  | @ -79,12 +84,15 @@ type OAuthProxy struct { | |||
| 	Footer              string | ||||
| } | ||||
| 
 | ||||
| // UpstreamProxy represents an upstream server to proxy to
 | ||||
| type UpstreamProxy struct { | ||||
| 	upstream string | ||||
| 	handler  http.Handler | ||||
| 	auth     hmacauth.HmacAuth | ||||
| } | ||||
| 
 | ||||
| // ServeHTTP proxies requests to the upstream provider while signing the
 | ||||
| // request headers
 | ||||
| func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	w.Header().Set("GAP-Upstream-Address", u.upstream) | ||||
| 	if u.auth != nil { | ||||
|  | @ -94,9 +102,12 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| 	u.handler.ServeHTTP(w, r) | ||||
| } | ||||
| 
 | ||||
| // NewReverseProxy creates a new reverse proxy for proxying requests to upstream
 | ||||
| // servers
 | ||||
| func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { | ||||
| 	return httputil.NewSingleHostReverseProxy(target) | ||||
| } | ||||
| 
 | ||||
| func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { | ||||
| 	director := proxy.Director | ||||
| 	proxy.Director = func(req *http.Request) { | ||||
|  | @ -107,6 +118,7 @@ func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { | |||
| 		req.URL.RawQuery = "" | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func setProxyDirector(proxy *httputil.ReverseProxy) { | ||||
| 	director := proxy.Director | ||||
| 	proxy.Director = func(req *http.Request) { | ||||
|  | @ -116,10 +128,13 @@ func setProxyDirector(proxy *httputil.ReverseProxy) { | |||
| 		req.URL.RawQuery = "" | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewFileServer creates a http.Handler to serve files from the filesystem
 | ||||
| func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { | ||||
| 	return http.StripPrefix(path, http.FileServer(http.Dir(filesystemPath))) | ||||
| } | ||||
| 
 | ||||
| // NewOAuthProxy creates a new instance of OOuthProxy from the options provided
 | ||||
| func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { | ||||
| 	serveMux := http.NewServeMux() | ||||
| 	var auth hmacauth.HmacAuth | ||||
|  | @ -214,6 +229,8 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetRedirectURI returns the redirectURL that the upstream OAuth Provider will
 | ||||
| // redirect clients to once authenticated
 | ||||
| func (p *OAuthProxy) GetRedirectURI(host string) string { | ||||
| 	// default to the request Host if not set
 | ||||
| 	if p.redirectURL.Host != "" { | ||||
|  | @ -259,6 +276,8 @@ func (p *OAuthProxy) redeemCode(host, code string) (s *providers.SessionState, e | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // MakeSessionCookie creates an http.Cookie containing the authenticated user's
 | ||||
| // authentication details
 | ||||
| func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie { | ||||
| 	if value != "" { | ||||
| 		value = cookie.SignedValue(p.CookieSeed, p.CookieName, value, now) | ||||
|  | @ -270,6 +289,7 @@ func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expirati | |||
| 	return p.makeCookie(req, p.CookieName, value, expiration, now) | ||||
| } | ||||
| 
 | ||||
| // MakeCSRFCookie creates a cookie for CSRF
 | ||||
| func (p *OAuthProxy) MakeCSRFCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie { | ||||
| 	return p.makeCookie(req, p.CSRFCookieName, value, expiration, now) | ||||
| } | ||||
|  | @ -296,14 +316,19 @@ func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, ex | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ClearCSRFCookie creates a cookie to unset the CSRF cookie stored in the user's
 | ||||
| // session
 | ||||
| func (p *OAuthProxy) ClearCSRFCookie(rw http.ResponseWriter, req *http.Request) { | ||||
| 	http.SetCookie(rw, p.MakeCSRFCookie(req, "", time.Hour*-1, time.Now())) | ||||
| } | ||||
| 
 | ||||
| // SetCSRFCookie adds a CSRF cookie to the response
 | ||||
| func (p *OAuthProxy) SetCSRFCookie(rw http.ResponseWriter, req *http.Request, val string) { | ||||
| 	http.SetCookie(rw, p.MakeCSRFCookie(req, val, p.CookieExpire, time.Now())) | ||||
| } | ||||
| 
 | ||||
| // ClearSessionCookie creates a cookie to unset the user's authentication cookie
 | ||||
| // stored in the user's session
 | ||||
| func (p *OAuthProxy) ClearSessionCookie(rw http.ResponseWriter, req *http.Request) { | ||||
| 	clr := p.MakeSessionCookie(req, "", time.Hour*-1, time.Now()) | ||||
| 	http.SetCookie(rw, clr) | ||||
|  | @ -316,10 +341,12 @@ func (p *OAuthProxy) ClearSessionCookie(rw http.ResponseWriter, req *http.Reques | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetSessionCookie adds the user's session cookie to the response
 | ||||
| func (p *OAuthProxy) SetSessionCookie(rw http.ResponseWriter, req *http.Request, val string) { | ||||
| 	http.SetCookie(rw, p.MakeSessionCookie(req, val, p.CookieExpire, time.Now())) | ||||
| } | ||||
| 
 | ||||
| // LoadCookiedSession reads the user's authentication details from the request
 | ||||
| func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*providers.SessionState, time.Duration, error) { | ||||
| 	var age time.Duration | ||||
| 	c, err := req.Cookie(p.CookieName) | ||||
|  | @ -341,6 +368,7 @@ func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*providers.SessionSt | |||
| 	return session, age, nil | ||||
| } | ||||
| 
 | ||||
| // SaveSession creates a new session cookie value and sets this on the response
 | ||||
| func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *providers.SessionState) error { | ||||
| 	value, err := p.provider.CookieForSession(s, p.CookieCipher) | ||||
| 	if err != nil { | ||||
|  | @ -350,16 +378,19 @@ func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *p | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RobotsTxt disallows scraping pages from the OAuthProxy
 | ||||
| func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter) { | ||||
| 	rw.WriteHeader(http.StatusOK) | ||||
| 	fmt.Fprintf(rw, "User-agent: *\nDisallow: /") | ||||
| } | ||||
| 
 | ||||
| // PingPage responds 200 OK to requests
 | ||||
| func (p *OAuthProxy) PingPage(rw http.ResponseWriter) { | ||||
| 	rw.WriteHeader(http.StatusOK) | ||||
| 	fmt.Fprintf(rw, "OK") | ||||
| } | ||||
| 
 | ||||
| // ErrorPage writes an error response
 | ||||
| func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, message string) { | ||||
| 	log.Printf("ErrorPage %d %s %s", code, title, message) | ||||
| 	rw.WriteHeader(code) | ||||
|  | @ -375,6 +406,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, m | |||
| 	p.templates.ExecuteTemplate(rw, "error.html", t) | ||||
| } | ||||
| 
 | ||||
| // SignInPage writes the sing in template to the response
 | ||||
| func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code int) { | ||||
| 	p.ClearSessionCookie(rw, req) | ||||
| 	rw.WriteHeader(code) | ||||
|  | @ -407,6 +439,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code | |||
| 	p.templates.ExecuteTemplate(rw, "sign_in.html", t) | ||||
| } | ||||
| 
 | ||||
| // ManualSignIn handles basic auth logins to the proxy
 | ||||
| func (p *OAuthProxy) ManualSignIn(rw http.ResponseWriter, req *http.Request) (string, bool) { | ||||
| 	if req.Method != "POST" || p.HtpasswdFile == nil { | ||||
| 		return "", false | ||||
|  | @ -424,6 +457,8 @@ func (p *OAuthProxy) ManualSignIn(rw http.ResponseWriter, req *http.Request) (st | |||
| 	return "", false | ||||
| } | ||||
| 
 | ||||
| // GetRedirect reads the query parameter to get the URL to redirect clients to
 | ||||
| // once authenticated with the OAuthProxy
 | ||||
| func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) { | ||||
| 	err = req.ParseForm() | ||||
| 	if err != nil { | ||||
|  | @ -438,11 +473,13 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| // IsWhitelistedRequest is used to check if auth should be skipped for this request
 | ||||
| func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) (ok bool) { | ||||
| 	isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS" | ||||
| 	return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path) | ||||
| } | ||||
| 
 | ||||
| // IsWhitelistedPath is used to check if the request path is allowed without auth
 | ||||
| func (p *OAuthProxy) IsWhitelistedPath(path string) (ok bool) { | ||||
| 	for _, u := range p.compiledRegex { | ||||
| 		ok = u.MatchString(path) | ||||
|  | @ -484,6 +521,7 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SignIn serves a page prompting users to sign in
 | ||||
| func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) { | ||||
| 	redirect, err := p.GetRedirect(req) | ||||
| 	if err != nil { | ||||
|  | @ -505,11 +543,13 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SignOut sends a response to clear the authentication cookie
 | ||||
| func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { | ||||
| 	p.ClearSessionCookie(rw, req) | ||||
| 	http.Redirect(rw, req, "/", 302) | ||||
| } | ||||
| 
 | ||||
| // OAuthStart starts the OAuth2 authentication flow
 | ||||
| func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { | ||||
| 	nonce, err := cookie.Nonce() | ||||
| 	if err != nil { | ||||
|  | @ -526,6 +566,8 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { | |||
| 	http.Redirect(rw, req, p.provider.GetLoginURL(redirectURI, fmt.Sprintf("%v:%v", nonce, redirect)), 302) | ||||
| } | ||||
| 
 | ||||
| // OAuthCallback is the OAuth2 authentication flow callback that finishes the
 | ||||
| // OAuth2 authentication flow
 | ||||
| func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { | ||||
| 	remoteAddr := getRemoteAddr(req) | ||||
| 
 | ||||
|  | @ -587,6 +629,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // AuthenticateOnly checks whether the user is currently logged in
 | ||||
| func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) { | ||||
| 	status := p.Authenticate(rw, req) | ||||
| 	if status == http.StatusAccepted { | ||||
|  | @ -596,6 +639,8 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Proxy proxies the user request if the user is authenticated else it prompts
 | ||||
| // them to authenticate
 | ||||
| func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | ||||
| 	status := p.Authenticate(rw, req) | ||||
| 	if status == http.StatusInternalServerError { | ||||
|  | @ -612,6 +657,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Authenticate checks whether a user is authenticated
 | ||||
| func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int { | ||||
| 	var saveSession, clearSession, revalidated bool | ||||
| 	remoteAddr := getRemoteAddr(req) | ||||
|  | @ -711,6 +757,8 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int | |||
| 	return http.StatusAccepted | ||||
| } | ||||
| 
 | ||||
| // CheckBasicAuth checks the requests Authorization header for basic auth
 | ||||
| // credentials and authenticates these against the proxies HtpasswdFile
 | ||||
| func (p *OAuthProxy) CheckBasicAuth(req *http.Request) (*providers.SessionState, error) { | ||||
| 	if p.HtpasswdFile == nil { | ||||
| 		return nil, nil | ||||
|  |  | |||
|  | @ -89,11 +89,13 @@ type Options struct { | |||
| 	oidcVerifier  *oidc.IDTokenVerifier | ||||
| } | ||||
| 
 | ||||
| // SignatureData holds hmacauth signature hash and key
 | ||||
| type SignatureData struct { | ||||
| 	hash crypto.Hash | ||||
| 	key  string | ||||
| } | ||||
| 
 | ||||
| // NewOptions constructs a new Options with defaulted values
 | ||||
| func NewOptions() *Options { | ||||
| 	return &Options{ | ||||
| 		ProxyPrefix:          "/oauth2", | ||||
|  | @ -126,6 +128,8 @@ func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string | |||
| 	return parsed, msgs | ||||
| } | ||||
| 
 | ||||
| // Validate checks that required options are set and validates those that they
 | ||||
| // are of the correct format
 | ||||
| func (o *Options) Validate() error { | ||||
| 	if o.SSLInsecureSkipVerify { | ||||
| 		// TODO: Accept a certificate bundle.
 | ||||
|  |  | |||
|  | @ -4,13 +4,16 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // StringArray is a type alias for a slice of strings
 | ||||
| type StringArray []string | ||||
| 
 | ||||
| // Set appends a string to the StringArray
 | ||||
| func (a *StringArray) Set(s string) error { | ||||
| 	*a = append(*a, s) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // String joins elements of the StringArray into a single comma separated string
 | ||||
| func (a *StringArray) String() string { | ||||
| 	return strings.Join(*a, ",") | ||||
| } | ||||
|  |  | |||
|  | @ -10,11 +10,13 @@ import ( | |||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // UserMap holds information from the authenticated emails file
 | ||||
| type UserMap struct { | ||||
| 	usersFile string | ||||
| 	m         unsafe.Pointer | ||||
| } | ||||
| 
 | ||||
| // NewUserMap parses the authenticated emails file into a new UserMap
 | ||||
| func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap { | ||||
| 	um := &UserMap{usersFile: usersFile} | ||||
| 	m := make(map[string]bool) | ||||
|  | @ -30,12 +32,15 @@ func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap { | |||
| 	return um | ||||
| } | ||||
| 
 | ||||
| // IsValid checks if an email is allowed
 | ||||
| func (um *UserMap) IsValid(email string) (result bool) { | ||||
| 	m := *(*map[string]bool)(atomic.LoadPointer(&um.m)) | ||||
| 	_, result = m[email] | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // LoadAuthenticatedEmailsFile loads the authenticated emails file from disk
 | ||||
| // and parses the contents as CSV
 | ||||
| func (um *UserMap) LoadAuthenticatedEmailsFile() { | ||||
| 	r, err := os.Open(um.usersFile) | ||||
| 	if err != nil { | ||||
|  | @ -91,6 +96,7 @@ func newValidatorImpl(domains []string, usersFile string, | |||
| 	return validator | ||||
| } | ||||
| 
 | ||||
| // NewValidator constructs a function to validate email addresses
 | ||||
| func NewValidator(domains []string, usersFile string) func(string) bool { | ||||
| 	return newValidatorImpl(domains, usersFile, nil, func() {}) | ||||
| } | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| package main | ||||
| 
 | ||||
| // VERSION contains version information
 | ||||
| const VERSION = "2.2.1-alpha" | ||||
|  |  | |||
|  | @ -11,6 +11,8 @@ import ( | |||
| 	fsnotify "gopkg.in/fsnotify/fsnotify.v1" | ||||
| ) | ||||
| 
 | ||||
| // WaitForReplacement waits for a file to exist on disk and then starts a watch
 | ||||
| // for the file
 | ||||
| func WaitForReplacement(filename string, op fsnotify.Op, | ||||
| 	watcher *fsnotify.Watcher) { | ||||
| 	const sleepInterval = 50 * time.Millisecond | ||||
|  | @ -30,6 +32,7 @@ func WaitForReplacement(filename string, op fsnotify.Op, | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WatchForUpdates performs an action every time a file on disk is updated
 | ||||
| func WatchForUpdates(filename string, done <-chan bool, action func()) { | ||||
| 	filename = filepath.Clean(filename) | ||||
| 	watcher, err := fsnotify.NewWatcher() | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue