mirror of
https://github.com/kopia/kopia.git
synced 2026-01-23 22:07:54 -05:00
Code movement and simplification, no functional changes. Objectives: - Allow callers specifying the needed key (or hash) size, instead of hard-coding it in the registered PBK derivers. Conceptually, the caller needs to specify the key size, since that is a requirement of the (encryption) algorithm being used in the caller. Now, the code changes here do not result in any functional changes since the key size is always 32 bytes. - Remove a global definition for the default PB key deriver to use. Instead, each of the 3 use case sets the default value. Changes: - `crypto.DeriveKeyFromPassword` now takes a key size. - Adds new constants for the key sizes at the callers. - Removes the global `crypto.MasterKeySize` const. - Removes the global `crypto.DefaultKeyDerivationAlgorithm` const. - Adds const for the default derivation algorithms for each use case. - Adds a const for the salt length in the `internal/user` package, to ensure the same salt length is used in both hash versions. - Unexports various functions, variables and constants in the `internal/crypto` & `internal/user` packages. - Renames various constants for consistency. - Removes unused functions and symbols. - Renames files to be consistent and better reflect the structure of the code. - Adds a couple of tests to ensure the const values are in sync and supported. - Fixes a couple of typos Followups to: - #3725 - #3770 - #3779 - #3799 - #3816 The individual commits show the code transformations to simplify the review of the changes.
135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
|
|
"github.com/alecthomas/kingpin/v2"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kopia/kopia/internal/user"
|
|
"github.com/kopia/kopia/repo"
|
|
)
|
|
|
|
type commandServerUserAddSet struct {
|
|
userAskPassword bool
|
|
userSetName string
|
|
userSetPassword string
|
|
userSetPasswordHashAlgorithm string
|
|
userSetPasswordHash string
|
|
|
|
isNew bool // true == 'add', false == 'update'
|
|
out textOutput
|
|
}
|
|
|
|
func (c *commandServerUserAddSet) setup(svc appServices, parent commandParent, isNew bool) {
|
|
var cmd *kingpin.CmdClause
|
|
|
|
c.isNew = isNew
|
|
|
|
if isNew {
|
|
cmd = parent.Command("add", "Add new repository user").Alias("create")
|
|
} else {
|
|
cmd = parent.Command("set", "Set password for a repository user.").Alias("update")
|
|
}
|
|
|
|
cmd.Flag("ask-password", "Ask for user password").BoolVar(&c.userAskPassword)
|
|
cmd.Flag("user-password", "Password").StringVar(&c.userSetPassword)
|
|
cmd.Flag("user-password-hash", "Password hash").StringVar(&c.userSetPasswordHash)
|
|
cmd.Flag("user-password-hashing-algorithm", "[Experimental] Password hashing algorithm").Hidden().Default(user.DefaultPasswordHashingAlgorithm).EnumVar(&c.userSetPasswordHashAlgorithm, user.PasswordHashingAlgorithms()...)
|
|
cmd.Arg("username", "Username").Required().StringVar(&c.userSetName)
|
|
cmd.Action(svc.repositoryWriterAction(c.runServerUserAddSet))
|
|
|
|
c.out.setup(svc)
|
|
}
|
|
|
|
func (c *commandServerUserAddSet) getExistingOrNewUserProfile(ctx context.Context, rep repo.Repository, username string) (*user.Profile, error) {
|
|
up, err := user.GetUserProfile(ctx, rep, username)
|
|
|
|
if c.isNew {
|
|
switch {
|
|
case err == nil:
|
|
return nil, errors.Errorf("user %q already exists", username)
|
|
|
|
case errors.Is(err, user.ErrUserNotFound):
|
|
passwordHashVersion, err := user.GetPasswordHashVersion(c.userSetPasswordHashAlgorithm)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to get password hash version")
|
|
}
|
|
|
|
return &user.Profile{
|
|
Username: username,
|
|
PasswordHashVersion: passwordHashVersion,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return up, errors.Wrap(err, "error getting user profile")
|
|
}
|
|
|
|
func (c *commandServerUserAddSet) runServerUserAddSet(ctx context.Context, rep repo.RepositoryWriter) error {
|
|
username := c.userSetName
|
|
|
|
up, err := c.getExistingOrNewUserProfile(ctx, rep, username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
changed := false
|
|
|
|
if p := c.userSetPassword; p != "" {
|
|
changed = true
|
|
|
|
if err := up.SetPassword(p); err != nil {
|
|
return errors.Wrap(err, "error setting password")
|
|
}
|
|
}
|
|
|
|
if p := c.userSetPasswordHash; p != "" {
|
|
ph, err := base64.StdEncoding.DecodeString(p)
|
|
if err != nil {
|
|
return errors.Wrap(err, "invalid password hash, must be valid base64 string")
|
|
}
|
|
|
|
up.PasswordHash = ph
|
|
changed = true
|
|
}
|
|
|
|
if up.PasswordHash == nil || c.userAskPassword {
|
|
pwd, err := askPass(c.out.stdout(), "Enter new password for user "+username+": ")
|
|
if err != nil {
|
|
return errors.Wrap(err, "error asking for password")
|
|
}
|
|
|
|
pwd2, err := askPass(c.out.stdout(), "Re-enter new password for verification: ")
|
|
if err != nil {
|
|
return errors.Wrap(err, "error asking for password")
|
|
}
|
|
|
|
if pwd != pwd2 {
|
|
return errors.Wrap(err, "passwords don't match")
|
|
}
|
|
|
|
changed = true
|
|
|
|
if err := up.SetPassword(pwd); err != nil {
|
|
return errors.Wrap(err, "error setting password")
|
|
}
|
|
}
|
|
|
|
if !changed && !c.isNew {
|
|
return errors.Errorf("no change")
|
|
}
|
|
|
|
if err := user.SetUserProfile(ctx, rep, up); err != nil {
|
|
return errors.Wrap(err, "error setting user profile")
|
|
}
|
|
|
|
log(ctx).Infof(`
|
|
Updated user credentials will take effect in 5-10 minutes or when the server is restarted.
|
|
To refresh credentials in a running server use 'kopia server refresh' command.
|
|
`)
|
|
|
|
return nil
|
|
}
|