source: code/trunk/cmd/sojuctl/main.go@ 477

Last change on this file since 477 was 432, checked in by coder.kalyan, 5 years ago

sojuctl: change-password: check if user exists

When changing the password, checks if the user exists *before* prompting
for a password change, instead of after.

File size: 3.1 KB
RevLine 
[98]1package main
2
3import (
[308]4 "bufio"
[98]5 "flag"
6 "fmt"
7 "log"
8 "os"
9
10 "git.sr.ht/~emersion/soju"
11 "git.sr.ht/~emersion/soju/config"
12 "golang.org/x/crypto/bcrypt"
13 "golang.org/x/crypto/ssh/terminal"
14)
15
16const usage = `usage: sojuctl [-config path] <action> [options...]
17
[330]18 create-user <username> [-admin] Create a new user
19 change-password <username> Change password for a user
20 help Show this help message
[98]21`
22
23func init() {
24 flag.Usage = func() {
25 fmt.Fprintf(flag.CommandLine.Output(), usage)
26 }
27}
28
29func main() {
30 var configPath string
31 flag.StringVar(&configPath, "config", "", "path to configuration file")
32 flag.Parse()
33
34 var cfg *config.Server
35 if configPath != "" {
36 var err error
37 cfg, err = config.Load(configPath)
38 if err != nil {
39 log.Fatalf("failed to load config file: %v", err)
40 }
41 } else {
42 cfg = config.Defaults()
43 }
44
45 db, err := soju.OpenSQLDB(cfg.SQLDriver, cfg.SQLSource)
46 if err != nil {
47 log.Fatalf("failed to open database: %v", err)
48 }
49
50 switch cmd := flag.Arg(0); cmd {
51 case "create-user":
52 username := flag.Arg(1)
53 if username == "" {
54 flag.Usage()
55 os.Exit(1)
56 }
57
[330]58 fs := flag.NewFlagSet("", flag.ExitOnError)
59 admin := fs.Bool("admin", false, "make the new user admin")
60 fs.Parse(flag.Args()[2:])
61
[308]62 password, err := readPassword()
[98]63 if err != nil {
64 log.Fatalf("failed to read password: %v", err)
65 }
66
67 hashed, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
68 if err != nil {
69 log.Fatalf("failed to hash password: %v", err)
70 }
71
72 user := soju.User{
73 Username: username,
74 Password: string(hashed),
[330]75 Admin: *admin,
[98]76 }
[324]77 if err := db.StoreUser(&user); err != nil {
[98]78 log.Fatalf("failed to create user: %v", err)
79 }
[251]80 case "change-password":
81 username := flag.Arg(1)
82 if username == "" {
83 flag.Usage()
84 os.Exit(1)
85 }
86
[432]87 user, err := db.GetUser(username)
88 if err != nil {
89 log.Fatalf("failed to get user: %v", err)
90 }
91
[308]92 password, err := readPassword()
[251]93 if err != nil {
[308]94 log.Fatalf("failed to read password: %v", err)
[251]95 }
96
97 hashed, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
98 if err != nil {
99 log.Fatalf("failed to hash password: %v", err)
100 }
101
[380]102 user.Password = string(hashed)
103 if err := db.StoreUser(user); err != nil {
[251]104 log.Fatalf("failed to update password: %v", err)
105 }
[98]106 default:
107 flag.Usage()
108 if cmd != "help" {
109 os.Exit(1)
110 }
111 }
112}
[308]113
114func readPassword() ([]byte, error) {
115 var password []byte
116 var err error
117 fd := int(os.Stdin.Fd())
118
119 if terminal.IsTerminal(fd) {
120 fmt.Printf("Password: ")
121 password, err = terminal.ReadPassword(int(os.Stdin.Fd()))
122 if err != nil {
123 return nil, err
124 }
125 fmt.Printf("\n")
126 } else {
127 fmt.Fprintf(os.Stderr, "Warning: Reading password from stdin.\n")
128 scanner := bufio.NewScanner(os.Stdin)
[326]129 if !scanner.Scan() {
130 if err := scanner.Err(); err != nil {
131 log.Fatalf("failed to read password from stdin: %v", err)
132 }
133 log.Fatalf("failed to read password from stdin: stdin is empty")
134 }
[308]135 password = scanner.Bytes()
136
137 if len(password) == 0 {
138 return nil, fmt.Errorf("zero length password")
139 }
140 }
141
142 return password, nil
143}
Note: See TracBrowser for help on using the repository browser.