From 3664286506f673737c0784b9cfd494cda1dc4618 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Thu, 19 May 2022 22:44:33 -0600 Subject: [PATCH] Actually use the auth middleware for assets routes --- config.nix | 25 +++++++++++++++---------- srv/default.nix | 3 +++ srv/src/api/api.go | 17 ++++++++++++++--- srv/src/api/auth.go | 10 ++++++---- srv/src/cmd/hash-password/main.go | 23 +++++++++++++++++++++++ srv/src/cmd/mediocre-blog/main.go | 9 +++++++++ 6 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 srv/src/cmd/hash-password/main.go diff --git a/config.nix b/config.nix index 95b2b0e..1e3726c 100644 --- a/config.nix +++ b/config.nix @@ -1,14 +1,19 @@ { - runDir = "/tmp/mediocre-blog/run"; - dataDir = "/tmp/mediocre-blog/data"; + runDir = "/tmp/mediocre-blog/run"; + dataDir = "/tmp/mediocre-blog/data"; - powSecret = "ssshhh"; - mlSMTPAddr = ""; - mlSMTPAuth = ""; - mlPublicURL = "http://localhost:4000"; - listenProto = "tcp"; - listenAddr = ":4000"; + powSecret = "ssshhh"; + mlSMTPAddr = ""; + mlSMTPAuth = ""; + mlPublicURL = "http://localhost:4000"; + listenProto = "tcp"; + listenAddr = ":4000"; - # If empty then a derived static directory is used - staticProxyURL = "http://127.0.0.1:4002"; + # If empty then a derived static directory is used + staticProxyURL = "http://127.0.0.1:4002"; + + # password is "bar". This should definitely be changed for prod. + apiAuthUsers = { + "foo" = "$2a$13$0JdWlUfHc.3XimEMpEu1cuu6RodhUvzD9l7iiAqa4YkM3mcFV5Pxi"; + }; } diff --git a/srv/default.nix b/srv/default.nix index 7067e1e..e7286b6 100644 --- a/srv/default.nix +++ b/srv/default.nix @@ -32,6 +32,9 @@ # listening export MEDIOCRE_BLOG_LISTEN_PROTO="${config.listenProto}" export MEDIOCRE_BLOG_LISTEN_ADDR="${config.listenAddr}" + + # api + export MEDIOCRE_BLOG_API_AUTH_USERS='${builtins.toJSON config.apiAuthUsers}' ''; build = buildGoModule { diff --git a/srv/src/api/api.go b/srv/src/api/api.go index a5ada2d..7538662 100644 --- a/srv/src/api/api.go +++ b/srv/src/api/api.go @@ -54,6 +54,11 @@ type Params struct { // reverse-proxied there. StaticDir string StaticProxy *url.URL + + // AuthUsers keys are usernames which are allowed to edit server-side data, + // and the values are the password hash which accompanies those users. The + // password hash must have been produced by NewPasswordHash. + AuthUsers map[string]string } // SetupCfg implement the cfg.Cfger interface. @@ -176,6 +181,8 @@ func (a *api) handler() http.Handler { return h } + auther := NewAuther(a.params.AuthUsers) + mux := http.NewServeMux() mux.Handle("/", staticHandler) @@ -208,9 +215,13 @@ func (a *api) handler() http.Handler { v2Mux.Handle("/posts/", a.renderPostHandler()) v2Mux.Handle("/assets/", http.StripPrefix("/assets", apiutil.MethodMux(map[string]http.Handler{ - "GET": a.getPostAssetHandler(), - "POST": formMiddleware(a.postPostAssetHandler()), - "DELETE": formMiddleware(a.deletePostAssetHandler()), + "GET": a.getPostAssetHandler(), + "POST": authMiddleware(auther, + formMiddleware(a.postPostAssetHandler()), + ), + "DELETE": authMiddleware(auther, + formMiddleware(a.deletePostAssetHandler()), + ), }), )) v2Mux.Handle("/", a.renderIndexHandler()) diff --git a/srv/src/api/auth.go b/srv/src/api/auth.go index e668d7b..0d946a3 100644 --- a/srv/src/api/auth.go +++ b/srv/src/api/auth.go @@ -3,13 +3,14 @@ package api import ( "net/http" + "github.com/mediocregopher/blog.mediocregopher.com/srv/api/apiutil" "golang.org/x/crypto/bcrypt" ) // NewPasswordHash returns the hash of the given plaintext password, for use // with Auther. func NewPasswordHash(plaintext string) string { - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plaintext), 12) + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plaintext), 13) if err != nil { panic(err) } @@ -48,9 +49,10 @@ func (a *auther) Allowed(username, password string) bool { func authMiddleware(auther Auther, h http.Handler) http.Handler { - respondUnauthorized := func(rw http.ResponseWriter) { + respondUnauthorized := func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("WWW-Authenticate", `Basic realm="NOPE"`) rw.WriteHeader(http.StatusUnauthorized) + apiutil.GetRequestLogger(r).WarnString(r.Context(), "unauthorized") } return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { @@ -58,12 +60,12 @@ func authMiddleware(auther Auther, h http.Handler) http.Handler { username, password, ok := r.BasicAuth() if !ok { - respondUnauthorized(rw) + respondUnauthorized(rw, r) return } if !auther.Allowed(username, password) { - respondUnauthorized(rw) + respondUnauthorized(rw, r) return } diff --git a/srv/src/cmd/hash-password/main.go b/srv/src/cmd/hash-password/main.go new file mode 100644 index 0000000..b787a4d --- /dev/null +++ b/srv/src/cmd/hash-password/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/mediocregopher/blog.mediocregopher.com/srv/api" +) + +func main() { + + fmt.Fprint(os.Stderr, "Password: ") + + line, err := bufio.NewReader(os.Stdin).ReadString('\n') + + if err != nil { + panic(err) + } + + fmt.Println(api.NewPasswordHash(strings.TrimSpace(line))) +} diff --git a/srv/src/cmd/mediocre-blog/main.go b/srv/src/cmd/mediocre-blog/main.go index 092c4da..5cb4d5f 100644 --- a/srv/src/cmd/mediocre-blog/main.go +++ b/srv/src/cmd/mediocre-blog/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "os" "os/signal" "syscall" @@ -55,6 +56,8 @@ func main() { pathPrefix := cfg.String("path-prefix", "", "Prefix which is optionally applied to all URL paths rendered by the blog") + apiAuthUsersStr := cfg.String("api-auth-users", "{}", "JSON object with usernames as values and password hashes (produced by the hash-password binary) as values. Denotes users which are able to edit server-side data") + // initialization err := cfg.Init(ctx) @@ -128,6 +131,11 @@ func main() { postStore := post.NewStore(postSQLDB) postAssetStore := post.NewAssetStore(postSQLDB) + var apiAuthUsers map[string]string + if err := json.Unmarshal([]byte(*apiAuthUsersStr), &apiAuthUsers); err != nil { + logger.Fatal(ctx, "unmarshaling -api-auth-users", err) + } + apiParams.Logger = logger.WithNamespace("api") apiParams.PowManager = powMgr apiParams.PathPrefix = *pathPrefix @@ -136,6 +144,7 @@ func main() { apiParams.MailingList = ml apiParams.GlobalRoom = chatGlobalRoom apiParams.UserIDCalculator = chatUserIDCalc + apiParams.AuthUsers = apiAuthUsers logger.Info(ctx, "listening") a, err := api.New(apiParams)