feat(users): add init functions
This commit is contained in:
parent
57124451ad
commit
2bcb337b4c
7 changed files with 156 additions and 53 deletions
30
cmd/main.go
30
cmd/main.go
|
@ -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.
|
@ -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
|
||||||
|
|
|
@ -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.
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue