diff --git a/internal/auth/auth.go b/internal/auth/auth.go index a0a5d318b..fe11159dc 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -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) diff --git a/internal/auth/password/account.go b/internal/auth/password/account.go index e25bcc18b..4334cbc76 100644 --- a/internal/auth/password/account.go +++ b/internal/auth/password/account.go @@ -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...) diff --git a/internal/auth/password/argon2.go b/internal/auth/password/argon2.go index 210dd37ec..3df1477a8 100644 --- a/internal/auth/password/argon2.go +++ b/internal/auth/password/argon2.go @@ -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 } diff --git a/internal/auth/password/argon2_test.go b/internal/auth/password/argon2_test.go index e5e2a7e24..a99ba3516 100644 --- a/internal/auth/password/argon2_test.go +++ b/internal/auth/password/argon2_test.go @@ -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) diff --git a/internal/auth/password/authmethod.go b/internal/auth/password/authmethod.go index 7293c1a3f..5ddce1224 100644 --- a/internal/auth/password/authmethod.go +++ b/internal/auth/password/authmethod.go @@ -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...) diff --git a/internal/auth/password/private_ids.go b/internal/auth/password/private_ids.go index ea99286ca..83cbb6b30 100644 --- a/internal/auth/password/private_ids.go +++ b/internal/auth/password/private_ids.go @@ -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 } diff --git a/internal/auth/password/public_ids.go b/internal/auth/password/public_ids.go index b40c9303c..ff699f124 100644 --- a/internal/auth/password/public_ids.go +++ b/internal/auth/password/public_ids.go @@ -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 } diff --git a/internal/auth/password/repository.go b/internal/auth/password/repository.go index e5a3cbc15..c174d6137 100644 --- a/internal/auth/password/repository.go +++ b/internal/auth/password/repository.go @@ -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...) diff --git a/internal/auth/password/repository_account.go b/internal/auth/password/repository_account.go index 940bbc027..763b309d6 100644 --- a/internal/auth/password/repository_account.go +++ b/internal/auth/password/repository_account.go @@ -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 diff --git a/internal/auth/password/repository_account_test.go b/internal/auth/password/repository_account_test.go index ef1e43572..84cf95a7c 100644 --- a/internal/auth/password/repository_account_test.go +++ b/internal/auth/password/repository_account_test.go @@ -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))) }) + } diff --git a/internal/auth/password/repository_authmethod.go b/internal/auth/password/repository_authmethod.go index 7ae759ee9..7f2231099 100644 --- a/internal/auth/password/repository_authmethod.go +++ b/internal/auth/password/repository_authmethod.go @@ -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 } diff --git a/internal/auth/password/repository_authmethod_test.go b/internal/auth/password/repository_authmethod_test.go index 3b8516d2c..10a0e5a6c 100644 --- a/internal/auth/password/repository_authmethod_test.go +++ b/internal/auth/password/repository_authmethod_test.go @@ -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) diff --git a/internal/auth/password/repository_configuration.go b/internal/auth/password/repository_configuration.go index a8101854c..9b83885ca 100644 --- a/internal/auth/password/repository_configuration.go +++ b/internal/auth/password/repository_configuration.go @@ -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] } diff --git a/internal/auth/password/repository_password.go b/internal/auth/password/repository_password.go index e24092178..9ce83ff16 100644 --- a/internal/auth/password/repository_password.go +++ b/internal/auth/password/repository_password.go @@ -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 } diff --git a/internal/auth/password/repository_password_test.go b/internal/auth/password/repository_password_test.go index bf1989a86..bf326252b 100644 --- a/internal/auth/password/repository_password_test.go +++ b/internal/auth/password/repository_password_test.go @@ -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) }) } diff --git a/internal/auth/password/repository_test.go b/internal/auth/password/repository_test.go index 11ffc9b45..80b9eb236 100644 --- a/internal/auth/password/repository_test.go +++ b/internal/auth/password/repository_test.go @@ -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) diff --git a/internal/authtoken/repository.go b/internal/authtoken/repository.go index 414de59b5..cbba3d835 100644 --- a/internal/authtoken/repository.go +++ b/internal/authtoken/repository.go @@ -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) diff --git a/internal/authtoken/repository_test.go b/internal/authtoken/repository_test.go index 289f8e241..08a1895dd 100644 --- a/internal/authtoken/repository_test.go +++ b/internal/authtoken/repository_test.go @@ -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", }, } diff --git a/internal/db/id.go b/internal/db/id.go index 4542035e3..9a58054c2 100644 --- a/internal/db/id.go +++ b/internal/db/id.go @@ -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 } diff --git a/internal/errors/code.go b/internal/errors/code.go index 71e21274a..9f1ca2ff7 100644 --- a/internal/errors/code.go +++ b/internal/errors/code.go @@ -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 diff --git a/internal/errors/code_test.go b/internal/errors/code_test.go index c2754de3a..46ecb10ef 100644 --- a/internal/errors/code_test.go +++ b/internal/errors/code_test.go @@ -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, diff --git a/internal/errors/info.go b/internal/errors/info.go index a21fc3370..6f0e2109b 100644 --- a/internal/errors/info.go +++ b/internal/errors/info.go @@ -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,