fix(singleuser): fix bugs in single user handlers

This commit is contained in:
hexxa 2020-12-05 22:00:20 +08:00
parent 31a1a331f7
commit 4d6e7ff938
10 changed files with 194 additions and 45 deletions

View file

@ -2,15 +2,30 @@ package singleuserhdr
import (
"errors"
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/ihexxa/gocfg"
"golang.org/x/crypto/bcrypt"
"github.com/ihexxa/quickshare/src/depidx"
q "github.com/ihexxa/quickshare/src/handlers"
)
var ErrInvalidUser = errors.New("invalid user name or password")
var (
ErrInvalidUser = errors.New("invalid user name or password")
ErrInvalidConfig = errors.New("invalid user config")
UserParam = "user"
PwdParam = "pwd"
RoleParam = "role"
ExpireParam = "expire"
TokenCookie = "tk"
AdminRole = "admin"
VisitorRole = "visitor"
UsersNamespace = "users"
RolesNamespace = "roles"
)
type SimpleUserHandlers struct {
cfg gocfg.ICfg
@ -24,52 +39,52 @@ func NewSimpleUserHandlers(cfg gocfg.ICfg, deps *depidx.Deps) *SimpleUserHandler
}
}
func (hdr *SimpleUserHandlers) Login(c *gin.Context) {
userName := c.Query("username")
pwd := c.Query("pwd")
if userName == "" || pwd == "" {
c.JSON(q.ErrResp(c, 400, ErrInvalidUser))
return
}
expectedName, ok1 := hdr.deps.KV().GetString("username")
expectedPwd, ok2 := hdr.deps.KV().GetString("pwd")
func (h *SimpleUserHandlers) Login(c *gin.Context) {
user, ok1 := c.GetPostForm(UserParam)
pwd, ok2 := c.GetPostForm(PwdParam)
if !ok1 || !ok2 {
c.JSON(q.ErrResp(c, 400, ErrInvalidUser))
c.JSON(q.ErrResp(c, 401, ErrInvalidUser))
return
}
if userName != expectedName || pwd != expectedPwd {
c.JSON(q.ErrResp(c, 400, ErrInvalidUser))
expectedHash, ok := h.deps.KV().GetStringIn(UsersNamespace, user)
if !ok {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return
}
token, err := hdr.deps.Token().ToToken(map[string]string{
"username": expectedName,
err := bcrypt.CompareHashAndPassword([]byte(expectedHash), []byte(pwd))
if err != nil {
c.JSON(q.ErrResp(c, 401, ErrInvalidUser))
return
}
role, ok := h.deps.KV().GetStringIn(RolesNamespace, user)
if !ok {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return
}
ttl := h.cfg.GrabInt("Users.CookieTTL")
token, err := h.deps.Token().ToToken(map[string]string{
UserParam: user,
RoleParam: role,
ExpireParam: fmt.Sprintf("%d", time.Now().Unix()+int64(ttl)),
})
if err != nil {
c.JSON(q.ErrResp(c, 500, err))
return
}
// TODO: use config
c.SetCookie("token", token, 3600, "/", "localhost", false, true)
hostname := h.cfg.GrabString("Server.Host")
secure := h.cfg.GrabBool("Users.CookieSecure")
httpOnly := h.cfg.GrabBool("Users.CookieHttpOnly")
c.SetCookie(TokenCookie, token, ttl, "/", hostname, secure, httpOnly)
c.JSON(q.Resp(200))
}
func (hdr *SimpleUserHandlers) Logout(c *gin.Context) {
token, err := c.Cookie("token")
if err != nil {
c.JSON(q.ErrResp(c, 400, err))
return
}
// TODO: // check if token expired
_, err = hdr.deps.Token().FromToken(token, map[string]string{"token": ""})
if err != nil {
c.JSON(q.ErrResp(c, 400, err))
return
}
c.SetCookie("token", "", 0, "/", "localhost", false, true)
func (h *SimpleUserHandlers) Logout(c *gin.Context) {
// token alreay verified in the authn middleware
c.SetCookie(TokenCookie, "", 0, "/", "nohost", false, true)
c.JSON(q.Resp(200))
}

View file

@ -0,0 +1,65 @@
package singleuserhdr
import (
"errors"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
q "github.com/ihexxa/quickshare/src/handlers"
)
func GetHandlerName(fullname string) (string, error) {
parts := strings.Split(fullname, ".")
if len(parts) == 0 {
return "", errors.New("invalid handler name")
}
return parts[len(parts)-1], nil
}
func (h *SimpleUserHandlers) Auth() gin.HandlerFunc {
return func(c *gin.Context) {
handlerName, err := GetHandlerName(c.HandlerName())
if err != nil {
c.JSON(q.ErrResp(c, 401, err))
return
}
// TODO: may also check the path
enableAuth := h.cfg.GrabBool("Users.EnableAuth")
if enableAuth && handlerName != "Login-fm" {
token, err := c.Cookie(TokenCookie)
if err != nil {
c.JSON(q.ErrResp(c, 401, err))
return
}
claims := map[string]string{
UserParam: "",
RoleParam: "",
ExpireParam: "",
}
_, err = h.deps.Token().FromToken(token, claims)
if err != nil {
c.JSON(q.ErrResp(c, 401, err))
return
}
now := time.Now().Unix()
expire, err := strconv.ParseInt(claims[ExpireParam], 10, 64)
if err != nil || expire <= now {
c.JSON(q.ErrResp(c, 401, err))
return
}
// visitor is only allowed to download
if claims[UserParam] != AdminRole && handlerName != "Download-fm" {
c.JSON(q.ErrResp(c, 401, err))
return
}
}
c.Next()
}
}