Add comments to exported methods for root package

This commit is contained in:
Joel Speed 2018-12-20 09:30:42 +00:00
parent 8ee802d4e5
commit ee913fb788
No known key found for this signature in database
GPG Key ID: 6E80578D6751DEFB
10 changed files with 88 additions and 1 deletions

View File

@ -6,8 +6,14 @@ import (
"strings" "strings"
) )
// EnvOptions holds program options loaded from the process environment
type EnvOptions map[string]interface{} 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{}) { func (cfg EnvOptions) LoadEnvForStruct(options interface{}) {
val := reflect.ValueOf(options).Elem() val := reflect.ValueOf(options).Elem()
typ := val.Type() typ := val.Type()

View File

@ -14,10 +14,12 @@ import (
// Lookup passwords in a htpasswd file // Lookup passwords in a htpasswd file
// Passwords must be generated with -B for bcrypt or -s for SHA1. // Passwords must be generated with -B for bcrypt or -s for SHA1.
// HtpasswdFile represents the structure of an htpasswd file
type HtpasswdFile struct { type HtpasswdFile struct {
Users map[string]string Users map[string]string
} }
// NewHtpasswdFromFile constructs an HtpasswdFile from the file at the path given
func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
r, err := os.Open(path) r, err := os.Open(path)
if err != nil { if err != nil {
@ -27,6 +29,7 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
return NewHtpasswd(r) return NewHtpasswd(r)
} }
// NewHtpasswd consctructs an HtpasswdFile from an io.Reader (opened file)
func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) { func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) {
csvReader := csv.NewReader(file) csvReader := csv.NewReader(file)
csvReader.Comma = ':' csvReader.Comma = ':'
@ -44,6 +47,7 @@ func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) {
return h, nil return h, nil
} }
// Validate checks a users password against the HtpasswdFile entries
func (h *HtpasswdFile) Validate(user string, password string) bool { func (h *HtpasswdFile) Validate(user string, password string) bool {
realPassword, exists := h.Users[user] realPassword, exists := h.Users[user]
if !exists { if !exists {

View File

@ -9,11 +9,13 @@ import (
"time" "time"
) )
// Server represents an HTTP server
type Server struct { type Server struct {
Handler http.Handler Handler http.Handler
Opts *Options Opts *Options
} }
// ListenAndServe will serve traffic on HTTP or HTTPS depending on TLS options
func (s *Server) ListenAndServe() { func (s *Server) ListenAndServe() {
if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" { if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" {
s.ServeHTTPS() s.ServeHTTPS()
@ -22,9 +24,10 @@ func (s *Server) ListenAndServe() {
} }
} }
// ServeHTTP constructs a net.Listener and starts handling HTTP requests
func (s *Server) ServeHTTP() { func (s *Server) ServeHTTP() {
HTTPAddress := s.Opts.HTTPAddress HTTPAddress := s.Opts.HTTPAddress
scheme := "" var scheme string
i := strings.Index(HTTPAddress, "://") i := strings.Index(HTTPAddress, "://")
if i > -1 { if i > -1 {
@ -57,6 +60,7 @@ func (s *Server) ServeHTTP() {
log.Printf("HTTP: closing %s", listener.Addr()) log.Printf("HTTP: closing %s", listener.Addr())
} }
// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
func (s *Server) ServeHTTPS() { func (s *Server) ServeHTTPS() {
addr := s.Opts.HTTPSAddress addr := s.Opts.HTTPSAddress
config := &tls.Config{ config := &tls.Config{

View File

@ -27,10 +27,13 @@ type responseLogger struct {
authInfo string authInfo string
} }
// Header returns the ResponseWriter's Header
func (l *responseLogger) Header() http.Header { func (l *responseLogger) Header() http.Header {
return l.w.Header() return l.w.Header()
} }
// ExtractGAPMetadata extracts and removes GAP headers from the ResponseWriter's
// Header
func (l *responseLogger) ExtractGAPMetadata() { func (l *responseLogger) ExtractGAPMetadata() {
upstream := l.w.Header().Get("GAP-Upstream-Address") upstream := l.w.Header().Get("GAP-Upstream-Address")
if upstream != "" { 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) { func (l *responseLogger) Write(b []byte) (int, error) {
if l.status == 0 { if l.status == 0 {
// The status will be StatusOK if WriteHeader has not been called yet // 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 return size, err
} }
// WriteHeader writes the status code for the Response
func (l *responseLogger) WriteHeader(s int) { func (l *responseLogger) WriteHeader(s int) {
l.ExtractGAPMetadata() l.ExtractGAPMetadata()
l.w.WriteHeader(s) l.w.WriteHeader(s)
l.status = s l.status = s
} }
// Status returns the response status code
func (l *responseLogger) Status() int { func (l *responseLogger) Status() int {
return l.status return l.status
} }
// Size returns teh response size
func (l *responseLogger) Size() int { func (l *responseLogger) Size() int {
return l.size return l.size
} }
@ -94,6 +101,7 @@ type loggingHandler struct {
logTemplate *template.Template 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 { func LoggingHandler(out io.Writer, h http.Handler, v bool, requestLoggingTpl string) http.Handler {
return loggingHandler{ return loggingHandler{
writer: out, writer: out,

View File

@ -20,12 +20,16 @@ import (
) )
const ( const (
// SignatureHeader is the name of the request header containing the GAP Signature
// Part of hmacauth
SignatureHeader = "GAP-Signature" SignatureHeader = "GAP-Signature"
httpScheme = "http" httpScheme = "http"
httpsScheme = "https" httpsScheme = "https"
) )
// SignatureHeaders contains the headers to be signed by the hmac algorithm
// Part of hmacauth
var SignatureHeaders = []string{ var SignatureHeaders = []string{
"Content-Length", "Content-Length",
"Content-Md5", "Content-Md5",
@ -39,6 +43,7 @@ var SignatureHeaders = []string{
"Gap-Auth", "Gap-Auth",
} }
// OAuthProxy is the main authentication proxy
type OAuthProxy struct { type OAuthProxy struct {
CookieSeed string CookieSeed string
CookieName string CookieName string
@ -79,12 +84,15 @@ type OAuthProxy struct {
Footer string Footer string
} }
// UpstreamProxy represents an upstream server to proxy to
type UpstreamProxy struct { type UpstreamProxy struct {
upstream string upstream string
handler http.Handler handler http.Handler
auth hmacauth.HmacAuth 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) { func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("GAP-Upstream-Address", u.upstream) w.Header().Set("GAP-Upstream-Address", u.upstream)
if u.auth != nil { if u.auth != nil {
@ -94,9 +102,12 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
u.handler.ServeHTTP(w, r) 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) { func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) {
return httputil.NewSingleHostReverseProxy(target) return httputil.NewSingleHostReverseProxy(target)
} }
func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
director := proxy.Director director := proxy.Director
proxy.Director = func(req *http.Request) { proxy.Director = func(req *http.Request) {
@ -107,6 +118,7 @@ func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
req.URL.RawQuery = "" req.URL.RawQuery = ""
} }
} }
func setProxyDirector(proxy *httputil.ReverseProxy) { func setProxyDirector(proxy *httputil.ReverseProxy) {
director := proxy.Director director := proxy.Director
proxy.Director = func(req *http.Request) { proxy.Director = func(req *http.Request) {
@ -116,10 +128,13 @@ func setProxyDirector(proxy *httputil.ReverseProxy) {
req.URL.RawQuery = "" req.URL.RawQuery = ""
} }
} }
// NewFileServer creates a http.Handler to serve files from the filesystem
func NewFileServer(path string, filesystemPath string) (proxy http.Handler) { func NewFileServer(path string, filesystemPath string) (proxy http.Handler) {
return http.StripPrefix(path, http.FileServer(http.Dir(filesystemPath))) 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 { func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
serveMux := http.NewServeMux() serveMux := http.NewServeMux()
var auth hmacauth.HmacAuth 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 { func (p *OAuthProxy) GetRedirectURI(host string) string {
// default to the request Host if not set // default to the request Host if not set
if p.redirectURL.Host != "" { if p.redirectURL.Host != "" {
@ -259,6 +276,8 @@ func (p *OAuthProxy) redeemCode(host, code string) (s *providers.SessionState, e
return 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 { func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie {
if value != "" { if value != "" {
value = cookie.SignedValue(p.CookieSeed, p.CookieName, value, now) 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) 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 { 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) 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) { func (p *OAuthProxy) ClearCSRFCookie(rw http.ResponseWriter, req *http.Request) {
http.SetCookie(rw, p.MakeCSRFCookie(req, "", time.Hour*-1, time.Now())) 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) { func (p *OAuthProxy) SetCSRFCookie(rw http.ResponseWriter, req *http.Request, val string) {
http.SetCookie(rw, p.MakeCSRFCookie(req, val, p.CookieExpire, time.Now())) 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) { func (p *OAuthProxy) ClearSessionCookie(rw http.ResponseWriter, req *http.Request) {
clr := p.MakeSessionCookie(req, "", time.Hour*-1, time.Now()) clr := p.MakeSessionCookie(req, "", time.Hour*-1, time.Now())
http.SetCookie(rw, clr) 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) { func (p *OAuthProxy) SetSessionCookie(rw http.ResponseWriter, req *http.Request, val string) {
http.SetCookie(rw, p.MakeSessionCookie(req, val, p.CookieExpire, time.Now())) 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) { func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*providers.SessionState, time.Duration, error) {
var age time.Duration var age time.Duration
c, err := req.Cookie(p.CookieName) c, err := req.Cookie(p.CookieName)
@ -341,6 +368,7 @@ func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*providers.SessionSt
return session, age, nil 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 { func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *providers.SessionState) error {
value, err := p.provider.CookieForSession(s, p.CookieCipher) value, err := p.provider.CookieForSession(s, p.CookieCipher)
if err != nil { if err != nil {
@ -350,16 +378,19 @@ func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *p
return nil return nil
} }
// RobotsTxt disallows scraping pages from the OAuthProxy
func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter) { func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter) {
rw.WriteHeader(http.StatusOK) rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "User-agent: *\nDisallow: /") fmt.Fprintf(rw, "User-agent: *\nDisallow: /")
} }
// PingPage responds 200 OK to requests
func (p *OAuthProxy) PingPage(rw http.ResponseWriter) { func (p *OAuthProxy) PingPage(rw http.ResponseWriter) {
rw.WriteHeader(http.StatusOK) rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "OK") fmt.Fprintf(rw, "OK")
} }
// ErrorPage writes an error response
func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, message string) { func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, message string) {
log.Printf("ErrorPage %d %s %s", code, title, message) log.Printf("ErrorPage %d %s %s", code, title, message)
rw.WriteHeader(code) 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) 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) { func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code int) {
p.ClearSessionCookie(rw, req) p.ClearSessionCookie(rw, req)
rw.WriteHeader(code) 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) 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) { func (p *OAuthProxy) ManualSignIn(rw http.ResponseWriter, req *http.Request) (string, bool) {
if req.Method != "POST" || p.HtpasswdFile == nil { if req.Method != "POST" || p.HtpasswdFile == nil {
return "", false return "", false
@ -424,6 +457,8 @@ func (p *OAuthProxy) ManualSignIn(rw http.ResponseWriter, req *http.Request) (st
return "", false 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) { func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error) {
err = req.ParseForm() err = req.ParseForm()
if err != nil { if err != nil {
@ -438,11 +473,13 @@ func (p *OAuthProxy) GetRedirect(req *http.Request) (redirect string, err error)
return return
} }
// IsWhitelistedRequest is used to check if auth should be skipped for this request
func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) (ok bool) { func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) (ok bool) {
isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS" isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
return isPreflightRequestAllowed || p.IsWhitelistedPath(req.URL.Path) 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) { func (p *OAuthProxy) IsWhitelistedPath(path string) (ok bool) {
for _, u := range p.compiledRegex { for _, u := range p.compiledRegex {
ok = u.MatchString(path) 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) { func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
redirect, err := p.GetRedirect(req) redirect, err := p.GetRedirect(req)
if err != nil { 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) { func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
p.ClearSessionCookie(rw, req) p.ClearSessionCookie(rw, req)
http.Redirect(rw, req, "/", 302) http.Redirect(rw, req, "/", 302)
} }
// OAuthStart starts the OAuth2 authentication flow
func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
nonce, err := cookie.Nonce() nonce, err := cookie.Nonce()
if err != nil { 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) 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) { func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
remoteAddr := getRemoteAddr(req) 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) { func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
status := p.Authenticate(rw, req) status := p.Authenticate(rw, req)
if status == http.StatusAccepted { 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) { func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
status := p.Authenticate(rw, req) status := p.Authenticate(rw, req)
if status == http.StatusInternalServerError { 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 { func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int {
var saveSession, clearSession, revalidated bool var saveSession, clearSession, revalidated bool
remoteAddr := getRemoteAddr(req) remoteAddr := getRemoteAddr(req)
@ -711,6 +757,8 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) int
return http.StatusAccepted 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) { func (p *OAuthProxy) CheckBasicAuth(req *http.Request) (*providers.SessionState, error) {
if p.HtpasswdFile == nil { if p.HtpasswdFile == nil {
return nil, nil return nil, nil

View File

@ -89,11 +89,13 @@ type Options struct {
oidcVerifier *oidc.IDTokenVerifier oidcVerifier *oidc.IDTokenVerifier
} }
// SignatureData holds hmacauth signature hash and key
type SignatureData struct { type SignatureData struct {
hash crypto.Hash hash crypto.Hash
key string key string
} }
// NewOptions constructs a new Options with defaulted values
func NewOptions() *Options { func NewOptions() *Options {
return &Options{ return &Options{
ProxyPrefix: "/oauth2", ProxyPrefix: "/oauth2",
@ -126,6 +128,8 @@ func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string
return parsed, msgs 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 { func (o *Options) Validate() error {
if o.SSLInsecureSkipVerify { if o.SSLInsecureSkipVerify {
// TODO: Accept a certificate bundle. // TODO: Accept a certificate bundle.

View File

@ -4,13 +4,16 @@ import (
"strings" "strings"
) )
// StringArray is a type alias for a slice of strings
type StringArray []string type StringArray []string
// Set appends a string to the StringArray
func (a *StringArray) Set(s string) error { func (a *StringArray) Set(s string) error {
*a = append(*a, s) *a = append(*a, s)
return nil return nil
} }
// String joins elements of the StringArray into a single comma separated string
func (a *StringArray) String() string { func (a *StringArray) String() string {
return strings.Join(*a, ",") return strings.Join(*a, ",")
} }

View File

@ -10,11 +10,13 @@ import (
"unsafe" "unsafe"
) )
// UserMap holds information from the authenticated emails file
type UserMap struct { type UserMap struct {
usersFile string usersFile string
m unsafe.Pointer m unsafe.Pointer
} }
// NewUserMap parses the authenticated emails file into a new UserMap
func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap { func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap {
um := &UserMap{usersFile: usersFile} um := &UserMap{usersFile: usersFile}
m := make(map[string]bool) m := make(map[string]bool)
@ -30,12 +32,15 @@ func NewUserMap(usersFile string, done <-chan bool, onUpdate func()) *UserMap {
return um return um
} }
// IsValid checks if an email is allowed
func (um *UserMap) IsValid(email string) (result bool) { func (um *UserMap) IsValid(email string) (result bool) {
m := *(*map[string]bool)(atomic.LoadPointer(&um.m)) m := *(*map[string]bool)(atomic.LoadPointer(&um.m))
_, result = m[email] _, result = m[email]
return return
} }
// LoadAuthenticatedEmailsFile loads the authenticated emails file from disk
// and parses the contents as CSV
func (um *UserMap) LoadAuthenticatedEmailsFile() { func (um *UserMap) LoadAuthenticatedEmailsFile() {
r, err := os.Open(um.usersFile) r, err := os.Open(um.usersFile)
if err != nil { if err != nil {
@ -91,6 +96,7 @@ func newValidatorImpl(domains []string, usersFile string,
return validator return validator
} }
// NewValidator constructs a function to validate email addresses
func NewValidator(domains []string, usersFile string) func(string) bool { func NewValidator(domains []string, usersFile string) func(string) bool {
return newValidatorImpl(domains, usersFile, nil, func() {}) return newValidatorImpl(domains, usersFile, nil, func() {})
} }

View File

@ -1,3 +1,4 @@
package main package main
// VERSION contains version information
const VERSION = "2.2.1-alpha" const VERSION = "2.2.1-alpha"

View File

@ -11,6 +11,8 @@ import (
fsnotify "gopkg.in/fsnotify/fsnotify.v1" 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, func WaitForReplacement(filename string, op fsnotify.Op,
watcher *fsnotify.Watcher) { watcher *fsnotify.Watcher) {
const sleepInterval = 50 * time.Millisecond 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()) { func WatchForUpdates(filename string, done <-chan bool, action func()) {
filename = filepath.Clean(filename) filename = filepath.Clean(filename)
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()