Add change password command and improve account page in settings

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-07-01 12:34:53 +02:00
parent c85ff1b5b7
commit d4f4af313d
4 changed files with 156 additions and 15 deletions

View file

@ -62,6 +62,7 @@ func main() {
commands.ResampleCommand,
commands.MigrateCommand,
commands.ConfigCommand,
commands.PasswdCommand,
commands.VersionCommand,
commands.StatusCommand,
}

View file

@ -1,16 +1,15 @@
<template>
<div class="p-tab p-settings-account">
<v-form lazy-validation dense
ref="form" class="form-password" accept-charset="UTF-8">
<v-form dense ref="form" class="form-password" accept-charset="UTF-8">
<v-card flat tile class="ma-2 application">
<v-card-actions>
<v-layout wrap align-top>
<v-flex xs12 class="px-2 pt-2 pb-4">
<v-flex xs12 class="pa-2">
<v-text-field
hide-details required
:disabled="busy"
browser-autocomplete="off"
label="Current Password"
:label="$gettext('Current Password')"
color="secondary-dark"
type="password"
placeholder="••••••••"
@ -20,30 +19,39 @@
<v-flex xs12 class="pa-2">
<v-text-field
hide-details required
required counter persistent-hint
:disabled="busy"
browser-autocomplete="off"
label="New Password"
:label="$gettext('New Password')"
color="secondary-dark"
type="password"
placeholder="••••••••"
v-model="newPassword"
:hint="$gettext('At least 6 characters.')"
></v-text-field>
</v-flex>
<v-flex xs12 class="pa-2">
<v-text-field
hide-details required
required counter persistent-hint
:disabled="busy"
browser-autocomplete="off"
label="Confirm Password"
:label="$gettext('Retype Password')"
color="secondary-dark"
type="password"
placeholder="••••••••"
v-model="confirmPassword"
:hint="$gettext('Please confirm your new password.')"
></v-text-field>
</v-flex>
<v-flex xs12 class="px-2 pt-4 pb-2">
<v-flex xs12 class="pa-2">
<p class="caption pa-0">
<translate>Note: Updating the password will not revoke access from already authenticated users.</translate>
</p>
</v-flex>
<v-flex xs12 class="pa-2">
<v-btn depressed color="secondary-dark"
@click.stop="confirm"
:disabled="disabled()"
@ -73,7 +81,7 @@
},
methods: {
disabled() {
return (this.busy || this.oldPassword === "" || this.newPassword === "" || (this.newPassword !== this.confirmPassword));
return (this.busy || this.oldPassword === "" || this.newPassword.length < 6 || (this.newPassword !== this.confirmPassword));
},
confirm() {
this.busy = true;

128
internal/commands/passwd.go Normal file
View file

@ -0,0 +1,128 @@
package commands
import (
"bufio"
"context"
"errors"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/txt"
"github.com/urfave/cli"
)
// 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)
cctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := conf.Init(cctx); err != nil {
return err
}
conf.InitDb()
user := entity.Admin
log.Infof("please enter a new password for %s (at least 6 characters)\n", txt.Quote(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", txt.Quote(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)
}

View file

@ -138,7 +138,7 @@ func FindPersonByUserName(userName string) *Person {
if err := Db().Where("user_name = ?", userName).First(&result).Error; err == nil {
return &result
} else {
log.Errorf("auth: user %s not found", txt.Quote(userName))
log.Errorf("user %s not found", txt.Quote(userName))
return nil
}
}
@ -154,7 +154,7 @@ func FindPersonByUID(uid string) *Person {
if err := Db().Where("person_uid = ?", uid).First(&result).Error; err == nil {
return &result
} else {
log.Errorf("auth: user %s not found", txt.Quote(uid))
log.Errorf("user %s not found", txt.Quote(uid))
return nil
}
}
@ -195,7 +195,11 @@ func (m *Person) Guest() bool {
// SetPassword sets a new password stored as hash.
func (m *Person) SetPassword(password string) error {
if !m.Registered() {
return fmt.Errorf("auth: only registered users can change their password")
return fmt.Errorf("only registered users can change their password")
}
if len(password) < 6 {
return fmt.Errorf("new password for %s must be at least 6 characters", txt.Quote(m.UserName))
}
pw := NewPassword(m.PersonUID, password)
@ -206,7 +210,7 @@ func (m *Person) SetPassword(password string) error {
// InitPassword sets the initial user password stored as hash.
func (m *Person) InitPassword(password string) {
if !m.Registered() {
log.Warn("auth: only registered users can change their password")
log.Warn("only registered users can change their password")
return
}
@ -230,7 +234,7 @@ func (m *Person) InitPassword(password string) {
// InvalidPassword returns true if the given password does not match the hash.
func (m *Person) InvalidPassword(password string) bool {
if !m.Registered() {
log.Warn("auth: only registered users can change their password")
log.Warn("only registered users can change their password")
return true
}