auth: auth flow complete on server-side

This commit is contained in:
Chris Sexton 2020-03-15 09:40:47 -04:00
parent ce02dca041
commit 1b8e5b4d70
4 changed files with 74 additions and 12 deletions

View File

@ -56,8 +56,12 @@ func New(db *db.Database, name, password string) (*User, error) {
return nil, err return nil, err
} }
invalidate := time.Now().Add(time.Duration(config.GetInt("invalidate.hours", 7*24)) * time.Hour) invalidate := time.Now().Add(time.Duration(config.GetInt("invalidate.hours", 7*24)) * time.Hour)
hash, err := bcrypt.GenerateFromPassword([]byte(password), config.GetInt("hash.cost", 16))
if err != nil {
return nil, err
}
res, err := db.Exec(q, name, password, key, invalidate) res, err := db.Exec(q, name, hash, key, invalidate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -96,6 +100,7 @@ func (u *User) Set(newPassword string) error {
func (u *User) Validate(password string) bool { func (u *User) Validate(password string) bool {
err := bcrypt.CompareHashAndPassword(u.Hash, []byte(password)) err := bcrypt.CompareHashAndPassword(u.Hash, []byte(password))
if err != nil { if err != nil {
log.Debug().Err(err).Msg("incorrect credentials")
return false return false
} }
return true return true

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"os" "os"
"code.chrissexton.org/cws/cabinet/auth"
"code.chrissexton.org/cws/cabinet/entry" "code.chrissexton.org/cws/cabinet/entry"
"code.chrissexton.org/cws/cabinet/db" "code.chrissexton.org/cws/cabinet/db"
@ -45,6 +46,9 @@ func main() {
if err := entry.PrepareTable(tx); err != nil { if err := entry.PrepareTable(tx); err != nil {
log.Fatal().Err(err).Msg("could not create database") log.Fatal().Err(err).Msg("could not create database")
} }
if err = auth.PrepareTable(tx); err != nil {
log.Fatal().Err(err).Msg("could not create database")
}
tx.Commit() tx.Commit()
s := web.New(*httpAddr, db, box) s := web.New(*httpAddr, db, box)

View File

@ -2,9 +2,12 @@ package web
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"code.chrissexton.org/cws/cabinet/auth" "code.chrissexton.org/cws/cabinet/auth"
"code.chrissexton.org/cws/cabinet/config"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -19,7 +22,21 @@ func (web *Web) auth(w http.ResponseWriter, r *http.Request) {
} }
if user.Validate(password) { if user.Validate(password) {
resp := struct {
Status bool
User auth.User
}{
true,
*user,
}
j, err := json.Marshal(resp)
if err != nil {
w.WriteHeader(500)
log.Error().Err(err).Msg("Error encoding json response")
return
}
w.WriteHeader(200) w.WriteHeader(200)
w.Write(j)
return return
} }
w.WriteHeader(401) w.WriteHeader(401)
@ -30,7 +47,35 @@ func (web *Web) auth(w http.ResponseWriter, r *http.Request) {
j, err := json.Marshal(resp) j, err := json.Marshal(resp)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
log.Error().Err(err).Msg("Error") log.Error().Err(err).Msg("Error encoding json response")
return
} }
w.Write(j) w.Write(j)
} }
func (web *Web) newUser(w http.ResponseWriter, r *http.Request) {
secret := r.Header.Get("X-secret")
if secret != config.Get("secret", "abc123") {
w.WriteHeader(401)
return
}
dec := json.NewDecoder(r.Body)
req := struct {
Username string
Password string
}{}
err := dec.Decode(&req)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
return
}
_, err = auth.New(web.db, req.Username, req.Password)
if err != nil {
w.WriteHeader(500)
fmt.Fprint(w, err)
log.Error().Err(err).Msg("Could not create user")
return
}
w.WriteHeader(200)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"code.chrissexton.org/cws/cabinet/auth"
"code.chrissexton.org/cws/cabinet/db" "code.chrissexton.org/cws/cabinet/db"
packr "github.com/gobuffalo/packr/v2" packr "github.com/gobuffalo/packr/v2"
@ -38,15 +39,20 @@ func New(addr string, db *db.Database, box *packr.Box) *Web {
return w return w
} }
type AuthMiddleware struct{} type AuthMiddleware struct {
db *db.Database
}
func (aw *AuthMiddleware) Middleware(next http.Handler) http.Handler { func (aw *AuthMiddleware) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Auth-Key") == "" { key := r.Header.Get("X-Auth-Key")
u, err := auth.GetByKey(aw.db, key)
if key == "" || err != nil {
w.WriteHeader(401) w.WriteHeader(401)
fmt.Fprint(w, "invalid login") fmt.Fprint(w, "invalid login")
return
} }
log.Debug().Msgf("This shit is authed!") log.Debug().Msgf("This shit is authed to user %s!", u.Name)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
@ -54,7 +60,7 @@ func (aw *AuthMiddleware) Middleware(next http.Handler) http.Handler {
func (web *Web) routeSetup() http.Handler { func (web *Web) routeSetup() http.Handler {
r := mux.NewRouter() r := mux.NewRouter()
api := r.PathPrefix("/v1/").Subrouter() api := r.PathPrefix("/v1/").Subrouter()
auth := AuthMiddleware{} auth := AuthMiddleware{web.db}
authedApi := r.PathPrefix("/v1/").Subrouter() authedApi := r.PathPrefix("/v1/").Subrouter()
authedApi.Use(auth.Middleware) authedApi.Use(auth.Middleware)
@ -69,16 +75,18 @@ func (web *Web) routeSetup() http.Handler {
// curl 'http://127.0.0.1:8080/v1/test' -X POST -H 'Accept: application/json, text/plain, */*' --compressed -H 'Content-Type: application/json;charset=utf-8' --data '{ "test": 1 }' // curl 'http://127.0.0.1:8080/v1/test' -X POST -H 'Accept: application/json, text/plain, */*' --compressed -H 'Content-Type: application/json;charset=utf-8' --data '{ "test": 1 }'
api.HandleFunc("/entries", web.allEntries).Methods(http.MethodGet) authedApi.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost)
api.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost) authedApi.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost).
api.HandleFunc("/entries", web.newEntry).Methods(http.MethodPost).
HeadersRegexp("Content-Type", "application/(text|json).*") HeadersRegexp("Content-Type", "application/(text|json).*")
api.HandleFunc("/entries", web.newMarkdownEntry).Methods(http.MethodPost). authedApi.HandleFunc("/entries", web.newMarkdownEntry).Methods(http.MethodPost).
HeadersRegexp("Content-Type", "application/markdown.*") HeadersRegexp("Content-Type", "application/markdown.*")
api.HandleFunc("/entries/{slug}", web.removeEntry).Methods(http.MethodDelete) authedApi.HandleFunc("/entries/{slug}", web.removeEntry).Methods(http.MethodDelete)
api.HandleFunc("/entries/{slug}", web.editEntry).Methods(http.MethodPut) authedApi.HandleFunc("/entries/{slug}", web.editEntry).Methods(http.MethodPut)
api.HandleFunc("/entries/{slug}", web.getEntry).Methods(http.MethodGet) api.HandleFunc("/entries/{slug}", web.getEntry).Methods(http.MethodGet)
api.HandleFunc("/entries", web.allEntries).Methods(http.MethodGet)
api.HandleFunc("/auth/new", web.newUser).Methods(http.MethodPost)
api.HandleFunc("/auth", web.auth).Methods(http.MethodPost) api.HandleFunc("/auth", web.auth).Methods(http.MethodPost)
r.PathPrefix("/").HandlerFunc(web.indexHandler("/index.html")) r.PathPrefix("/").HandlerFunc(web.indexHandler("/index.html"))
loggedRouter := handlers.LoggingHandler(os.Stdout, r) loggedRouter := handlers.LoggingHandler(os.Stdout, r)