|
package main |
|
|
|
import ( |
|
"fmt" |
|
"net/http" |
|
"strings" |
|
|
|
au "github.com/alexaandru/elastic_guardian/authentication" |
|
az "github.com/alexaandru/elastic_guardian/authorization" |
|
"github.com/justinas/alice" |
|
) |
|
|
|
const AppEnv = "development" |
|
|
|
const Realm = "Secret Area" |
|
|
|
const AuthHdr = "X-Authenticated-User" |
|
|
|
type Routes map[string]http.HandlerFunc |
|
|
|
// the /dev/null equivalent for HTTP - where all requests we don't care about end up. |
|
func httpNullGen(routes Routes) alice.Constructor { |
|
return func(next http.Handler) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
if _, exists := routes[r.URL.Path]; exists { |
|
next.ServeHTTP(w, r) |
|
return |
|
} |
|
|
|
if r.URL.Path == "/favicon.ico" { |
|
r.Header.Set("X-Silence-Logging", "yes") |
|
} |
|
|
|
errHandler(w, r, http.StatusNotFound) |
|
}) |
|
} |
|
} |
|
|
|
func errHandler(w http.ResponseWriter, r *http.Request, status int, msgs ...string) { |
|
if status == http.StatusInternalServerError { |
|
msgs = append(msgs, r.Header.Get("X-Panic")) |
|
} |
|
|
|
msg := strings.Join(msgs, "; ") |
|
if AppEnv != "production" && len(msg) > 0 { |
|
msg = "\n\n(" + msg + ")" |
|
} |
|
|
|
http.Error(w, http.StatusText(status)+msg, status) |
|
} |
|
|
|
func dieHard(next http.Handler) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
defer func() { |
|
if err := recover(); err != nil { |
|
r.Header.Set("X-Panic", fmt.Sprintf("%v", err)) |
|
errHandler(w, r, http.StatusInternalServerError) |
|
} |
|
}() |
|
|
|
next.ServeHTTP(w, r) |
|
}) |
|
} |
|
|
|
func auHandler(next http.Handler) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
status, user := au.BasicAuthPassed(r.Header.Get("Authorization")) |
|
if status == au.Passed { |
|
r.Header.Set(AuthHdr, user) |
|
next.ServeHTTP(w, r) |
|
} else if status == au.NotAttempted { |
|
w.Header().Set("WWW-Authenticate", "Basic realm=\""+Realm+"\"") |
|
errHandler(w, r, http.StatusUnauthorized) |
|
} else { |
|
errHandler(w, r, http.StatusForbidden, "authentication") |
|
} |
|
}) |
|
} |
|
|
|
func azHandler(next http.Handler) http.Handler { |
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|
if az.AuthorizationPassed(r.Header.Get(AuthHdr), r.Method, r.URL.Path) { |
|
next.ServeHTTP(w, r) |
|
} else { |
|
errHandler(w, r, http.StatusForbidden, "authorization") |
|
} |
|
}) |
|
} |
|
|
|
func init() { |
|
au.LoadCredentials(au.CredentialsStore{ |
|
"john": au.Hash("secret"), |
|
}) |
|
az.LoadAuthorizations(az.AuthorizationStore{ |
|
"john": az.AuthorizationRules{az.Deny, []string{"GET /about"}}, |
|
}) |
|
} |