fine tune existing and add more middleware
This commit is contained in:
parent
1dd23fe176
commit
2f41f53ebf
7 changed files with 98 additions and 19 deletions
|
@ -36,12 +36,12 @@ Features may be added here at any time as things are in early stages right now:
|
||||||
|
|
||||||
### etc
|
### etc
|
||||||
|
|
||||||
* [ ] middleware for easing auth flow:
|
* [x] middleware for easing auth flow:
|
||||||
- [x] `Protected`: require login
|
- [x] `Protected`: require login
|
||||||
- [x] `Authorize`: login and redirect
|
- [x] `Authorize`: login and redirect
|
||||||
- [ ] `Bunt`: logout and redirect
|
- [x] `Bunt`: logout and redirect
|
||||||
- [ ] `Fortify`: setup CSRF protection (use on the form)
|
- [x] `Fortify`: setup CSRF protection (use on the form)
|
||||||
- [ ] `Defend`: enact CSRF protection (use on the endpoint)
|
- [x] `Defend`: enact CSRF protection (use on the endpoint)
|
||||||
* [ ] generic DAL wrapper? might be unneccessary
|
* [ ] generic DAL wrapper? might be unneccessary
|
||||||
|
|
||||||
## license
|
## license
|
||||||
|
|
|
@ -23,6 +23,8 @@ type UserStore interface {
|
||||||
AddUser(user string, password string) error
|
AddUser(user string, password string) error
|
||||||
DeleteUser(user string) error
|
DeleteUser(user string) error
|
||||||
ChangePassword(user string, oldPassword string, newPassword string) error
|
ChangePassword(user string, oldPassword string, newPassword string) error
|
||||||
|
GetLastLoginTime(user string) (time.Time, error)
|
||||||
|
GetLastTimeSeen(user string) (time.Time, error)
|
||||||
SetData(user string, key string, value interface{}) error
|
SetData(user string, key string, value interface{}) error
|
||||||
GetData(user string, key string) (interface{}, error)
|
GetData(user string, key string) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +34,9 @@ func Login(user string, password string, userStore UserStore, w http.ResponseWri
|
||||||
if loginErr == nil {
|
if loginErr == nil {
|
||||||
cookie.StoreToken("user", user, w, t)
|
cookie.StoreToken("user", user, w, t)
|
||||||
cookie.StoreToken("session", session, w, t)
|
cookie.StoreToken("session", session, w, t)
|
||||||
|
csrfToken := cookie.GenToken(64)
|
||||||
|
cookie.StoreToken("csrfToken", csrfToken, w, t)
|
||||||
|
userStore.SetData(user, "csrfToken", csrfToken)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return loginErr
|
return loginErr
|
||||||
|
@ -42,6 +47,8 @@ func Logout(user string, userStore UserStore, w http.ResponseWriter) error {
|
||||||
if logoutErr == nil {
|
if logoutErr == nil {
|
||||||
cookie.StoreToken("user", "", w, 0)
|
cookie.StoreToken("user", "", w, 0)
|
||||||
cookie.StoreToken("session", "", w, 0)
|
cookie.StoreToken("session", "", w, 0)
|
||||||
|
cookie.StoreToken("csrfToken", "", w, 0)
|
||||||
|
userStore.SetData(user, "csrfToken", "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return logoutErr
|
return logoutErr
|
||||||
|
|
|
@ -117,6 +117,20 @@ func (self *IndentalUserDB) AddUser(user string, password string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *IndentalUserDB) GetLastLoginTime(user string) (time.Time, error) {
|
||||||
|
if usr, exists := self.Users[user]; exists {
|
||||||
|
return usr.LoginTime, nil
|
||||||
|
}
|
||||||
|
return time.UnixMicro(0), errors.New("User not in DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *IndentalUserDB) GetLastTimeSeen(user string) (time.Time, error) {
|
||||||
|
if usr, exists := self.Users[user]; exists {
|
||||||
|
return usr.LastSeen, nil
|
||||||
|
}
|
||||||
|
return time.UnixMicro(0), errors.New("User not in DB")
|
||||||
|
}
|
||||||
|
|
||||||
func (self *IndentalUserDB) SetData(user string, key string, value interface{}) error {
|
func (self *IndentalUserDB) SetData(user string, key string, value interface{}) error {
|
||||||
if _, exists := self.Users[user]; !exists {
|
if _, exists := self.Users[user]; !exists {
|
||||||
return errors.New("User not in DB")
|
return errors.New("User not in DB")
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"nilfm.cc/git/quartzgun/cookie"
|
"nilfm.cc/git/quartzgun/cookie"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Protected(next http.Handler, method string, userStore auth.UserStore) http.Handler {
|
func Protected(next http.Handler, method string, userStore auth.UserStore, login string) http.Handler {
|
||||||
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
user, err := cookie.GetToken("user", req)
|
user, err := cookie.GetToken("user", req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -26,13 +26,34 @@ func Protected(next http.Handler, method string, userStore auth.UserStore) http.
|
||||||
}
|
}
|
||||||
fmt.Printf("unauthorized...\n")
|
fmt.Printf("unauthorized...\n")
|
||||||
req.Method = http.MethodGet
|
req.Method = http.MethodGet
|
||||||
http.Redirect(w, req, "/login", http.StatusSeeOther)
|
http.Redirect(w, req, login, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Authorize(next string, userStore auth.UserStore) http.Handler {
|
func Bunt(next string, userStore auth.UserStore, denied string) http.Handler {
|
||||||
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
user, err := cookie.GetToken("user", req)
|
||||||
|
if err == nil {
|
||||||
|
err := auth.Logout(
|
||||||
|
user,
|
||||||
|
userStore,
|
||||||
|
w)
|
||||||
|
if err == nil {
|
||||||
|
req.Method = http.MethodGet
|
||||||
|
http.Redirect(w, req, next, http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Method = http.MethodGet
|
||||||
|
http.Redirect(w, req, denied, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Authorize(next string, userStore auth.UserStore, denied string) http.Handler {
|
||||||
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
err := auth.Login(
|
err := auth.Login(
|
||||||
req.FormValue("user"),
|
req.FormValue("user"),
|
||||||
|
@ -45,16 +66,49 @@ func Authorize(next string, userStore auth.UserStore) http.Handler {
|
||||||
fmt.Printf("logged in as %s\n", req.FormValue("user"))
|
fmt.Printf("logged in as %s\n", req.FormValue("user"))
|
||||||
http.Redirect(w, req, next, http.StatusSeeOther)
|
http.Redirect(w, req, next, http.StatusSeeOther)
|
||||||
} else {
|
} else {
|
||||||
*req = *req.WithContext(
|
|
||||||
context.WithValue(
|
|
||||||
req.Context(),
|
|
||||||
"message",
|
|
||||||
"Incorrect credentials"))
|
|
||||||
fmt.Printf("login failed!\n")
|
fmt.Printf("login failed!\n")
|
||||||
req.Method = http.MethodGet
|
req.Method = http.MethodGet
|
||||||
http.Redirect(w, req, "/login", http.StatusSeeOther)
|
http.Redirect(w, req, denied, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Fortify(next http.Handler) http.Handler {
|
||||||
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
token, err := cookie.GetToken("csrfToken", req)
|
||||||
|
if err == nil {
|
||||||
|
*req = *req.WithContext(
|
||||||
|
context.WithValue(
|
||||||
|
req.Context(),
|
||||||
|
"csrfToken",
|
||||||
|
token))
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Defend(next http.Handler, userStore auth.UserStore, denied string) http.Handler {
|
||||||
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
user, err := cookie.GetToken("user", req)
|
||||||
|
if err == nil {
|
||||||
|
masterToken, err := userStore.GetData(user, "csrfToken")
|
||||||
|
if err == nil {
|
||||||
|
cookieToken, err := cookie.GetToken("csrfToken", req)
|
||||||
|
if err == nil {
|
||||||
|
formToken := req.FormValue("csrfToken")
|
||||||
|
if formToken == cookieToken && formToken == masterToken.(string) {
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.Redirect(w, req, denied, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
}
|
||||||
|
|
|
@ -47,11 +47,11 @@ func TestMain(m *testing.M) {
|
||||||
rtr.Get("/login", renderer.Template(
|
rtr.Get("/login", renderer.Template(
|
||||||
"testData/templates/login.html"))
|
"testData/templates/login.html"))
|
||||||
|
|
||||||
rtr.Post("/login", middleware.Authorize("/", udb))
|
rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1"))
|
||||||
|
|
||||||
rtr.Get("/", middleware.Protected(
|
rtr.Get("/", middleware.Protected(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
"testData/templates/test.html"), http.MethodGet, udb))
|
"testData/templates/test.html"), http.MethodGet, udb, "/login"))
|
||||||
|
|
||||||
rtr.Get("/json", ApiSomething(renderer.JSON("apiData")))
|
rtr.Get("/json", ApiSomething(renderer.JSON("apiData")))
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package renderer
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +12,10 @@ func Template(t ...string) http.Handler {
|
||||||
tmpl := template.Must(template.ParseFiles(t...))
|
tmpl := template.Must(template.ParseFiles(t...))
|
||||||
|
|
||||||
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
tmpl.Execute(w, req)
|
err := tmpl.Execute(w, req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{ $errorMsg := (.Context).Value "message" }}
|
{{ $tryagain := .FormValue "tryagain" }}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang='en'>
|
<html lang='en'>
|
||||||
|
@ -9,8 +9,8 @@
|
||||||
<title>Nirvash — Login</title>
|
<title>Nirvash — Login</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{ if $errorMsg }}
|
{{ if $tryagain }}
|
||||||
<div class="error">{{ $errorMsg }}</div>
|
<div class="error">Incorrect credentials; please try again.</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<form action='/login' method='post'>
|
<form action='/login' method='post'>
|
||||||
<input type="text" name="user" placeholder="user">
|
<input type="text" name="user" placeholder="user">
|
||||||
|
|
Loading…
Reference in a new issue