aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go59
1 files changed, 36 insertions, 23 deletions
diff --git a/main.go b/main.go
index c03a0d7..2e55100 100644
--- a/main.go
+++ b/main.go
@@ -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})
})
}