feat(users): add init functions

This commit is contained in:
hexxa 2020-12-06 12:17:29 +08:00
parent 57124451ad
commit 2bcb337b4c
7 changed files with 156 additions and 53 deletions

View file

@ -8,9 +8,10 @@ import (
) )
var opts struct { var opts struct {
host string `short:"h" long:"host" description:"server hostname"` Host string `short:"h" long:"host" description:"server hostname"`
port string `short:"f" long:"file" description:"A file"` Port int `short:"p" long:"port" description:"server port"`
configs []string `short:"c" description:"config path"` Debug bool `short:"d" long:"debug" description:"debug mode"`
Configs []string `short:"c" description:"config path"`
} }
func main() { func main() {
@ -18,10 +19,17 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
defaultCfg, err := server.DefaultConfig()
if err != nil {
panic(err)
}
cfg := gocfg.New(server.NewDefaultConfig()) cfg, err := gocfg.New(server.NewConfig()).Load(gocfg.JSONStr(defaultCfg))
if len(opts.configs) > 0 { if err != nil {
for _, configPath := range opts.configs { panic(err)
}
if len(opts.Configs) > 0 {
for _, configPath := range opts.Configs {
cfg, err = cfg.Load(gocfg.JSON(configPath)) cfg, err = cfg.Load(gocfg.JSON(configPath))
if err != nil { if err != nil {
panic(err) panic(err)
@ -29,6 +37,16 @@ func main() {
} }
} }
if opts.Host != "" {
cfg.SetString("Server.Host", opts.Host)
}
if opts.Port != 0 {
cfg.SetInt("Server.Port", opts.Port)
}
if opts.Debug {
cfg.SetBool("Server.Debug", opts.Debug)
}
srv, err := server.NewServer(cfg) srv, err := server.NewServer(cfg)
if err != nil { if err != nil {
panic(err) panic(err)

Binary file not shown.

View file

@ -1,6 +1,8 @@
package singleuserhdr package singleuserhdr
import ( import (
"crypto/rand"
"crypto/sha1"
"errors" "errors"
"fmt" "fmt"
"time" "time"
@ -21,11 +23,13 @@ var (
NewPwdParam = "newpwd" NewPwdParam = "newpwd"
RoleParam = "role" RoleParam = "role"
ExpireParam = "expire" ExpireParam = "expire"
InitTimeParam = "initTime"
TokenCookie = "tk" TokenCookie = "tk"
AdminRole = "admin" AdminRole = "admin"
VisitorRole = "visitor" VisitorRole = "visitor"
UsersNamespace = "users" InitNs = "usersInit"
RolesNamespace = "roles" UsersNs = "users"
RolesNs = "roles"
) )
type SimpleUserHandlers struct { type SimpleUserHandlers struct {
@ -33,11 +37,65 @@ type SimpleUserHandlers struct {
deps *depidx.Deps deps *depidx.Deps
} }
func NewSimpleUserHandlers(cfg gocfg.ICfg, deps *depidx.Deps) *SimpleUserHandlers { func NewSimpleUserHandlers(cfg gocfg.ICfg, deps *depidx.Deps) (*SimpleUserHandlers, error) {
var err error
if err = deps.KV().AddNamespace(InitNs); err != nil {
return nil, err
}
if err = deps.KV().AddNamespace(UsersNs); err != nil {
return nil, err
}
if err = deps.KV().AddNamespace(RolesNs); err != nil {
return nil, err
}
return &SimpleUserHandlers{ return &SimpleUserHandlers{
cfg: cfg, cfg: cfg,
deps: deps, deps: deps,
}, nil
}
func (h *SimpleUserHandlers) IsInited() bool {
_, ok := h.deps.KV().GetStringIn(InitNs, InitTimeParam)
return ok
}
func generatePwd() (string, error) {
size := 10
buf := make([]byte, size)
size, err := rand.Read(buf)
if err != nil {
return "", err
} }
return fmt.Sprintf("%x", sha1.Sum(buf[:size]))[:8], nil
}
func (h *SimpleUserHandlers) Init(userName string) (string, error) {
if userName == "" {
return "", errors.New("user name can not be empty")
}
var err error
tmpPwd, err := generatePwd()
if err != nil {
return "", err
}
err = h.deps.KV().SetStringIn(UsersNs, userName, tmpPwd)
if err != nil {
return "", err
}
err = h.deps.KV().SetStringIn(RolesNs, RoleParam, AdminRole)
if err != nil {
return "", err
}
err = h.deps.KV().SetStringIn(InitNs, InitTimeParam, fmt.Sprintf("%d", time.Now().Unix()))
if err != nil {
return "", err
}
return tmpPwd, nil
} }
func (h *SimpleUserHandlers) Login(c *gin.Context) { func (h *SimpleUserHandlers) Login(c *gin.Context) {
@ -48,7 +106,7 @@ func (h *SimpleUserHandlers) Login(c *gin.Context) {
return return
} }
expectedHash, ok := h.deps.KV().GetStringIn(UsersNamespace, user) expectedHash, ok := h.deps.KV().GetStringIn(UsersNs, user)
if !ok { if !ok {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return return
@ -60,7 +118,7 @@ func (h *SimpleUserHandlers) Login(c *gin.Context) {
return return
} }
role, ok := h.deps.KV().GetStringIn(RolesNamespace, user) role, ok := h.deps.KV().GetStringIn(RolesNs, user)
if !ok { if !ok {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return return
@ -99,7 +157,7 @@ func (h *SimpleUserHandlers) SetPwd(c *gin.Context) {
return return
} }
expectedHash, ok := h.deps.KV().GetStringIn(UsersNamespace, user) expectedHash, ok := h.deps.KV().GetStringIn(UsersNs, user)
if !ok { if !ok {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return return
@ -116,7 +174,7 @@ func (h *SimpleUserHandlers) SetPwd(c *gin.Context) {
c.JSON(q.ErrResp(c, 500, errors.New("fail to set password"))) c.JSON(q.ErrResp(c, 500, errors.New("fail to set password")))
return return
} }
err = h.deps.KV().SetStringIn(UsersNamespace, user, string(newHash)) err = h.deps.KV().SetStringIn(UsersNs, user, string(newHash))
if err != nil { if err != nil {
c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) c.JSON(q.ErrResp(c, 500, ErrInvalidConfig))
return return

View file

@ -1,5 +1,7 @@
package server package server
import "encoding/json"
type FSConfig struct { type FSConfig struct {
Root string `json:"root"` Root string `json:"root"`
OpensLimit int `json:"opensLimit"` OpensLimit int `json:"opensLimit"`
@ -8,6 +10,7 @@ type FSConfig struct {
type UsersCfg struct { type UsersCfg struct {
EnableAuth bool `json:"enableAuth"` EnableAuth bool `json:"enableAuth"`
DefaultAdmin string `json:"defaultAdmin" cfg:"env"`
CookieTTL int `json:"cookieTTL"` CookieTTL int `json:"cookieTTL"`
CookieSecure bool `json:"cookieSecure"` CookieSecure bool `json:"cookieSecure"`
CookieHttpOnly bool `json:"cookieHttpOnly"` CookieHttpOnly bool `json:"cookieHttpOnly"`
@ -33,12 +36,12 @@ type Config struct {
Users *UsersCfg `json:"users"` Users *UsersCfg `json:"users"`
} }
func NewEmptyConfig() *Config { func NewConfig() *Config {
return &Config{} return &Config{}
} }
func NewDefaultConfig() *Config { func DefaultConfig() (string, error) {
return &Config{ defaultCfg := &Config{
Fs: &FSConfig{ Fs: &FSConfig{
Root: ".", Root: ".",
OpensLimit: 128, OpensLimit: 128,
@ -46,6 +49,7 @@ func NewDefaultConfig() *Config {
}, },
Users: &UsersCfg{ Users: &UsersCfg{
EnableAuth: true, EnableAuth: true,
DefaultAdmin: "",
CookieTTL: 3600 * 24 * 7, // 1 week CookieTTL: 3600 * 24 * 7, // 1 week
CookieSecure: false, CookieSecure: false,
CookieHttpOnly: true, CookieHttpOnly: true,
@ -62,4 +66,7 @@ func NewDefaultConfig() *Config {
MaxHeaderBytes: 512, MaxHeaderBytes: 512,
}, },
} }
cfgBytes, err := json.Marshal(defaultCfg)
return string(cfgBytes), err
} }

Binary file not shown.

View file

@ -31,18 +31,17 @@ type Server struct {
func NewServer(cfg gocfg.ICfg) (*Server, error) { func NewServer(cfg gocfg.ICfg) (*Server, error) {
deps := initDeps(cfg) deps := initDeps(cfg)
if cfg.BoolOr("Server.Debug", false) { if !cfg.BoolOr("Server.Debug", false) {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
} }
router := gin.Default() router := gin.Default()
router, err := addHandlers(router, cfg, deps) router, err := initHandlers(router, cfg, deps)
if err != nil { if err != nil {
return nil, err return nil, err
} }
srv := &http.Server{ srv := &http.Server{
// TODO: set more options
Addr: fmt.Sprintf("%s:%d", cfg.GrabString("Server.Host"), cfg.GrabInt("Server.Port")), Addr: fmt.Sprintf("%s:%d", cfg.GrabString("Server.Host"), cfg.GrabInt("Server.Port")),
Handler: router, Handler: router,
ReadTimeout: time.Duration(cfg.GrabInt("Server.ReadTimeout")) * time.Millisecond, ReadTimeout: time.Duration(cfg.GrabInt("Server.ReadTimeout")) * time.Millisecond,
@ -56,23 +55,6 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) {
}, nil }, nil
} }
func (s *Server) depsFS() fs.ISimpleFS {
return s.deps.FS()
}
func (s *Server) depsKVStore() kvstore.IKVStore {
return s.deps.KV()
}
func makeRandToken() string {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
return string(b)
}
func initDeps(cfg gocfg.ICfg) *depidx.Deps { func initDeps(cfg gocfg.ICfg) *depidx.Deps {
secret, ok := cfg.String("ENV.TOKENSECRET") secret, ok := cfg.String("ENV.TOKENSECRET")
if !ok { if !ok {
@ -89,12 +71,6 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps {
jwtEncDec := jwt.NewJWTEncDec(secret) jwtEncDec := jwt.NewJWTEncDec(secret)
logger := simplelog.NewSimpleLogger() logger := simplelog.NewSimpleLogger()
kv := boltdbpvd.New(".", 1024) kv := boltdbpvd.New(".", 1024)
if err := kv.AddNamespace(singleuserhdr.UsersNamespace); err != nil {
panic(err)
}
if err := kv.AddNamespace(singleuserhdr.RolesNamespace); err != nil {
panic(err)
}
deps := depidx.NewDeps(cfg) deps := depidx.NewDeps(cfg)
deps.SetFS(filesystem) deps.SetFS(filesystem)
@ -112,13 +88,35 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps {
return deps return deps
} }
func addHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) {
userHdrs := singleuserhdr.NewSimpleUserHandlers(cfg, deps) userHdrs, err := singleuserhdr.NewSimpleUserHandlers(cfg, deps)
if err != nil {
return nil, err
}
if cfg.BoolOr("Users.EnableAuth", true) && !userHdrs.IsInited() {
adminName, ok := cfg.String("ENV.DEFAULTADMIN")
if !ok || adminName == "" {
// TODO: print sceen only
fmt.Println("Please input admin name:")
fmt.Scanf("%s", &adminName)
}
adminTmpPwd, err := userHdrs.Init(adminName)
if err != nil {
return nil, err
}
fmt.Printf("%s is created, its password is %s, please update it after login\n", adminName, adminTmpPwd)
}
fileHdrs, err := fileshdr.NewFileHandlers(cfg, deps) fileHdrs, err := fileshdr.NewFileHandlers(cfg, deps)
if err != nil {
return nil, err
}
// middleware // middleware
router.Use(userHdrs.Auth()) router.Use(userHdrs.Auth())
// handler
v1 := router.Group("/v1") v1 := router.Group("/v1")
users := v1.Group("/users") users := v1.Group("/users")
@ -126,9 +124,6 @@ func addHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.En
users.POST("/logout", userHdrs.Logout) users.POST("/logout", userHdrs.Logout)
filesSvc := v1.Group("/fs") filesSvc := v1.Group("/fs")
if err != nil {
panic(err)
}
filesSvc.POST("/files", fileHdrs.Create) filesSvc.POST("/files", fileHdrs.Create)
filesSvc.DELETE("/files", fileHdrs.Delete) filesSvc.DELETE("/files", fileHdrs.Delete)
filesSvc.GET("/files", fileHdrs.Download) filesSvc.GET("/files", fileHdrs.Download)
@ -158,3 +153,20 @@ func (s *Server) Shutdown() error {
// TODO: add timeout // TODO: add timeout
return s.server.Shutdown(context.Background()) return s.server.Shutdown(context.Background())
} }
func (s *Server) depsFS() fs.ISimpleFS {
return s.deps.FS()
}
func (s *Server) depsKVStore() kvstore.IKVStore {
return s.deps.KV()
}
func makeRandToken() string {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
return string(b)
}

View file

@ -16,8 +16,16 @@ import (
) )
func startTestServer(config string) *Server { func startTestServer(config string) *Server {
cfg, err := gocfg.New(NewDefaultConfig()). defaultCfg, err := DefaultConfig()
Load(gocfg.JSONStr(config)) if err != nil {
panic(err)
}
cfg, err := gocfg.New(NewConfig()).
Load(
gocfg.JSONStr(defaultCfg),
gocfg.JSONStr(config),
)
if err != nil { if err != nil {
panic(err) panic(err)
} }