diff --git a/api/user.go b/api/user.go index 7c93aa0b..5438da43 100644 --- a/api/user.go +++ b/api/user.go @@ -5,21 +5,22 @@ type User struct { CreatedTs int64 `json:"createdTs"` UpdatedTs int64 `json:"updatedTs"` - OpenId string `json:"openId"` - Name string `json:"name"` - Password string `json:"-"` + OpenId string `json:"openId"` + Name string `json:"name"` + PasswordHash string `json:"-"` } type UserCreate struct { - OpenId string `json:"openId"` - Name string `json:"name"` - Password string `json:"password"` + OpenId string + Name string + PasswordHash string } type UserPatch struct { Id int - OpenId *string + OpenId *string + PasswordHash *string Name *string `json:"name"` Password *string `json:"password"` @@ -29,9 +30,8 @@ type UserPatch struct { type UserFind struct { Id *int `json:"id"` - Name *string `json:"name"` - Password *string - OpenId *string + Name *string `json:"name"` + OpenId *string } type UserRenameCheck struct { diff --git a/bin/server/cmd/profile.go b/bin/server/cmd/profile.go index bed3b0e2..b176bb0c 100644 --- a/bin/server/cmd/profile.go +++ b/bin/server/cmd/profile.go @@ -42,7 +42,7 @@ func checkDSN(dataDir string) (string, error) { func GetProfile() Profile { mode := flag.String("mode", "dev", "") port := flag.Int("port", 8080, "") - data := flag.String("data", "/var/opt/memos", "") + data := flag.String("data", "", "") flag.Parse() dataDir, err := checkDSN(*data) diff --git a/docs/deploy/docker-deploy-guide.md b/docs/deploy/docker-deploy-guide.md index 16ff30cd..96e87a3d 100644 --- a/docs/deploy/docker-deploy-guide.md +++ b/docs/deploy/docker-deploy-guide.md @@ -14,4 +14,4 @@ docker run --name memos --restart always --publish 8080:8080 --volume ~/path/to/your/data/:/var/opt/memos/ neosmemo/memos:next -mode release ``` -The default user account is `guest` with password `123456`. +The default user account is `guest` with password `secret`. diff --git a/server/auth.go b/server/auth.go index bd42ce3b..6c5dad16 100644 --- a/server/auth.go +++ b/server/auth.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/labstack/echo/v4" + "golang.org/x/crypto/bcrypt" ) func (s *Server) registerAuthRoutes(g *echo.Group) { @@ -28,9 +29,10 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found: %s", login.Name)) } - // Compare the stored password - if login.Password != user.Password { - return echo.NewHTTPError(http.StatusBadRequest, "Incorrect password").SetInternal(err) + // Compare the stored hashed password, with the hashed version of the password that was received. + if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(login.Password)); err != nil { + // If the two passwords don't match, return a 401 status. + return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect password").SetInternal(err) } err = setUserSession(c, user) @@ -60,6 +62,13 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err) } + if len(signup.Name) <= 5 { + return echo.NewHTTPError(http.StatusBadRequest, "Username is too short, minimum length is 6.") + } + if len(signup.Password) <= 5 { + return echo.NewHTTPError(http.StatusBadRequest, "Password is too short, minimum length is 6.") + } + userFind := &api.UserFind{ Name: &signup.Name, } @@ -71,10 +80,15 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("Existed user found: %s", signup.Name)) } + passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err) + } + userCreate := &api.UserCreate{ - Name: signup.Name, - Password: signup.Password, - OpenId: common.GenUUID(), + Name: signup.Name, + PasswordHash: string(passwordHash), + OpenId: common.GenUUID(), } user, err = s.UserService.CreateUser(userCreate) if err != nil { diff --git a/server/user.go b/server/user.go index c444ab82..db57dfe4 100644 --- a/server/user.go +++ b/server/user.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/labstack/echo/v4" + "golang.org/x/crypto/bcrypt" ) func (s *Server) registerUserRoutes(g *echo.Group) { @@ -75,8 +76,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) { } userFind := &api.UserFind{ - Id: &userId, - Password: &userPasswordCheck.Password, + Id: &userId, } user, err := s.UserService.FindUser(userFind) if err != nil { @@ -84,7 +84,8 @@ func (s *Server) registerUserRoutes(g *echo.Group) { } isValid := false - if user != nil { + // Compare the stored hashed password, with the hashed version of the password that was received. + if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(userPasswordCheck.Password)); err == nil { isValid = true } @@ -109,6 +110,16 @@ func (s *Server) registerUserRoutes(g *echo.Group) { userPatch.OpenId = &openId } + if userPatch.Password != nil && *userPatch.Password != "" { + passwordHash, err := bcrypt.GenerateFromPassword([]byte(*userPatch.Password), bcrypt.DefaultCost) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err) + } + + passwordHashStr := string(passwordHash) + userPatch.PasswordHash = &passwordHashStr + } + user, err := s.UserService.PatchUser(userPatch) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err) diff --git a/store/seed/10001_schema.sql b/store/seed/10001_schema.sql index f8f0e681..a450ac4b 100644 --- a/store/seed/10001_schema.sql +++ b/store/seed/10001_schema.sql @@ -2,7 +2,7 @@ CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, - password TEXT NOT NULL, + password_hash TEXT NOT NULL, open_id TEXT NOT NULL, created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), @@ -117,10 +117,21 @@ WHERE END; -INSERT INTO user - (`id`, `name`, `password`, `open_id`) +INSERT INTO + user ( + `id`, + `name`, + `open_id`, + `password_hash` + ) VALUES - (1, 'guest', '123456', 'guest_open_id'); + ( + 1, + 'guest', + 'guest_open_id', + -- "secret" + '$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK' + ); INSERT INTO memo (`content`, `creator_id`) diff --git a/store/user.go b/store/user.go index 08450abf..02b7d184 100644 --- a/store/user.go +++ b/store/user.go @@ -52,14 +52,14 @@ func createUser(db *DB, create *api.UserCreate) (*api.User, error) { row, err := db.Db.Query(` INSERT INTO user ( name, - password, + password_hash, open_id ) VALUES (?, ?, ?) - RETURNING id, name, password, open_id, created_ts, updated_ts + RETURNING id, name, password_hash, open_id, created_ts, updated_ts `, create.Name, - create.Password, + create.PasswordHash, create.OpenId, ) if err != nil { @@ -72,7 +72,7 @@ func createUser(db *DB, create *api.UserCreate) (*api.User, error) { if err := row.Scan( &user.Id, &user.Name, - &user.Password, + &user.PasswordHash, &user.OpenId, &user.CreatedTs, &user.UpdatedTs, @@ -89,8 +89,8 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) { if v := patch.Name; v != nil { set, args = append(set, "name = ?"), append(args, v) } - if v := patch.Password; v != nil { - set, args = append(set, "password = ?"), append(args, v) + if v := patch.PasswordHash; v != nil { + set, args = append(set, "password_hash = ?"), append(args, v) } if v := patch.OpenId; v != nil { set, args = append(set, "open_id = ?"), append(args, v) @@ -102,7 +102,7 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) { UPDATE user SET `+strings.Join(set, ", ")+` WHERE id = ? - RETURNING id, name, password, open_id, created_ts, updated_ts + RETURNING id, name, password_hash, open_id, created_ts, updated_ts `, args...) if err != nil { return nil, FormatError(err) @@ -114,7 +114,7 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) { if err := row.Scan( &user.Id, &user.Name, - &user.Password, + &user.PasswordHash, &user.OpenId, &user.CreatedTs, &user.UpdatedTs, @@ -145,7 +145,7 @@ func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) { SELECT id, name, - password, + password_hash, open_id, created_ts, updated_ts @@ -164,7 +164,7 @@ func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) { if err := rows.Scan( &user.Id, &user.Name, - &user.Password, + &user.PasswordHash, &user.OpenId, &user.CreatedTs, &user.UpdatedTs, diff --git a/web/src/pages/Signin.tsx b/web/src/pages/Signin.tsx index 47ccf198..d8261d0e 100644 --- a/web/src/pages/Signin.tsx +++ b/web/src/pages/Signin.tsx @@ -101,7 +101,7 @@ const Signin: React.FC = () => { try { signinBtnsClickLoadingState.setLoading(); - await api.login("guest", "123456"); + await api.login("guest", "secret"); const user = await userService.doSignIn(); if (user) {