diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-03 22:41:07 -0500 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-03 22:41:07 -0500 |
| commit | 8b736f3c0b9b583cb69f01424d214d93d6a9136c (patch) | |
| tree | 5bf0670367f6c679dfe3e1531621a40b2dbcb2c6 | |
| parent | 95e648598f441ec5f0563ba3be136c7ab71c94bd (diff) | |
| download | paste-8b736f3c0b9b583cb69f01424d214d93d6a9136c.tar.gz paste-8b736f3c0b9b583cb69f01424d214d93d6a9136c.tar.xz | |
Minor tweaks. Reduce code duplication. Add some docs
| -rw-r--r-- | main.go | 59 |
1 files changed, 36 insertions, 23 deletions
@@ -42,7 +42,7 @@ var ( type App struct { static fs.FS - users map[string]string + users UsersMap jwtKey string sessionHours int storage string // path to where the paste files are actually stored @@ -171,7 +171,7 @@ func main() { storage: storage, jwtKey: jwtKey, sessionHours: rSessionHours, - users: getUsersFromEnviron(), + users: LoadUsersFromEnviron(), } rp, err := getProxyHandler(proxyURL) @@ -296,9 +296,8 @@ func (a *App) Handler() http.Handler { } for pth, handler := range secHandlers { - mux.Handle(pth, authHandler( + mux.Handle(pth, a.users.AuthHandler( handler, - a.users, a.jwtKey, )) } @@ -318,7 +317,7 @@ func (a *App) Handler() http.Handler { return mux } -func getUsersFromEnviron() map[string]string { +func LoadUsersFromEnviron() UsersMap { users := map[string]string{} for _, entry := range os.Environ() { if !strings.HasPrefix(entry, "USER_") { @@ -347,10 +346,14 @@ func genTokenKey() string { return base64.RawURLEncoding.EncodeToString(r) } +// sendJSON Sends down the response to the writer specified, automatically +// encoding the response status code in the header, JSON, and a friendly +// message as well as any other exported members of the Response struct. func sendJSON(er Response) { er.w.WriteHeader(er.Code) er.w.Header().Add("Content-type", "application/json") enc := json.NewEncoder(er.w) + enc.SetIndent("", " ") _ = enc.Encode(er) } @@ -367,6 +370,9 @@ func sendPlain(r Response, rdr io.Reader) { } } +// GenId grabs cryptographically random data, and dumps out a URL encoded base64 +// string of it. Suitable for identifiers. Bytes used to create the string +// is controlled by ID_BYTES func GenId() string { r := make([]byte, ID_BYTES) _, err := rand.Read(r) @@ -377,7 +383,12 @@ func GenId() string { return base64.RawURLEncoding.EncodeToString(r) } -func hasValidJWT(users map[string]string, tokenString, jwtKey string) bool { +// UsersMap is simply a convience wrapp around a map[string]string, +// with a few methods to handle validation of usernames/passwords +// as well as JWTs +type UsersMap map[string]string + +func (users UsersMap) HasValidJWT(tokenString, jwtKey string) bool { token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) { @@ -407,7 +418,9 @@ func hasValidJWT(users map[string]string, tokenString, jwtKey string) bool { return err == nil } -func isValidLogin(users map[string]string, username, password string) bool { +// IsValidLogin lets you check directly whether a particular username and +// password pair is valid for the UsersMap in question +func (users UsersMap) IsValidLogin(username, password string) bool { if _, haveUser := users[username]; !haveUser { return false } @@ -425,13 +438,15 @@ func isValidLogin(users map[string]string, username, password string) bool { return err == nil } -func hasValidPlainAuth(r *http.Request, users map[string]string) bool { +// HasValidPlainAuth will return true if the request contains a basic auth +// header that validates one of the users in the UsersMap +func (users UsersMap) HasValidPlainAuth(r *http.Request) bool { username, passwd, ok := r.BasicAuth() if !ok { return false } - return isValidLogin(users, username, passwd) + return users.IsValidLogin(username, passwd) } // getCookie simply returns a cookie value if any, or an empty string if @@ -454,7 +469,10 @@ func respAskForBasicLogin(w http.ResponseWriter) { "Basic realm=\"Login\", charset=\"UTF-8\"") } -func authHandler(next http.Handler, users map[string]string, jwtKey string, +// AuthHandler is a fairly basic authentication middleware that validates +// credentials based on either a valid JWT in the 'Auth' cookie or +// Username/Password via Basic Auth +func (users UsersMap) AuthHandler(next http.Handler, jwtKey string, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { respUnAuth := func() { @@ -462,8 +480,8 @@ func authHandler(next http.Handler, users map[string]string, jwtKey string, sendJSON(Response{w, http.StatusUnauthorized, "Unauthorized", nil}) } - if !hasValidPlainAuth(r, users) && - !hasValidJWT(users, getCookie(r, "Auth"), jwtKey) { + if !users.HasValidPlainAuth(r) && + !users.HasValidJWT(getCookie(r, "Auth"), jwtKey) { respUnAuth() return @@ -473,7 +491,7 @@ func authHandler(next http.Handler, users map[string]string, jwtKey string, }) } -func loginHandler(users map[string]string, jwtKey string, sessionHours int, +func loginHandler(users UsersMap, jwtKey string, sessionHours int, ) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { @@ -491,7 +509,7 @@ func loginHandler(users map[string]string, jwtKey string, sessionHours int, username := r.PostFormValue("username") password := r.PostFormValue("password") - if !isValidLogin(users, username, password) { + if !users.IsValidLogin(username, password) { sendJSON(Response{w, http.StatusUnauthorized, "Invalid username or password", nil}) return @@ -622,12 +640,9 @@ func (a *App) HandleViewJSON() http.Handler { return } - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-type", "application/json") - enc := json.NewEncoder(w) - _ = enc.Encode(struct { - Content string - }{string(b)}) + sendJSON(Response{w, http.StatusOK, "Ok", struct{ Content string }{ + string(b), + }}) }) } @@ -718,9 +733,7 @@ func (a *App) HandleListJSON() http.Handler { pl = handleSkipLimitSort(pl, r.URL) - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - _ = enc.Encode(pl) + sendJSON(Response{w, http.StatusOK, "Ok", pl}) }) } |
