use TTL with LastSeen instead of hard expiry time
This commit is contained in:
parent
48dbb967f3
commit
92f0f035a9
5 changed files with 36 additions and 38 deletions
|
@ -17,7 +17,7 @@ type User struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserStore interface {
|
type UserStore interface {
|
||||||
InitiateSession(user string, password string) (string, error)
|
InitiateSession(user string, password string, ttl int) (string, error)
|
||||||
ValidateUser(user string, sessionId string) (bool, error)
|
ValidateUser(user string, sessionId string) (bool, error)
|
||||||
EndSession(user string) error
|
EndSession(user string) error
|
||||||
AddUser(user string, password string) error
|
AddUser(user string, password string) error
|
||||||
|
@ -27,13 +27,13 @@ type UserStore interface {
|
||||||
GetLastTimeSeen(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)
|
||||||
GrantToken(user, password, scope string, minutes int) (string, error)
|
GrantToken(user, password string, ttl int) (string, error)
|
||||||
ValidateToken(token string) (bool, error)
|
ValidateToken(token string) (bool, error)
|
||||||
ValidateTokenWithScopes(token string, scopes map[string]string) (bool, error)
|
ValidateTokenWithScopes(token string, scopes map[string]string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Login(user string, password string, userStore UserStore, w http.ResponseWriter, t int) error {
|
func Login(user string, password string, userStore UserStore, w http.ResponseWriter, t int) error {
|
||||||
session, loginErr := userStore.InitiateSession(user, password)
|
session, loginErr := userStore.InitiateSession(user, password, t)
|
||||||
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)
|
||||||
|
|
|
@ -18,11 +18,11 @@ func GenToken(length int) string {
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreToken(field string, token string, w http.ResponseWriter, hrs int) {
|
func StoreToken(field string, token string, w http.ResponseWriter, ttl int) {
|
||||||
cookie := http.Cookie{
|
cookie := http.Cookie{
|
||||||
Name: field,
|
Name: field,
|
||||||
Value: token,
|
Value: token,
|
||||||
Expires: time.Now().Add(time.Duration(hrs) * time.Hour),
|
Expires: time.Now().Add(time.Duration(ttl) * time.Minute),
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"nilfm.cc/git/quartzgun/cookie"
|
"nilfm.cc/git/quartzgun/cookie"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ func CreateIndentalUserDB(filePath string) *IndentalUserDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *IndentalUserDB) InitiateSession(user string, password string) (string, error) {
|
func (self *IndentalUserDB) InitiateSession(user string, password string, ttl int) (string, 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")
|
||||||
}
|
}
|
||||||
|
@ -47,42 +48,45 @@ func (self *IndentalUserDB) InitiateSession(user string, password string) (strin
|
||||||
self.Users[user].Session = sessionId
|
self.Users[user].Session = sessionId
|
||||||
self.Users[user].LoginTime = time.Now()
|
self.Users[user].LoginTime = time.Now()
|
||||||
self.Users[user].LastSeen = time.Now()
|
self.Users[user].LastSeen = time.Now()
|
||||||
|
self.SetData(user, "token_expiry", strconv.Itoa(ttl))
|
||||||
writeDB(self.Basis, self.Users)
|
writeDB(self.Basis, self.Users)
|
||||||
return sessionId, nil
|
return sessionId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *IndentalUserDB) GrantToken(user, password, scope string, minutes int) (string, error) {
|
func (self *IndentalUserDB) GrantToken(user, password string, ttl int) (string, 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")
|
||||||
}
|
}
|
||||||
if bcrypt.CompareHashAndPassword([]byte(self.Users[user].Pass), []byte(password)) != nil {
|
if bcrypt.CompareHashAndPassword([]byte(self.Users[user].Pass), []byte(password)) != nil {
|
||||||
return "", errors.New("Incorrect password")
|
return "", errors.New("Incorrect password")
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := self.GetData(user, "scope")
|
|
||||||
if err == nil && s == scope {
|
|
||||||
sessionId := cookie.GenToken(64)
|
sessionId := cookie.GenToken(64)
|
||||||
self.Users[user].Session = sessionId
|
self.Users[user].Session = sessionId
|
||||||
self.Users[user].LoginTime = time.Now()
|
self.Users[user].LoginTime = time.Now()
|
||||||
self.Users[user].LastSeen = time.Now()
|
self.Users[user].LastSeen = time.Now()
|
||||||
self.SetData(user, "token_expiry", time.Now().Add(time.Minute*time.Duration(minutes)).Format(timeFmt))
|
self.SetData(user, "token_expiry", strconv.Itoa(ttl))
|
||||||
writeDB(self.Basis, self.Users)
|
writeDB(self.Basis, self.Users)
|
||||||
return base64.StdEncoding.EncodeToString([]byte(user + "\n" + sessionId)), nil
|
return base64.StdEncoding.EncodeToString([]byte(user + "\n" + sessionId)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("Incorrect scope for this user")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *IndentalUserDB) ValidateUser(user string, sessionId string) (bool, error) {
|
func (self *IndentalUserDB) ValidateUser(user string, sessionId string) (bool, error) {
|
||||||
if _, exists := self.Users[user]; !exists {
|
if _, exists := self.Users[user]; !exists {
|
||||||
return false, errors.New("User not in DB")
|
return false, errors.New("User not in DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
validated := self.Users[user].Session == sessionId
|
validated := self.Users[user].Session == sessionId
|
||||||
|
expiry, err3 := self.GetData(user, "token_expiry")
|
||||||
|
expiryInt, err4 := strconv.ParseInt(expiry.(string), 10, 64)
|
||||||
|
expiryTime := self.Users[user].LastSeen.Add(time.Minute * time.Duration(expiryInt))
|
||||||
if validated {
|
if validated {
|
||||||
|
if err3 == nil && err4 == nil && time.Now().After(expiryTime) {
|
||||||
|
self.EndSession(user)
|
||||||
|
return true, errors.New("Cookie or token expired")
|
||||||
|
} else {
|
||||||
self.Users[user].LastSeen = time.Now()
|
self.Users[user].LastSeen = time.Now()
|
||||||
writeDB(self.Basis, self.Users)
|
writeDB(self.Basis, self.Users)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return validated, nil
|
return validated, nil
|
||||||
}
|
}
|
||||||
|
@ -92,16 +96,9 @@ func (self *IndentalUserDB) ValidateToken(token string) (bool, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
parts := strings.Split(string(data), "\n")
|
parts := strings.Split(string(data), "\n")
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
expiry, err3 := self.GetData(parts[0], "token_expiry")
|
|
||||||
expiryTime, err4 := time.Parse(timeFmt, expiry.(string))
|
|
||||||
if err3 == nil && err4 == nil && time.Now().After(expiryTime) {
|
|
||||||
self.EndSession(parts[0])
|
|
||||||
return false, errors.New("token has expired")
|
|
||||||
} else {
|
|
||||||
return self.ValidateUser(parts[0], parts[1])
|
return self.ValidateUser(parts[0], parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false, errors.New("Token was not in a valid format: b64(USER\nSESSION)")
|
return false, errors.New("Token was not in a valid format: b64(USER\nSESSION)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +209,7 @@ func (self *IndentalUserDB) GetData(user string, key string) (interface{}, error
|
||||||
}
|
}
|
||||||
data, exists := self.Users[user].Data[key]
|
data, exists := self.Users[user].Data[key]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, errors.New("No data key for user")
|
return nil, errors.New("Key not found in user data")
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|
|
@ -29,6 +29,8 @@ func Protected(next http.Handler, method string, userStore auth.UserStore, login
|
||||||
req.Method = method
|
req.Method = method
|
||||||
next.ServeHTTP(w, req)
|
next.ServeHTTP(w, req)
|
||||||
return
|
return
|
||||||
|
} else if err != nil && err.Error() == "Cookie or token expired"{
|
||||||
|
auth.Logout(user, userStore, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,14 +62,14 @@ func Bunt(next string, userStore auth.UserStore, denied string) http.Handler {
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Authorize(next string, userStore auth.UserStore, denied string) http.Handler {
|
func Authorize(next string, userStore auth.UserStore, denied string, ttl int) 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"),
|
||||||
req.FormValue("password"),
|
req.FormValue("password"),
|
||||||
userStore,
|
userStore,
|
||||||
w,
|
w,
|
||||||
24*7*52)
|
ttl)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
req.Method = http.MethodGet
|
req.Method = http.MethodGet
|
||||||
fmt.Printf("logged in as %s\n", req.FormValue("user"))
|
fmt.Printf("logged in as %s\n", req.FormValue("user"))
|
||||||
|
@ -82,17 +84,16 @@ func Authorize(next string, userStore auth.UserStore, denied string) http.Handle
|
||||||
return http.HandlerFunc(handlerFunc)
|
return http.HandlerFunc(handlerFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Provision(userStore auth.UserStore, minutes int) http.Handler {
|
func Provision(userStore auth.UserStore, ttl int) http.Handler {
|
||||||
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
handlerFunc := func(w http.ResponseWriter, req *http.Request) {
|
||||||
user, password, ok := req.BasicAuth()
|
user, password, ok := req.BasicAuth()
|
||||||
scope := req.FormValue("scope")
|
if ok {
|
||||||
if ok && scope != "" {
|
token, err := userStore.GrantToken(user, password, ttl)
|
||||||
token, err := userStore.GrantToken(user, password, scope, minutes)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
token := TokenPayload{
|
token := TokenPayload{
|
||||||
access_token: token,
|
access_token: token,
|
||||||
token_type: "bearer",
|
token_type: "bearer",
|
||||||
expires_in: minutes,
|
expires_in: ttl,
|
||||||
}
|
}
|
||||||
util.AddContextValue(req, "token", token)
|
util.AddContextValue(req, "token", token)
|
||||||
renderer.JSON("token").ServeHTTP(w, req)
|
renderer.JSON("token").ServeHTTP(w, req)
|
||||||
|
|
|
@ -34,7 +34,7 @@ func ApiSomething(next http.Handler) http.Handler {
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
udb := indentalUserDB.CreateIndentalUserDB("testData/userDB.ndtl")
|
udb := indentalUserDB.CreateIndentalUserDB("testData/userDB.ndtl")
|
||||||
udb.AddUser("nilix", "questing")
|
udb.AddUser("nilix", "questing")
|
||||||
sesh, _ := udb.InitiateSession("nilix", "questing")
|
sesh, _ := udb.InitiateSession("nilix", "questing", 60)
|
||||||
|
|
||||||
fmt.Printf("%s // %s\n", sesh, sesh)
|
fmt.Printf("%s // %s\n", sesh, sesh)
|
||||||
rtr := &router.Router{
|
rtr := &router.Router{
|
||||||
|
@ -47,7 +47,7 @@ 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, "/login?tryagain=1"))
|
rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1", 120))
|
||||||
|
|
||||||
rtr.Get("/", middleware.Protected(
|
rtr.Get("/", middleware.Protected(
|
||||||
renderer.Template(
|
renderer.Template(
|
||||||
|
|
Loading…
Reference in a new issue