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:
20
api/user.go
20
api/user.go
@ -5,21 +5,22 @@ type User struct {
|
|||||||
CreatedTs int64 `json:"createdTs"`
|
CreatedTs int64 `json:"createdTs"`
|
||||||
UpdatedTs int64 `json:"updatedTs"`
|
UpdatedTs int64 `json:"updatedTs"`
|
||||||
|
|
||||||
OpenId string `json:"openId"`
|
OpenId string `json:"openId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Password string `json:"-"`
|
PasswordHash string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserCreate struct {
|
type UserCreate struct {
|
||||||
OpenId string `json:"openId"`
|
OpenId string
|
||||||
Name string `json:"name"`
|
Name string
|
||||||
Password string `json:"password"`
|
PasswordHash string
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserPatch struct {
|
type UserPatch struct {
|
||||||
Id int
|
Id int
|
||||||
|
|
||||||
OpenId *string
|
OpenId *string
|
||||||
|
PasswordHash *string
|
||||||
|
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Password *string `json:"password"`
|
Password *string `json:"password"`
|
||||||
@ -29,9 +30,8 @@ type UserPatch struct {
|
|||||||
type UserFind struct {
|
type UserFind struct {
|
||||||
Id *int `json:"id"`
|
Id *int `json:"id"`
|
||||||
|
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Password *string
|
OpenId *string
|
||||||
OpenId *string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRenameCheck struct {
|
type UserRenameCheck struct {
|
||||||
|
@ -42,7 +42,7 @@ func checkDSN(dataDir string) (string, error) {
|
|||||||
func GetProfile() Profile {
|
func GetProfile() Profile {
|
||||||
mode := flag.String("mode", "dev", "")
|
mode := flag.String("mode", "dev", "")
|
||||||
port := flag.Int("port", 8080, "")
|
port := flag.Int("port", 8080, "")
|
||||||
data := flag.String("data", "/var/opt/memos", "")
|
data := flag.String("data", "", "")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
dataDir, err := checkDSN(*data)
|
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
|
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"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) registerAuthRoutes(g *echo.Group) {
|
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))
|
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found: %s", login.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the stored password
|
// Compare the stored hashed password, with the hashed version of the password that was received.
|
||||||
if login.Password != user.Password {
|
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(login.Password)); err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Incorrect password").SetInternal(err)
|
// If the two passwords don't match, return a 401 status.
|
||||||
|
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect password").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setUserSession(c, user)
|
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)
|
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{
|
userFind := &api.UserFind{
|
||||||
Name: &signup.Name,
|
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))
|
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{
|
userCreate := &api.UserCreate{
|
||||||
Name: signup.Name,
|
Name: signup.Name,
|
||||||
Password: signup.Password,
|
PasswordHash: string(passwordHash),
|
||||||
OpenId: common.GenUUID(),
|
OpenId: common.GenUUID(),
|
||||||
}
|
}
|
||||||
user, err = s.UserService.CreateUser(userCreate)
|
user, err = s.UserService.CreateUser(userCreate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) registerUserRoutes(g *echo.Group) {
|
func (s *Server) registerUserRoutes(g *echo.Group) {
|
||||||
@ -75,8 +76,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userFind := &api.UserFind{
|
userFind := &api.UserFind{
|
||||||
Id: &userId,
|
Id: &userId,
|
||||||
Password: &userPasswordCheck.Password,
|
|
||||||
}
|
}
|
||||||
user, err := s.UserService.FindUser(userFind)
|
user, err := s.UserService.FindUser(userFind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -84,7 +84,8 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isValid := false
|
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
|
isValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +110,16 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
|
|||||||
userPatch.OpenId = &openId
|
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)
|
user, err := s.UserService.PatchUser(userPatch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
CREATE TABLE user (
|
CREATE TABLE user (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password_hash TEXT NOT NULL,
|
||||||
open_id TEXT NOT NULL,
|
open_id TEXT NOT NULL,
|
||||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
@ -117,10 +117,21 @@ WHERE
|
|||||||
END;
|
END;
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO user
|
INSERT INTO
|
||||||
(`id`, `name`, `password`, `open_id`)
|
user (
|
||||||
|
`id`,
|
||||||
|
`name`,
|
||||||
|
`open_id`,
|
||||||
|
`password_hash`
|
||||||
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'guest', '123456', 'guest_open_id');
|
(
|
||||||
|
1,
|
||||||
|
'guest',
|
||||||
|
'guest_open_id',
|
||||||
|
-- "secret"
|
||||||
|
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO memo
|
INSERT INTO memo
|
||||||
(`content`, `creator_id`)
|
(`content`, `creator_id`)
|
||||||
|
@ -52,14 +52,14 @@ func createUser(db *DB, create *api.UserCreate) (*api.User, error) {
|
|||||||
row, err := db.Db.Query(`
|
row, err := db.Db.Query(`
|
||||||
INSERT INTO user (
|
INSERT INTO user (
|
||||||
name,
|
name,
|
||||||
password,
|
password_hash,
|
||||||
open_id
|
open_id
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?)
|
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.Name,
|
||||||
create.Password,
|
create.PasswordHash,
|
||||||
create.OpenId,
|
create.OpenId,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -72,7 +72,7 @@ func createUser(db *DB, create *api.UserCreate) (*api.User, error) {
|
|||||||
if err := row.Scan(
|
if err := row.Scan(
|
||||||
&user.Id,
|
&user.Id,
|
||||||
&user.Name,
|
&user.Name,
|
||||||
&user.Password,
|
&user.PasswordHash,
|
||||||
&user.OpenId,
|
&user.OpenId,
|
||||||
&user.CreatedTs,
|
&user.CreatedTs,
|
||||||
&user.UpdatedTs,
|
&user.UpdatedTs,
|
||||||
@ -89,8 +89,8 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) {
|
|||||||
if v := patch.Name; v != nil {
|
if v := patch.Name; v != nil {
|
||||||
set, args = append(set, "name = ?"), append(args, v)
|
set, args = append(set, "name = ?"), append(args, v)
|
||||||
}
|
}
|
||||||
if v := patch.Password; v != nil {
|
if v := patch.PasswordHash; v != nil {
|
||||||
set, args = append(set, "password = ?"), append(args, v)
|
set, args = append(set, "password_hash = ?"), append(args, v)
|
||||||
}
|
}
|
||||||
if v := patch.OpenId; v != nil {
|
if v := patch.OpenId; v != nil {
|
||||||
set, args = append(set, "open_id = ?"), append(args, v)
|
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
|
UPDATE user
|
||||||
SET `+strings.Join(set, ", ")+`
|
SET `+strings.Join(set, ", ")+`
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
RETURNING id, name, password, open_id, created_ts, updated_ts
|
RETURNING id, name, password_hash, open_id, created_ts, updated_ts
|
||||||
`, args...)
|
`, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, FormatError(err)
|
return nil, FormatError(err)
|
||||||
@ -114,7 +114,7 @@ func patchUser(db *DB, patch *api.UserPatch) (*api.User, error) {
|
|||||||
if err := row.Scan(
|
if err := row.Scan(
|
||||||
&user.Id,
|
&user.Id,
|
||||||
&user.Name,
|
&user.Name,
|
||||||
&user.Password,
|
&user.PasswordHash,
|
||||||
&user.OpenId,
|
&user.OpenId,
|
||||||
&user.CreatedTs,
|
&user.CreatedTs,
|
||||||
&user.UpdatedTs,
|
&user.UpdatedTs,
|
||||||
@ -145,7 +145,7 @@ func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) {
|
|||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
password,
|
password_hash,
|
||||||
open_id,
|
open_id,
|
||||||
created_ts,
|
created_ts,
|
||||||
updated_ts
|
updated_ts
|
||||||
@ -164,7 +164,7 @@ func findUserList(db *DB, find *api.UserFind) ([]*api.User, error) {
|
|||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&user.Id,
|
&user.Id,
|
||||||
&user.Name,
|
&user.Name,
|
||||||
&user.Password,
|
&user.PasswordHash,
|
||||||
&user.OpenId,
|
&user.OpenId,
|
||||||
&user.CreatedTs,
|
&user.CreatedTs,
|
||||||
&user.UpdatedTs,
|
&user.UpdatedTs,
|
||||||
|
@ -101,7 +101,7 @@ const Signin: React.FC<Props> = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
signinBtnsClickLoadingState.setLoading();
|
signinBtnsClickLoadingState.setLoading();
|
||||||
await api.login("guest", "123456");
|
await api.login("guest", "secret");
|
||||||
|
|
||||||
const user = await userService.doSignIn();
|
const user = await userService.doSignIn();
|
||||||
if (user) {
|
if (user) {
|
||||||
|
Reference in New Issue
Block a user