Repository navigation
Expand file tree
/
Copy pathcli_reset.go
More file actions
84 lines (74 loc) · 2.3 KB
/
Copy pathcli_reset.go
File metadata and controls
84 lines (74 loc) · 2.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main
import (
"bufio"
"fmt"
"os"
"os/user"
"strings"
"github.com/soleilouisol/socAdmin/core/auth"
"github.com/soleilouisol/socAdmin/core/logger"
"golang.org/x/crypto/bcrypt"
"golang.org/x/term"
)
// runPasswordReset is the CLI password-reset flow.
// Locks down via filesystem perms — anyone who can run the binary AND read
// socadmin.db is already a de-facto admin. Asks for explicit confirmation
// to avoid scripted abuse, and writes an audit log line.
func runPasswordReset(repo *auth.Repository, email string) error {
user, err := repo.FindByEmail(email)
if err != nil {
return fmt.Errorf("database error: %w", err)
}
if user == nil {
return fmt.Errorf("You don't have any permissions for %q", email)
}
fmt.Printf("About to reset password for: %s (id=%d, role=%s)\n", user.Email, user.ID, user.Role)
fmt.Printf("Type \"RESET %s\" to confirm: ", user.Email)
reader := bufio.NewReader(os.Stdin)
confirmation, _ := reader.ReadString('\n')
confirmation = strings.TrimSpace(confirmation)
expected := "RESET " + user.Email
if confirmation != expected {
return fmt.Errorf("confirmation mismatch, aborted")
}
fmt.Print("New password: ")
pass1, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
return fmt.Errorf("failed to read password: %w", err)
}
if err := auth.ValidatePassword(string(pass1)); err != nil {
return err
}
fmt.Print("Confirm new password: ")
pass2, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println()
if err != nil {
return fmt.Errorf("failed to read confirmation: %w", err)
}
if string(pass1) != string(pass2) {
return fmt.Errorf("passwords do not match")
}
hashed, err := bcrypt.GenerateFromPassword(pass1, 12)
if err != nil {
return fmt.Errorf("failed to hash password: %w", err)
}
if err := repo.UpdatePassword(user.ID, string(hashed)); err != nil {
return fmt.Errorf("failed to update password: %w", err)
}
repo.RevokeAllRefreshTokens(user.ID)
osUser := "unknown"
if u, err := osUserName(); err == nil {
osUser = u
}
logger.Auth(fmt.Sprintf("password_reset_cli os_user=%s", osUser), user.ID, "cli")
fmt.Printf("Password updated for %s. All refresh tokens revoked.\n", user.Email)
return nil
}
func osUserName() (string, error) {
u, err := user.Current()
if err != nil {
return "", err
}
return u.Username, nil
}