mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: user hash password
This commit is contained in:
10
api/user.go
10
api/user.go
@ -7,19 +7,20 @@ type User struct {
|
||||
|
||||
OpenId string `json:"openId"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"-"`
|
||||
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
|
||||
PasswordHash *string
|
||||
|
||||
Name *string `json:"name"`
|
||||
Password *string `json:"password"`
|
||||
@ -30,7 +31,6 @@ type UserFind struct {
|
||||
Id *int `json:"id"`
|
||||
|
||||
Name *string `json:"name"`
|
||||
Password *string
|
||||
OpenId *string
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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`.
|
||||
|
@ -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,9 +80,14 @@ 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,
|
||||
PasswordHash: string(passwordHash),
|
||||
OpenId: common.GenUUID(),
|
||||
}
|
||||
user, err = s.UserService.CreateUser(userCreate)
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||
@ -76,7 +77,6 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||
|
||||
userFind := &api.UserFind{
|
||||
Id: &userId,
|
||||
Password: &userPasswordCheck.Password,
|
||||
}
|
||||
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)
|
||||
|
@ -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`)
|
||||
|
@ -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,
|
||||
|
@ -101,7 +101,7 @@ const Signin: React.FC<Props> = () => {
|
||||
|
||||
try {
|
||||
signinBtnsClickLoadingState.setLoading();
|
||||
await api.login("guest", "123456");
|
||||
await api.login("guest", "secret");
|
||||
|
||||
const user = await userService.doSignIn();
|
||||
if (user) {
|
||||
|
Reference in New Issue
Block a user