diff --git a/cmd/main.go b/cmd/main.go index 2161900..ea3e9e2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,9 +8,10 @@ import ( ) var opts struct { - host string `short:"h" long:"host" description:"server hostname"` - port string `short:"f" long:"file" description:"A file"` - configs []string `short:"c" description:"config path"` + Host string `short:"h" long:"host" description:"server hostname"` + Port int `short:"p" long:"port" description:"server port"` + Debug bool `short:"d" long:"debug" description:"debug mode"` + Configs []string `short:"c" description:"config path"` } func main() { @@ -18,10 +19,17 @@ func main() { if err != nil { panic(err) } + defaultCfg, err := server.DefaultConfig() + if err != nil { + panic(err) + } - cfg := gocfg.New(server.NewDefaultConfig()) - if len(opts.configs) > 0 { - for _, configPath := range opts.configs { + cfg, err := gocfg.New(server.NewConfig()).Load(gocfg.JSONStr(defaultCfg)) + if err != nil { + panic(err) + } + if len(opts.Configs) > 0 { + for _, configPath := range opts.Configs { cfg, err = cfg.Load(gocfg.JSON(configPath)) if err != nil { 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) if err != nil { panic(err) diff --git a/cmd/quickshare.db b/cmd/quickshare.db deleted file mode 100644 index b55996e..0000000 Binary files a/cmd/quickshare.db and /dev/null differ diff --git a/src/handlers/singleuserhdr/handlers.go b/src/handlers/singleuserhdr/handlers.go index ca9da85..86d4b70 100644 --- a/src/handlers/singleuserhdr/handlers.go +++ b/src/handlers/singleuserhdr/handlers.go @@ -1,6 +1,8 @@ package singleuserhdr import ( + "crypto/rand" + "crypto/sha1" "errors" "fmt" "time" @@ -21,11 +23,13 @@ var ( NewPwdParam = "newpwd" RoleParam = "role" ExpireParam = "expire" + InitTimeParam = "initTime" TokenCookie = "tk" AdminRole = "admin" VisitorRole = "visitor" - UsersNamespace = "users" - RolesNamespace = "roles" + InitNs = "usersInit" + UsersNs = "users" + RolesNs = "roles" ) type SimpleUserHandlers struct { @@ -33,11 +37,65 @@ type SimpleUserHandlers struct { 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{ cfg: cfg, 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) { @@ -48,7 +106,7 @@ func (h *SimpleUserHandlers) Login(c *gin.Context) { return } - expectedHash, ok := h.deps.KV().GetStringIn(UsersNamespace, user) + expectedHash, ok := h.deps.KV().GetStringIn(UsersNs, user) if !ok { c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) return @@ -60,7 +118,7 @@ func (h *SimpleUserHandlers) Login(c *gin.Context) { return } - role, ok := h.deps.KV().GetStringIn(RolesNamespace, user) + role, ok := h.deps.KV().GetStringIn(RolesNs, user) if !ok { c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) return @@ -99,7 +157,7 @@ func (h *SimpleUserHandlers) SetPwd(c *gin.Context) { return } - expectedHash, ok := h.deps.KV().GetStringIn(UsersNamespace, user) + expectedHash, ok := h.deps.KV().GetStringIn(UsersNs, user) if !ok { c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) return @@ -116,7 +174,7 @@ func (h *SimpleUserHandlers) SetPwd(c *gin.Context) { c.JSON(q.ErrResp(c, 500, errors.New("fail to set password"))) return } - err = h.deps.KV().SetStringIn(UsersNamespace, user, string(newHash)) + err = h.deps.KV().SetStringIn(UsersNs, user, string(newHash)) if err != nil { c.JSON(q.ErrResp(c, 500, ErrInvalidConfig)) return diff --git a/src/server/config.go b/src/server/config.go index 0fd9297..692d738 100644 --- a/src/server/config.go +++ b/src/server/config.go @@ -1,5 +1,7 @@ package server +import "encoding/json" + type FSConfig struct { Root string `json:"root"` OpensLimit int `json:"opensLimit"` @@ -7,10 +9,11 @@ type FSConfig struct { } type UsersCfg struct { - EnableAuth bool `json:"enableAuth"` - CookieTTL int `json:"cookieTTL"` - CookieSecure bool `json:"cookieSecure"` - CookieHttpOnly bool `json:"cookieHttpOnly"` + EnableAuth bool `json:"enableAuth"` + DefaultAdmin string `json:"defaultAdmin" cfg:"env"` + CookieTTL int `json:"cookieTTL"` + CookieSecure bool `json:"cookieSecure"` + CookieHttpOnly bool `json:"cookieHttpOnly"` } type Secrets struct { @@ -33,12 +36,12 @@ type Config struct { Users *UsersCfg `json:"users"` } -func NewEmptyConfig() *Config { +func NewConfig() *Config { return &Config{} } -func NewDefaultConfig() *Config { - return &Config{ +func DefaultConfig() (string, error) { + defaultCfg := &Config{ Fs: &FSConfig{ Root: ".", OpensLimit: 128, @@ -46,6 +49,7 @@ func NewDefaultConfig() *Config { }, Users: &UsersCfg{ EnableAuth: true, + DefaultAdmin: "", CookieTTL: 3600 * 24 * 7, // 1 week CookieSecure: false, CookieHttpOnly: true, @@ -62,4 +66,7 @@ func NewDefaultConfig() *Config { MaxHeaderBytes: 512, }, } + + cfgBytes, err := json.Marshal(defaultCfg) + return string(cfgBytes), err } diff --git a/src/server/quickshare.db b/src/server/quickshare.db deleted file mode 100644 index ddf4b9e..0000000 Binary files a/src/server/quickshare.db and /dev/null differ diff --git a/src/server/server.go b/src/server/server.go index 6baba22..90537b7 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -31,18 +31,17 @@ type Server struct { func NewServer(cfg gocfg.ICfg) (*Server, error) { deps := initDeps(cfg) - if cfg.BoolOr("Server.Debug", false) { + if !cfg.BoolOr("Server.Debug", false) { gin.SetMode(gin.ReleaseMode) } router := gin.Default() - router, err := addHandlers(router, cfg, deps) + router, err := initHandlers(router, cfg, deps) if err != nil { return nil, err } srv := &http.Server{ - // TODO: set more options Addr: fmt.Sprintf("%s:%d", cfg.GrabString("Server.Host"), cfg.GrabInt("Server.Port")), Handler: router, ReadTimeout: time.Duration(cfg.GrabInt("Server.ReadTimeout")) * time.Millisecond, @@ -56,23 +55,6 @@ func NewServer(cfg gocfg.ICfg) (*Server, error) { }, 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 { secret, ok := cfg.String("ENV.TOKENSECRET") if !ok { @@ -89,12 +71,6 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps { jwtEncDec := jwt.NewJWTEncDec(secret) logger := simplelog.NewSimpleLogger() 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.SetFS(filesystem) @@ -112,13 +88,35 @@ func initDeps(cfg gocfg.ICfg) *depidx.Deps { return deps } -func addHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { - userHdrs := singleuserhdr.NewSimpleUserHandlers(cfg, deps) +func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.Engine, error) { + 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) + if err != nil { + return nil, err + } // middleware router.Use(userHdrs.Auth()) + // handler v1 := router.Group("/v1") 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) filesSvc := v1.Group("/fs") - if err != nil { - panic(err) - } filesSvc.POST("/files", fileHdrs.Create) filesSvc.DELETE("/files", fileHdrs.Delete) filesSvc.GET("/files", fileHdrs.Download) @@ -158,3 +153,20 @@ func (s *Server) Shutdown() error { // TODO: add timeout 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) +} diff --git a/src/server/server_files_test.go b/src/server/server_files_test.go index 1a5094b..3c48d73 100644 --- a/src/server/server_files_test.go +++ b/src/server/server_files_test.go @@ -16,8 +16,16 @@ import ( ) func startTestServer(config string) *Server { - cfg, err := gocfg.New(NewDefaultConfig()). - Load(gocfg.JSONStr(config)) + defaultCfg, err := DefaultConfig() + if err != nil { + panic(err) + } + + cfg, err := gocfg.New(NewConfig()). + Load( + gocfg.JSONStr(defaultCfg), + gocfg.JSONStr(config), + ) if err != nil { panic(err) }