forked from alterminal/alterminal
first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"alterminal/internal/auth"
|
||||
"alterminal/internal/store"
|
||||
)
|
||||
|
||||
// runUpdatePassword 解析旗標並重設指定帳號的密碼。此為管理用指令,
|
||||
// 不需驗證舊密碼;密碼更新成功後一併撤銷該使用者所有 Session,
|
||||
// 避免既有登入在密碼重設後續存。
|
||||
func runUpdatePassword(args []string) error {
|
||||
fs := flag.NewFlagSet("update-password", flag.ExitOnError)
|
||||
username := fs.String("username", "", "登入帳號(必填)")
|
||||
password := fs.String("password", "", "新密碼(選填;省略時於終端機輸入)")
|
||||
fs.Parse(args)
|
||||
|
||||
name := strings.TrimSpace(*username)
|
||||
if name == "" {
|
||||
return errors.New("username 不可為空")
|
||||
}
|
||||
pw, err := resolvePassword(*password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := store.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("database: %w", err)
|
||||
}
|
||||
|
||||
u, err := findUserByUsername(db, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.SetPassword(pw); err != nil {
|
||||
return fmt.Errorf("hash password: %w", err)
|
||||
}
|
||||
revoked, err := updateUserPassword(db, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("密碼更新成功:id=%d username=%s(已撤銷 %d 個 Session)\n", u.ID, u.Username, revoked)
|
||||
return nil
|
||||
}
|
||||
|
||||
// findUserByUsername 以帳號查詢使用者,查無時回傳可讀的錯誤。
|
||||
func findUserByUsername(db *gorm.DB, username string) (*auth.User, error) {
|
||||
var u auth.User
|
||||
err := db.Where("username = ?", username).First(&u).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("username %q 不存在", username)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query user: %w", err)
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
// updateUserPassword 於單一交易內寫入新密碼雜湊並刪除該使用者所有
|
||||
// Session,回傳撤銷的 Session 數;交易確保密碼與 Session 不會只更新一半。
|
||||
func updateUserPassword(db *gorm.DB, u *auth.User) (int64, error) {
|
||||
var revoked int64
|
||||
err := db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(u).Update("password_hash", u.PasswordHash).Error; err != nil {
|
||||
return fmt.Errorf("update password: %w", err)
|
||||
}
|
||||
res := tx.Where("user_id = ?", u.ID).Delete(&auth.Session{})
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("delete sessions: %w", res.Error)
|
||||
}
|
||||
revoked = res.RowsAffected
|
||||
return nil
|
||||
})
|
||||
return revoked, err
|
||||
}
|
||||
Reference in New Issue
Block a user