transfer.sh/transfersh-server/vendor/github.com/PuerkitoBio/ghost/handlers/basicauth.go
2016-07-11 22:15:19 +02:00

123 lines
3.5 KiB
Go

package handlers
// Inspired by node.js' Connect library implementation of the basicAuth middleware.
// https://github.com/senchalabs/connect
import (
"bytes"
"encoding/base64"
"fmt"
"net/http"
"strings"
)
// Internal writer that keeps track of the currently authenticated user.
type userResponseWriter struct {
http.ResponseWriter
user interface{}
userName string
}
// Implement the WrapWriter interface.
func (this *userResponseWriter) WrappedWriter() http.ResponseWriter {
return this.ResponseWriter
}
// Writes an unauthorized response to the client, specifying the expected authentication
// information.
func Unauthorized(w http.ResponseWriter, realm string) {
w.Header().Set("Www-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized"))
}
// Writes a bad request response to the client, with an optional message.
func BadRequest(w http.ResponseWriter, msg string) {
w.WriteHeader(http.StatusBadRequest)
if msg == "" {
msg = "Bad Request"
}
w.Write([]byte(msg))
}
// BasicAuthHandlerFunc is the same as BasicAuthHandler, it is just a convenience
// signature that accepts a func(http.ResponseWriter, *http.Request) instead of
// a http.Handler interface. It saves the boilerplate http.HandlerFunc() cast.
func BasicAuthHandlerFunc(h http.HandlerFunc,
authFn func(string, string) (interface{}, bool), realm string) http.HandlerFunc {
return BasicAuthHandler(h, authFn, realm)
}
// Returns a Basic Authentication handler, protecting the wrapped handler from
// being accessed if the authentication function is not successful.
func BasicAuthHandler(h http.Handler,
authFn func(string, string) (interface{}, bool), realm string) http.HandlerFunc {
if realm == "" {
realm = "Authorization Required"
}
return func(w http.ResponseWriter, r *http.Request) {
// Self-awareness
if _, ok := GetUser(w); ok {
h.ServeHTTP(w, r)
return
}
authInfo := r.Header.Get("Authorization")
if authInfo == "" {
// No authorization info, return 401
Unauthorized(w, realm)
return
}
parts := strings.Split(authInfo, " ")
if len(parts) != 2 {
BadRequest(w, "Bad authorization header")
return
}
scheme := parts[0]
creds, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
BadRequest(w, "Bad credentials encoding")
return
}
index := bytes.Index(creds, []byte(":"))
if scheme != "Basic" || index < 0 {
BadRequest(w, "Bad authorization header")
return
}
user, pwd := string(creds[:index]), string(creds[index+1:])
udata, ok := authFn(user, pwd)
if ok {
// Save user data and continue
uw := &userResponseWriter{w, udata, user}
h.ServeHTTP(uw, r)
} else {
Unauthorized(w, realm)
}
}
}
// Return the currently authenticated user. This is the same data that was returned
// by the authentication function passed to BasicAuthHandler.
func GetUser(w http.ResponseWriter) (interface{}, bool) {
usr, ok := GetResponseWriter(w, func(tst http.ResponseWriter) bool {
_, ok := tst.(*userResponseWriter)
return ok
})
if ok {
return usr.(*userResponseWriter).user, true
}
return nil, false
}
// Return the currently authenticated user name. This is the user name that was
// authenticated for the current request.
func GetUserName(w http.ResponseWriter) (string, bool) {
usr, ok := GetResponseWriter(w, func(tst http.ResponseWriter) bool {
_, ok := tst.(*userResponseWriter)
return ok
})
if ok {
return usr.(*userResponseWriter).userName, true
}
return "", false
}