| // Package proxylogin implements alogin.Login when letting a reverse proxy handle |
| // authentication |
| package proxylogin |
| |
| import ( |
| "net/http" |
| "regexp" |
| "strings" |
| |
| "github.com/gorilla/mux" |
| "go.skia.org/infra/go/alogin" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/go/sklog" |
| ) |
| |
| // proxyLogin implements alogin.Login by relying on a reverse proxy doing the |
| // authentication and then passing the user's logged in status via header value. |
| // |
| // See https://grafana.com/docs/grafana/latest/auth/auth-proxy/ and |
| // https://cloud.google.com/iap/docs/identity-howto#getting_the_users_identity_with_signed_headers |
| type proxyLogin struct { |
| // headerName is the name of the header we expect to have the users email. |
| headerName string |
| |
| // emailRegex is an optional regex to extract the email address from the header value. |
| emailRegex *regexp.Regexp |
| |
| // loginURL is the URL to visit to log in. |
| loginURL string |
| |
| // logoutURL is the URL to visit to log out. |
| logoutURL string |
| } |
| |
| // New returns a new instance of proxyLogin. |
| // |
| // headerName is the name of the header that contains the proxy authentication |
| // information. |
| // |
| // emailRegex is a regex to extract the email address from the header value. |
| // This value can be nil. This is useful for reverse proxies that include other |
| // information in the header in addition to the email address, such as |
| // https://cloud.google.com/iap/docs/identity-howto#getting_the_users_identity_with_signed_headers |
| // |
| // If supplied, the Regex must have a single subexpression that matches the email |
| // address. |
| func New(headerName, emailRegex, loginURL, logoutURL string) (*proxyLogin, error) { |
| var compiledRegex *regexp.Regexp = nil |
| var err error |
| if emailRegex != "" { |
| compiledRegex, err = regexp.Compile(emailRegex) |
| if err != nil { |
| return nil, skerr.Wrapf(err, "Failed to compile email regex %q", emailRegex) |
| } |
| } |
| |
| return &proxyLogin{ |
| headerName: headerName, |
| emailRegex: compiledRegex, |
| loginURL: loginURL, |
| logoutURL: logoutURL, |
| }, nil |
| } |
| |
| // LoggedInAs implements alogin.Login. |
| func (p *proxyLogin) LoggedInAs(r *http.Request) alogin.EMail { |
| value := r.Header.Get(p.headerName) |
| value = strings.TrimSpace(value) |
| if p.emailRegex == nil { |
| return alogin.EMail(value) |
| } |
| submatches := p.emailRegex.FindStringSubmatch(value) |
| if len(submatches) != 2 { |
| sklog.Errorf("Wrong number of regex matches for %q: %q", value, submatches) |
| return "" |
| } |
| return alogin.EMail(submatches[1]) |
| } |
| |
| // NeedsAuthentication implements alogin.Login. |
| func (p *proxyLogin) NeedsAuthentication(w http.ResponseWriter, r *http.Request) { |
| http.Error(w, "Forbidden", http.StatusForbidden) |
| } |
| |
| // RegisterHandlers implements alogin.Login. |
| func (p *proxyLogin) RegisterHandlers(router *mux.Router) { |
| // Noop. |
| } |
| |
| func (p *proxyLogin) Status(r *http.Request) alogin.Status { |
| return alogin.Status{ |
| EMail: p.LoggedInAs(r), |
| LoginURL: p.loginURL, |
| LogoutURL: p.logoutURL, |
| } |
| } |
| |
| // Assert proxyLogin implements alogin.Login. |
| var _ alogin.Login = (*proxyLogin)(nil) |