photoprism/internal/commands/passwd.go

131 lines
2.8 KiB
Go
Raw Normal View History

package commands
import (
"bufio"
"context"
"errors"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/sanitize"
)
// PasswdCommand updates a password.
var PasswdCommand = cli.Command{
Name: "passwd",
Usage: "Changes the admin password",
Action: passwdAction,
}
// passwdAction updates a password.
func passwdAction(ctx *cli.Context) error {
conf := config.NewConfig(ctx)
_, cancel := context.WithCancel(context.Background())
defer cancel()
if err := conf.Init(); err != nil {
return err
}
conf.InitDb()
user := entity.Admin
log.Infof("please enter a new password for %s (at least 6 characters)\n", sanitize.Log(user.Username()))
newPassword := getPassword("New Password: ")
if len(newPassword) < 6 {
return errors.New("new password is too short, please try again")
}
retypePassword := getPassword("Retype Password: ")
if newPassword != retypePassword {
return errors.New("passwords did not match, please try again")
}
if err := user.SetPassword(newPassword); err != nil {
return err
}
log.Infof("changed password for %s\n", sanitize.Log(user.Username()))
conf.Shutdown()
return nil
}
// License: MIT Open Source
// Copyright (c) Joe Linoff 2016
// Go code to prompt for password using only standard packages by utilizing syscall.ForkExec() and syscall.Wait4().
// Correctly resets terminal echo after ^C interrupts.
//
// techEcho() - turns terminal echo on or off.
func termEcho(on bool) {
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
cmd := "echo"
if on == false {
cmd = "-echo"
}
// Enable/disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", cmd},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
}
// getPassword - Prompt for password.
func getPassword(prompt string) string {
fmt.Print(prompt)
// Catch a ^C interrupt.
// Make sure that we reset term echo before exiting.
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, os.Interrupt)
go func() {
for range signalChannel {
fmt.Println("")
termEcho(true)
os.Exit(1)
}
}()
// Echo is disabled, now grab the data.
termEcho(false) // disable terminal echo
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
termEcho(true) // always re-enable terminal echo
fmt.Println("")
if err != nil {
// The terminal has been reset, go ahead and exit.
fmt.Println("ERROR:", err.Error())
os.Exit(1)
}
return strings.TrimSpace(text)
}