201 lines
4.2 KiB
Go
Raw Normal View History

2019-01-30 16:52:47 +00:00
package main
import (
// "fmt"
"net/http"
"net/url"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
)
type Server struct {
mux *mux.Router
}
func NewServer() *Server {
s := &Server{}
s.buildRoutes()
return s
}
func (s *Server) buildRoutes() {
s.mux = mux.NewRouter()
// Let's build a server
for _, rules := range config.Rules {
// fmt.Printf("Rule: %s\n", name)
for _, match := range rules.Match {
s.attachHandler(&match, rules.Action)
}
}
// Add callback handler
s.mux.Handle(config.Path, s.AuthCallbackHandler())
// Add a default handler
s.mux.NewRoute().Handler(s.AuthHandler())
}
func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) {
// Modify request
r.URL, _ = url.Parse(r.Header.Get("X-Forwarded-Uri"))
// Pass to mux
s.mux.ServeHTTP(w, r)
}
// Handler that allows requests
func (s *Server) AllowHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s.logger(r, "Allowing request")
w.WriteHeader(200)
}
}
// Authenticate requests
func (s *Server) AuthHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Logging setup
logger := s.logger(r, "Authenticating request")
// Get auth cookie
c, err := r.Cookie(config.CookieName)
if err != nil {
// Error indicates no cookie, generate nonce
err, nonce := fw.Nonce()
if err != nil {
logger.Errorf("Error generating nonce, %v", err)
http.Error(w, "Service unavailable", 503)
return
}
// Set the CSRF cookie
http.SetCookie(w, fw.MakeCSRFCookie(r, nonce))
logger.Debug("Set CSRF cookie and redirecting to google login")
// Forward them on
http.Redirect(w, r, fw.GetLoginURL(r, nonce), http.StatusTemporaryRedirect)
return
}
// Validate cookie
valid, email, err := fw.ValidateCookie(r, c)
if !valid {
logger.Errorf("Invalid cookie: %v", err)
http.Error(w, "Not authorized", 401)
return
}
// Validate user
valid = fw.ValidateEmail(email)
if !valid {
logger.WithFields(logrus.Fields{
"email": email,
}).Errorf("Invalid email")
http.Error(w, "Not authorized", 401)
return
}
// Valid request
logger.Debugf("Allowing valid request ")
w.Header().Set("X-Forwarded-User", email)
w.WriteHeader(200)
}
}
// Handle auth callback
func (s *Server) AuthCallbackHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Logging setup
logger := s.logger(r, "Handling callback")
// Check for CSRF cookie
c, err := r.Cookie(config.CSRFCookieName)
if err != nil {
logger.Warn("Missing csrf cookie")
http.Error(w, "Not authorized", 401)
return
}
// Validate state
valid, redirect, err := fw.ValidateCSRFCookie(r, c)
if !valid {
logger.Warnf("Error validating csrf cookie: %v", err)
http.Error(w, "Not authorized", 401)
return
}
// Clear CSRF cookie
http.SetCookie(w, fw.ClearCSRFCookie(r))
// Exchange code for token
token, err := fw.ExchangeCode(r)
if err != nil {
logger.Errorf("Code exchange failed with: %v", err)
http.Error(w, "Service unavailable", 503)
return
}
// Get user
user, err := fw.GetUser(token)
if err != nil {
logger.Errorf("Error getting user: %s", err)
return
}
// Generate cookie
http.SetCookie(w, fw.MakeCookie(r, user.Email))
logger.WithFields(logrus.Fields{
"user": user.Email,
}).Infof("Generated auth cookie")
// Redirect
http.Redirect(w, r, redirect, http.StatusTemporaryRedirect)
}
}
// Build a handler for a given matcher
func (s *Server) attachHandler(m *Match, action string) {
// Build a new route matcher
route := s.mux.NewRoute()
for _, host := range m.Host {
route.Host(host)
}
for _, pathPrefix := range m.PathPrefix {
route.PathPrefix(pathPrefix)
}
for _, header := range m.Header {
if len(header) != 2 {
panic("todo")
}
route.Headers(header[0], header[1])
}
// Add handler to new route
if action == "allow" {
route.Handler(s.AllowHandler())
} else {
route.Handler(s.AuthHandler())
}
}
func (s *Server) logger(r *http.Request, msg string) *logrus.Entry {
// Create logger
logger := log.WithFields(logrus.Fields{
"RemoteAddr": r.RemoteAddr,
})
// Log request
logger.WithFields(logrus.Fields{
"Headers": r.Header,
}).Debugf(msg)
return logger
}