feat(be/fe): enable captcha (#69)

* feat(ui): enable captcha

* feat(server): enable captcha

* fix(ui): fix login pane layout

* fix(config): remove unused config and files

* fix(be/fe): clean up code

* chore(fe/be): clean up code
This commit is contained in:
Hexxa 2021-08-06 22:27:24 -05:00 committed by GitHub
parent 021e5090be
commit 1fcb2223a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 262 additions and 82 deletions

View file

@ -0,0 +1,42 @@
package multiusers
import (
"bytes"
"errors"
"github.com/dchest/captcha"
"github.com/gin-gonic/gin"
q "github.com/ihexxa/quickshare/src/handlers"
)
type GetCaptchaIDResp struct {
CaptchaID string `json:"id"`
}
func (h *MultiUsersSvc) GetCaptchaID(c *gin.Context) {
captchaID := captcha.New()
c.JSON(200, &GetCaptchaIDResp{CaptchaID: captchaID})
}
// path: /captchas/imgs?id=xxx
func (h *MultiUsersSvc) GetCaptchaImg(c *gin.Context) {
captchaID := c.Query(q.CaptchaIDParam)
if captchaID == "" {
c.JSON(q.ErrResp(c, 400, errors.New("empty captcha ID")))
return
}
capWidth := h.cfg.IntOr("Users.CaptchaWidth", 256)
capHeight := h.cfg.IntOr("Users.CaptchaHeight", 64)
// TODO: improve performance
buf := new(bytes.Buffer)
err := captcha.WriteImage(buf, captchaID, capWidth, capHeight)
if err != nil {
c.JSON(q.ErrResp(c, 500, err))
return
}
c.Data(200, "image/png", buf.Bytes())
}

View file

@ -7,6 +7,7 @@ import (
"strconv"
"time"
"github.com/dchest/captcha"
"github.com/gin-gonic/gin"
"github.com/ihexxa/gocfg"
"golang.org/x/crypto/bcrypt"
@ -61,6 +62,8 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error)
apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/uploadings"): true,
apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/metadata"): true,
apiRuleCname(userstore.AdminRole, "OPTIONS", "/v1/settings/health"): true,
apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/"): true,
apiRuleCname(userstore.AdminRole, "GET", "/v1/captchas/imgs"): true,
// user rules
apiRuleCname(userstore.UserRole, "GET", "/"): true,
apiRuleCname(userstore.UserRole, "GET", publicPath): true,
@ -82,6 +85,8 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error)
apiRuleCname(userstore.UserRole, "DELETE", "/v1/fs/uploadings"): true,
apiRuleCname(userstore.UserRole, "GET", "/v1/fs/metadata"): true,
apiRuleCname(userstore.UserRole, "OPTIONS", "/v1/settings/health"): true,
apiRuleCname(userstore.UserRole, "GET", "/v1/captchas/"): true,
apiRuleCname(userstore.UserRole, "GET", "/v1/captchas/imgs"): true,
// visitor rules
apiRuleCname(userstore.VisitorRole, "GET", "/"): true,
apiRuleCname(userstore.VisitorRole, "GET", publicPath): true,
@ -90,6 +95,8 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error)
apiRuleCname(userstore.VisitorRole, "GET", "/v1/users/self"): true,
apiRuleCname(userstore.VisitorRole, "GET", "/v1/fs/files"): true,
apiRuleCname(userstore.VisitorRole, "OPTIONS", "/v1/settings/health"): true,
apiRuleCname(userstore.VisitorRole, "GET", "/v1/captchas/"): true,
apiRuleCname(userstore.VisitorRole, "GET", "/v1/captchas/imgs"): true,
}
return &MultiUsersSvc{
@ -122,8 +129,10 @@ func (h *MultiUsersSvc) IsInited() bool {
}
type LoginReq struct {
User string `json:"user"`
Pwd string `json:"pwd"`
User string `json:"user"`
Pwd string `json:"pwd"`
CaptchaID string `json:"captchaId"`
CaptchaInput string `json:"captchaInput"`
}
func (h *MultiUsersSvc) Login(c *gin.Context) {
@ -133,6 +142,15 @@ func (h *MultiUsersSvc) Login(c *gin.Context) {
return
}
// TODO: add rate limiter for verifying
captchaEnabled := h.cfg.BoolOr("Users.CaptchaEnabled", true)
if captchaEnabled {
if !captcha.VerifyString(req.CaptchaID, req.CaptchaInput) {
c.JSON(q.ErrResp(c, 403, errors.New("login failed")))
return
}
}
user, err := h.deps.Users().GetUserByName(req.User)
if err != nil {
c.JSON(q.ErrResp(c, 500, err))

View file

@ -15,14 +15,15 @@ var (
FsDir = "files"
FsRootDir = "files"
UserIDParam = "uid"
UserParam = "user"
PwdParam = "pwd"
NewPwdParam = "newpwd"
RoleParam = "role"
ExpireParam = "expire"
TokenCookie = "tk"
LastID = "lid"
UserIDParam = "uid"
UserParam = "user"
PwdParam = "pwd"
NewPwdParam = "newpwd"
RoleParam = "role"
ExpireParam = "expire"
CaptchaIDParam = "capid"
TokenCookie = "tk"
LastID = "lid"
ErrAccessDenied = errors.New("access denied")
ErrUnauthorized = errors.New("unauthorized")