1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
package rest
import (
"encoding/base64"
"errors"
"log"
"net/http"
"strings"
)
// AuthBasicMiddleware provides a simple AuthBasic implementation. On failure, a 401 HTTP response
//is returned. On success, the wrapped middleware is called, and the userId is made available as
// request.Env["REMOTE_USER"].(string)
type AuthBasicMiddleware struct {
// Realm name to display to the user. Required.
Realm string
// Callback function that should perform the authentication of the user based on userId and
// password. Must return true on success, false on failure. Required.
Authenticator func(userId string, password string) bool
// Callback function that should perform the authorization of the authenticated user. Called
// only after an authentication success. Must return true on success, false on failure.
// Optional, default to success.
Authorizator func(userId string, request *Request) bool
}
// MiddlewareFunc makes AuthBasicMiddleware implement the Middleware interface.
func (mw *AuthBasicMiddleware) MiddlewareFunc(handler HandlerFunc) HandlerFunc {
if mw.Realm == "" {
log.Fatal("Realm is required")
}
if mw.Authenticator == nil {
log.Fatal("Authenticator is required")
}
if mw.Authorizator == nil {
mw.Authorizator = func(userId string, request *Request) bool {
return true
}
}
return func(writer ResponseWriter, request *Request) {
authHeader := request.Header.Get("Authorization")
if authHeader == "" {
mw.unauthorized(writer)
return
}
providedUserId, providedPassword, err := mw.decodeBasicAuthHeader(authHeader)
if err != nil {
Error(writer, "Invalid authentication", http.StatusBadRequest)
return
}
if !mw.Authenticator(providedUserId, providedPassword) {
mw.unauthorized(writer)
return
}
if !mw.Authorizator(providedUserId, request) {
mw.unauthorized(writer)
return
}
request.Env["REMOTE_USER"] = providedUserId
handler(writer, request)
}
}
func (mw *AuthBasicMiddleware) unauthorized(writer ResponseWriter) {
writer.Header().Set("WWW-Authenticate", "Basic realm="+mw.Realm)
Error(writer, "Not Authorized", http.StatusUnauthorized)
}
func (mw *AuthBasicMiddleware) decodeBasicAuthHeader(header string) (user string, password string, err error) {
parts := strings.SplitN(header, " ", 2)
if !(len(parts) == 2 && parts[0] == "Basic") {
return "", "", errors.New("Invalid authentication")
}
decoded, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return "", "", errors.New("Invalid base64")
}
creds := strings.SplitN(string(decoded), ":", 2)
if len(creds) != 2 {
return "", "", errors.New("Invalid authentication")
}
return creds[0], creds[1], nil
}
|