Smail/backend/services/adminreset/password_linux.go
smail b183aef0c7
Some checks are pending
Security Checks / security (push) Waiting to run
完善管理员重置结果提示并缩短邮件验证等待
2026-09-19 22:53:58 +08:00

99 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build linux
package adminreset
import (
"context"
"errors"
"io"
"os/exec"
"strings"
"syscall"
"time"
"git.pcux.cn/xuans/Smail/utils/password"
)
func ReadProtectedPassword() (string, error) {
first, err := askPassword("请输入新的超级管理员密码")
if err != nil {
return "", err
}
second, err := askPassword("请再次输入新的超级管理员密码")
if err != nil {
clearPassword(&first)
return "", err
}
if first == "" || first != second {
clearPassword(&first)
clearPassword(&second)
return "", errors.New("密码为空或两次输入不一致")
}
clearPassword(&second)
return first, nil
}
func askPassword(prompt string) (string, error) {
value, err := readSystemdInput(prompt, "no")
if err != nil {
return "", err
}
if password.ValidateUserPassword(value) != nil {
return "", errors.New("密码长度无效")
}
return value, nil
}
func ReadVisibleInput(prompt string) (string, error) {
return ReadVisibleInputContext(context.Background(), prompt)
}
func ReadVisibleInputContext(ctx context.Context, prompt string) (string, error) {
return readSystemdInputContext(ctx, prompt, "yes")
}
// ShowResult 使用当前 systemctl 的密码代理显示结果,避免提示只进入系统日志。
func ShowResult(message string) {
_, _ = readSystemdInputContext(context.Background(), message+"\n按回车结束", "yes", "--timeout=30")
}
func readSystemdInput(prompt, echo string) (string, error) {
return readSystemdInputContext(context.Background(), prompt, echo)
}
func readSystemdInputContext(ctx context.Context, prompt, echo string, options ...string) (string, error) {
prompt = strings.TrimRight(prompt, ":: ") + ":"
// systemd 代理把提示保存在单行配置中,用终端换行控制显示列表,避免配置被拆行。
prompt = strings.ReplaceAll(strings.ReplaceAll(prompt, "\r\n", "\n"), "\n", "\x1bE")
args := append([]string{"--no-tty", "--echo=" + echo}, options...)
command := exec.CommandContext(ctx, "systemd-ask-password", append(args, prompt)...)
// 先允许代理清理请求文件,避免取消后继续弹出已经失效的输入提示。
command.Cancel = func() error { return command.Process.Signal(syscall.SIGTERM) }
command.WaitDelay = time.Second
command.Stderr = io.Discard
output, err := command.Output()
if err != nil {
return "", errors.New("受保护密码代理不可用")
}
output = stripPasswordLineEnding(output)
value := string(output)
defer clearBytes(output)
return value, nil
}
func stripPasswordLineEnding(value []byte) []byte {
if len(value) > 0 && value[len(value)-1] == '\n' {
value = value[:len(value)-1]
}
if len(value) > 0 && value[len(value)-1] == '\r' {
value = value[:len(value)-1]
}
return value
}
func clearBytes(value []byte) {
for index := range value {
value[index] = 0
}
}