diff options
Diffstat (limited to 'srv/src')
-rw-r--r-- | srv/src/api/api.go | 17 | ||||
-rw-r--r-- | srv/src/api/auth.go | 10 | ||||
-rw-r--r-- | srv/src/cmd/hash-password/main.go | 23 | ||||
-rw-r--r-- | srv/src/cmd/mediocre-blog/main.go | 9 |
4 files changed, 52 insertions, 7 deletions
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) |