mirror of
https://github.com/kemko/reproxy.git
synced 2026-01-02 08:15:51 +03:00
74 lines
2.4 KiB
Go
74 lines
2.4 KiB
Go
package rest
|
|
|
|
import (
|
|
"context"
|
|
"crypto/subtle"
|
|
"net/http"
|
|
)
|
|
|
|
const baContextKey = "authorizedWithBasicAuth"
|
|
|
|
// BasicAuth middleware requires basic auth and matches user & passwd with client-provided checker
|
|
func BasicAuth(checker func(user, passwd string) bool) func(http.Handler) http.Handler {
|
|
|
|
return func(h http.Handler) http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
u, p, ok := r.BasicAuth()
|
|
if !ok {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
if !checker(u, p) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
return
|
|
}
|
|
h.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKey(baContextKey), true)))
|
|
}
|
|
return http.HandlerFunc(fn)
|
|
}
|
|
}
|
|
|
|
// BasicAuthWithUserPasswd middleware requires basic auth and matches user & passwd with client-provided values
|
|
func BasicAuthWithUserPasswd(user, passwd string) func(http.Handler) http.Handler {
|
|
checkFn := func(reqUser, reqPasswd string) bool {
|
|
matchUser := subtle.ConstantTimeCompare([]byte(user), []byte(reqUser))
|
|
matchPass := subtle.ConstantTimeCompare([]byte(passwd), []byte(reqPasswd))
|
|
return matchUser == 1 && matchPass == 1
|
|
}
|
|
return BasicAuth(checkFn)
|
|
}
|
|
|
|
// IsAuthorized returns true is user authorized.
|
|
// it can be used in handlers to check if BasicAuth middleware was applied
|
|
func IsAuthorized(ctx context.Context) bool {
|
|
v := ctx.Value(contextKey(baContextKey))
|
|
return v != nil && v.(bool)
|
|
}
|
|
|
|
// BasicAuthWithPrompt middleware requires basic auth and matches user & passwd with client-provided values
|
|
// If the user is not authorized, it will prompt for basic auth
|
|
func BasicAuthWithPrompt(user, passwd string) func(http.Handler) http.Handler {
|
|
checkFn := func(reqUser, reqPasswd string) bool {
|
|
matchUser := subtle.ConstantTimeCompare([]byte(user), []byte(reqUser))
|
|
matchPass := subtle.ConstantTimeCompare([]byte(passwd), []byte(reqPasswd))
|
|
return matchUser == 1 && matchPass == 1
|
|
}
|
|
|
|
return func(h http.Handler) http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// extract basic auth from request
|
|
u, p, ok := r.BasicAuth()
|
|
if ok && checkFn(u, p) {
|
|
h.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), contextKey(baContextKey), true)))
|
|
return
|
|
}
|
|
// not authorized, prompt for basic auth
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
}
|
|
return http.HandlerFunc(fn)
|
|
}
|
|
}
|