Smail/backend/services/adminreset/client.go
smail f10dc342f7
Some checks are pending
Security Checks / security (push) Waiting to run
将管理员重置验证方式改为编号列表
2026-09-19 22:07:00 +08:00

111 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.

package adminreset
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
"strings"
"time"
)
var (
ErrVerificationUnavailable = errors.New("no verification method available")
ErrVerificationFailed = errors.New("verification failed")
)
func responseError(code string) error {
switch code {
case "VERIFICATION_UNAVAILABLE":
return ErrVerificationUnavailable
case "VERIFICATION_FAILED":
return ErrVerificationFailed
case "REPLAY_OR_EXPIRED":
return ErrExpiredSession
default:
return ErrInvalidRequest
}
}
func SendVerifiedReset(ctx context.Context, conn net.Conn, input func(string) (string, error), readPassword func() (string, error)) error {
if conn == nil || input == nil || readPassword == nil {
return ErrInvalidRequest
}
deadline := time.Now().Add(controlRequestTimeout)
if contextDeadline, ok := ctx.Deadline(); ok && contextDeadline.Before(deadline) {
deadline = contextDeadline
}
if err := conn.SetDeadline(deadline); err != nil {
return fmt.Errorf("set reset deadline: %w", err)
}
defer conn.SetDeadline(time.Time{})
challenge, err := DecodeResponse(conn)
if err != nil {
return err
}
if !challenge.OK {
return responseError(challenge.Code)
}
if challenge.Code != "CHALLENGE" || challenge.Nonce == "" {
return ErrInvalidRequest
}
var prompt strings.Builder
prompt.WriteString("请选择安全验证方式\n\n")
for i, method := range challenge.Methods {
fmt.Fprintf(&prompt, "%d. %s\n", i+1, method.Label)
}
prompt.WriteString("\n选项:")
choice, err := input(prompt.String())
if err != nil {
return err
}
index, err := strconv.Atoi(strings.TrimSpace(choice))
if err != nil || index < 1 || index > len(challenge.Methods) {
return ErrInvalidRequest
}
exchange := func(request Request) error {
request.Version, request.Nonce = ProtocolVersion, challenge.Nonce
if err := json.NewEncoder(conn).Encode(request); err != nil {
return err
}
response, err := DecodeResponse(conn)
if err != nil {
return err
}
if !response.OK {
return responseError(response.Code)
}
return nil
}
if err := exchange(Request{Action: "select", Method: challenge.Methods[index-1].ID}); err != nil {
return err
}
code, err := input("请输入所选方式的验证码或安全码")
if err != nil {
return err
}
err = exchange(Request{Action: "verify", Code: code})
clearPassword(&code)
if err != nil {
return err
}
value, err := readPassword()
if err != nil {
return err
}
defer clearPassword(&value)
if err := json.NewEncoder(conn).Encode(Request{Version: ProtocolVersion, Nonce: challenge.Nonce, Action: "reset", NewPassword: value}); err != nil {
return fmt.Errorf("send reset request: %w", err)
}
response, err := DecodeResponse(conn)
if err != nil {
return fmt.Errorf("read reset response: %w", err)
}
if !response.OK {
return responseError(response.Code)
}
return nil
}