Refactor internal/auth to return domain specific errors (#905)
* Refactor internal/auth to return domain specific errors
This commit is contained in:
1 parent
22462cdd84
commit
6623be8bfb
22 files changed
+502
-365
No files matched your search
@@ -2,13 +2,13 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/tokens"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
@@ -321,8 +321,9 @@ func (v *verifier) decryptToken() {
|
||||
}
|
||||
|
||||
func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string, scopeInfo *scopes.ScopeInfo, retAcl perms.ACL, retErr error) {
|
||||
const op = "auth.(verifier).performAuthCheck"
|
||||
// Ensure we return an error by default if we forget to set this somewhere
|
||||
retErr = errors.New("unknown")
|
||||
retErr = errors.New(errors.Unknown, op, "default auth error")
|
||||
// Make the linter happy
|
||||
_ = retErr
|
||||
scopeInfo = new(scopes.ScopeInfo)
|
||||
@@ -346,7 +347,7 @@ func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string
|
||||
}
|
||||
tokenRepo, err := v.authTokenRepoFn()
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("perform auth check: failed to get authtoken repo: %w", err)
|
||||
retErr = errors.Wrap(err, op)
|
||||
return
|
||||
}
|
||||
at, err := tokenRepo.ValidateToken(v.ctx, v.requestInfo.PublicId, v.requestInfo.Token)
|
||||
@@ -369,7 +370,7 @@ func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string
|
||||
|
||||
iamRepo, err := v.iamRepoFn()
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("perform auth check: failed to get iam repo: %w", err)
|
||||
retErr = errors.Wrap(err, op, errors.WithMsg("failed to get iam repo"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -388,11 +389,11 @@ func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string
|
||||
default:
|
||||
scp, err := iamRepo.LookupScope(v.ctx, v.res.ScopeId)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("perform auth check: failed to lookup scope: %w", err)
|
||||
retErr = errors.Wrap(err, op)
|
||||
return
|
||||
}
|
||||
if scp == nil {
|
||||
retErr = fmt.Errorf("perform auth check: non-existent scope %q", v.res.ScopeId)
|
||||
retErr = errors.New(errors.InvalidParameter, op, fmt.Sprint("non-existent scope $q", v.res.ScopeId))
|
||||
return
|
||||
}
|
||||
scopeInfo = &scopes.ScopeInfo{
|
||||
@@ -418,7 +419,7 @@ func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string
|
||||
// u_anon and u_auth)
|
||||
grantPairs, err = iamRepo.GrantsForUser(v.ctx, userId)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("perform auth check: failed to query for user grants: %w", err)
|
||||
retErr = errors.Wrap(err, op)
|
||||
return
|
||||
}
|
||||
parsedGrants = make([]perms.Grant, 0, len(grantPairs))
|
||||
@@ -433,7 +434,7 @@ func (v verifier) performAuthCheck() (aclResults perms.ACLResults, userId string
|
||||
perms.WithAccountId(accountId),
|
||||
perms.WithSkipFinalValidation(true))
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("perform auth check: failed to parse grant %#v: %w", pair.Grant, err)
|
||||
retErr = errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed to parse grant %#v", pair.Grant)))
|
||||
return
|
||||
}
|
||||
parsedGrants = append(parsedGrants, parsed)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password/store"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
@@ -28,10 +26,11 @@ func allocAccount() *Account {
|
||||
// NewAccount creates a new in memory Account. LoginName, name, and
|
||||
// description are the only valid options. All other options are ignored.
|
||||
func NewAccount(authMethodId string, opt ...Option) (*Account, error) {
|
||||
const op = "password.NewAccount"
|
||||
// NOTE(mgaffney): The scopeId in the embedded *store.Account is
|
||||
// populated by a trigger in the database.
|
||||
if authMethodId == "" {
|
||||
return nil, fmt.Errorf("new: password account: no auth method id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing auth method id")
|
||||
}
|
||||
|
||||
opts := getOpts(opt...)
|
||||
|
||||
@@ -3,7 +3,6 @@ package password
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password/store"
|
||||
@@ -116,19 +115,20 @@ type Argon2Credential struct {
|
||||
}
|
||||
|
||||
func newArgon2Credential(accountId string, password string, conf *Argon2Configuration) (*Argon2Credential, error) {
|
||||
const op = "password.newArgon2Credential"
|
||||
if accountId == "" {
|
||||
return nil, fmt.Errorf("new: password argon2 credential: no accountId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing accountId")
|
||||
}
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("new: password argon2 credential: no password: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing password")
|
||||
}
|
||||
if conf == nil {
|
||||
return nil, fmt.Errorf("new: password argon2 credential: no argon2 configuration: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing argon2 configuration")
|
||||
}
|
||||
|
||||
id, err := newArgon2CredentialId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new: password argon2 credential: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
c := &Argon2Credential{
|
||||
@@ -142,7 +142,7 @@ func newArgon2Credential(accountId string, password string, conf *Argon2Configur
|
||||
|
||||
salt := make([]byte, conf.SaltLength)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, fmt.Errorf("new: password argon2 credential: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Io))
|
||||
}
|
||||
c.Salt = salt
|
||||
c.DerivedKey = argon2.IDKey([]byte(password), c.Salt, conf.Iterations, conf.Memory, uint8(conf.Threads), conf.KeyLength)
|
||||
@@ -170,16 +170,18 @@ func (c *Argon2Credential) SetTableName(n string) {
|
||||
}
|
||||
|
||||
func (c *Argon2Credential) encrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "password.(Argon2Credential).encrypt"
|
||||
if err := structwrapping.WrapStruct(ctx, cipher, c.Argon2Credential, nil); err != nil {
|
||||
return fmt.Errorf("error encrypting argon2 credential: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
c.KeyId = cipher.KeyID()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Argon2Credential) decrypt(ctx context.Context, cipher wrapping.Wrapper) error {
|
||||
const op = "password.(Argon2Credential).decrypt"
|
||||
if err := structwrapping.UnwrapStruct(ctx, cipher, c.Argon2Credential, nil); err != nil {
|
||||
return fmt.Errorf("error decrypting argon2 credential: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithCode(errors.Decrypt))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -315,10 +315,11 @@ func TestArgon2Credential_New(t *testing.T) {
|
||||
conf *Argon2Configuration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Argon2Credential
|
||||
wantIsErr error
|
||||
name string
|
||||
args args
|
||||
want *Argon2Credential
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "blank-accountId",
|
||||
@@ -327,8 +328,9 @@ func TestArgon2Credential_New(t *testing.T) {
|
||||
password: "foobarcity",
|
||||
conf: confs[0],
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.newArgon2Credential: missing accountId: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "blank-password",
|
||||
@@ -337,8 +339,9 @@ func TestArgon2Credential_New(t *testing.T) {
|
||||
password: "",
|
||||
conf: confs[0],
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.newArgon2Credential: missing password: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-configuration",
|
||||
@@ -347,8 +350,9 @@ func TestArgon2Credential_New(t *testing.T) {
|
||||
password: "foobarcity",
|
||||
conf: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.newArgon2Credential: missing argon2 configuration: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "valid-password",
|
||||
@@ -398,9 +402,9 @@ func TestArgon2Credential_New(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, err := newArgon2Credential(tt.args.accountId, tt.args.password, tt.args.conf)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password/store"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/oplog"
|
||||
@@ -27,8 +25,9 @@ func allocAuthMethod() AuthMethod {
|
||||
// ignored. MinLoginNameLength and MinPasswordLength are pre-set to the
|
||||
// default values of 5 and 8 respectively.
|
||||
func NewAuthMethod(scopeId string, opt ...Option) (*AuthMethod, error) {
|
||||
const op = "password.NewAuthMethod"
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("new: password auth method: no scope id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
|
||||
opts := getOpts(opt...)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
// Prefixes for private ids for types in the password package.
|
||||
@@ -13,17 +12,19 @@ const (
|
||||
)
|
||||
|
||||
func newArgon2ConfigurationId() (string, error) {
|
||||
const op = "password.newArgon2ConfigurationId"
|
||||
id, err := db.NewPrivateId(argon2ConfigurationPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new password argon2 configuration id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
func newArgon2CredentialId() (string, error) {
|
||||
const op = "password.newArgon2CredentialId"
|
||||
id, err := db.NewPrivateId(argon2CredentialPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new password argon2 configuration id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
)
|
||||
|
||||
// PublicId prefixes for the resources in the password package.
|
||||
@@ -13,17 +12,19 @@ const (
|
||||
)
|
||||
|
||||
func newAuthMethodId() (string, error) {
|
||||
const op = "password.newAuthMethodId"
|
||||
id, err := db.NewPublicId(AuthMethodPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new password auth method id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
func newAccountId() (string, error) {
|
||||
const op = "password.newAccountId"
|
||||
id, err := db.NewPublicId(AccountPrefix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new password account id: %w", err)
|
||||
return "", errors.Wrap(err, op)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -24,13 +23,14 @@ type Repository struct {
|
||||
// routines to access it. WithLimit option is used as a repo wide default
|
||||
// limit applied to all ListX methods.
|
||||
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) {
|
||||
const op = "password.NewRepository"
|
||||
switch {
|
||||
case r == nil:
|
||||
return nil, fmt.Errorf("db.Reader: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing db.Reader")
|
||||
case w == nil:
|
||||
return nil, fmt.Errorf("db.Writer: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing db.Writer")
|
||||
case kms == nil:
|
||||
return nil, fmt.Errorf("kms: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing kms")
|
||||
}
|
||||
|
||||
opts := getOpts(opt...)
|
||||
|
||||
@@ -26,38 +26,39 @@ import (
|
||||
// Both a.Name and a.Description are optional. If a.Name is set, it must be
|
||||
// unique within a.AuthMethodId.
|
||||
func (r *Repository) CreateAccount(ctx context.Context, scopeId string, a *Account, opt ...Option) (*Account, error) {
|
||||
const op = "password.(Repository).CreateAccount"
|
||||
if a == nil {
|
||||
return nil, fmt.Errorf("create: password account: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing Account")
|
||||
}
|
||||
if a.Account == nil {
|
||||
return nil, fmt.Errorf("create: password account: embedded Account: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing embedded Account")
|
||||
}
|
||||
if a.AuthMethodId == "" {
|
||||
return nil, fmt.Errorf("create: password account: no auth method id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing auth method id")
|
||||
}
|
||||
if a.PublicId != "" {
|
||||
return nil, fmt.Errorf("create: password account: public id not empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "public id must be empty")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("create: password account: scope id empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
if !validLoginName(a.LoginName) {
|
||||
return nil, fmt.Errorf("create: password account: invalid login name; must be all-lowercase alphanumeric, period or hyphen: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("login name must be all-lowercase alphanumeric, period or hyphen. got: %s", a.LoginName))
|
||||
}
|
||||
|
||||
cc, err := r.currentConfig(ctx, a.AuthMethodId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password account: retrieve current configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("retrieve current configuration"))
|
||||
}
|
||||
|
||||
if cc.MinLoginNameLength > len(a.LoginName) {
|
||||
return nil, fmt.Errorf("create: password account: user name %q: %w", a.LoginName, ErrTooShort)
|
||||
return nil, errors.New(errors.TooShort, op, fmt.Sprintf("username: %s, must be longer than %d", a.LoginName, cc.MinLoginNameLength))
|
||||
}
|
||||
|
||||
a = a.clone()
|
||||
id, err := newAccountId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password account: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
a.PublicId = id
|
||||
|
||||
@@ -66,20 +67,20 @@ func (r *Repository) CreateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
var cred *Argon2Credential
|
||||
if opts.withPassword {
|
||||
if cc.MinPasswordLength > len(opts.password) {
|
||||
return nil, fmt.Errorf("create: password account: password: %w", ErrTooShort)
|
||||
return nil, errors.New(errors.PasswordTooShort, op, fmt.Sprintf("must be longer than %v", cc.MinPasswordLength))
|
||||
}
|
||||
if cred, err = newArgon2Credential(id, opts.password, cc.argon2()); err != nil {
|
||||
return nil, fmt.Errorf("create: password account: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password account: unable to get oplog wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper"), errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password account: unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"), errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
|
||||
var newCred *Argon2Credential
|
||||
@@ -106,10 +107,10 @@ func (r *Repository) CreateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, fmt.Errorf("create: password account: in auth method: %s: name %q or loginName %q already exists: %w",
|
||||
a.AuthMethodId, a.Name, a.LoginName, errors.ErrNotUnique)
|
||||
return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("in auth method %s: name %q or loginName %q already exists",
|
||||
a.AuthMethodId, a.Name, a.LoginName))
|
||||
}
|
||||
return nil, fmt.Errorf("create: password account: in auth method: %s: %w", a.AuthMethodId, err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg(a.AuthMethodId))
|
||||
}
|
||||
return newAccount, nil
|
||||
}
|
||||
@@ -117,8 +118,9 @@ func (r *Repository) CreateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
// LookupAccount will look up an account in the repository. If the account is not
|
||||
// found, it will return nil, nil. All options are ignored.
|
||||
func (r *Repository) LookupAccount(ctx context.Context, withPublicId string, opt ...Option) (*Account, error) {
|
||||
const op = "password.(Repository).LookupAccount"
|
||||
if withPublicId == "" {
|
||||
return nil, fmt.Errorf("lookup: password account: missing public id %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
a := allocAccount()
|
||||
a.PublicId = withPublicId
|
||||
@@ -126,15 +128,16 @@ func (r *Repository) LookupAccount(ctx context.Context, withPublicId string, opt
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("lookup: password account: failed %w for %s", err, withPublicId)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId)))
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// ListAccounts in an auth method and supports WithLimit option.
|
||||
func (r *Repository) ListAccounts(ctx context.Context, withAuthMethodId string, opt ...Option) ([]*Account, error) {
|
||||
const op = "password.(Repository).ListAccounts"
|
||||
if withAuthMethodId == "" {
|
||||
return nil, fmt.Errorf("list: password account: missing auth method id %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing auth method id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
@@ -145,7 +148,7 @@ func (r *Repository) ListAccounts(ctx context.Context, withAuthMethodId string,
|
||||
var accts []*Account
|
||||
err := r.reader.SearchWhere(ctx, &accts, "auth_method_id = ?", []interface{}{withAuthMethodId}, db.WithLimit(limit))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list: password account: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return accts, nil
|
||||
}
|
||||
@@ -153,18 +156,19 @@ func (r *Repository) ListAccounts(ctx context.Context, withAuthMethodId string,
|
||||
// DeleteAccount deletes the account for the provided id from the repository returning a count of the
|
||||
// number of records deleted. All options are ignored.
|
||||
func (r *Repository) DeleteAccount(ctx context.Context, scopeId, withPublicId string, opt ...Option) (int, error) {
|
||||
const op = "password.(Repository).DeleteAccount"
|
||||
if withPublicId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password account: missing public id: %w", errors.ErrInvalidParameter)
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password account: scope id empty: %w", errors.ErrInvalidParameter)
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
ac := allocAccount()
|
||||
ac.PublicId = withPublicId
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password account: unable to get oplog wrapper: %w", err)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
@@ -184,7 +188,7 @@ func (r *Repository) DeleteAccount(ctx context.Context, scopeId, withPublicId st
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password account: %s: %w", withPublicId, err)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(withPublicId))
|
||||
}
|
||||
|
||||
return rowsDeleted, nil
|
||||
@@ -213,20 +217,22 @@ func validLoginName(u string) bool {
|
||||
// in a is the zero value and it is included in fieldMaskPaths. a.LoginName
|
||||
// cannot be set to NULL.
|
||||
func (r *Repository) UpdateAccount(ctx context.Context, scopeId string, a *Account, version uint32, fieldMaskPaths []string, opt ...Option) (*Account, int, error) {
|
||||
const op = "password.(Repository).UpdateAccount"
|
||||
if a == nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing Account")
|
||||
}
|
||||
if a.Account == nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: embedded Account: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing embedded Account")
|
||||
}
|
||||
if a.PublicId == "" {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: missing public id: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: no version supplied: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: scope id empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
|
||||
}
|
||||
|
||||
var changeLoginName bool
|
||||
@@ -236,11 +242,12 @@ func (r *Repository) UpdateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
case strings.EqualFold("Description", f):
|
||||
case strings.EqualFold("LoginName", f):
|
||||
if !validLoginName(a.LoginName) {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: invalid user name: must be all-lowercase alphanumeric, period or hyphen %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op,
|
||||
fmt.Sprintf("invalid username: must be all-lowercase alphanumeric, period or hyphen, got %s", a.LoginName))
|
||||
}
|
||||
changeLoginName = true
|
||||
default:
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: field: %s: %w", f, errors.ErrInvalidFieldMask)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, f)
|
||||
}
|
||||
}
|
||||
var dbMask, nullFields []string
|
||||
@@ -254,22 +261,24 @@ func (r *Repository) UpdateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
nil,
|
||||
)
|
||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: %w", errors.ErrEmptyFieldMask)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.EmptyFieldMask, op, "missing field mask")
|
||||
}
|
||||
|
||||
if changeLoginName {
|
||||
cc, err := r.currentConfigForAccount(ctx, a.PublicId)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: retrieve current configuration: %w", err)
|
||||
return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("retrieve current configuration"))
|
||||
}
|
||||
if cc.MinLoginNameLength > len(a.LoginName) {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: user name %q: %w", a.LoginName, ErrTooShort)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.TooShort, op,
|
||||
fmt.Sprintf("username: %s, must be longer than %v", a.LoginName, cc.MinLoginNameLength))
|
||||
}
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: unable to get oplog wrapper: %w", err)
|
||||
return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithCode(errors.Encrypt),
|
||||
errors.WithMsg(("unable to get oplog wrapper")))
|
||||
}
|
||||
|
||||
a = a.clone()
|
||||
@@ -292,10 +301,11 @@ func (r *Repository) UpdateAccount(ctx context.Context, scopeId string, a *Accou
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: %s: name %s already exists: %w",
|
||||
a.PublicId, a.Name, errors.ErrNotUnique)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.NotUnique, op,
|
||||
fmt.Sprintf("name %s already exists: %s", a.Name, a.PublicId))
|
||||
|
||||
}
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password account: %s: %w", a.PublicId, err)
|
||||
return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(a.PublicId))
|
||||
}
|
||||
|
||||
return returnedAccount, rowsUpdated, nil
|
||||
|
||||
@@ -51,27 +51,31 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
authMethod := authMethods[0]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in *Account
|
||||
opts []Option
|
||||
want *Account
|
||||
wantIsErr error
|
||||
name string
|
||||
in *Account
|
||||
opts []Option
|
||||
want *Account
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "nil-Account",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "nil-Account",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: missing Account: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-embedded-Account",
|
||||
in: &Account{},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "nil-embedded-Account",
|
||||
in: &Account{},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: missing embedded Account: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-scope-id",
|
||||
in: &Account{
|
||||
Account: &store.Account{},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: missing auth method id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-public-id-set",
|
||||
@@ -81,7 +85,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
PublicId: "hcst_OOOOOOOOOO",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: public id must be empty: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-loginname-uppercase",
|
||||
@@ -91,7 +96,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
LoginName: "KaZmiErcZak11",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: login name must be all-lowercase alphanumeric, period or hyphen. got: KaZmiErcZak11: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-loginname-leading-space",
|
||||
@@ -101,7 +107,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
LoginName: " kazmierczak12",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: login name must be all-lowercase alphanumeric, period or hyphen. got: kazmierczak12: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-loginname-trailing-space",
|
||||
@@ -111,7 +118,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
LoginName: "kazmierczak13 ",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: login name must be all-lowercase alphanumeric, period or hyphen. got: kazmierczak13 : parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-loginname-space-in-name",
|
||||
@@ -121,7 +129,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
LoginName: "kazmier czak14",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: login name must be all-lowercase alphanumeric, period or hyphen. got: kazmier czak14: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-loginname-too-short",
|
||||
@@ -131,7 +140,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
LoginName: "ka",
|
||||
},
|
||||
},
|
||||
wantIsErr: ErrTooShort,
|
||||
wantIsErr: errors.TooShort,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: username: ka, must be longer than 3: integrity violation: error #113",
|
||||
},
|
||||
{
|
||||
name: "invalid-password-too-short",
|
||||
@@ -144,7 +154,8 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
opts: []Option{
|
||||
WithPassword("a"),
|
||||
},
|
||||
wantIsErr: ErrTooShort,
|
||||
wantIsErr: errors.PasswordTooShort,
|
||||
wantErrMsg: "password.(Repository).CreateAccount: must be longer than 8: password violation: error #200",
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
@@ -223,9 +234,9 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.CreateAccount(context.Background(), org.GetPublicId(), tt.in, tt.opts...)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -247,6 +258,14 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestRepository_CreateAccount_DuplicateNames(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
t.Run("invalid-duplicate-names", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
@@ -276,7 +295,7 @@ func TestRepository_CreateAccount(t *testing.T) {
|
||||
assert.Equal(got.CreateTime, got.UpdateTime)
|
||||
|
||||
got2, err := repo.CreateAccount(context.Background(), org.GetPublicId(), in)
|
||||
assert.Truef(errors.Is(err, errors.ErrNotUnique), "want err: %v got: %v", errors.ErrNotUnique, err)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "Unexpected error %s", err)
|
||||
assert.Nil(got2)
|
||||
})
|
||||
|
||||
@@ -334,14 +353,16 @@ func TestRepository_LookupAccount(t *testing.T) {
|
||||
newAcctId, err := newAccountId()
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want *Account
|
||||
wantIsErr error
|
||||
name string
|
||||
in string
|
||||
want *Account
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "password.(Repository).LookupAccount: missing public id: parameter violation: error #102",
|
||||
},
|
||||
{
|
||||
name: "With non existing account id",
|
||||
@@ -362,9 +383,9 @@ func TestRepository_LookupAccount(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.LookupAccount(context.Background(), tt.in)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -388,14 +409,16 @@ func TestRepository_DeleteAccount(t *testing.T) {
|
||||
newAcctId, err := newAccountId()
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want int
|
||||
wantIsErr error
|
||||
name string
|
||||
in string
|
||||
want int
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "password.(Repository).DeleteAccount: missing public id: parameter violation: error #102",
|
||||
},
|
||||
{
|
||||
name: "With non existing account id",
|
||||
@@ -417,9 +440,9 @@ func TestRepository_DeleteAccount(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.DeleteAccount(context.Background(), org.GetPublicId(), tt.in)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Zero(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -443,15 +466,17 @@ func TestRepository_ListAccounts(t *testing.T) {
|
||||
_ = accounts2
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
opts []Option
|
||||
want []*Account
|
||||
wantIsErr error
|
||||
name string
|
||||
in string
|
||||
opts []Option
|
||||
want []*Account
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no auth method id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no auth method id",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).ListAccounts: missing auth method id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "With no accounts id",
|
||||
@@ -473,9 +498,9 @@ func TestRepository_ListAccounts(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.ListAccounts(context.Background(), tt.in, tt.opts...)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -620,40 +645,44 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
orig *Account
|
||||
chgFn func(*Account) *Account
|
||||
masks []string
|
||||
want *Account
|
||||
wantCount int
|
||||
wantIsErr errors.Code
|
||||
name string
|
||||
orig *Account
|
||||
chgFn func(*Account) *Account
|
||||
masks []string
|
||||
want *Account
|
||||
wantCount int
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "nil-Account",
|
||||
orig: &Account{
|
||||
Account: &store.Account{},
|
||||
},
|
||||
chgFn: makeNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
chgFn: makeNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: missing Account: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-embedded-Account",
|
||||
orig: &Account{
|
||||
Account: &store.Account{},
|
||||
},
|
||||
chgFn: makeEmbeddedNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
chgFn: makeEmbeddedNil(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: missing embedded Account: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "no-public-id",
|
||||
orig: &Account{
|
||||
Account: &store.Account{},
|
||||
},
|
||||
chgFn: deletePublicId(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
chgFn: deletePublicId(),
|
||||
masks: []string{"Name", "Description"},
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: missing public id: parameter violation: error #102",
|
||||
},
|
||||
{
|
||||
name: "updating-non-existent-Account",
|
||||
@@ -662,9 +691,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
Name: "test-name-repo",
|
||||
},
|
||||
},
|
||||
chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")),
|
||||
masks: []string{"Name"},
|
||||
wantIsErr: errors.RecordNotFound,
|
||||
chgFn: combine(nonExistentPublicId(), changeName("test-update-name-repo")),
|
||||
masks: []string{"Name"},
|
||||
wantIsErr: errors.RecordNotFound,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: abcd_OOOOOOOOOO: db.DoTx: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100",
|
||||
},
|
||||
{
|
||||
name: "empty-field-mask",
|
||||
@@ -673,8 +703,9 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
Name: "test-name-repo",
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
wantIsErr: errors.EmptyFieldMask,
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
wantIsErr: errors.EmptyFieldMask,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: missing field mask: parameter violation: error #104",
|
||||
},
|
||||
{
|
||||
name: "read-only-fields-in-field-mask",
|
||||
@@ -683,9 +714,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
Name: "test-name-repo",
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"PublicId", "CreateTime", "UpdateTime", "AuthMethodId"},
|
||||
wantIsErr: errors.InvalidFieldMask,
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"PublicId", "CreateTime", "UpdateTime", "AuthMethodId"},
|
||||
wantIsErr: errors.InvalidFieldMask,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: PublicId: parameter violation: error #103",
|
||||
},
|
||||
{
|
||||
name: "unknown-field-in-field-mask",
|
||||
@@ -694,9 +726,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
Name: "test-name-repo",
|
||||
},
|
||||
},
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"Bilbo"},
|
||||
wantIsErr: errors.InvalidFieldMask,
|
||||
chgFn: changeName("test-update-name-repo"),
|
||||
masks: []string{"Bilbo"},
|
||||
wantIsErr: errors.InvalidFieldMask,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: Bilbo: parameter violation: error #103",
|
||||
},
|
||||
{
|
||||
name: "change-name",
|
||||
@@ -841,9 +874,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
LoginName: "kazmierczak",
|
||||
},
|
||||
},
|
||||
chgFn: changeLoginName("KaZmIeRcZaK"),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
chgFn: changeLoginName("KaZmIeRcZaK"),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: invalid username: must be all-lowercase alphanumeric, period or hyphen, got KaZmIeRcZaK: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "change-login-name-to-short",
|
||||
@@ -852,9 +886,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
LoginName: "kazmierczak",
|
||||
},
|
||||
},
|
||||
chgFn: changeLoginName("ka"),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.PasswordTooShort,
|
||||
chgFn: changeLoginName("ka"),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.TooShort,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: username: ka, must be longer than 3: integrity violation: error #113",
|
||||
},
|
||||
{
|
||||
name: "delete-login-name",
|
||||
@@ -863,9 +898,10 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
LoginName: "kazmierczak",
|
||||
},
|
||||
},
|
||||
chgFn: changeLoginName(""),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
chgFn: changeLoginName(""),
|
||||
masks: []string{"LoginName"},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).UpdateAccount: invalid username: must be all-lowercase alphanumeric, period or hyphen, got : parameter violation: error #100",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -894,6 +930,7 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
got, gotCount, err := repo.UpdateAccount(context.Background(), org.GetPublicId(), orig, 1, tt.masks)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
assert.Equal(tt.wantCount, gotCount, "row count")
|
||||
assert.Nil(got)
|
||||
return
|
||||
@@ -927,6 +964,15 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRepository_UpdateAccount_DupeNames(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
t.Run("invalid-duplicate-names", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
repo, err := NewRepository(rw, rw, kms)
|
||||
@@ -951,7 +997,7 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
|
||||
ab.Name = name
|
||||
got2, gotCount2, err := repo.UpdateAccount(context.Background(), org.GetPublicId(), ab, 1, []string{"name"})
|
||||
assert.Truef(errors.Is(err, errors.ErrNotUnique), "want err: %v got: %v", errors.ErrNotUnique, err)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "Unexpected error %s", err)
|
||||
assert.Nil(got2)
|
||||
assert.Equal(db.NoRowsAffected, gotCount2, "row count")
|
||||
err = db.TestVerifyOplog(t, rw, ab.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
||||
@@ -1029,7 +1075,7 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
|
||||
ab.LoginName = loginName
|
||||
got2, gotCount2, err := repo.UpdateAccount(context.Background(), org.GetPublicId(), ab, 1, []string{"LoginName"})
|
||||
assert.Truef(errors.Is(err, errors.ErrNotUnique), "want err: %v got: %v", errors.ErrNotUnique, err)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "Unexpected error %s", err)
|
||||
assert.Nil(got2)
|
||||
assert.Equal(db.NoRowsAffected, gotCount2, "row count")
|
||||
err = db.TestVerifyOplog(t, rw, ab.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
||||
@@ -1108,4 +1154,5 @@ func TestRepository_UpdateAccount(t *testing.T) {
|
||||
assert.Equal(1, gotCount1, "row count")
|
||||
assert.NoError(db.TestVerifyOplog(t, rw, aa.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)))
|
||||
})
|
||||
|
||||
}
|
||||
@@ -23,17 +23,18 @@ import (
|
||||
// Both m.Name and m.Description are optional. If m.Name is set, it must be
|
||||
// unique within m.ScopeId.
|
||||
func (r *Repository) CreateAuthMethod(ctx context.Context, m *AuthMethod, opt ...Option) (*AuthMethod, error) {
|
||||
const op = "password.(Repository).CreateAuthMethod"
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("create: password auth method: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing AuthMethod")
|
||||
}
|
||||
if m.AuthMethod == nil {
|
||||
return nil, fmt.Errorf("create: password auth method: embedded AuthMethod: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing embedded AuthMethod")
|
||||
}
|
||||
if m.ScopeId == "" {
|
||||
return nil, fmt.Errorf("create: password auth method: no scope id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
if m.PublicId != "" {
|
||||
return nil, fmt.Errorf("create: password auth method: public id not empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
m = m.clone()
|
||||
|
||||
@@ -41,35 +42,35 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, m *AuthMethod, opt ..
|
||||
|
||||
if opts.withPublicId != "" {
|
||||
if !strings.HasPrefix(opts.withPublicId, AuthMethodPrefix+"_") {
|
||||
return nil, fmt.Errorf("create: password auth method: passed-in public ID %q has wrong prefix, should be %q: %w", opts.withPublicId, AuthMethodPrefix, errors.ErrInvalidPublicId)
|
||||
return nil, errors.New(errors.InvalidPublicId, op, fmt.Sprintf("passed-in public ID %q has wrong prefix, should be %q", opts.withPublicId, AuthMethodPrefix))
|
||||
}
|
||||
m.PublicId = opts.withPublicId
|
||||
} else {
|
||||
id, err := newAuthMethodId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password auth method: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
m.PublicId = id
|
||||
}
|
||||
|
||||
c, ok := opts.withConfig.(*Argon2Configuration)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("create: password auth method: unknown configuration: %w", ErrUnsupportedConfiguration)
|
||||
return nil, errors.New(errors.PasswordUnsupportedConfiguration, op, "unknown configuration")
|
||||
}
|
||||
if err := c.validate(); err != nil {
|
||||
return nil, fmt.Errorf("create: password auth method: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
var err error
|
||||
c.PrivateId, err = newArgon2ConfigurationId()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password auth method: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
m.PasswordConfId, c.PasswordMethodId = c.PrivateId, m.PublicId
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, m.GetScopeId(), kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create: password auth method: unable to get oplog wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var newAuthMethod *AuthMethod
|
||||
@@ -87,10 +88,9 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, m *AuthMethod, opt ..
|
||||
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, fmt.Errorf("create: password auth method: in scope: %s: name %s already exists: %w",
|
||||
m.ScopeId, m.Name, errors.ErrNotUnique)
|
||||
return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("in scope: %s: name %s already exists", m.ScopeId, m.Name))
|
||||
}
|
||||
return nil, fmt.Errorf("create: password auth method: in scope: %s: %w", m.ScopeId, err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg(m.ScopeId))
|
||||
}
|
||||
return newAuthMethod, nil
|
||||
}
|
||||
@@ -98,8 +98,9 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, m *AuthMethod, opt ..
|
||||
// LookupAuthMethod will look up an auth method in the repository. If the auth method is not
|
||||
// found, it will return nil, nil. All options are ignored.
|
||||
func (r *Repository) LookupAuthMethod(ctx context.Context, publicId string, opt ...Option) (*AuthMethod, error) {
|
||||
const op = "password.(Repository).LookupAuthMethod"
|
||||
if publicId == "" {
|
||||
return nil, fmt.Errorf("lookup: password auth method: missing public id %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
a := allocAuthMethod()
|
||||
a.PublicId = publicId
|
||||
@@ -107,15 +108,16 @@ func (r *Repository) LookupAuthMethod(ctx context.Context, publicId string, opt
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("lookup: password auth method: failed %w for %s", err, publicId)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId)))
|
||||
}
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// ListAuthMethods returns a slice of AuthMethods for the scopeId. WithLimit is the only option supported.
|
||||
func (r *Repository) ListAuthMethods(ctx context.Context, scopeIds []string, opt ...Option) ([]*AuthMethod, error) {
|
||||
const op = "password.(Repository).ListAuthMethods"
|
||||
if len(scopeIds) == 0 {
|
||||
return nil, fmt.Errorf("list: password auth method: missing scope id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
limit := r.defaultLimit
|
||||
@@ -126,7 +128,7 @@ func (r *Repository) ListAuthMethods(ctx context.Context, scopeIds []string, opt
|
||||
var authMethods []*AuthMethod
|
||||
err := r.reader.SearchWhere(ctx, &authMethods, "scope_id in (?)", []interface{}{scopeIds}, db.WithLimit(limit))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list: password auth method: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return authMethods, nil
|
||||
}
|
||||
@@ -134,15 +136,17 @@ func (r *Repository) ListAuthMethods(ctx context.Context, scopeIds []string, opt
|
||||
// DeleteAuthMethod deletes the auth method for the provided id from the repository returning a count of the
|
||||
// number of records deleted. All options are ignored.
|
||||
func (r *Repository) DeleteAuthMethod(ctx context.Context, scopeId, publicId string, opt ...Option) (int, error) {
|
||||
const op = "password.(Repository).DeleteAuthMethod"
|
||||
if publicId == "" {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password auth method: missing public id: %w", errors.ErrInvalidParameter)
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
am := allocAuthMethod()
|
||||
am.PublicId = publicId
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password auth method: unable to get oplog wrapper: %w", err)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithCode(errors.Encrypt),
|
||||
errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsDeleted int
|
||||
@@ -162,7 +166,7 @@ func (r *Repository) DeleteAuthMethod(ctx context.Context, scopeId, publicId str
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return db.NoRowsAffected, fmt.Errorf("delete: password auth method: %s: %w", publicId, err)
|
||||
return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(publicId))
|
||||
}
|
||||
|
||||
return rowsDeleted, nil
|
||||
@@ -181,14 +185,15 @@ func (r *Repository) DeleteAuthMethod(ctx context.Context, scopeId, publicId str
|
||||
// and MinLoginNameLength are the only updatable fields, If no updatable fields
|
||||
// are included in the fieldMaskPaths, then an error is returned.
|
||||
func (r *Repository) UpdateAuthMethod(ctx context.Context, authMethod *AuthMethod, version uint32, fieldMaskPaths []string, opt ...Option) (*AuthMethod, int, error) {
|
||||
const op = "password.(Repository).UpdateAuthMethod"
|
||||
if authMethod == nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: missing authMethod: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing authMethod")
|
||||
}
|
||||
if authMethod.PublicId == "" {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: missing authMethod public id: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing authMethod public id")
|
||||
}
|
||||
if authMethod.ScopeId == "" {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: scope id empty: %w", errors.ErrInvalidParameter)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing scope id")
|
||||
}
|
||||
for _, f := range fieldMaskPaths {
|
||||
switch {
|
||||
@@ -197,7 +202,7 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, authMethod *AuthMetho
|
||||
case strings.EqualFold("MinLoginNameLength", f):
|
||||
case strings.EqualFold("MinPasswordLength", f):
|
||||
default:
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: field: %s: %w", f, errors.ErrInvalidFieldMask)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, f)
|
||||
}
|
||||
}
|
||||
var dbMask, nullFields []string
|
||||
@@ -212,12 +217,13 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, authMethod *AuthMetho
|
||||
nil,
|
||||
)
|
||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: %w", errors.ErrEmptyFieldMask)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.EmptyFieldMask, op, "field mask must not be empty")
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, authMethod.ScopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: unable to get oplog wrapper: %w", err)
|
||||
return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithCode(errors.Encrypt),
|
||||
errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
upAuthMethod := authMethod.clone()
|
||||
@@ -247,9 +253,9 @@ func (r *Repository) UpdateAuthMethod(ctx context.Context, authMethod *AuthMetho
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: authMethod %s already exists in scope %s: %w", authMethod.Name, authMethod.ScopeId, errors.ErrNotUnique)
|
||||
return nil, db.NoRowsAffected, errors.New(errors.NotUnique, op, fmt.Sprintf("authMethod %s already exists in scope %s", authMethod.Name, authMethod.ScopeId))
|
||||
}
|
||||
return nil, db.NoRowsAffected, fmt.Errorf("update: password auth method: %w for %s", err, authMethod.PublicId)
|
||||
return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(authMethod.PublicId))
|
||||
}
|
||||
return upAuthMethod, rowsUpdated, err
|
||||
}
|
||||
@@ -29,27 +29,31 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
org, _ := iam.TestScopes(t, iamRepo)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in *AuthMethod
|
||||
opts []Option
|
||||
want *AuthMethod
|
||||
wantIsErr error
|
||||
name string
|
||||
in *AuthMethod
|
||||
opts []Option
|
||||
want *AuthMethod
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "nil-AuthMethod",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "nil-AuthMethod",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: missing AuthMethod: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-embedded-AuthMethod",
|
||||
in: &AuthMethod{},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "nil-embedded-AuthMethod",
|
||||
in: &AuthMethod{},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: missing embedded AuthMethod: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-scope-id",
|
||||
in: &AuthMethod{
|
||||
AuthMethod: &store.AuthMethod{},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: missing scope id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-public-id-set",
|
||||
@@ -59,7 +63,8 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
PublicId: "hcst_OOOOOOOOOO",
|
||||
},
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: public id not empty: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "valid-no-options",
|
||||
@@ -114,7 +119,8 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
opts: []Option{
|
||||
WithConfiguration(&Argon2Configuration{}),
|
||||
},
|
||||
wantIsErr: ErrInvalidConfiguration,
|
||||
wantIsErr: errors.PasswordInvalidConfiguration,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: invalid parameters in password configuration, password violation: error #202",
|
||||
},
|
||||
{
|
||||
name: "invalid-with-config-unknown-config-type",
|
||||
@@ -126,7 +132,8 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
opts: []Option{
|
||||
WithConfiguration(tconf(0)),
|
||||
},
|
||||
wantIsErr: ErrUnsupportedConfiguration,
|
||||
wantIsErr: errors.PasswordUnsupportedConfiguration,
|
||||
wantErrMsg: "password.(Repository).CreateAuthMethod: unknown configuration: password violation: error #201",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -138,9 +145,9 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
require.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.CreateAuthMethod(context.Background(), tt.in, tt.opts...)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -153,6 +160,14 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
assert.Equal(got.CreateTime, got.UpdateTime)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepository_CreateAuthMethod_DupeNames(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
t.Run("invalid-duplicate-names", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
@@ -178,7 +193,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
assert.Equal(got.CreateTime, got.UpdateTime)
|
||||
|
||||
got2, err := repo.CreateAuthMethod(context.Background(), in)
|
||||
assert.Truef(errors.Is(err, errors.ErrNotUnique), "want err: %v got: %v", errors.ErrNotUnique, err)
|
||||
assert.Truef(errors.Match(errors.T(errors.NotUnique), err), "Unexpected error %s", err)
|
||||
assert.Nil(got2)
|
||||
})
|
||||
|
||||
@@ -217,6 +232,14 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
assert.Equal(in2.Description, got2.Description)
|
||||
assert.Equal(got2.CreateTime, got2.UpdateTime)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepository_CreateAuthMethod_PublicId(t *testing.T) {
|
||||
conn, _ := db.TestSetup(t, "postgres")
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
kms := kms.TestKms(t, conn, wrapper)
|
||||
iamRepo := iam.TestRepo(t, conn, wrapper)
|
||||
|
||||
t.Run("valid-with-publicid", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
@@ -251,7 +274,7 @@ func TestRepository_CreateAuthMethod(t *testing.T) {
|
||||
got, err := repo.CreateAuthMethod(context.Background(), &in, WithPublicId("invalid_idwithabadprefix"))
|
||||
assert.Error(err)
|
||||
assert.Nil(got)
|
||||
assert.True(errors.Is(err, errors.ErrInvalidPublicId))
|
||||
assert.Truef(errors.Match(errors.T(errors.InvalidPublicId), err), "Unexpected error %s", err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -267,14 +290,16 @@ func TestRepository_LookupAuthMethod(t *testing.T) {
|
||||
amId, err := newAuthMethodId()
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want *AuthMethod
|
||||
wantIsErr error
|
||||
name string
|
||||
in string
|
||||
want *AuthMethod
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "password.(Repository).LookupAuthMethod: missing public id: parameter violation: error #102",
|
||||
},
|
||||
{
|
||||
name: "With non existing auth method id",
|
||||
@@ -295,9 +320,9 @@ func TestRepository_LookupAuthMethod(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.LookupAuthMethod(context.Background(), tt.in)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -318,14 +343,16 @@ func TestRepository_DeleteAuthMethod(t *testing.T) {
|
||||
newAuthMethodId, err := newAuthMethodId()
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
want int
|
||||
wantIsErr error
|
||||
name string
|
||||
in string
|
||||
want int
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no public id",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "password.(Repository).DeleteAuthMethod: missing public id: parameter violation: error #102",
|
||||
},
|
||||
{
|
||||
name: "With non existing auth method id",
|
||||
@@ -347,9 +374,9 @@ func TestRepository_DeleteAuthMethod(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.DeleteAuthMethod(context.Background(), o.GetPublicId(), tt.in)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Zero(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -369,15 +396,17 @@ func TestRepository_ListAuthMethods(t *testing.T) {
|
||||
authMethods := TestAuthMethods(t, conn, o.GetPublicId(), 3)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in []string
|
||||
opts []Option
|
||||
want []*AuthMethod
|
||||
wantIsErr error
|
||||
name string
|
||||
in []string
|
||||
opts []Option
|
||||
want []*AuthMethod
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "With no scope id",
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
name: "With no scope id",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).ListAuthMethods: missing scope id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "Scope with no auth methods",
|
||||
@@ -399,9 +428,9 @@ func TestRepository_ListAuthMethods(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
require.NotNil(repo)
|
||||
got, err := repo.ListAuthMethods(context.Background(), tt.in, tt.opts...)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
@@ -2,7 +2,6 @@ package password
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/auth/password/store"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -21,12 +20,13 @@ type Configuration interface {
|
||||
|
||||
// GetConfiguration returns the current configuration for authMethodId.
|
||||
func (r *Repository) GetConfiguration(ctx context.Context, authMethodId string) (Configuration, error) {
|
||||
const op = "password.(Repository).GetConfiguration"
|
||||
if authMethodId == "" {
|
||||
return nil, fmt.Errorf("get password configuration: no auth method id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing auth method id")
|
||||
}
|
||||
cc, err := r.currentConfig(ctx, authMethodId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get password configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return cc.argon2(), nil
|
||||
}
|
||||
@@ -42,29 +42,31 @@ func (r *Repository) GetConfiguration(ctx context.Context, authMethodId string)
|
||||
// equal to a previous configuration for c.AuthMethodId, SetConfiguration
|
||||
// updates AuthMethod to use the previous configuration.
|
||||
func (r *Repository) SetConfiguration(ctx context.Context, scopeId string, c Configuration) (Configuration, error) {
|
||||
const op = "password.(Repository).SetConfiguration"
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("set password configuration: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing configuration")
|
||||
}
|
||||
if c.AuthMethodId() == "" {
|
||||
return nil, fmt.Errorf("set password configuration: no auth method id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing auth method id")
|
||||
}
|
||||
if err := c.validate(); err != nil {
|
||||
return nil, fmt.Errorf("set password configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
switch v := c.(type) {
|
||||
case *Argon2Configuration:
|
||||
out, err := r.setArgon2Conf(ctx, scopeId, v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return out, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("set password configuration: %w", ErrUnsupportedConfiguration)
|
||||
return nil, errors.New(errors.PasswordUnsupportedConfiguration, op, "unknown configuration")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) setArgon2Conf(ctx context.Context, scopeId string, c *Argon2Configuration) (*Argon2Configuration, error) {
|
||||
const op = "password.(Repository).setArgon2Conf"
|
||||
c = c.clone()
|
||||
|
||||
id, err := newArgon2ConfigurationId()
|
||||
@@ -81,7 +83,7 @@ func (r *Repository) setArgon2Conf(ctx context.Context, scopeId string, c *Argon
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update: password account: unable to get oplog wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
newArgon2Conf := &Argon2Configuration{Argon2Configuration: &store.Argon2Configuration{}}
|
||||
@@ -134,6 +136,7 @@ func (r *Repository) currentConfig(ctx context.Context, authMethodId string) (*c
|
||||
}
|
||||
|
||||
func (r *Repository) currentConfigForAccount(ctx context.Context, accountId string) (*currentConfig, error) {
|
||||
const op = "password.(Repository).currentConfigForAccount"
|
||||
var confs []currentConfig
|
||||
|
||||
rows, err := r.reader.Query(ctx, currentConfigForAccountQuery, []interface{}{accountId})
|
||||
@@ -155,7 +158,7 @@ func (r *Repository) currentConfigForAccount(ctx context.Context, accountId stri
|
||||
return nil, nil
|
||||
case len(confs) > 1:
|
||||
// this should never happen
|
||||
return nil, fmt.Errorf("multiple current configs returned for account")
|
||||
return nil, errors.New(errors.Unknown, op, "multiple current configs returned for account")
|
||||
default:
|
||||
cc = confs[0]
|
||||
}
|
||||
|
||||
@@ -32,27 +32,28 @@ type authAccount struct {
|
||||
// password settings for authMethodId if authentication is successful and
|
||||
// the stored values are not using the current password settings.
|
||||
func (r *Repository) Authenticate(ctx context.Context, scopeId, authMethodId, loginName, password string) (*Account, error) {
|
||||
const op = "password.(Repository).Authenticate"
|
||||
if authMethodId == "" {
|
||||
return nil, fmt.Errorf("password authenticate: no authMethodId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing authMethodId")
|
||||
}
|
||||
if loginName == "" {
|
||||
return nil, fmt.Errorf("password authenticate: no loginName: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing loginName")
|
||||
}
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("password authenticate: no password: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing password")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("password authenticate: no scopeId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scopeId")
|
||||
}
|
||||
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("password authenticate: unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
|
||||
acct, err := r.authenticate(ctx, scopeId, authMethodId, loginName, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("password authenticate: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if acct == nil {
|
||||
return nil, nil
|
||||
@@ -61,22 +62,22 @@ func (r *Repository) Authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
if !acct.IsCurrentConf {
|
||||
cc, err := r.currentConfig(ctx, authMethodId)
|
||||
if err != nil {
|
||||
return acct.Account, fmt.Errorf("password authenticate: retrieve current password configuration: %w", err)
|
||||
return acct.Account, errors.Wrap(err, op, errors.WithMsg("retrieve current password configuration"))
|
||||
}
|
||||
cred, err := newArgon2Credential(acct.PublicId, password, cc.argon2())
|
||||
if err != nil {
|
||||
return acct.Account, fmt.Errorf("password authenticate: update credential: %w", err)
|
||||
return acct.Account, errors.Wrap(err, op, errors.WithCode(errors.PasswordInvalidConfiguration))
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return acct.Account, fmt.Errorf("password authenticate: unable to get oplog wrapper: %w", err)
|
||||
return acct.Account, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplong wrapper"))
|
||||
}
|
||||
|
||||
// do not change the Credential Id
|
||||
cred.PrivateId = acct.CredentialId
|
||||
if err := cred.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return acct.Account, fmt.Errorf("password authenticate: update credential: encrypt: %w", err)
|
||||
return acct.Account, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("update credential"))
|
||||
}
|
||||
|
||||
fields := []string{"CtSalt", "DerivedKey", "PasswordConfId"}
|
||||
@@ -92,7 +93,7 @@ func (r *Repository) Authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return acct.Account, fmt.Errorf("password authenticate: update credential: %w", err)
|
||||
return acct.Account, errors.Wrap(err, op, errors.WithMsg("update credential"))
|
||||
}
|
||||
}
|
||||
return acct.Account, nil
|
||||
@@ -106,45 +107,46 @@ func (r *Repository) Authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
// Returns nil, nil if old does not match the stored password for accountId.
|
||||
// Returns nil, ErrPasswordsEqual if old and new are equal.
|
||||
func (r *Repository) ChangePassword(ctx context.Context, scopeId, accountId, old, new string, version uint32) (*Account, error) {
|
||||
const op = "password.(Repository).ChangePassword"
|
||||
if accountId == "" {
|
||||
return nil, fmt.Errorf("change password: no account id: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing account id")
|
||||
}
|
||||
if old == "" {
|
||||
return nil, fmt.Errorf("change password: no old password: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing old password")
|
||||
}
|
||||
if new == "" {
|
||||
return nil, fmt.Errorf("change password: no new password: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing new password")
|
||||
}
|
||||
if old == new {
|
||||
return nil, fmt.Errorf("change password: %w", ErrPasswordsEqual)
|
||||
return nil, errors.New(errors.PasswordsEqual, op, "passwords must not equal")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, fmt.Errorf("change password: no version supplied: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("change password: no scopeId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scopeId")
|
||||
}
|
||||
|
||||
authAccount, err := r.LookupAccount(ctx, accountId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: lookup account: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if authAccount == nil {
|
||||
return nil, fmt.Errorf("change password: lookup account: account not found: %w", errors.ErrRecordNotFound)
|
||||
return nil, errors.New(errors.RecordNotFound, op, "account not found")
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: unable to get oplog wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
|
||||
acct, err := r.authenticate(ctx, scopeId, authAccount.GetAuthMethodId(), authAccount.GetLoginName(), old)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if acct == nil {
|
||||
return nil, nil
|
||||
@@ -152,18 +154,18 @@ func (r *Repository) ChangePassword(ctx context.Context, scopeId, accountId, old
|
||||
|
||||
cc, err := r.currentConfig(ctx, authAccount.GetAuthMethodId())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: retrieve current password configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("retrieve current password configuration"))
|
||||
}
|
||||
if cc.MinPasswordLength > len(new) {
|
||||
return nil, fmt.Errorf("change password: %w", ErrTooShort)
|
||||
return nil, errors.New(errors.PasswordTooShort, op, fmt.Sprintf("must be at least %d", cc.MinPasswordLength))
|
||||
}
|
||||
newCred, err := newArgon2Credential(accountId, new, cc.argon2())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
if err := newCred.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, fmt.Errorf("change password: encrypt: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
|
||||
oldCred := acct.Argon2Credential
|
||||
@@ -176,10 +178,10 @@ func (r *Repository) ChangePassword(ctx context.Context, scopeId, accountId, old
|
||||
updatedAccount.Version = version + 1
|
||||
rowsUpdated, err := w.Update(ctx, updatedAccount, []string{"Version"}, nil, db.WithOplog(oplogWrapper, acct.Account.oplog(oplog.OpType_OP_TYPE_UPDATE)), db.WithVersion(&version))
|
||||
if err != nil {
|
||||
return fmt.Errorf("change password: unable to update account version: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithMsg("unable to update account version"))
|
||||
}
|
||||
if rowsUpdated != 1 {
|
||||
return fmt.Errorf("change password: updated account and %d rows updated", rowsUpdated)
|
||||
return errors.New(db.NoRowsAffected, op, fmt.Sprintf("updated account and %d rows updated", rowsUpdated))
|
||||
}
|
||||
|
||||
rowsDeleted, err := w.Delete(ctx, oldCred, db.WithOplog(oplogWrapper, oldCred.oplog(oplog.OpType_OP_TYPE_DELETE)))
|
||||
@@ -193,7 +195,7 @@ func (r *Repository) ChangePassword(ctx context.Context, scopeId, accountId, old
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("change password: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
// change the Credential Id
|
||||
@@ -202,6 +204,7 @@ func (r *Repository) ChangePassword(ctx context.Context, scopeId, accountId, old
|
||||
}
|
||||
|
||||
func (r *Repository) authenticate(ctx context.Context, scopeId, authMethodId, loginName, password string) (*authAccount, error) {
|
||||
const op = "password.(Repository).authenticate"
|
||||
var accts []authAccount
|
||||
|
||||
rows, err := r.reader.Query(ctx, authenticateQuery, []interface{}{authMethodId, loginName})
|
||||
@@ -223,7 +226,7 @@ func (r *Repository) authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
return nil, nil
|
||||
case len(accts) > 1:
|
||||
// this should never happen
|
||||
return nil, fmt.Errorf("multiple accounts returned for user name")
|
||||
return nil, errors.New(errors.Unknown, op, "multiple accounts returned for user name")
|
||||
default:
|
||||
acct = accts[0]
|
||||
}
|
||||
@@ -231,11 +234,11 @@ func (r *Repository) authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
// We don't pass a wrapper in here because for ecryption we want to indicate the expected key ID
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase, kms.WithKeyId(acct.GetKeyId()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
|
||||
if err := acct.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, fmt.Errorf("cannot decrypt credential: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Decrypt), errors.WithMsg("unable to decrypt credential"))
|
||||
}
|
||||
|
||||
inputKey := argon2.IDKey([]byte(password), acct.Salt, acct.Iterations, acct.Memory, uint8(acct.Threads), acct.KeyLength)
|
||||
@@ -249,43 +252,44 @@ func (r *Repository) authenticate(ctx context.Context, scopeId, authMethodId, lo
|
||||
// SetPassword sets the password for accountId to password. If password
|
||||
// contains an empty string, the password for accountId will be deleted.
|
||||
func (r *Repository) SetPassword(ctx context.Context, scopeId, accountId, password string, version uint32) (*Account, error) {
|
||||
const op = "password.(Repository).SetPassword"
|
||||
if accountId == "" {
|
||||
return nil, fmt.Errorf("set password: no accountId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing accountId")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, fmt.Errorf("set password: no version supplied: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if scopeId == "" {
|
||||
return nil, fmt.Errorf("set password: no scopeId: %w", errors.ErrInvalidParameter)
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing scopeId")
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password: unable to get oplog wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, scopeId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password: unable to get database wrapper: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
|
||||
var newCred *Argon2Credential
|
||||
if password != "" {
|
||||
cc, err := r.currentConfigForAccount(ctx, accountId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password: retrieve current configuration: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if cc == nil {
|
||||
return nil, fmt.Errorf("set password: retrieve current configuration: %w", errors.ErrRecordNotFound)
|
||||
return nil, errors.New(errors.RecordNotFound, op, "unable to retrieve current configuration")
|
||||
}
|
||||
if cc.MinPasswordLength > len(password) {
|
||||
return nil, fmt.Errorf("set password: new password: %w", ErrTooShort)
|
||||
return nil, errors.New(errors.PasswordTooShort, op, fmt.Sprintf("password must be at least %v", cc.MinPasswordLength))
|
||||
}
|
||||
newCred, err = newArgon2Credential(accountId, password, cc.argon2())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
if err := newCred.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, fmt.Errorf("set password: encrypt: %w", err)
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,10 +301,10 @@ func (r *Repository) SetPassword(ctx context.Context, scopeId, accountId, passwo
|
||||
updatedAccount.Version = version + 1
|
||||
rowsUpdated, err := w.Update(ctx, updatedAccount, []string{"Version"}, nil, db.WithOplog(oplogWrapper, updatedAccount.oplog(oplog.OpType_OP_TYPE_UPDATE)), db.WithVersion(&version))
|
||||
if err != nil {
|
||||
return fmt.Errorf("set password: unable to update account version: %w", err)
|
||||
return errors.Wrap(err, op, errors.WithMsg("unable to update account version"))
|
||||
}
|
||||
if rowsUpdated != 1 {
|
||||
return fmt.Errorf("set password: updated account and %d rows updated", rowsUpdated)
|
||||
return errors.New(db.NoRowsAffected, op, fmt.Sprintf("updated account and %d rows updated", rowsUpdated))
|
||||
}
|
||||
acct = updatedAccount
|
||||
|
||||
@@ -327,7 +331,7 @@ func (r *Repository) SetPassword(ctx context.Context, scopeId, accountId, passwo
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("set password: %w", err)
|
||||
return nil, errors.Wrap(err, op)
|
||||
}
|
||||
return acct, nil
|
||||
}
|
||||
@@ -46,10 +46,11 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Account
|
||||
wantIsErr error
|
||||
name string
|
||||
args args
|
||||
want *Account
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "invalid-no-authMethodId",
|
||||
@@ -58,7 +59,8 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
loginName: inAcct.LoginName,
|
||||
password: passwd,
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).Authenticate: missing authMethodId: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-loginName",
|
||||
@@ -67,7 +69,8 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
loginName: "",
|
||||
password: passwd,
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).Authenticate: missing loginName: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-password",
|
||||
@@ -76,7 +79,8 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
loginName: inAcct.LoginName,
|
||||
password: "",
|
||||
},
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).Authenticate: missing password: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "valid-authenticate",
|
||||
@@ -95,7 +99,7 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
password: "foobar",
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: nil,
|
||||
wantIsErr: errors.Unknown,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -103,9 +107,9 @@ func TestRepository_Authenticate(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
authAcct, err := repo.Authenticate(context.Background(), o.GetPublicId(), tt.args.authMethodId, tt.args.loginName, tt.args.password)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(authAcct, "returned account")
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
@@ -277,6 +281,7 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
args args
|
||||
wantAccount bool
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "invalid-no-accountId",
|
||||
@@ -285,7 +290,8 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
old: passwd,
|
||||
new: "12345678-changed",
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: missing account id: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-current-password",
|
||||
@@ -294,7 +300,8 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
old: "",
|
||||
new: "12345678-changed",
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: missing old password: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-no-new-password",
|
||||
@@ -303,7 +310,8 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
old: passwd,
|
||||
new: "",
|
||||
},
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: missing new password: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "invalid-same-passwords",
|
||||
@@ -312,7 +320,8 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
old: passwd,
|
||||
new: passwd,
|
||||
},
|
||||
wantIsErr: errors.PasswordsEqual,
|
||||
wantIsErr: errors.PasswordsEqual,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: passwords must not equal: password violation: error #203",
|
||||
},
|
||||
{
|
||||
name: "auth-failure-unknown-accountId",
|
||||
@@ -323,6 +332,7 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
},
|
||||
wantAccount: false,
|
||||
wantIsErr: errors.RecordNotFound,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: account not found: search issue: error #1100",
|
||||
},
|
||||
{
|
||||
name: "auth-failure-wrong-current-password",
|
||||
@@ -334,13 +344,14 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
wantAccount: false,
|
||||
},
|
||||
{
|
||||
name: "password-to-short",
|
||||
name: "password-too-short",
|
||||
args: args{
|
||||
acctId: acct.PublicId,
|
||||
old: passwd,
|
||||
new: "1",
|
||||
},
|
||||
wantIsErr: errors.PasswordTooShort,
|
||||
wantIsErr: errors.PasswordTooShort,
|
||||
wantErrMsg: "password.(Repository).ChangePassword: must be at least 8: password violation: error #200",
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
@@ -369,8 +380,8 @@ func TestRepository_ChangePassword(t *testing.T) {
|
||||
|
||||
chgAuthAcct, err := repo.ChangePassword(context.Background(), o.GetPublicId(), tt.args.acctId, tt.args.old, tt.args.new, authAcct1.Version)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Error(err)
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
assert.Nil(chgAuthAcct, "returned account")
|
||||
authAcct2 := authFn(passwd, "error changing password: using old password")
|
||||
assert.Equal(authAcct1.CredentialId, authAcct2.CredentialId, "CredentialId should not change")
|
||||
@@ -501,30 +512,34 @@ func TestRepository_SetPassword(t *testing.T) {
|
||||
|
||||
badInputAcct := createAccount("badinputusername")("")
|
||||
badInputCases := []struct {
|
||||
name string
|
||||
accountId string
|
||||
pw string
|
||||
version uint32
|
||||
wantError error
|
||||
name string
|
||||
accountId string
|
||||
pw string
|
||||
version uint32
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "no id",
|
||||
pw: "anylongpassword",
|
||||
version: 1,
|
||||
wantError: errors.ErrInvalidParameter,
|
||||
name: "no id",
|
||||
pw: "anylongpassword",
|
||||
version: 1,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).SetPassword: missing accountId: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "short pw",
|
||||
accountId: badInputAcct.PublicId,
|
||||
pw: "c",
|
||||
version: 1,
|
||||
wantError: ErrTooShort,
|
||||
name: "short pw",
|
||||
accountId: badInputAcct.PublicId,
|
||||
pw: "c",
|
||||
version: 1,
|
||||
wantIsErr: errors.PasswordTooShort,
|
||||
wantErrMsg: "password.(Repository).SetPassword: password must be at least 8: password violation: error #200",
|
||||
},
|
||||
{
|
||||
name: "no version",
|
||||
accountId: badInputAcct.PublicId,
|
||||
pw: "anylongpassword",
|
||||
wantError: errors.ErrInvalidParameter,
|
||||
name: "no version",
|
||||
accountId: badInputAcct.PublicId,
|
||||
pw: "anylongpassword",
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.(Repository).SetPassword: missing version: parameter violation: error #100",
|
||||
},
|
||||
}
|
||||
for _, tt := range badInputCases {
|
||||
@@ -533,7 +548,8 @@ func TestRepository_SetPassword(t *testing.T) {
|
||||
|
||||
acct, err := repo.SetPassword(context.Background(), o.GetPublicId(), tt.accountId, tt.pw, tt.version)
|
||||
assert.Error(err)
|
||||
assert.Truef(errors.Is(err, tt.wantError), "want err: %q got: %q", tt.wantError, err)
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
assert.Nil(acct)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,10 +25,11 @@ func TestRepository_New(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Repository
|
||||
wantIsErr error
|
||||
name string
|
||||
args args
|
||||
want *Repository
|
||||
wantIsErr errors.Code
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
@@ -66,8 +67,9 @@ func TestRepository_New(t *testing.T) {
|
||||
w: rw,
|
||||
kms: kmsCache,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.NewRepository: missing db.Reader: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-writer",
|
||||
@@ -76,8 +78,9 @@ func TestRepository_New(t *testing.T) {
|
||||
w: nil,
|
||||
kms: kmsCache,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.NewRepository: missing db.Writer: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
@@ -86,8 +89,9 @@ func TestRepository_New(t *testing.T) {
|
||||
w: rw,
|
||||
kms: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.NewRepository: missing kms: parameter violation: error #100",
|
||||
},
|
||||
{
|
||||
name: "all-nils",
|
||||
@@ -96,8 +100,9 @@ func TestRepository_New(t *testing.T) {
|
||||
w: nil,
|
||||
kms: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantIsErr: errors.ErrInvalidParameter,
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "password.NewRepository: missing db.Reader: parameter violation: error #100",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -105,9 +110,9 @@ func TestRepository_New(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms, tt.args.opts...)
|
||||
if tt.wantIsErr != nil {
|
||||
assert.Truef(errors.Is(err, tt.wantIsErr), "want err: %q got: %q", tt.wantIsErr, err)
|
||||
assert.Nil(got)
|
||||
if tt.wantIsErr != 0 {
|
||||
assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "Unexpected error %s", err)
|
||||
assert.Equal(tt.wantErrMsg, err.Error())
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
|
||||
@@ -138,7 +138,7 @@ func (r *Repository) CreateAuthToken(ctx context.Context, withIamUser *iam.User,
|
||||
func (r *Repository) LookupAuthToken(ctx context.Context, id string, opt ...Option) (*AuthToken, error) {
|
||||
const op = "authtoken.(Repository).LookupAuthToken"
|
||||
if id == "" {
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
|
||||
@@ -157,7 +157,7 @@ func (r *Repository) LookupAuthToken(ctx context.Context, id string, opt ...Opti
|
||||
if opts.withTokenValue {
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, at.GetScopeId(), kms.KeyPurposeDatabase, kms.WithKeyId(at.GetKeyId()))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
return nil, errors.Wrap(err, op, errors.WithCode(errors.Encrypt), errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := at.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(err, op)
|
||||
@@ -182,7 +182,7 @@ func (r *Repository) ValidateToken(ctx context.Context, id, token string, opt ..
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing token")
|
||||
}
|
||||
if id == "" {
|
||||
return nil, errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
return nil, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
|
||||
retAT, err := r.LookupAuthToken(ctx, id, withTokenValue())
|
||||
@@ -301,7 +301,7 @@ func (r *Repository) ListAuthTokens(ctx context.Context, withScopeIds []string,
|
||||
func (r *Repository) DeleteAuthToken(ctx context.Context, id string, opt ...Option) (int, error) {
|
||||
const op = "authtoken.(Repository).DeleteAuthToken"
|
||||
if id == "" {
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id")
|
||||
return db.NoRowsAffected, errors.New(errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
|
||||
at, err := r.LookupAuthToken(ctx, id)
|
||||
|
||||
@@ -305,8 +305,8 @@ func TestRepository_LookupAuthToken(t *testing.T) {
|
||||
name: "bad-public-id",
|
||||
id: "",
|
||||
want: nil,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "authtoken.(Repository).LookupAuthToken: missing public id: parameter violation: error #100",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "authtoken.(Repository).LookupAuthToken: missing public id: parameter violation: error #102",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -575,8 +575,8 @@ func TestRepository_DeleteAuthToken(t *testing.T) {
|
||||
name: "empty-public-id",
|
||||
id: "",
|
||||
want: 0,
|
||||
wantIsErr: errors.InvalidParameter,
|
||||
wantErrMsg: "authtoken.(Repository).DeleteAuthToken: missing public id: parameter violation: error #100",
|
||||
wantIsErr: errors.InvalidPublicId,
|
||||
wantErrMsg: "authtoken.(Repository).DeleteAuthToken: missing public id: parameter violation: error #102",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ func newId(prefix string) (string, error) {
|
||||
}
|
||||
publicId, err := base62.Random(10)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, op, errors.WithCode(errors.Io))
|
||||
return "", errors.Wrap(err, op, errors.WithMsg("unable to generate id"), errors.WithCode(errors.Io))
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", prefix, publicId), nil
|
||||
}
|
||||
@@ -34,6 +34,7 @@ const (
|
||||
SessionNotFound Code = 110 // SessionNotFound represents that the session was not found
|
||||
InvalidSessionState Code = 111 // InvalidSessionState represents that the session was in an invalid state
|
||||
TokenMismatch Code = 112 // TokenMismatch represents that there was a token mismatch
|
||||
TooShort Code = 113 // TooShort represents an error that means the provided input is not meeting minimum length requirements
|
||||
|
||||
// PasswordTooShort results from attempting to set a password which is to short.
|
||||
PasswordTooShort Code = 200
|
||||
|
||||
@@ -92,6 +92,11 @@ func TestCode_Both_String_Info(t *testing.T) {
|
||||
c: TokenMismatch,
|
||||
want: TokenMismatch,
|
||||
},
|
||||
{
|
||||
name: "TooShort",
|
||||
c: TooShort,
|
||||
want: TooShort,
|
||||
},
|
||||
{
|
||||
name: "PasswordTooShort",
|
||||
c: PasswordTooShort,
|
||||
|
||||
@@ -68,6 +68,10 @@ var errorCodeInfo = map[Code]Info{
|
||||
Message: "token mismatch",
|
||||
Kind: Integrity,
|
||||
},
|
||||
TooShort: {
|
||||
Message: "too short",
|
||||
Kind: Integrity,
|
||||
},
|
||||
PasswordTooShort: {
|
||||
Message: "too short",
|
||||
Kind: Password,
|
||||
|
||||
Reference in new issue
Block a user