2022-05-20 17:17:31 +00:00
|
|
|
package http
|
2022-05-20 03:35:45 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
|
2022-05-20 17:17:31 +00:00
|
|
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
2022-05-20 03:35:45 +00:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewPasswordHash returns the hash of the given plaintext password, for use
|
|
|
|
// with Auther.
|
|
|
|
func NewPasswordHash(plaintext string) string {
|
2022-05-20 04:44:33 +00:00
|
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plaintext), 13)
|
2022-05-20 03:35:45 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return string(hashedPassword)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Auther determines who can do what.
|
|
|
|
type Auther interface {
|
|
|
|
Allowed(username, password string) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type auther struct {
|
|
|
|
users map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAuther initializes and returns an Auther will which allow the given
|
|
|
|
// username and password hash combinations. Password hashes must have been
|
|
|
|
// created using NewPasswordHash.
|
|
|
|
func NewAuther(users map[string]string) Auther {
|
|
|
|
return &auther{users: users}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *auther) Allowed(username, password string) bool {
|
|
|
|
|
|
|
|
hashedPassword, ok := a.users[username]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
err := bcrypt.CompareHashAndPassword(
|
|
|
|
[]byte(hashedPassword), []byte(password),
|
|
|
|
)
|
|
|
|
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func authMiddleware(auther Auther, h http.Handler) http.Handler {
|
|
|
|
|
2022-05-20 04:44:33 +00:00
|
|
|
respondUnauthorized := func(rw http.ResponseWriter, r *http.Request) {
|
2022-05-20 03:35:45 +00:00
|
|
|
rw.Header().Set("WWW-Authenticate", `Basic realm="NOPE"`)
|
|
|
|
rw.WriteHeader(http.StatusUnauthorized)
|
2022-05-20 04:44:33 +00:00
|
|
|
apiutil.GetRequestLogger(r).WarnString(r.Context(), "unauthorized")
|
2022-05-20 03:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
username, password, ok := r.BasicAuth()
|
|
|
|
|
|
|
|
if !ok {
|
2022-05-20 04:44:33 +00:00
|
|
|
respondUnauthorized(rw, r)
|
2022-05-20 03:35:45 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if !auther.Allowed(username, password) {
|
2022-05-20 04:44:33 +00:00
|
|
|
respondUnauthorized(rw, r)
|
2022-05-20 03:35:45 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
h.ServeHTTP(rw, r)
|
|
|
|
})
|
|
|
|
}
|