diff --git a/CHANGELOG.md b/CHANGELOG.md index c21974fab..5005b6ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Canonical reference for changes, improvements, and bugfixes for Boundary. ### New and Improved +* controller: Improved error handling in iam repo + ([PR](https://github.com/hashicorp/boundary/pull/841)) * controller: Improved error handling in db ([PR](https://github.com/hashicorp/boundary/pull/815)) diff --git a/internal/db/common/update.go b/internal/db/common/update.go index 8c376f7f0..03c8c58b2 100644 --- a/internal/db/common/update.go +++ b/internal/db/common/update.go @@ -33,7 +33,7 @@ func UpdateFields(i interface{}, fieldMaskPaths []string, setToNullPaths []strin inter, maskPaths, nullPaths, err := Intersection(fieldMaskPaths, setToNullPaths) if err != nil { - return nil, err + return nil, errors.Wrap(err, op) } if len(inter) != 0 { return nil, errors.New(errors.InvalidParameter, op, "fieldMashPaths and setToNullPaths cannot intersect") diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 36aac27ca..cf96c5d05 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -264,7 +264,7 @@ func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error { if !opts.withSkipVetForWrite { if vetter, ok := i.(VetForWriter); ok { if err := vetter.VetForWrite(ctx, rw, CreateOp); err != nil { - return errors.Wrap(err, op, errors.WithMsg("vet for write failed")) + return errors.Wrap(err, op) } } } @@ -435,7 +435,7 @@ func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string if !opts.withSkipVetForWrite { if vetter, ok := i.(VetForWriter); ok { if err := vetter.VetForWrite(ctx, rw, UpdateOp, WithFieldMaskPaths(fieldMaskPaths), WithNullPaths(setToNullPaths)); err != nil { - return NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("vet for write failed")) + return NoRowsAffected, errors.Wrap(err, op) } } } @@ -791,7 +791,7 @@ func (rw *Db) addOplog(ctx context.Context, opType OpType, opts Options, ticket ticketer, ) if err != nil { - return err + return errors.Wrap(err, op) } msg, err := rw.newOplogMessage(ctx, opType, i, WithFieldMaskPaths(opts.WithFieldMaskPaths), WithNullPaths(opts.WithNullPaths)) if err != nil { @@ -985,7 +985,7 @@ func (rw *Db) LookupWhere(_ context.Context, resource interface{}, where string, if err == gorm.ErrRecordNotFound { return errors.E(errors.WithCode(errors.RecordNotFound), errors.WithOp(op)) } - return err + return errors.Wrap(err, op) } return nil } diff --git a/internal/host/static/public_ids.go b/internal/host/static/public_ids.go index 498107ea2..b77e38a97 100644 --- a/internal/host/static/public_ids.go +++ b/internal/host/static/public_ids.go @@ -17,7 +17,7 @@ func newHostCatalogId() (string, error) { if err != nil { return "", errors.Wrap(err, "static.newHostCatalogId") } - return id, err + return id, nil } func newHostId() (string, error) { @@ -25,7 +25,7 @@ func newHostId() (string, error) { if err != nil { return "", errors.Wrap(err, "static.newHostId") } - return id, err + return id, nil } func newHostSetId() (string, error) { @@ -33,5 +33,5 @@ func newHostSetId() (string, error) { if err != nil { return "", errors.Wrap(err, "static.newHostSetId") } - return id, err + return id, nil } diff --git a/internal/host/static/repository_host.go b/internal/host/static/repository_host.go index f39d2afde..b06e53b44 100644 --- a/internal/host/static/repository_host.go +++ b/internal/host/static/repository_host.go @@ -133,7 +133,7 @@ func (r *Repository) UpdateHost(ctx context.Context, scopeId string, h *Host, ve return nil, db.NoRowsAffected, errors.New(errors.InvalidAddress, op, "invalid address") } default: - return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("field: %s", f)) + return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } var dbMask, nullFields []string @@ -164,10 +164,13 @@ func (r *Repository) UpdateHost(ctx context.Context, scopeId string, h *Host, ve rowsUpdated, err = w.Update(ctx, returnedHost, dbMask, nullFields, db.WithOplog(oplogWrapper, h.oplog(oplog.OpType_OP_TYPE_UPDATE)), db.WithVersion(&version)) - if err == nil && rowsUpdated > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsUpdated > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - return err + return nil }, ) @@ -250,10 +253,13 @@ func (r *Repository) DeleteHost(ctx context.Context, scopeId string, publicId st func(_ db.Reader, w db.Writer) (err error) { dh := h.clone() rowsDeleted, err = w.Delete(ctx, dh, db.WithOplog(oplogWrapper, h.oplog(oplog.OpType_OP_TYPE_DELETE))) - if err == nil && rowsDeleted > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsDeleted > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - return err + return nil }, ) diff --git a/internal/host/static/repository_host_catalog.go b/internal/host/static/repository_host_catalog.go index 916d04a07..3c54cafd8 100644 --- a/internal/host/static/repository_host_catalog.go +++ b/internal/host/static/repository_host_catalog.go @@ -128,7 +128,7 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version dbMask = append(dbMask, "description") default: - return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("field: %s", f)) + return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } @@ -158,10 +158,13 @@ func (r *Repository) UpdateCatalog(ctx context.Context, c *HostCatalog, version db.WithOplog(oplogWrapper, metadata), db.WithVersion(&version), ) - if err == nil && rowsUpdated > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsUpdated > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - return err + return nil }, ) @@ -253,10 +256,13 @@ func (r *Repository) DeleteCatalog(ctx context.Context, id string, opt ...Option deleteCatalog, db.WithOplog(oplogWrapper, metadata), ) - if err == nil && rowsDeleted > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsDeleted > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - return err + return nil }, ) diff --git a/internal/host/static/repository_host_set.go b/internal/host/static/repository_host_set.go index 86551f3e5..5688f70ee 100644 --- a/internal/host/static/repository_host_set.go +++ b/internal/host/static/repository_host_set.go @@ -116,7 +116,7 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, case strings.EqualFold("Name", f): case strings.EqualFold("Description", f): default: - return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("field: %s", f)) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } var dbMask, nullFields []string @@ -154,14 +154,17 @@ func (r *Repository) UpdateSet(ctx context.Context, scopeId string, s *HostSet, rowsUpdated, err = w.Update(ctx, returnedHostSet, dbMask, nullFields, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_UPDATE)), db.WithVersion(&version)) - if err == nil && rowsUpdated > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsUpdated > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - if err != nil { - return err - } hosts, err = getHosts(ctx, reader, s.PublicId, limit) - return err + if err != nil { + return errors.Wrap(err, op) + } + return nil }, ) @@ -201,11 +204,17 @@ func (r *Repository) LookupSet(ctx context.Context, publicId string, opt ...Opti s = nil return nil } - return err + if err != nil { + return errors.Wrap(err, op) + } + return nil } var err error hosts, err = getHosts(ctx, reader, s.PublicId, limit) - return err + if err != nil { + return errors.Wrap(err, op) + } + return nil }) if err != nil { @@ -261,10 +270,13 @@ func (r *Repository) DeleteSet(ctx context.Context, scopeId string, publicId str func(_ db.Reader, w db.Writer) (err error) { ds := s.clone() rowsDeleted, err = w.Delete(ctx, ds, db.WithOplog(oplogWrapper, s.oplog(oplog.OpType_OP_TYPE_DELETE))) - if err == nil && rowsDeleted > 1 { + if err != nil { + return errors.Wrap(err, op) + } + if rowsDeleted > 1 { return errors.E(errors.WithCode(errors.MultipleRecords)) } - return err + return nil }, ) diff --git a/internal/host/static/repository_host_set_member.go b/internal/host/static/repository_host_set_member.go index 00d773a6c..1162a499f 100644 --- a/internal/host/static/repository_host_set_member.go +++ b/internal/host/static/repository_host_set_member.go @@ -51,15 +51,18 @@ func (r *Repository) AddSetMembers(ctx context.Context, scopeId string, setId st // Create host set members msgs, err := createMembers(ctx, w, members) if err != nil { - return err + return errors.Wrap(err, op) } // Update host set version if err := updateVersion(ctx, w, wrapper, metadata, msgs, set, version); err != nil { - return err + return errors.Wrap(err, op) } hosts, err = getHosts(ctx, reader, setId, unlimited) - return err + if err != nil { + return errors.Wrap(err, op) + } + return nil }) if err != nil { return nil, errors.Wrap(err, op) @@ -188,11 +191,15 @@ func (r *Repository) DeleteSetMembers(ctx context.Context, scopeId string, setId // Delete host set members msgs, err := deleteMembers(ctx, w, members) if err != nil { - return err + return errors.Wrap(err, op) } // Update host set version - return updateVersion(ctx, w, wrapper, metadata, msgs, set, version) + err = updateVersion(ctx, w, wrapper, metadata, msgs, set, version) + if err != nil { + return errors.Wrap(err, op) + } + return nil }) if err != nil { @@ -278,7 +285,7 @@ func (r *Repository) SetSetMembers(ctx context.Context, scopeId string, setId st if len(deletions) > 0 { deletedMsgs, err := deleteMembers(ctx, w, deletions) if err != nil { - return err + return errors.Wrap(err, op) } msgs = append(msgs, deletedMsgs...) metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) @@ -296,11 +303,14 @@ func (r *Repository) SetSetMembers(ctx context.Context, scopeId string, setId st // Update host set version if err := updateVersion(ctx, w, wrapper, metadata, msgs, set, version); err != nil { - return err + return errors.Wrap(err, op) } hosts, err = getHosts(ctx, reader, setId, unlimited) - return err + if err != nil { + return errors.Wrap(err, op) + } + return nil }) if err != nil { diff --git a/internal/iam/account.go b/internal/iam/account.go index 1d4234e59..aef09a203 100644 --- a/internal/iam/account.go +++ b/internal/iam/account.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" authStore "github.com/hashicorp/boundary/internal/auth/store" "github.com/hashicorp/boundary/internal/db" @@ -43,11 +42,12 @@ func (a *authAccount) Clone() interface{} { // VetForWrite implements db.VetForWrite() interface. func (a *authAccount) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(authAccount).VetForWrite" if a.PublicId == "" { - return fmt.Errorf("error public id is empty string for auth account write: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing public id") } if err := validateScopeForWrite(ctx, r, a, opType, opt...); err != nil { - return err + return errors.Wrap(err, op) } return nil } diff --git a/internal/iam/group.go b/internal/iam/group.go index b11e1d3c2..7e8b7f753 100644 --- a/internal/iam/group.go +++ b/internal/iam/group.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -31,8 +30,9 @@ var _ db.VetForWriter = (*Group)(nil) // NewGroup creates a new in memory group with a scope (project/org) // and allowed options include: withDescripion, WithName. func NewGroup(scopeId string, opt ...Option) (*Group, error) { + const op = "iam.NewGroup" if scopeId == "" { - return nil, fmt.Errorf("new group: missing scope id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } opts := getOpts(opt...) g := &Group{ @@ -62,11 +62,12 @@ func allocGroup() Group { // VetForWrite implements db.VetForWrite() interface and validates the group // before it's written. func (g *Group) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(Group).VetForWrite" if g.PublicId == "" { - return fmt.Errorf("group vet for write: missing public id: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing public id") } if err := validateScopeForWrite(ctx, r, g, opType, opt...); err != nil { - return err + return errors.Wrap(err, op) } return nil } diff --git a/internal/iam/group_member.go b/internal/iam/group_member.go index ad3889ece..72e5491cc 100644 --- a/internal/iam/group_member.go +++ b/internal/iam/group_member.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -69,12 +68,13 @@ var _ db.VetForWriter = (*GroupMemberUser)(nil) // NewGroupMemberUser creates a new in memory user member of the group. No // options are currently supported. -func NewGroupMemberUser(groupId, userId string, opt ...Option) (*GroupMemberUser, error) { +func NewGroupMemberUser(groupId, userId string, _ ...Option) (*GroupMemberUser, error) { + const op = "iam.NewGroupMemberUser" if groupId == "" { - return nil, fmt.Errorf("new group member: missing group id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group id") } if userId == "" { - return nil, fmt.Errorf("new group member: missing user id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user id") } return &GroupMemberUser{ GroupMemberUser: &store.GroupMemberUser{ @@ -100,11 +100,12 @@ func (m *GroupMemberUser) Clone() interface{} { // VetForWrite implements db.VetForWrite() interface for group members. func (m *GroupMemberUser) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(GroupMemberUser).VetForWrite" if m.GroupId == "" { - return fmt.Errorf("group member: missing group id: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing group id") } if m.MemberId == "" { - return fmt.Errorf("group member: missing member id: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing member id") } return nil } diff --git a/internal/iam/group_member_test.go b/internal/iam/group_member_test.go index f6e2edfa5..c0cd0284f 100644 --- a/internal/iam/group_member_test.go +++ b/internal/iam/group_member_test.go @@ -35,7 +35,7 @@ func Test_NewGroupMember(t *testing.T) { args args want *GroupMemberUser wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid-org", @@ -83,7 +83,7 @@ func Test_NewGroupMember(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "missing-user", @@ -92,7 +92,7 @@ func Test_NewGroupMember(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -102,7 +102,7 @@ func Test_NewGroupMember(t *testing.T) { got, err := NewGroupMemberUser(tt.args.groupId, tt.args.userId, tt.args.opt...) if tt.wantErr { require.Error(err) - assert.True(errors.Is(err, tt.wantIsErr)) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) return } require.NoError(err) diff --git a/internal/iam/group_test.go b/internal/iam/group_test.go index b845c912f..e676aa4f5 100644 --- a/internal/iam/group_test.go +++ b/internal/iam/group_test.go @@ -34,7 +34,7 @@ func TestNewGroup(t *testing.T) { args args wantErr bool wantErrMsg string - wantIsErr error + wantIsErr errors.Code wantName string wantDescription string }{ @@ -71,8 +71,8 @@ func TestNewGroup(t *testing.T) { opt: []Option{WithName(id)}, }, wantErr: true, - wantErrMsg: "new group: missing scope id invalid parameter:", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "iam.NewGroup: missing scope id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -82,8 +82,8 @@ func TestNewGroup(t *testing.T) { if tt.wantErr { require.Error(err) assert.Contains(err.Error(), tt.wantErrMsg) - if tt.wantIsErr != nil { - assert.True(errors.Is(err, tt.wantIsErr)) + if tt.wantIsErr != 0 { + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) } return } @@ -171,7 +171,7 @@ func Test_GroupCreate(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: iam.validateScopeForWrite: scope is not found: search issue: error #1100", + wantErrMsg: "db.Create: iam.(Group).VetForWrite: iam.validateScopeForWrite: scope is not found: search issue: error #1100", }, } @@ -247,7 +247,7 @@ func Test_GroupUpdate(t *testing.T) { ScopeId: proj.PublicId, }, wantErr: true, - wantErrMsg: "db.Update: vet for write failed: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", + wantErrMsg: "db.Update: iam.(Group).VetForWrite: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", }, { name: "proj-scope-id-not-in-mask", diff --git a/internal/iam/ids.go b/internal/iam/ids.go index 0b08de525..18cbe9b02 100644 --- a/internal/iam/ids.go +++ b/internal/iam/ids.go @@ -18,7 +18,7 @@ const ( func newRoleId() (string, error) { id, err := db.NewPublicId(RolePrefix) if err != nil { - return "", fmt.Errorf("new role id: %w", err) + return "", errors.Wrap(err, "iam.newRoleId") } return id, nil } @@ -26,7 +26,7 @@ func newRoleId() (string, error) { func newUserId() (string, error) { id, err := db.NewPublicId(UserPrefix) if err != nil { - return "", fmt.Errorf("new user id: %w", err) + return "", errors.Wrap(err, "iam.newUserId") } return id, nil } @@ -34,18 +34,19 @@ func newUserId() (string, error) { func newGroupId() (string, error) { id, err := db.NewPublicId(GroupPrefix) if err != nil { - return "", fmt.Errorf("new group id: %w", err) + return "", errors.Wrap(err, "iam.newGroupId") } return id, nil } func newScopeId(scopeType scope.Type) (string, error) { + const op = "iam.newScopeId" if scopeType == scope.Unknown { - return "", fmt.Errorf("new scope id: unknown is not supported %w", errors.ErrInvalidParameter) + return "", errors.New(errors.InvalidParameter, op, "unknown scope is not supported") } id, err := db.NewPublicId(scopeType.Prefix()) if err != nil { - return "", fmt.Errorf("new %s id: %w", scopeType.String(), err) + return "", errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("scope type: %s", scopeType.String()))) } return id, nil } diff --git a/internal/iam/ids_test.go b/internal/iam/ids_test.go index 287fa38ae..e28ce8fd6 100644 --- a/internal/iam/ids_test.go +++ b/internal/iam/ids_test.go @@ -38,6 +38,6 @@ func Test_PublicIds(t *testing.T) { id, err = newScopeId(scope.Unknown) require.Error(t, err) assert.Empty(t, id) - assert.True(t, errors.Is(err, errors.ErrInvalidParameter)) + assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err)) }) } diff --git a/internal/iam/principal_role.go b/internal/iam/principal_role.go index 9bbae767f..1b033537a 100644 --- a/internal/iam/principal_role.go +++ b/internal/iam/principal_role.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -73,12 +72,13 @@ var _ db.VetForWriter = (*UserRole)(nil) // NewUserRole creates a new user role in memory. No options are supported // currently. -func NewUserRole(roleId, userId string, opt ...Option) (*UserRole, error) { +func NewUserRole(roleId, userId string, _ ...Option) (*UserRole, error) { + const op = "iam.NewUserRole" if roleId == "" { - return nil, fmt.Errorf("new user role: missing role id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } if userId == "" { - return nil, fmt.Errorf("new user role: missing user id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user id") } return &UserRole{ UserRole: &store.UserRole{ @@ -103,12 +103,13 @@ func (r *UserRole) Clone() interface{} { } // VetForWrite implements db.VetForWrite() interface for user roles. -func (role *UserRole) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { +func (role *UserRole) VetForWrite(_ context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error { + const op = "iam.(UserRole).VetForWrite" if role.RoleId == "" { - return fmt.Errorf("new user role: missing role id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing role id") } if role.PrincipalId == "" { - return fmt.Errorf("new user role: missing user id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing user id") } return nil } @@ -147,11 +148,12 @@ var _ db.VetForWriter = (*GroupRole)(nil) // NewGroupRole creates a new group role in memory. No options are supported // currently. func NewGroupRole(roleId, groupId string, opt ...Option) (*GroupRole, error) { + const op = "iam.NewGroupRole" if roleId == "" { - return nil, fmt.Errorf("new group role: missing role id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } if groupId == "" { - return nil, fmt.Errorf("new group role: missing group id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group id") } return &GroupRole{ GroupRole: &store.GroupRole{ @@ -177,11 +179,12 @@ func (r *GroupRole) Clone() interface{} { // VetForWrite implements db.VetForWrite() interface for group roles. func (role *GroupRole) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(GroupRole).VetForWrite" if role.RoleId == "" { - return fmt.Errorf("new group role: missing role id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing role id") } if role.PrincipalId == "" { - return fmt.Errorf("new group role: missing user id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing user id") } return nil } diff --git a/internal/iam/principal_role_test.go b/internal/iam/principal_role_test.go index a5a994212..9705ea1ad 100644 --- a/internal/iam/principal_role_test.go +++ b/internal/iam/principal_role_test.go @@ -32,7 +32,7 @@ func TestNewUserRole(t *testing.T) { args args want *UserRole wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid-org", @@ -68,7 +68,7 @@ func TestNewUserRole(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "empty-user-id", @@ -78,7 +78,7 @@ func TestNewUserRole(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -87,7 +87,7 @@ func TestNewUserRole(t *testing.T) { got, err := NewUserRole(tt.args.roleId, tt.args.userId, tt.args.opt...) if tt.wantErr { require.Error(err) - assert.True(errors.Is(err, tt.wantIsErr)) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) return } require.NoError(err) @@ -112,7 +112,7 @@ func TestUserRole_Create(t *testing.T) { wantDup bool wantErr bool wantErrMsg string - wantIsErr error + wantIsErr errors.Code }{ { name: "valid-with-org", @@ -206,8 +206,8 @@ func TestUserRole_Create(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: new user role: missing role id invalid parameter", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "db.Create: iam.(UserRole).VetForWrite: missing role id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, { name: "missing-user-id", @@ -223,8 +223,8 @@ func TestUserRole_Create(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: new user role: missing user id invalid parameter", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "db.Create: iam.(UserRole).VetForWrite: missing user id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, { name: "dup-at-org", @@ -257,8 +257,8 @@ func TestUserRole_Create(t *testing.T) { if tt.wantErr { require.Error(err) assert.Contains(err.Error(), tt.wantErrMsg) - if tt.wantIsErr != nil { - assert.True(errors.Is(err, tt.wantIsErr)) + if tt.wantIsErr != 0 { + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) } return } @@ -395,7 +395,7 @@ func TestNewGroupRole(t *testing.T) { args args want *GroupRole wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid-org", @@ -431,7 +431,7 @@ func TestNewGroupRole(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "empty-group-id", @@ -441,7 +441,7 @@ func TestNewGroupRole(t *testing.T) { }, want: nil, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -450,7 +450,7 @@ func TestNewGroupRole(t *testing.T) { got, err := NewGroupRole(tt.args.roleId, tt.args.groupId, tt.args.opt...) if tt.wantErr { require.Error(err) - assert.True(errors.Is(err, tt.wantIsErr)) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) return } require.NoError(err) @@ -475,7 +475,7 @@ func TestGroupRole_Create(t *testing.T) { wantDup bool wantErr bool wantErrMsg string - wantIsErr error + wantIsErr errors.Code }{ { name: "valid-with-org", @@ -569,8 +569,8 @@ func TestGroupRole_Create(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: new group role: missing role id invalid parameter", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "db.Create: iam.(GroupRole).VetForWrite: missing role id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, { name: "missing-user-id", @@ -586,8 +586,8 @@ func TestGroupRole_Create(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: new group role: missing user id invalid parameter", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "db.Create: iam.(GroupRole).VetForWrite: missing user id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, { name: "dup-at-org", @@ -635,8 +635,8 @@ func TestGroupRole_Create(t *testing.T) { if tt.wantErr { require.Error(err) assert.Contains(err.Error(), tt.wantErrMsg) - if tt.wantIsErr != nil { - assert.True(errors.Is(err, tt.wantIsErr)) + if tt.wantIsErr != 0 { + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) } return } diff --git a/internal/iam/repository.go b/internal/iam/repository.go index a80b8171b..ea9c3924d 100644 --- a/internal/iam/repository.go +++ b/internal/iam/repository.go @@ -2,18 +2,18 @@ package iam import ( "context" - "errors" "fmt" "strings" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" "github.com/hashicorp/boundary/internal/types/scope" ) var ( - ErrMetadataScopeNotFound = errors.New("scope not found for metadata") + ErrMetadataScopeNotFound = errors.New(errors.RecordNotFound, "iam", "scope not found for metadata") ) // Repository is the iam database repository @@ -29,14 +29,15 @@ type Repository struct { // NewRepository creates a new iam Repository. Supports the options: WithLimit // which sets a default limit on results returned by repo operations. func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repository, error) { + const op = "iam.NewRepository" if r == nil { - return nil, errors.New("error creating db repository with nil reader") + return nil, errors.New(errors.InvalidParameter, op, "nil reader") } if w == nil { - return nil, errors.New("error creating db repository with nil writer") + return nil, errors.New(errors.InvalidParameter, op, "nil writer") } if kms == nil { - return nil, errors.New("error creating db repository with nil kms") + return nil, errors.New(errors.InvalidParameter, op, "nil kms") } opts := getOpts(opt...) if opts.withLimit == 0 { @@ -64,27 +65,28 @@ func (r *Repository) list(ctx context.Context, resources interface{}, where stri } // create will create a new iam resource in the db repository with an oplog entry -func (r *Repository) create(ctx context.Context, resource Resource, opt ...Option) (Resource, error) { +func (r *Repository) create(ctx context.Context, resource Resource, _ ...Option) (Resource, error) { + const op = "iam.(Repository).create" if resource == nil { - return nil, errors.New("error creating resource that is nil") + return nil, errors.New(errors.InvalidParameter, op, "missing resource") } resourceCloner, ok := resource.(Cloneable) if !ok { - return nil, errors.New("error resource is not Cloneable for create") + return nil, errors.New(errors.InvalidParameter, op, "resource is not Cloneable") } metadata, err := r.stdMetadata(ctx, resource) if err != nil { - return nil, fmt.Errorf("error getting metadata for create: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("error getting metadata")) } metadata["op-type"] = []string{oplog.OpType_OP_TYPE_CREATE.String()} scope, err := resource.GetScope(ctx, r.reader) if err != nil { - return nil, fmt.Errorf("unable to get scope: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get scope")) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var returnedResource interface{} @@ -101,24 +103,28 @@ func (r *Repository) create(ctx context.Context, resource Resource, opt ...Optio ) }, ) - return returnedResource.(Resource), err + if err != nil { + return nil, errors.Wrap(err, op) + } + return returnedResource.(Resource), nil } // update will update an iam resource in the db repository with an oplog entry func (r *Repository) update(ctx context.Context, resource Resource, version uint32, fieldMaskPaths []string, setToNullPaths []string, opt ...Option) (Resource, int, error) { + const op = "iam.(Repository).update" if version == 0 { - return nil, db.NoRowsAffected, errors.New("resource version cannot be zero during update") + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } if resource == nil { - return nil, db.NoRowsAffected, errors.New("error updating resource that is nil") + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing resource") } resourceCloner, ok := resource.(Cloneable) if !ok { - return nil, db.NoRowsAffected, errors.New("error resource is not Cloneable for update") + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "resource is not Cloneable") } metadata, err := r.stdMetadata(ctx, resource) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("error getting metadata for update: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("error getting metadata")) } metadata["op-type"] = []string{oplog.OpType_OP_TYPE_UPDATE.String()} @@ -137,12 +143,12 @@ func (r *Repository) update(ctx context.Context, resource Resource, version uint default: scope, err = resource.GetScope(ctx, r.reader) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("unable to get scope: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get scope")) } } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("unable to get oplog wrapper: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } dbOpts = append(dbOpts, db.WithOplog(oplogWrapper, metadata)) @@ -161,38 +167,45 @@ func (r *Repository) update(ctx context.Context, resource Resource, version uint setToNullPaths, dbOpts..., ) - if err == nil && rowsUpdated > 1 { - // return err, which will result in a rollback of the update - return errors.New("error more than 1 resource would have been updated ") + if err != nil { + return errors.Wrap(err, op) } - return err + if rowsUpdated > 1 { + // return err, which will result in a rollback of the update + return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been updated") + } + return nil }, ) - return returnedResource.(Resource), rowsUpdated, err + if err != nil { + return nil, db.NoRowsAffected, errors.Wrap(err, op) + } + return returnedResource.(Resource), rowsUpdated, nil } // delete will delete an iam resource in the db repository with an oplog entry -func (r *Repository) delete(ctx context.Context, resource Resource, opt ...Option) (int, error) { +func (r *Repository) delete(ctx context.Context, resource Resource, _ ...Option) (int, error) { + const op = "iam.(Repository).delete" if resource == nil { - return db.NoRowsAffected, errors.New("error deleting resource that is nil") + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing resource") } resourceCloner, ok := resource.(Cloneable) if !ok { - return db.NoRowsAffected, errors.New("error resource is not Cloneable for delete") + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "resource is not Cloneable") } metadata, err := r.stdMetadata(ctx, resource) if err != nil { - return db.NoRowsAffected, fmt.Errorf("error getting metadata for delete: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("error getting metadata")) } metadata["op-type"] = []string{oplog.OpType_OP_TYPE_DELETE.String()} scope, err := resource.GetScope(ctx, r.reader) if err != nil { - return db.NoRowsAffected, fmt.Errorf("unable to get scope: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get scope")) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return db.NoRowsAffected, fmt.Errorf("unable to get oplog wrapper: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var rowsDeleted int @@ -208,24 +221,31 @@ func (r *Repository) delete(ctx context.Context, resource Resource, opt ...Optio deleteResource, db.WithOplog(oplogWrapper, metadata), ) - if err == nil && rowsDeleted > 1 { - // return err, which will result in a rollback of the delete - return errors.New("error more than 1 resource would have been deleted ") + if err != nil { + return errors.Wrap(err, op) } - return err + if rowsDeleted > 1 { + // return err, which will result in a rollback of the delete + return errors.New(errors.MultipleRecords, op, "more than 1 resource would have been deleted") + } + return nil }, ) - return rowsDeleted, err + if err != nil { + return db.NoRowsAffected, errors.Wrap(err, op) + } + return rowsDeleted, nil } func (r *Repository) stdMetadata(ctx context.Context, resource Resource) (oplog.Metadata, error) { + const op = "iam.(Repository).stdMetadata" if s, ok := resource.(*Scope); ok { newScope := allocScope() newScope.PublicId = s.PublicId newScope.Type = s.Type if newScope.Type == "" { if err := r.reader.LookupByPublicId(ctx, &newScope); err != nil { - return nil, ErrMetadataScopeNotFound + return nil, errors.Wrap(ErrMetadataScopeNotFound, op) } } switch newScope.Type { @@ -244,16 +264,16 @@ func (r *Repository) stdMetadata(ctx context.Context, resource Resource) (oplog. "resource-type": []string{resource.ResourceType().String()}, }, nil default: - return nil, fmt.Errorf("not a supported scope for metadata: %s", s.Type) + return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("not a supported scope for metadata: %s", s.Type)) } } scope, err := resource.GetScope(ctx, r.reader) if err != nil { - return nil, fmt.Errorf("unable to get scope for standard metadata: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get scope")) } if scope == nil { - return nil, errors.New("scope was nil for standard metadata") + return nil, errors.E(errors.WithOp(op), errors.WithMsg("nil scope")) } return oplog.Metadata{ "resource-public-id": []string{resource.GetPublicId()}, diff --git a/internal/iam/repository_group.go b/internal/iam/repository_group.go index c421c78c3..5a8ae7364 100644 --- a/internal/iam/repository_group.go +++ b/internal/iam/repository_group.go @@ -14,33 +14,34 @@ import ( // CreateGroup will create a group in the repository and return the written // group. No options are currently supported. -func (r *Repository) CreateGroup(ctx context.Context, group *Group, opt ...Option) (*Group, error) { +func (r *Repository) CreateGroup(ctx context.Context, group *Group, _ ...Option) (*Group, error) { + const op = "iam.(Repository).CreateGroup" if group == nil { - return nil, fmt.Errorf("create group: missing group %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group") } if group.Group == nil { - return nil, fmt.Errorf("create group: missing group store %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group store") } if group.PublicId != "" { - return nil, fmt.Errorf("create group: public id not empty: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "public id not empty") } if group.ScopeId == "" { - return nil, fmt.Errorf("create group: missing group scope id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } id, err := newGroupId() if err != nil { - return nil, fmt.Errorf("create group: %w", err) + return nil, errors.Wrap(err, op) } g := group.Clone().(*Group) g.PublicId = id resource, err := r.create(ctx, g) if err != nil { if errors.IsUniqueError(err) { - return nil, fmt.Errorf("create group: group %s already exists in scope %s: %w", group.Name, group.ScopeId, errors.ErrNotUnique) + return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("group %s already exists in scope %s", group.Name, group.ScopeId)) } - return nil, fmt.Errorf("create group: %w for %s", err, g.PublicId) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for group %s", g.PublicId))) } - return resource.(*Group), err + return resource.(*Group), nil } // UpdateGroup will update a group in the repository and return the written @@ -48,22 +49,23 @@ func (r *Repository) CreateGroup(ctx context.Context, group *Group, opt ...Optio // be updated. Fields will be set to NULL if the field is a zero value and // included in fieldMask. Name and Description are the only updatable fields, // If no updatable fields are included in the fieldMaskPaths, then an error is returned. -func (r *Repository) UpdateGroup(ctx context.Context, group *Group, version uint32, fieldMaskPaths []string, opt ...Option) (*Group, []*GroupMember, int, error) { +func (r *Repository) UpdateGroup(ctx context.Context, group *Group, version uint32, fieldMaskPaths []string, _ ...Option) (*Group, []*GroupMember, int, error) { + const op = "iam.(Repository).UpdateGroup" if group == nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: missing group %w", errors.ErrInvalidParameter) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing group") } if group.Group == nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: missing group store %w", errors.ErrInvalidParameter) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing group store") } if group.PublicId == "" { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: missing group public id %w", errors.ErrInvalidParameter) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } for _, f := range fieldMaskPaths { switch { case strings.EqualFold("name", f): case strings.EqualFold("description", f): default: - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: field: %s: %w", f, errors.ErrInvalidFieldMask) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } var dbMask, nullFields []string @@ -76,7 +78,7 @@ func (r *Repository) UpdateGroup(ctx context.Context, group *Group, version uint nil, ) if len(dbMask) == 0 && len(nullFields) == 0 { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: %w", errors.ErrEmptyFieldMask) + return nil, nil, db.NoRowsAffected, errors.New(errors.EmptyFieldMask, op, "empty field mask") } var resource Resource var rowsUpdated int @@ -90,33 +92,34 @@ func (r *Repository) UpdateGroup(ctx context.Context, group *Group, version uint g := group.Clone().(*Group) resource, rowsUpdated, err = r.update(ctx, g, version, dbMask, nullFields) if err != nil { - return err + return errors.Wrap(err, op) } repo, err := NewRepository(read, w, r.kms) if err != nil { - return fmt.Errorf("update group: failed creating inner repo: %w for %s", err, group.PublicId) + return errors.Wrap(err, op) } members, err = repo.ListGroupMembers(ctx, group.PublicId) if err != nil { - return fmt.Errorf("update group: listing group members: %w for %s", err, group.PublicId) + return errors.Wrap(err, op) } return nil }, ) if err != nil { if errors.IsUniqueError(err) { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: group %s already exists in org %s: %w", group.Name, group.ScopeId, errors.ErrNotUnique) + return nil, nil, db.NoRowsAffected, errors.New(errors.NotUnique, op, fmt.Sprintf("group %s already exists in scope %s", group.Name, group.ScopeId)) } - return nil, nil, db.NoRowsAffected, fmt.Errorf("update group: %w for %s", err, group.PublicId) + return nil, nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for group %s", group.PublicId))) } - return resource.(*Group), members, rowsUpdated, err + return resource.(*Group), members, rowsUpdated, nil } // LookupGroup will look up a group in the repository. If the group is not // found, it will return nil, nil. -func (r *Repository) LookupGroup(ctx context.Context, withPublicId string, opt ...Option) (*Group, []*GroupMember, error) { +func (r *Repository) LookupGroup(ctx context.Context, withPublicId string, _ ...Option) (*Group, []*GroupMember, error) { + const op = "iam.(Repository).LookupGroup" if withPublicId == "" { - return nil, nil, fmt.Errorf("lookup group: missing public id %w", errors.ErrInvalidParameter) + return nil, nil, errors.New(errors.InvalidParameter, op, "missing public id") } g := allocGroup() g.PublicId = withPublicId @@ -127,15 +130,15 @@ func (r *Repository) LookupGroup(ctx context.Context, withPublicId string, opt . db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { if err := read.LookupByPublicId(ctx, &g); err != nil { - return fmt.Errorf("lookup group: failed %w for %s", err, withPublicId) + return errors.Wrap(err, op) } repo, err := NewRepository(read, w, r.kms) if err != nil { - return fmt.Errorf("lookup group: failed creating inner repo: %w for %s", err, withPublicId) + return errors.Wrap(err, op) } members, err = repo.ListGroupMembers(ctx, withPublicId) if err != nil { - return fmt.Errorf("lookup group: listing group members: %w for %s", err, withPublicId) + return errors.Wrap(err, op) } return nil }, @@ -144,49 +147,52 @@ func (r *Repository) LookupGroup(ctx context.Context, withPublicId string, opt . if errors.IsNotFoundError(err) { return nil, nil, nil } - return nil, nil, fmt.Errorf("lookup group: failed %w for %s", err, withPublicId) + return nil, nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for group %s", withPublicId))) } return &g, members, nil } // DeleteGroup will delete a group from the repository. -func (r *Repository) DeleteGroup(ctx context.Context, withPublicId string, opt ...Option) (int, error) { +func (r *Repository) DeleteGroup(ctx context.Context, withPublicId string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteGroup" if withPublicId == "" { - return db.NoRowsAffected, fmt.Errorf("delete group: missing public id %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } g := allocGroup() g.PublicId = withPublicId if err := r.reader.LookupByPublicId(ctx, &g); err != nil { - return db.NoRowsAffected, fmt.Errorf("delete group: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for group %s", withPublicId))) } rowsDeleted, err := r.delete(ctx, &g) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete group: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for group %s", withPublicId))) } return rowsDeleted, nil } // ListGroups in a scope and supports WithLimit option. func (r *Repository) ListGroups(ctx context.Context, withScopeId string, opt ...Option) ([]*Group, error) { + const op = "iam.(Repository).ListGroups" if withScopeId == "" { - return nil, fmt.Errorf("list groups: missing scope id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } var grps []*Group err := r.list(ctx, &grps, "scope_id = ?", []interface{}{withScopeId}, opt...) if err != nil { - return nil, fmt.Errorf("list groups: %w", err) + return nil, errors.Wrap(err, op) } return grps, nil } // ListGroupMembers of a group and supports WithLimit option. func (r *Repository) ListGroupMembers(ctx context.Context, withGroupId string, opt ...Option) ([]*GroupMember, error) { + const op = "iam.(Repository).ListGroups" if withGroupId == "" { - return nil, fmt.Errorf("list group members: missing group id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group id") } members := []*GroupMember{} if err := r.list(ctx, &members, "group_id = ?", []interface{}{withGroupId}, opt...); err != nil { - return nil, fmt.Errorf("list group members: %w", err) + return nil, errors.Wrap(err, op) } return members, nil } @@ -195,35 +201,36 @@ func (r *Repository) ListGroupMembers(ctx context.Context, withGroupId string, o // (groupId). The group's current db version must match the groupVersion or an // error will be returned. Zero is not a valid value for the WithVersion option // and will return an error. -func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, opt ...Option) ([]*GroupMember, error) { +func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, _ ...Option) ([]*GroupMember, error) { + const op = "iam.(Repository).AddGroupMembers" if groupId == "" { - return nil, fmt.Errorf("add group members: missing group id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing group id") } if len(userIds) == 0 { - return nil, fmt.Errorf("add group members: missing user ids to add %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user ids") } if groupVersion == 0 { - return nil, fmt.Errorf("add group members: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing version") } group := allocGroup() group.PublicId = groupId scope, err := group.GetScope(ctx, r.reader) if err != nil { - return nil, fmt.Errorf("add group members: unable to get group %s scope: %w", groupId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get group members %s scope", groupId))) } newGroupMembers := make([]interface{}, 0, len(userIds)) for _, id := range userIds { gm, err := NewGroupMemberUser(groupId, id) if err != nil { - return nil, fmt.Errorf("add group members: unable to create in memory group member: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group member")) } newGroupMembers = append(newGroupMembers, gm) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("add group members: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentMembers []*GroupMember @@ -235,7 +242,7 @@ func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupV msgs := make([]*oplog.Message, 0, 2) groupTicket, err := w.GetTicket(&group) if err != nil { - return fmt.Errorf("add group members: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedGroup := allocGroup() updatedGroup.PublicId = groupId @@ -243,15 +250,15 @@ func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupV var groupOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedGroup, []string{"Version"}, nil, db.NewOplogMsg(&groupOplogMsg), db.WithVersion(&groupVersion)) if err != nil { - return fmt.Errorf("add group members: unable to update group version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update group version")) } if rowsUpdated != 1 { - return fmt.Errorf("add group members: updated group and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated group and %d rows updated", rowsUpdated)) } msgs = append(msgs, &groupOplogMsg) memberOplogMsgs := make([]*oplog.Message, 0, len(newGroupMembers)) if err := w.CreateItems(ctx, newGroupMembers, db.NewOplogMsgs(&memberOplogMsgs)); err != nil { - return fmt.Errorf("add group members: unable to add users: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add users")) } msgs = append(msgs, memberOplogMsgs...) metadata := oplog.Metadata{ @@ -261,7 +268,7 @@ func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupV "resource-public-id": []string{groupId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, groupTicket, metadata, msgs); err != nil { - return fmt.Errorf("add group members: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := Repository{ @@ -273,13 +280,13 @@ func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupV } currentMembers, err = txRepo.ListGroupMembers(ctx, groupId) if err != nil { - return fmt.Errorf("set group members: unable to retrieve current group members after sets: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current group members after sets")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("add group members: error adding members: %w", err) + return nil, errors.Wrap(err, op) } return currentMembers, nil } @@ -287,35 +294,36 @@ func (r *Repository) AddGroupMembers(ctx context.Context, groupId string, groupV // DeleteGroupMembers (userIds) from a group (groupId). The group's current db version // must match the groupVersion or an error will be returned. Zero is not a valid // value for the WithVersion option and will return an error. -func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, opt ...Option) (int, error) { +func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteGroupMembers" if groupId == "" { - return db.NoRowsAffected, fmt.Errorf("delete group members: missing group id: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing group id") } if len(userIds) == 0 { - return db.NoRowsAffected, fmt.Errorf("delete group members: missing either user or groups to delete %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing either user or groups to delete") } if groupVersion == 0 { - return db.NoRowsAffected, fmt.Errorf("delete group members: version cannot be zero: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } group := allocGroup() group.PublicId = groupId scope, err := group.GetScope(ctx, r.reader) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete group members: unable to get group %s scope: %w", groupId, err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get group members %s scope", groupId))) } deleteMembers := make([]interface{}, 0, len(userIds)) for _, id := range userIds { member, err := NewGroupMemberUser(groupId, id) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete group members: unable to create in memory group member: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group member")) } deleteMembers = append(deleteMembers, member) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return db.NoRowsAffected, fmt.Errorf("add group members: unable to get oplog wrapper: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var totalRowsDeleted int @@ -327,7 +335,7 @@ func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, gro msgs := make([]*oplog.Message, 0, 2) groupTicket, err := w.GetTicket(&group) if err != nil { - return fmt.Errorf("delete group members: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedGroup := allocGroup() updatedGroup.PublicId = groupId @@ -335,19 +343,19 @@ func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, gro var groupOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedGroup, []string{"Version"}, nil, db.NewOplogMsg(&groupOplogMsg), db.WithVersion(&groupVersion)) if err != nil { - return fmt.Errorf("delete group members: unable to update group version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update group version")) } if rowsUpdated != 1 { - return fmt.Errorf("delete group members: updated group and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated group and %d rows updated", rowsUpdated)) } msgs = append(msgs, &groupOplogMsg) userOplogMsgs := make([]*oplog.Message, 0, len(deleteMembers)) rowsDeleted, err := w.DeleteItems(ctx, deleteMembers, db.NewOplogMsgs(&userOplogMsgs)) if err != nil { - return fmt.Errorf("delete group members: unable to delete group members: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete group members")) } if rowsDeleted != len(deleteMembers) { - return fmt.Errorf("delete group members: group members deleted %d did not match request for %d", rowsDeleted, len(deleteMembers)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("group members deleted %d did not match request for %d", rowsDeleted, len(deleteMembers))) } totalRowsDeleted += rowsDeleted msgs = append(msgs, userOplogMsgs...) @@ -358,13 +366,13 @@ func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, gro "resource-public-id": []string{groupId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, groupTicket, metadata, msgs); err != nil { - return fmt.Errorf("delete group members: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } return nil }, ) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete group members: error deleting members: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op) } return totalRowsDeleted, nil } @@ -372,23 +380,24 @@ func (r *Repository) DeleteGroupMembers(ctx context.Context, groupId string, gro // SetGroupMembers will set the group's members. If userIds is empty, the // members will be cleared. Zero is not a valid value for the WithVersion option // and will return an error. -func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, opt ...Option) ([]*GroupMember, int, error) { +func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupVersion uint32, userIds []string, _ ...Option) ([]*GroupMember, int, error) { + const op = "iam.(Repository).SetGroupMembers" if groupId == "" { - return nil, db.NoRowsAffected, fmt.Errorf("set group members: missing group id: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing group id") } if groupVersion == 0 { - return nil, db.NoRowsAffected, fmt.Errorf("set group members: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } group := allocGroup() group.PublicId = groupId scope, err := group.GetScope(ctx, r.reader) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set group members: unable to get members %s scope: %w", groupId, err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get group members %s scope", groupId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("add group members: unable to get oplog wrapper: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentMembers []*GroupMember @@ -408,13 +417,13 @@ func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupV } addMembers, deleteMembers, err := groupMemberChanges(ctx, reader, groupId, userIds) if err != nil { - return fmt.Errorf("set associated accounts: unable to determine changes: %w", err) + return errors.Wrap(err, op) } // handle no change to existing group members if len(addMembers) == 0 && len(deleteMembers) == 0 { currentMembers, err = txRepo.ListGroupMembers(ctx, groupId) if err != nil { - return fmt.Errorf("set group members: unable to retrieve current group members after sets: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current group members after sets")) } return nil } @@ -431,7 +440,7 @@ func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupV // we need to write oplog entries for deletes and adds groupTicket, err := w.GetTicket(&group) if err != nil { - return fmt.Errorf("set group members: unable to get ticket for group: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedGroup := allocGroup() updatedGroup.PublicId = groupId @@ -439,19 +448,19 @@ func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupV var groupOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedGroup, []string{"Version"}, nil, db.NewOplogMsg(&groupOplogMsg), db.WithVersion(&groupVersion)) if err != nil { - return fmt.Errorf("set group members: unable to update group version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update group verison")) } if rowsUpdated != 1 { - return fmt.Errorf("set group members: updated group and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated group and %d rows updated", rowsUpdated)) } if len(deleteMembers) > 0 { userOplogMsgs := make([]*oplog.Message, 0, len(deleteMembers)) rowsDeleted, err := w.DeleteItems(ctx, deleteMembers, db.NewOplogMsgs(&userOplogMsgs)) if err != nil { - return fmt.Errorf("set group members: unable to delete group member: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete group member")) } if rowsDeleted != len(deleteMembers) { - return fmt.Errorf("set group members: members deleted %d did not match request for %d", rowsDeleted, len(deleteMembers)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("members deleted %d did not match request for %d", rowsDeleted, len(deleteMembers))) } totalRowsAffected += rowsDeleted msgs = append(msgs, userOplogMsgs...) @@ -460,7 +469,7 @@ func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupV if len(addMembers) > 0 { userOplogMsgs := make([]*oplog.Message, 0, len(addMembers)) if err := w.CreateItems(ctx, addMembers, db.NewOplogMsgs(&userOplogMsgs)); err != nil { - return fmt.Errorf("set group members: unable to add users: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add users")) } totalRowsAffected += len(addMembers) msgs = append(msgs, userOplogMsgs...) @@ -470,22 +479,23 @@ func (r *Repository) SetGroupMembers(ctx context.Context, groupId string, groupV // we're done with all the membership writes, so let's write the // group's update oplog message if err := w.WriteOplogEntryWith(ctx, oplogWrapper, groupTicket, metadata, msgs); err != nil { - return fmt.Errorf("set group members: unable to write oplog for additions: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } currentMembers, err = txRepo.ListGroupMembers(ctx, groupId) if err != nil { - return fmt.Errorf("set group members: unable to retrieve current group members after sets: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current group members after set")) } return nil }) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set group members: unable to set group members: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op) } return currentMembers, totalRowsAffected, nil } // groupMemberChanges returns two slices: members to add and delete func groupMemberChanges(ctx context.Context, reader db.Reader, groupId string, userIds []string) ([]interface{}, []interface{}, error) { + const op = "iam.groupMemberChanges" var inClauseSpots []string // starts at 2 because there is already a $1 in the query for i := 2; i < len(userIds)+2; i++ { @@ -505,7 +515,7 @@ func groupMemberChanges(ctx context.Context, reader db.Reader, groupId string, u // fmt.Println(query, params) rows, err := reader.Query(ctx, query, params) if err != nil { - return nil, nil, fmt.Errorf("changes: query failed: %w", err) + return nil, nil, errors.Wrap(err, op) } defer rows.Close() @@ -517,7 +527,7 @@ func groupMemberChanges(ctx context.Context, reader db.Reader, groupId string, u for rows.Next() { var chg change if err := reader.ScanRows(rows, &chg); err != nil { - return nil, nil, fmt.Errorf("changes: scan row failed: %w", err) + return nil, nil, errors.Wrap(err, op) } changes = append(changes, &chg) } @@ -525,23 +535,23 @@ func groupMemberChanges(ctx context.Context, reader db.Reader, groupId string, u deleteMembers := []interface{}{} for _, c := range changes { if c.MemberId == "" { - return nil, nil, fmt.Errorf("changes: missing user id in change result") + return nil, nil, errors.New(errors.InvalidParameter, op, "missing user id in change result") } switch c.Action { case "add": gm, err := NewGroupMemberUser(groupId, c.MemberId) if err != nil { - return nil, nil, fmt.Errorf("set group members: unable to create in memory group member for add: %w", err) + return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group member for add")) } addMembers = append(addMembers, gm) case "delete": gm, err := NewGroupMemberUser(groupId, c.MemberId) if err != nil { - return nil, nil, fmt.Errorf("set group members: unable to create in memory group member for delete: %w", err) + return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group member for delete")) } deleteMembers = append(deleteMembers, gm) default: - return nil, nil, fmt.Errorf("changes: unknown action %s for %s", c.Action, c.MemberId) + return nil, nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("unknown action %s for %s", c.Action, c.MemberId)) } } diff --git a/internal/iam/repository_group_test.go b/internal/iam/repository_group_test.go index 444e14b8e..a26127343 100644 --- a/internal/iam/repository_group_test.go +++ b/internal/iam/repository_group_test.go @@ -36,7 +36,7 @@ func TestRepository_CreateGroup(t *testing.T) { wantDup bool wantErr bool wantErrMsg string - wantIsError error + wantIsError errors.Code }{ { name: "valid-org", @@ -70,8 +70,8 @@ func TestRepository_CreateGroup(t *testing.T) { return g }(), }, - wantErrMsg: "create group: public id not empty: invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).CreateGroup: public id not empty: parameter violation: error #100", + wantIsError: errors.InvalidParameter, wantErr: true, }, { @@ -80,8 +80,8 @@ func TestRepository_CreateGroup(t *testing.T) { group: nil, }, wantErr: true, - wantErrMsg: "create group: missing group invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).CreateGroup: missing group: parameter violation: error #10", + wantIsError: errors.InvalidParameter, }, { name: "nil-store", @@ -93,8 +93,8 @@ func TestRepository_CreateGroup(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "create group: missing group store invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).CreateGroup: missing group store: parameter violation: error #100", + wantIsError: errors.InvalidParameter, }, { name: "bad-scope-id", @@ -106,8 +106,8 @@ func TestRepository_CreateGroup(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "create group: error getting metadata for create: unable to get scope for standard metadata: db.LookupWhere: record not found", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).create: error getting metadata: iam.(Repository).stdMetadata: unable to get scope: iam.LookupScope: db.LookupWhere: record not found, search issue: error #1100", + wantIsError: errors.RecordNotFound, }, { name: "dup-name", @@ -122,7 +122,7 @@ func TestRepository_CreateGroup(t *testing.T) { wantDup: true, wantErr: true, wantErrMsg: "already exists in scope ", - wantIsError: errors.ErrNotUnique, + wantIsError: errors.NotUnique, }, { name: "dup-name-but-diff-scope", @@ -154,6 +154,7 @@ func TestRepository_CreateGroup(t *testing.T) { assert.Error(err) assert.Nil(grp) assert.Contains(err.Error(), tt.wantErrMsg) + assert.True(errors.Match(errors.T(tt.wantIsError), err)) return } assert.NoError(err) @@ -237,7 +238,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update group: db.DoTx: db.DoTx: db.Update: db.lookupAfterWrite: db.LookupById: record not found", + wantErrMsg: "db.DoTx: iam.(Repository).UpdateGroup: iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100", wantIsError: errors.RecordNotFound, }, { @@ -274,7 +275,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update group: empty field mask", + wantErrMsg: "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -287,7 +288,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update group: empty field mask", + wantErrMsg: "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -300,7 +301,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update group: field: CreateTime: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateGroup: invalid field mask: CreateTime: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -313,7 +314,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update group: field: Alice: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateGroup: invalid field mask: Alice: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -326,7 +327,7 @@ func TestRepository_UpdateGroup(t *testing.T) { }, newScopeId: org.PublicId, wantErr: true, - wantErrMsg: "update group: missing group public id invalid parameter", + wantErrMsg: "iam.(Repository).UpdateGroup: missing public id: parameter violation: error #100", wantIsError: errors.InvalidParameter, wantRowsUpdate: 0, }, @@ -338,7 +339,7 @@ func TestRepository_UpdateGroup(t *testing.T) { }, newScopeId: org.PublicId, wantErr: true, - wantErrMsg: "update group: empty field mask", + wantErrMsg: "iam.(Repository).UpdateGroup: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -375,7 +376,7 @@ func TestRepository_UpdateGroup(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantDup: true, - wantErrMsg: " already exists in org " + org.PublicId, + wantErrMsg: " already exists in scope " + org.PublicId, wantIsError: errors.NotUnique, }, { @@ -388,7 +389,7 @@ func TestRepository_UpdateGroup(t *testing.T) { }, newScopeId: org.PublicId, wantErr: true, - wantErrMsg: `db.DoTx: db.Update: immutable column: iam_group.scope_id`, + wantErrMsg: `iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: immutable column: iam_group.scope_id: integrity violation: error #1003`, directUpdate: true, }, } @@ -474,6 +475,9 @@ func TestRepository_DeleteGroup(t *testing.T) { repo := TestRepo(t, conn, wrapper) org, _ := TestScopes(t, repo) + grpId, err := newGroupId() + a.NoError(err) + type args struct { group *Group opt []Option @@ -503,23 +507,21 @@ func TestRepository_DeleteGroup(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "delete group: missing public id invalid parameter", + wantErrMsg: "iam.(Repository).DeleteGroup: missing public id: parameter violation: error #100", }, { name: "not-found", args: args{ group: func() *Group { - id, err := newGroupId() - a.NoError(err) g, err := NewGroup(org.PublicId) - g.PublicId = id + g.PublicId = grpId a.NoError(err) return g }(), }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "delete group: failed db.LookupById: record not found", + wantErrMsg: "iam.(Repository).DeleteGroup: for group " + grpId + ": db.LookupById: record not found, search issue: error #1100", }, } for _, tt := range tests { @@ -727,7 +729,7 @@ func TestRepository_ListMembers(t *testing.T) { got, err := repo.ListGroupMembers(context.Background(), "") require.Error(err) require.Nil(got) - require.Truef(errors.Is(err, errors.ErrInvalidParameter), "unexpected error %s", err.Error()) + require.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) }) } @@ -879,7 +881,7 @@ func TestRepository_DeleteGroupMembers(t *testing.T) { args args wantRowsDeleted int wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid", @@ -926,7 +928,7 @@ func TestRepository_DeleteGroupMembers(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "bad-version", @@ -949,7 +951,7 @@ func TestRepository_DeleteGroupMembers(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -979,8 +981,8 @@ func TestRepository_DeleteGroupMembers(t *testing.T) { if tt.wantErr { assert.Error(err) assert.Equal(0, deletedRows) - if tt.wantIsErr != nil { - assert.Truef(errors.Is(err, tt.wantIsErr), "unexpected error %s", err.Error()) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "unexpected error %s", err.Error()) } err = db.TestVerifyOplog(t, rw, tt.args.group.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.Error(err) diff --git a/internal/iam/repository_principal_role.go b/internal/iam/repository_principal_role.go index b93617a47..02797e52d 100644 --- a/internal/iam/repository_principal_role.go +++ b/internal/iam/repository_principal_role.go @@ -16,26 +16,27 @@ import ( // roleVersion or an error will be returned. The list of current PrincipalRoles // after the adds will be returned on success. Zero is not a valid value for // the WithVersion option and will return an error. -func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) ([]PrincipalRole, error) { +func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, _ ...Option) ([]PrincipalRole, error) { + const op = "iam.(Repository).AddPrincipalRoles" if roleId == "" { - return nil, fmt.Errorf("add principal roles: missing role id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } if roleVersion == 0 { - return nil, fmt.Errorf("add principal roles: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing version") } userIds, groupIds, err := splitPrincipals(principalIds) if err != nil { - return nil, fmt.Errorf("add principal roles: error parsing principals: %w", err) + return nil, errors.Wrap(err, op) } if len(userIds) == 0 && len(groupIds) == 0 { - return nil, fmt.Errorf("add principal roles: missing either user or groups to add: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing either user or groups to add") } newUserRoles := make([]interface{}, 0, len(userIds)) for _, id := range userIds { usrRole, err := NewUserRole(roleId, id) if err != nil { - return nil, fmt.Errorf("add principal roles: unable to create in memory user role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory user role")) } newUserRoles = append(newUserRoles, usrRole) } @@ -43,7 +44,7 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV for _, id := range groupIds { grpRole, err := NewGroupRole(roleId, id) if err != nil { - return nil, fmt.Errorf("add principal roles: unable to create in memory group role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group role")) } newGrpRoles = append(newGrpRoles, grpRole) } @@ -52,12 +53,12 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV role.PublicId = roleId scope, err := role.GetScope(ctx, r.reader) if err != nil { - return nil, fmt.Errorf("add principal roles: unable to get role %s scope: %w", roleId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("add principal roles: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentPrincipals []PrincipalRole @@ -69,7 +70,7 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV msgs := make([]*oplog.Message, 0, 2) roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("add principal roles: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedRole := allocRole() updatedRole.PublicId = roleId @@ -77,23 +78,23 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("add principal roles: unable to update role version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version")) } if rowsUpdated != 1 { - return fmt.Errorf("add principal roles: updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) if len(newUserRoles) > 0 { userOplogMsgs := make([]*oplog.Message, 0, len(newUserRoles)) if err := w.CreateItems(ctx, newUserRoles, db.NewOplogMsgs(&userOplogMsgs)); err != nil { - return fmt.Errorf("add principal roles: unable to add users: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add users")) } msgs = append(msgs, userOplogMsgs...) } if len(newGrpRoles) > 0 { grpOplogMsgs := make([]*oplog.Message, 0, len(newGrpRoles)) if err := w.CreateItems(ctx, newGrpRoles, db.NewOplogMsgs(&grpOplogMsgs)); err != nil { - return fmt.Errorf("add principal roles: unable to add groups: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add groups")) } msgs = append(msgs, grpOplogMsgs...) } @@ -104,7 +105,7 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV "resource-public-id": []string{roleId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("add principal roles: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -116,13 +117,13 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV } currentPrincipals, err = txRepo.ListPrincipalRoles(ctx, roleId) if err != nil { - return fmt.Errorf("add principal roles: unable to retrieve current principal roles after adds: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current principal roles after adds")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("add principal roles: error creating roles: %w", err) + return nil, errors.Wrap(err, op) } return currentPrincipals, nil } @@ -132,12 +133,13 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV // requested. If both userIds and groupIds are empty, the principal roles will // be cleared. Zero is not a valid value for the WithVersion option and will // return an error. -func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) ([]PrincipalRole, int, error) { +func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, _ ...Option) ([]PrincipalRole, int, error) { + const op = "iam.(Repository).SetPrincipalRoles" if roleId == "" { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing role id: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role id") } if roleVersion == 0 { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } role := allocRole() role.PublicId = roleId @@ -147,11 +149,11 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // changing. userIds, groupIds, err := splitPrincipals(principalIds) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: error parsing principals: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op) } toSet, err := r.principalsToSet(ctx, &role, userIds, groupIds) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to determine set: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op) } // handle no change to existing principal roles @@ -161,11 +163,11 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV scope, err := role.GetScope(ctx, r.reader) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to get role %s scope: %w", roleId, err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to get oplog wrapper: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentPrincipals []PrincipalRole @@ -180,7 +182,7 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // we need to write oplog entries for deletes and adds roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("set principal roles: unable to get ticket for role: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket for role")) } updatedRole := allocRole() updatedRole.PublicId = roleId @@ -188,10 +190,10 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("set principal roles: unable to update role version: %w", err) + return errors.Wrap(err, op) } if rowsUpdated != 1 { - return fmt.Errorf("set principal roles: updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs := make([]*oplog.Message, 0, 5) metadata := oplog.Metadata{ @@ -208,10 +210,10 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV userOplogMsgs := make([]*oplog.Message, 0, len(toSet.deleteUserRoles)) rowsDeleted, err := w.DeleteItems(ctx, toSet.deleteUserRoles, db.NewOplogMsgs(&userOplogMsgs)) if err != nil { - return fmt.Errorf("set principal roles: unable to delete user roles: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete user roles")) } if rowsDeleted != len(toSet.deleteUserRoles) { - return fmt.Errorf("set principal roles: user roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteUserRoles)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("user roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteUserRoles))) } totalRowsAffected += rowsDeleted msgs = append(msgs, userOplogMsgs...) @@ -220,10 +222,10 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV grpOplogMsgs := make([]*oplog.Message, 0, len(toSet.deleteGroupRoles)) rowsDeleted, err := w.DeleteItems(ctx, toSet.deleteGroupRoles, db.NewOplogMsgs(&grpOplogMsgs)) if err != nil { - return fmt.Errorf("set principal roles: unable to delete groups: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete groups")) } if rowsDeleted != len(toSet.deleteGroupRoles) { - return fmt.Errorf("set principal roles: group roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteGroupRoles)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("group roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteGroupRoles))) } totalRowsAffected += rowsDeleted msgs = append(msgs, grpOplogMsgs...) @@ -234,7 +236,7 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV if len(toSet.addUserRoles) > 0 { userOplogMsgs := make([]*oplog.Message, 0, len(toSet.addUserRoles)) if err := w.CreateItems(ctx, toSet.addUserRoles, db.NewOplogMsgs(&userOplogMsgs)); err != nil { - return fmt.Errorf("set principal roles: unable to add users: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add users")) } totalRowsAffected += len(toSet.addUserRoles) msgs = append(msgs, userOplogMsgs...) @@ -242,14 +244,14 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV if len(toSet.addGroupRoles) > 0 { grpOplogMsgs := make([]*oplog.Message, 0, len(toSet.addGroupRoles)) if err := w.CreateItems(ctx, toSet.addGroupRoles, db.NewOplogMsgs(&grpOplogMsgs)); err != nil { - return fmt.Errorf("set principal roles: unable to add groups: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add groups")) } totalRowsAffected += len(toSet.addGroupRoles) msgs = append(msgs, grpOplogMsgs...) } } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("set principal roles: unable to write oplog for additions: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -261,12 +263,12 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV } currentPrincipals, err = txRepo.ListPrincipalRoles(ctx, roleId) if err != nil { - return fmt.Errorf("set principal roles: unable to retrieve current principal roles after sets: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current principal roles after sets")) } return nil }) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to set principals: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op) } return currentPrincipals, totalRowsAffected, nil } @@ -275,19 +277,20 @@ func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleV // (roleId). The role's current db version must match the roleVersion or an // error will be returned. Zero is not a valid value for the WithVersion option // and will return an error. -func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, opt ...Option) (int, error) { +func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion uint32, principalIds []string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeletePrincipalRoles" if roleId == "" { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing role id: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role id") } userIds, groupIds, err := splitPrincipals(principalIds) if err != nil { - return db.NoRowsAffected, fmt.Errorf("remove principal roles: error parsing principals: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op) } if len(userIds) == 0 && len(groupIds) == 0 { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing either user or groups to delete: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing either user or groups to delete") } if roleVersion == 0 { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: version cannot be zero: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } role := allocRole() role.PublicId = roleId @@ -296,7 +299,7 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro for _, id := range userIds { usrRole, err := NewUserRole(roleId, id) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to create in memory user role: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to create in memory user role")) } deleteUserRoles = append(deleteUserRoles, usrRole) } @@ -304,18 +307,18 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro for _, id := range groupIds { grpRole, err := NewGroupRole(roleId, id) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to create in memory group role: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group role")) } deleteGrpRoles = append(deleteGrpRoles, grpRole) } scope, err := role.GetScope(ctx, r.reader) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to get role %s scope to create metadata: %w", roleId, err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope to create metadata", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to get oplog wrapper: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var totalRowsDeleted int @@ -327,7 +330,7 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro msgs := make([]*oplog.Message, 0, 2) roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("delete principal roles: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedRole := allocRole() updatedRole.PublicId = roleId @@ -335,20 +338,20 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("delete principal roles: unable to update role version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version")) } if rowsUpdated != 1 { - return fmt.Errorf("delete principal roles: updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) if len(deleteUserRoles) > 0 { userOplogMsgs := make([]*oplog.Message, 0, len(deleteUserRoles)) rowsDeleted, err := w.DeleteItems(ctx, deleteUserRoles, db.NewOplogMsgs(&userOplogMsgs)) if err != nil { - return fmt.Errorf("delete principal roles: unable to delete user roles: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete user roles")) } if rowsDeleted != len(deleteUserRoles) { - return fmt.Errorf("delete principal roles: user roles deleted %d did not match request for %d", rowsDeleted, len(deleteUserRoles)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("user roles deleted %d did not match request for %d", rowsDeleted, len(deleteUserRoles))) } totalRowsDeleted += rowsDeleted msgs = append(msgs, userOplogMsgs...) @@ -357,10 +360,10 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro grpOplogMsgs := make([]*oplog.Message, 0, len(deleteGrpRoles)) rowsDeleted, err := w.DeleteItems(ctx, deleteGrpRoles, db.NewOplogMsgs(&grpOplogMsgs)) if err != nil { - return fmt.Errorf("delete principal roles: unable to delete groups: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete groups")) } if rowsDeleted != len(deleteGrpRoles) { - return fmt.Errorf("delete principal roles: group roles deleted %d did not match request for %d", rowsDeleted, len(deleteGrpRoles)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("group roles deleted %d did not match request for %d", rowsDeleted, len(deleteGrpRoles))) } totalRowsDeleted += rowsDeleted msgs = append(msgs, grpOplogMsgs...) @@ -372,25 +375,26 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro "resource-public-id": []string{roleId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("delete principal roles: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } return nil }, ) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete principal roles: error deleting principal roles: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op) } return totalRowsDeleted, nil } // ListPrincipalRoles returns the principal roles for the roleId and supports the WithLimit option. func (r *Repository) ListPrincipalRoles(ctx context.Context, roleId string, opt ...Option) ([]PrincipalRole, error) { + const op = "iam.(Repository).ListPrincipalRoles" if roleId == "" { - return nil, fmt.Errorf("lookup principal roles: missing role id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } var roles []PrincipalRole if err := r.list(ctx, &roles, "role_id = ?", []interface{}{roleId}, opt...); err != nil { - return nil, fmt.Errorf("lookup principal role: unable to lookup roles: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to lookup roles")) } principals := make([]PrincipalRole, 0, len(roles)) principals = append(principals, roles...) @@ -409,13 +413,14 @@ type principalSet struct { // TODO: Should this be moved inside the transaction, at this point? func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, groupIds []string) (*principalSet, error) { + const op = "iam.(Repository).principalsToSet" // TODO(mgaffney) 08/2020: Use SQL to calculate changes. if role == nil { - return nil, fmt.Errorf("missing role: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role") } existing, err := r.ListPrincipalRoles(ctx, role.PublicId) if err != nil { - return nil, fmt.Errorf("unable to list existing principal role %s: %w", role.PublicId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to list existing principal role %s", role.PublicId))) } existingUsers := map[string]PrincipalRole{} existingGroups := map[string]PrincipalRole{} @@ -426,7 +431,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g case GroupRoleType.String(): existingGroups[p.PrincipalId] = p default: - return nil, fmt.Errorf("%s is unknown principal type %s", p.PrincipalId, p.GetType()) + return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s is unknown principal type %s", p.PrincipalId, p.GetType())) } } var newUserRoles []interface{} @@ -436,7 +441,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g if _, ok := existingUsers[id]; !ok { usrRole, err := NewUserRole(role.PublicId, id) if err != nil { - return nil, fmt.Errorf("unable to create in memory user role for add: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory user role for add")) } newUserRoles = append(newUserRoles, usrRole) } @@ -448,7 +453,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g if _, ok := existingGroups[id]; !ok { grpRole, err := NewGroupRole(role.PublicId, id) if err != nil { - return nil, fmt.Errorf("unable to create in memory group role for add: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group role for add")) } newGrpRoles = append(newGrpRoles, grpRole) } @@ -458,7 +463,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g if _, ok := userIdsMap[p.PrincipalId]; !ok { usrRole, err := NewUserRole(p.GetRoleId(), p.GetPrincipalId()) if err != nil { - return nil, fmt.Errorf("unable to create in memory user role for delete: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory user role for delete")) } deleteUserRoles = append(deleteUserRoles, usrRole) } @@ -468,7 +473,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g if _, ok := groupIdsMap[p.PrincipalId]; !ok { grpRole, err := NewGroupRole(p.GetRoleId(), p.GetPrincipalId()) if err != nil { - return nil, fmt.Errorf("unable to create in memory group role for delete: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory group role for delete")) } deleteGrpRoles = append(deleteGrpRoles, grpRole) } @@ -489,6 +494,7 @@ func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, g } func splitPrincipals(principals []string) ([]string, []string, error) { + const op = "iam.splitPrincipals" var users, groups []string for _, principal := range principals { switch { @@ -498,7 +504,7 @@ func splitPrincipals(principals []string) ([]string, []string, error) { case strings.HasPrefix(principal, GroupPrefix): groups = append(groups, principal) default: - return nil, nil, fmt.Errorf("invalid principal ID %q: %w", principal, errors.ErrInvalidParameter) + return nil, nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("invalid principal ID %q", principal)) } } diff --git a/internal/iam/repository_principal_role_test.go b/internal/iam/repository_principal_role_test.go index 096c4dfb0..1fa5751eb 100644 --- a/internal/iam/repository_principal_role_test.go +++ b/internal/iam/repository_principal_role_test.go @@ -320,7 +320,7 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { args args wantRowsDeleted int wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid", @@ -355,7 +355,7 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "just-user-roles", @@ -402,7 +402,7 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "zero-version", @@ -416,7 +416,7 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "bad-version", @@ -494,8 +494,8 @@ func TestRepository_DeletePrincipalRoles(t *testing.T) { if tt.wantErr { assert.Error(err) assert.Equal(0, deletedRows) - if tt.wantIsErr != nil { - assert.Truef(errors.Is(err, tt.wantIsErr), "unexpected error %s", err.Error()) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "unexpected error %s", err.Error()) } err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.Error(err) @@ -772,7 +772,7 @@ func TestRepository_principalsToSet(t *testing.T) { got, err := repo.principalsToSet(context.Background(), nil, users, grps) require.Error(err) assert.Nil(got) - assert.Truef(errors.Is(err, errors.ErrInvalidParameter), "unexpected error %s", err.Error()) + assert.Truef(errors.Match(errors.T(errors.InvalidParameter), err), "unexpected error %s", err.Error()) }) t.Run("no change", func(t *testing.T) { assert, require := assert.New(t), require.New(t) diff --git a/internal/iam/repository_role.go b/internal/iam/repository_role.go index 38a5c1975..ba6db91f1 100644 --- a/internal/iam/repository_role.go +++ b/internal/iam/repository_role.go @@ -12,33 +12,34 @@ import ( // CreateRole will create a role in the repository and return the written // role. No options are currently supported. -func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option) (*Role, error) { +func (r *Repository) CreateRole(ctx context.Context, role *Role, _ ...Option) (*Role, error) { + const op = "iam.(Repository).CreateRole" if role == nil { - return nil, fmt.Errorf("create role: missing role %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role") } if role.Role == nil { - return nil, fmt.Errorf("create role: missing role store %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role store") } if role.PublicId != "" { - return nil, fmt.Errorf("create role: public id not empty: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "public id not empty") } if role.ScopeId == "" { - return nil, fmt.Errorf("create role: missing role scope id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } id, err := newRoleId() if err != nil { - return nil, fmt.Errorf("create role: %w", err) + return nil, errors.Wrap(err, op) } c := role.Clone().(*Role) c.PublicId = id resource, err := r.create(ctx, c) if err != nil { if errors.IsUniqueError(err) { - return nil, fmt.Errorf("create role: role %s already exists in scope %s: %w", role.Name, role.ScopeId, errors.ErrNotUnique) + return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("role %s already exists in scope %s", role.Name, role.ScopeId)) } - return nil, fmt.Errorf("create role: %w for %s", err, c.PublicId) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", c.PublicId))) } - return resource.(*Role), err + return resource.(*Role), nil } // UpdateRole will update a role in the repository and return the written role. @@ -47,15 +48,16 @@ func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option) // included in fieldMask. Name, Description, and GrantScopeId are the only // updatable fields, If no updatable fields are included in the fieldMaskPaths, // then an error is returned. -func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, fieldMaskPaths []string, opt ...Option) (*Role, []PrincipalRole, []*RoleGrant, int, error) { +func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, fieldMaskPaths []string, _ ...Option) (*Role, []PrincipalRole, []*RoleGrant, int, error) { + const op = "iam.(Repository).UpdateRole" if role == nil { - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: missing role %w", errors.ErrInvalidParameter) + return nil, nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role") } if role.Role == nil { - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: missing role store %w", errors.ErrInvalidParameter) + return nil, nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role store") } if role.PublicId == "" { - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: missing role public id %w", errors.ErrInvalidParameter) + return nil, nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } for _, f := range fieldMaskPaths { switch { @@ -63,7 +65,7 @@ func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, case strings.EqualFold("description", f): case strings.EqualFold("grantscopeid", f): default: - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: field: %s: %w", f, errors.ErrInvalidFieldMask) + return nil, nil, nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } var dbMask, nullFields []string @@ -77,7 +79,7 @@ func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, nil, ) if len(dbMask) == 0 && len(nullFields) == 0 { - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: %w", errors.ErrEmptyFieldMask) + return nil, nil, nil, db.NoRowsAffected, errors.E(errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op)) } var resource Resource var rowsUpdated int @@ -92,37 +94,38 @@ func (r *Repository) UpdateRole(ctx context.Context, role *Role, version uint32, c := role.Clone().(*Role) resource, rowsUpdated, err = r.update(ctx, c, version, dbMask, nullFields) if err != nil { - return err + return errors.Wrap(err, op) } repo, err := NewRepository(read, w, r.kms) if err != nil { - return fmt.Errorf("update role: failed creating inner repo: %w for %s", err, role.PublicId) + return errors.Wrap(err, op) } pr, err = repo.ListPrincipalRoles(ctx, role.PublicId) if err != nil { - return fmt.Errorf("update role: listing principal roles: %w for %s", err, role.PublicId) + return errors.Wrap(err, op) } rg, err = repo.ListRoleGrants(ctx, role.PublicId) if err != nil { - return fmt.Errorf("update role: listing principal roles: %w for %s", err, role.PublicId) + return errors.Wrap(err, op) } return nil }, ) if err != nil { if errors.IsUniqueError(err) { - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: role %s already exists in org %s: %w", role.Name, role.ScopeId, errors.ErrNotUnique) + return nil, nil, nil, db.NoRowsAffected, errors.New(errors.NotUnique, op, fmt.Sprintf("role %s already exists in org %s", role.Name, role.ScopeId)) } - return nil, nil, nil, db.NoRowsAffected, fmt.Errorf("update role: %w for %s", err, role.PublicId) + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", role.PublicId))) } - return resource.(*Role), pr, rg, rowsUpdated, err + return resource.(*Role), pr, rg, rowsUpdated, nil } // LookupRole will look up a role in the repository. If the role is not // found, it will return nil, nil. -func (r *Repository) LookupRole(ctx context.Context, withPublicId string, opt ...Option) (*Role, []PrincipalRole, []*RoleGrant, error) { +func (r *Repository) LookupRole(ctx context.Context, withPublicId string, _ ...Option) (*Role, []PrincipalRole, []*RoleGrant, error) { + const op = "iam.(Repository).LookupRole" if withPublicId == "" { - return nil, nil, nil, fmt.Errorf("lookup role: missing public id %w", errors.ErrInvalidParameter) + return nil, nil, nil, errors.New(errors.InvalidParameter, op, "missing public id") } role := allocRole() role.PublicId = withPublicId @@ -134,19 +137,19 @@ func (r *Repository) LookupRole(ctx context.Context, withPublicId string, opt .. db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { if err := read.LookupByPublicId(ctx, &role); err != nil { - return fmt.Errorf("lookup role: failed %w for %s", err, withPublicId) + return errors.Wrap(err, op) } repo, err := NewRepository(read, w, r.kms) if err != nil { - return fmt.Errorf("lookup role: failed creating inner repo: %w for %s", err, withPublicId) + return errors.Wrap(err, op) } pr, err = repo.ListPrincipalRoles(ctx, withPublicId) if err != nil { - return fmt.Errorf("lookup role: listing principal roles: %w for %s", err, withPublicId) + return errors.Wrap(err, op) } rg, err = repo.ListRoleGrants(ctx, withPublicId) if err != nil { - return fmt.Errorf("lookup role: listing principal roles: %w for %s", err, withPublicId) + return errors.Wrap(err, op) } return nil }, @@ -155,37 +158,39 @@ func (r *Repository) LookupRole(ctx context.Context, withPublicId string, opt .. if errors.IsNotFoundError(err) { return nil, nil, nil, nil } - return nil, nil, nil, err + return nil, nil, nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", withPublicId))) } return &role, pr, rg, nil } // DeleteRole will delete a role from the repository. -func (r *Repository) DeleteRole(ctx context.Context, withPublicId string, opt ...Option) (int, error) { +func (r *Repository) DeleteRole(ctx context.Context, withPublicId string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteRole" if withPublicId == "" { - return db.NoRowsAffected, fmt.Errorf("delete role: missing public id %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } role := allocRole() role.PublicId = withPublicId if err := r.reader.LookupByPublicId(ctx, &role); err != nil { - return db.NoRowsAffected, fmt.Errorf("delete role: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId))) } rowsDeleted, err := r.delete(ctx, &role) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete role: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId))) } return rowsDeleted, nil } // ListRoles in a scope and supports WithLimit option. func (r *Repository) ListRoles(ctx context.Context, withScopeId string, opt ...Option) ([]*Role, error) { + const op = "iam.(Repository).ListRoles" if withScopeId == "" { - return nil, fmt.Errorf("list roles: missing scope id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } var roles []*Role err := r.list(ctx, &roles, "scope_id = ?", []interface{}{withScopeId}, opt...) if err != nil { - return nil, fmt.Errorf("list roles: %w", err) + return nil, errors.Wrap(err, op) } return roles, nil } diff --git a/internal/iam/repository_role_grant.go b/internal/iam/repository_role_grant.go index 5fc1fff66..280ecbf44 100644 --- a/internal/iam/repository_role_grant.go +++ b/internal/iam/repository_role_grant.go @@ -14,15 +14,16 @@ import ( // AddRoleGrant will add role grants associated with the role ID in the // repository. No options are currently supported. Zero is not a valid value for // the WithVersion option and will return an error. -func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, opt ...Option) ([]*RoleGrant, error) { +func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, _ ...Option) ([]*RoleGrant, error) { + const op = "iam.(Repository).AddRoleGrants" if roleId == "" { - return nil, fmt.Errorf("add role grants: missing role id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } if len(grants) == 0 { - return nil, fmt.Errorf("add role grants: missing grants: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing grants") } if roleVersion == 0 { - return nil, fmt.Errorf("add role grants: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing version") } role := allocRole() role.PublicId = roleId @@ -31,18 +32,18 @@ func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersi for _, grant := range grants { roleGrant, err := NewRoleGrant(roleId, grant) if err != nil { - return nil, fmt.Errorf("add role grants: unable to create in memory role grant: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } newRoleGrants = append(newRoleGrants, roleGrant) } scope, err := role.GetScope(ctx, r.reader) if err != nil { - return nil, fmt.Errorf("add role grants: unable to get role %s scope: %w", roleId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("add role grants: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } _, err = r.writer.DoTx( @@ -53,7 +54,7 @@ func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersi msgs := make([]*oplog.Message, 0, 2) roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } // We need to update the role version as that's the aggregate @@ -63,15 +64,15 @@ func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersi var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("unable to update role version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version")) } if rowsUpdated != 1 { - return fmt.Errorf("updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) roleGrantOplogMsgs := make([]*oplog.Message, 0, len(newRoleGrants)) if err := w.CreateItems(ctx, newRoleGrants, db.NewOplogMsgs(&roleGrantOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants")) } msgs = append(msgs, roleGrantOplogMsgs...) @@ -82,14 +83,14 @@ func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersi "resource-public-id": []string{roleId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("add role grants: error creating grants: %w", err) + return nil, errors.Wrap(err, op) } roleGrants := make([]*RoleGrant, 0, len(newRoleGrants)) for _, grant := range newRoleGrants { @@ -102,26 +103,27 @@ func (r *Repository) AddRoleGrants(ctx context.Context, roleId string, roleVersi // current db version must match the roleVersion or an error will be returned. // Zero is not a valid value for the WithVersion option and will return an // error. -func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, opt ...Option) (int, error) { +func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteRoleGrants" if roleId == "" { - return 0, fmt.Errorf("delete role grants: missing role id %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role id") } if len(grants) == 0 { - return 0, fmt.Errorf("delete role grants: missing grants: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing grants") } if roleVersion == 0 { - return 0, fmt.Errorf("delete role grants: version cannot be zero: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } role := allocRole() role.PublicId = roleId scope, err := role.GetScope(ctx, r.reader) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete role grants: unable to get role %s scope to create metadata: %w", roleId, err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope to create metadata", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete role grants: unable to get oplog wrapper: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var totalRowsDeleted int @@ -133,7 +135,7 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe msgs := make([]*oplog.Message, 0, 2) roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("delete role grants: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedRole := allocRole() updatedRole.PublicId = roleId @@ -141,17 +143,17 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("delete role grants: unable to update role version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version")) } if rowsUpdated != 1 { - return fmt.Errorf("delete roles grants: updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) // Find existing grants roleGrants := []*RoleGrant{} if err := reader.SearchWhere(ctx, &roleGrants, "role_id = ?", []interface{}{roleId}); err != nil { - return fmt.Errorf("delete role grants: unable to search for grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to search for grants")) } found := map[string]bool{} for _, rg := range roleGrants { @@ -165,7 +167,7 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe // Use a fake scope, just want to get out a canonical string perm, err := perms.Parse("o_abcd1234", grant, perms.WithSkipFinalValidation(true)) if err != nil { - return fmt.Errorf("delete role grants: error parsing grant string: %w", err) + return errors.Wrap(err, op, errors.WithMsg("parsing grant string")) } // We don't have what they want to delete, so ignore it if !found[perm.CanonicalString()] { @@ -174,7 +176,7 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe roleGrant, err := NewRoleGrant(roleId, grant) if err != nil { - return fmt.Errorf("delete role grants: unable to create in memory role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } deleteRoleGrants = append(deleteRoleGrants, roleGrant) } @@ -186,10 +188,10 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe roleGrantOplogMsgs := make([]*oplog.Message, 0, len(deleteRoleGrants)) rowsDeleted, err := w.DeleteItems(ctx, deleteRoleGrants, db.NewOplogMsgs(&roleGrantOplogMsgs)) if err != nil { - return fmt.Errorf("delete role grants: unable to delete role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete role grant")) } if rowsDeleted != len(deleteRoleGrants) { - return fmt.Errorf("delete role grants: role grants deleted %d did not match request for %d", rowsDeleted, len(deleteRoleGrants)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("role grants deleted %d did not match request for %d", rowsDeleted, len(deleteRoleGrants))) } totalRowsDeleted = rowsDeleted msgs = append(msgs, roleGrantOplogMsgs...) @@ -201,14 +203,14 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe "resource-public-id": []string{roleId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("delete role grants: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } return nil }, ) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete role grants: error deleting role grants: %w", err) + return db.NoRowsAffected, errors.Wrap(err, op) } return totalRowsDeleted, nil } @@ -216,17 +218,18 @@ func (r *Repository) DeleteRoleGrants(ctx context.Context, roleId string, roleVe // SetRoleGrants sets grants on a role (roleId). The role's current db version // must match the roleVersion or an error will be returned. Zero is not a valid // value for the WithVersion option and will return an error. -func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, opt ...Option) ([]*RoleGrant, int, error) { +func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersion uint32, grants []string, _ ...Option) ([]*RoleGrant, int, error) { + const op = "iam.(Repository).SetRoleGrants" if roleId == "" { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: missing role id %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing role id") } if roleVersion == 0 { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: version cannot be zero: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing version") } // Explicitly set to zero clears, but treat nil as a mistake if grants == nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: nil grants: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing grants") } role := allocRole() @@ -241,7 +244,7 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi // Find existing grants roleGrants := []*RoleGrant{} if err := r.reader.SearchWhere(ctx, &roleGrants, "role_id = ?", []interface{}{roleId}); err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: unable to search for grants: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to search for grants")) } found := map[string]*RoleGrant{} for _, rg := range roleGrants { @@ -256,7 +259,7 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi // Use a fake scope, just want to get out a canonical string perm, err := perms.Parse("o_abcd1234", grant, perms.WithSkipFinalValidation(true)) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: error parsing grant string: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("error parsing grant string")) } canonicalString := perm.CanonicalString() @@ -272,7 +275,7 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi // Not found, so add rg, err = NewRoleGrant(roleId, grant) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: unable to create in memory role grant: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } addRoleGrants = append(addRoleGrants, rg) currentRoleGrants = append(currentRoleGrants, rg) @@ -290,11 +293,11 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi scope, err := role.GetScope(ctx, r.reader) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: unable to get role %s scope: %w", roleId, err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to get role %s scope", roleId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: unable to get oplog wrapper: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var totalRowsDeleted int @@ -306,7 +309,7 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi msgs := make([]*oplog.Message, 0, 2) roleTicket, err := w.GetTicket(&role) if err != nil { - return fmt.Errorf("set role grants: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedRole := allocRole() updatedRole.PublicId = roleId @@ -314,10 +317,10 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&roleVersion)) if err != nil { - return fmt.Errorf("set role grants: unable to update role version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version")) } if rowsUpdated != 1 { - return fmt.Errorf("set roles grants: updated role and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role and %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) @@ -325,7 +328,7 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi if len(addRoleGrants) > 0 { roleGrantOplogMsgs := make([]*oplog.Message, 0, len(addRoleGrants)) if err := w.CreateItems(ctx, addRoleGrants, db.NewOplogMsgs(&roleGrantOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants during set: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants during set")) } msgs = append(msgs, roleGrantOplogMsgs...) } @@ -335,10 +338,10 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi roleGrantOplogMsgs := make([]*oplog.Message, 0, len(deleteRoleGrants)) rowsDeleted, err := w.DeleteItems(ctx, deleteRoleGrants, db.NewOplogMsgs(&roleGrantOplogMsgs)) if err != nil { - return fmt.Errorf("set role grants: unable to delete role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to delete role grant")) } if rowsDeleted != len(deleteRoleGrants) { - return fmt.Errorf("set role grants: role grants deleted %d did not match request for %d", rowsDeleted, len(deleteRoleGrants)) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("role grants deleted %d did not match request for %d", rowsDeleted, len(deleteRoleGrants))) } totalRowsDeleted = rowsDeleted msgs = append(msgs, roleGrantOplogMsgs...) @@ -351,19 +354,19 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi "resource-public-id": []string{roleId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("set role grants: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } currentRoleGrants, err = r.ListRoleGrants(ctx, roleId) if err != nil { - return fmt.Errorf("set role grants: unable to retrieve current role grants after set: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current role grants after set")) } return nil }, ) if err != nil { - return nil, db.NoRowsAffected, fmt.Errorf("set role grants: error set role grants: %w", err) + return nil, db.NoRowsAffected, errors.Wrap(err, op) } return currentRoleGrants, totalRowsDeleted, nil } @@ -371,19 +374,21 @@ func (r *Repository) SetRoleGrants(ctx context.Context, roleId string, roleVersi // ListRoleGrants returns the grants for the roleId and supports the WithLimit // option. func (r *Repository) ListRoleGrants(ctx context.Context, roleId string, opt ...Option) ([]*RoleGrant, error) { + const op = "iam.(Repository).ListRoleGrants" if roleId == "" { - return nil, fmt.Errorf("add role grants: missing role id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } var roleGrants []*RoleGrant if err := r.list(ctx, &roleGrants, "role_id = ?", []interface{}{roleId}, opt...); err != nil { - return nil, fmt.Errorf("lookup role grants: unable to lookup role grants: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to lookup role grants")) } return roleGrants, nil } -func (r *Repository) GrantsForUser(ctx context.Context, userId string, opt ...Option) ([]perms.GrantPair, error) { +func (r *Repository) GrantsForUser(ctx context.Context, userId string, _ ...Option) ([]perms.GrantPair, error) { + const op = "iam.(Repository).GrantsForUser" if userId == "" { - return nil, fmt.Errorf("get grants for user: missing user id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user id") } const ( @@ -451,13 +456,13 @@ select role_scope as scope_id, role_grant as grant from final; var grants []perms.GrantPair rows, err := r.reader.Query(ctx, query, []interface{}{userId}) if err != nil { - return nil, err + return nil, errors.Wrap(err, op) } defer rows.Close() for rows.Next() { var g perms.GrantPair if err := r.reader.ScanRows(rows, &g); err != nil { - return nil, err + return nil, errors.Wrap(err, op) } grants = append(grants, g) } diff --git a/internal/iam/repository_role_grant_test.go b/internal/iam/repository_role_grant_test.go index b97c9cc76..7db7340fa 100644 --- a/internal/iam/repository_role_grant_test.go +++ b/internal/iam/repository_role_grant_test.go @@ -240,7 +240,7 @@ func TestRepository_DeleteRoleGrants(t *testing.T) { args args wantRowsDeleted int wantErr bool - wantIsErr error + wantIsErr errors.Code }{ { name: "valid", @@ -271,7 +271,7 @@ func TestRepository_DeleteRoleGrants(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { @@ -295,7 +295,7 @@ func TestRepository_DeleteRoleGrants(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "invalid-grant-strings", @@ -375,8 +375,8 @@ func TestRepository_DeleteRoleGrants(t *testing.T) { if tt.wantErr { assert.Error(err) assert.Equal(0, deletedRows) - if tt.wantIsErr != nil { - assert.Truef(errors.Is(err, tt.wantIsErr), "unexpected error %s", err.Error()) + if tt.wantIsErr != 0 { + assert.Truef(errors.Match(errors.T(tt.wantIsErr), err), "unexpected error %s", err.Error()) } err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) assert.Error(err) diff --git a/internal/iam/repository_role_test.go b/internal/iam/repository_role_test.go index b0ffef3a2..d2e100e68 100644 --- a/internal/iam/repository_role_test.go +++ b/internal/iam/repository_role_test.go @@ -37,7 +37,7 @@ func TestRepository_CreateRole(t *testing.T) { wantDup bool wantErr bool wantErrMsg string - wantIsError error + wantIsError errors.Code }{ { name: "valid-org", @@ -71,8 +71,8 @@ func TestRepository_CreateRole(t *testing.T) { return r }(), }, - wantErrMsg: "create role: public id not empty: invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "am.(Repository).CreateRole: public id not empty: parameter violation: error #100", + wantIsError: errors.InvalidParameter, wantErr: true, }, { @@ -81,8 +81,8 @@ func TestRepository_CreateRole(t *testing.T) { role: nil, }, wantErr: true, - wantErrMsg: "create role: missing role invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).CreateRole: missing role: parameter violation: error #100", + wantIsError: errors.InvalidParameter, }, { name: "nil-store", @@ -94,8 +94,8 @@ func TestRepository_CreateRole(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "create role: missing role store invalid parameter", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).CreateRole: missing role store: parameter violation: error #100", + wantIsError: errors.InvalidParameter, }, { name: "bad-scope-id", @@ -107,8 +107,8 @@ func TestRepository_CreateRole(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "create role: error getting metadata for create: unable to get scope for standard metadata: db.LookupWhere: record not found", - wantIsError: errors.ErrInvalidParameter, + wantErrMsg: "iam.(Repository).create: error getting metadata: iam.(Repository).stdMetadata: unable to get scope: iam.LookupScope: db.LookupWhere: record not found, search issue: error #1100", + wantIsError: errors.RecordNotFound, }, { name: "dup-name", @@ -123,7 +123,7 @@ func TestRepository_CreateRole(t *testing.T) { wantDup: true, wantErr: true, wantErrMsg: "already exists in scope ", - wantIsError: errors.ErrNotUnique, + wantIsError: errors.NotUnique, }, { name: "dup-name-but-diff-scope", @@ -155,6 +155,7 @@ func TestRepository_CreateRole(t *testing.T) { assert.Error(err) assert.Nil(grp) assert.Contains(err.Error(), tt.wantErrMsg) + assert.True(errors.Match(errors.T(tt.wantIsError), err)) return } assert.NoError(err) @@ -238,7 +239,7 @@ func TestRepository_UpdateRole(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update role: db.DoTx: db.DoTx: db.Update: db.lookupAfterWrite: db.LookupById: record not found", + wantErrMsg: "db.DoTx: iam.(Repository).UpdateRole: iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100", wantIsError: errors.RecordNotFound, }, { @@ -275,7 +276,7 @@ func TestRepository_UpdateRole(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update role: empty field mask", + wantErrMsg: "iam.(Repository).UpdateRole: empty field mask, parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -288,7 +289,7 @@ func TestRepository_UpdateRole(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update role: empty field mask", + wantErrMsg: "iam.(Repository).UpdateRole: empty field mask, parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -301,7 +302,7 @@ func TestRepository_UpdateRole(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update role: field: CreateTime: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateRole: invalid field mask: CreateTime: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -314,7 +315,7 @@ func TestRepository_UpdateRole(t *testing.T) { newScopeId: org.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update role: field: Alice: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateRole: invalid field mask: Alice: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -327,7 +328,7 @@ func TestRepository_UpdateRole(t *testing.T) { }, newScopeId: org.PublicId, wantErr: true, - wantErrMsg: "update role: missing role public id invalid parameter", + wantErrMsg: "iam.(Repository).UpdateRole: missing public id: parameter violation: error #100", wantIsError: errors.InvalidParameter, wantRowsUpdate: 0, }, @@ -339,7 +340,7 @@ func TestRepository_UpdateRole(t *testing.T) { }, newScopeId: org.PublicId, wantErr: true, - wantErrMsg: "update role: empty field mask", + wantErrMsg: "iam.(Repository).UpdateRole: empty field mask, parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -471,6 +472,9 @@ func TestRepository_DeleteRole(t *testing.T) { repo := TestRepo(t, conn, wrapper) org, _ := TestScopes(t, repo) + roleId, err := newRoleId() + require.NoError(t, err) + type args struct { role *Role opt []Option @@ -500,24 +504,22 @@ func TestRepository_DeleteRole(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "delete role: missing public id invalid parameter", + wantErrMsg: "iam.(Repository).DeleteRole: missing public id: parameter violation: error #100", }, { name: "not-found", args: args{ role: func() *Role { - id, err := newRoleId() - require.NoError(t, err) r, err := NewRole(org.PublicId) - r.PublicId = id + r.PublicId = roleId require.NoError(t, err) return r }(), }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "delete role: failed db.LookupById: record not found", + wantErrMsg: "iam.(Repository).DeleteRole: failed for " + roleId + ": db.LookupById: record not found, search issue: error #1100", }, } for _, tt := range tests { diff --git a/internal/iam/repository_scope.go b/internal/iam/repository_scope.go index 4a8766dce..64c0f8eb8 100644 --- a/internal/iam/repository_scope.go +++ b/internal/iam/repository_scope.go @@ -19,14 +19,15 @@ import ( // CreateScope will create a scope in the repository and return the written // scope. Supported options include: WithPublicId and WithRandomReader. func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, opt ...Option) (*Scope, error) { + const op = "iam.(Repository).CreateScope" if s == nil { - return nil, fmt.Errorf("create scope: missing scope %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope") } if s.Scope == nil { - return nil, fmt.Errorf("create scope: missing scope store %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope store") } if s.PublicId != "" { - return nil, fmt.Errorf("create scope: public id not empty: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "public id not empty") } var parentOplogWrapper wrapping.Wrapper @@ -34,13 +35,13 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o var err error switch s.Type { case scope.Unknown.String(): - return nil, fmt.Errorf("create scope: unknown type: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "unknown type") case scope.Global.String(): - return nil, fmt.Errorf("create scope: invalid type: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "invalid type") default: switch s.ParentId { case "": - return nil, fmt.Errorf("create scope: missing parent id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing parent id") case scope.Global.String(): parentOplogWrapper, err = r.kms.GetWrapper(ctx, scope.Global.String(), kms.KeyPurposeOplog) default: @@ -49,7 +50,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o externalWrappers = r.kms.GetExternalWrappers() } if err != nil { - return nil, fmt.Errorf("create scope: unable to get oplog wrapper: %w", err) + return nil, errors.New(errors.InvalidParameter, op, "unable to get oplog wrapper") } opts := getOpts(opt...) @@ -61,13 +62,13 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o scopeType := scope.Map[s.Type] if opts.withPublicId != "" { if !strings.HasPrefix(opts.withPublicId, scopeType.Prefix()+"_") { - return nil, fmt.Errorf("create scope: passed-in public ID %q has wrong prefix for type %q which uses prefix %q", opts.withPublicId, scopeType.String(), scopeType.Prefix()) + return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("passed-in public ID %q has wrong prefix for type %q which uses prefix %q", opts.withPublicId, scopeType.String(), scopeType.Prefix())) } scopePublicId = opts.withPublicId } else { scopePublicId, err = newScopeId(scopeType) if err != nil { - return nil, fmt.Errorf("create scope: error generating public id for new scope: %w", err) + return nil, errors.Wrap(err, op) } } sc := s.Clone().(*Scope) @@ -75,7 +76,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o scopeRaw = sc scopeMetadata, err = r.stdMetadata(ctx, sc) if err != nil { - return nil, fmt.Errorf("create scope: error getting metadata for scope create: %w", err) + return nil, errors.Wrap(err, op) } scopeMetadata["op-type"] = []string{oplog.OpType_OP_TYPE_CREATE.String()} } @@ -102,11 +103,11 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o default: adminRole, err = NewRole(scopePublicId) if err != nil { - return nil, fmt.Errorf("create scope: error instantiating new admin role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("error instantiating new admin role")) } adminRolePublicId, err = newRoleId() if err != nil { - return nil, fmt.Errorf("create scope: error generating public id for new admin role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("error generating public id for new admin role")) } adminRole.PublicId = adminRolePublicId adminRole.Name = "Administration" @@ -128,11 +129,11 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o if !opts.withSkipDefaultRoleCreation && s.Type == scope.Org.String() { defaultRole, err = NewRole(scopePublicId) if err != nil { - return nil, fmt.Errorf("create scope: error instantiating new default role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("error instantiating new default role")) } defaultRolePublicId, err = newRoleId() if err != nil { - return nil, fmt.Errorf("create scope: error generating public id for new default role: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("error generating public id for new default role")) } defaultRole.PublicId = defaultRolePublicId defaultRole.Name = "Login and Default Grants" @@ -162,7 +163,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o scopeRaw, db.WithOplog(parentOplogWrapper, scopeMetadata), ); err != nil { - return fmt.Errorf("error creating scope: %w", err) + return errors.Wrap(err, op, errors.WithMsg("error creating scope")) } s := scopeRaw.(*Scope) @@ -170,16 +171,16 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o // Create the scope's keys _, err = kms.CreateKeysTx(ctx, dbr, w, externalWrappers.Root(), reader, s.PublicId) if err != nil { - return fmt.Errorf("error creating scope keys: %w", err) + return errors.Wrap(err, op, errors.WithMsg("error creating scope keys")) } kmsRepo, err := kms.NewRepository(dbr, w) if err != nil { - return fmt.Errorf("error creating new kms repo: %w", err) + return errors.Wrap(err, op, errors.WithMsg("error creating new kms repo")) } childOplogWrapper, err := r.kms.GetWrapper(ctx, s.PublicId, kms.KeyPurposeOplog, kms.WithRepository(kmsRepo)) if err != nil { - return fmt.Errorf("error fetching new scope oplog wrapper: %w", err) + return errors.New(errors.InvalidParameter, op, "unable to get oplog wrapper") } // We create a new role, then set grants and principals on it. This @@ -191,7 +192,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o adminRoleRaw, db.WithOplog(childOplogWrapper, adminRoleMetadata), ); err != nil { - return fmt.Errorf("error creating role: %w", err) + return errors.Wrap(err, op, errors.WithMsg("error creating role")) } adminRole = adminRoleRaw.(*Role) @@ -199,38 +200,38 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o msgs := make([]*oplog.Message, 0, 3) roleTicket, err := w.GetTicket(adminRole) if err != nil { - return fmt.Errorf("unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } // We need to update the role version as that's the aggregate var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, adminRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&adminRole.Version)) if err != nil { - return fmt.Errorf("unable to update role version for adding grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version for adding grant")) } if rowsUpdated != 1 { - return fmt.Errorf("updated role but %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role but %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) roleGrant, err := NewRoleGrant(adminRolePublicId, "id=*;type=*;actions=*") if err != nil { - return fmt.Errorf("unable to create in memory role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } roleGrantOplogMsgs := make([]*oplog.Message, 0, 1) if err := w.CreateItems(ctx, []interface{}{roleGrant}, db.NewOplogMsgs(&roleGrantOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants")) } msgs = append(msgs, roleGrantOplogMsgs...) rolePrincipal, err := NewUserRole(adminRolePublicId, userId) if err != nil { - return fmt.Errorf("unable to create in memory role user: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role user")) } roleUserOplogMsgs := make([]*oplog.Message, 0, 1) if err := w.CreateItems(ctx, []interface{}{rolePrincipal}, db.NewOplogMsgs(&roleUserOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants")) } msgs = append(msgs, roleUserOplogMsgs...) @@ -241,7 +242,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o "resource-public-id": []string{adminRole.PublicId}, } if err := w.WriteOplogEntryWith(ctx, childOplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } } @@ -254,7 +255,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o defaultRoleRaw, db.WithOplog(childOplogWrapper, defaultRoleMetadata), ); err != nil { - return fmt.Errorf("error creating role: %w", err) + return errors.Wrap(err, op, errors.WithMsg("error creating role")) } defaultRole = defaultRoleRaw.(*Role) @@ -262,17 +263,17 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o msgs := make([]*oplog.Message, 0, 6) roleTicket, err := w.GetTicket(defaultRole) if err != nil { - return fmt.Errorf("unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } // We need to update the role version as that's the aggregate var roleOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, defaultRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(&defaultRole.Version)) if err != nil { - return fmt.Errorf("unable to update role version for adding grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update role version for adding grant")) } if rowsUpdated != 1 { - return fmt.Errorf("updated role but %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated role but %d rows updated", rowsUpdated)) } msgs = append(msgs, &roleOplogMsg) @@ -281,24 +282,24 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o grants := []interface{}{} roleGrant, err := NewRoleGrant(defaultRolePublicId, "type=scope;actions=list") if err != nil { - return fmt.Errorf("unable to create in memory role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } grants = append(grants, roleGrant) roleGrant, err = NewRoleGrant(defaultRolePublicId, "id=*;type=auth-method;actions=authenticate,list") if err != nil { - return fmt.Errorf("unable to create in memory role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } grants = append(grants, roleGrant) roleGrant, err = NewRoleGrant(defaultRolePublicId, "id={{account.id}};actions=read,change-password") if err != nil { - return fmt.Errorf("unable to create in memory role grant: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role grant")) } grants = append(grants, roleGrant) roleGrantOplogMsgs := make([]*oplog.Message, 0, 3) if err := w.CreateItems(ctx, grants, db.NewOplogMsgs(&roleGrantOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants")) } msgs = append(msgs, roleGrantOplogMsgs...) } @@ -308,13 +309,13 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o principals := []interface{}{} rolePrincipal, err := NewUserRole(defaultRolePublicId, "u_anon") if err != nil { - return fmt.Errorf("unable to create in memory role user: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to create in memory role user")) } principals = append(principals, rolePrincipal) roleUserOplogMsgs := make([]*oplog.Message, 0, 2) if err := w.CreateItems(ctx, principals, db.NewOplogMsgs(&roleUserOplogMsgs)); err != nil { - return fmt.Errorf("unable to add grants: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to add grants")) } msgs = append(msgs, roleUserOplogMsgs...) } @@ -326,7 +327,7 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o "resource-public-id": []string{defaultRole.PublicId}, } if err := w.WriteOplogEntryWith(ctx, childOplogWrapper, roleTicket, metadata, msgs); err != nil { - return fmt.Errorf("unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } } @@ -336,9 +337,9 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o if err != nil { if errors.IsUniqueError(err) { - return nil, fmt.Errorf("create scope: scope %s/%s already exists: %w", scopePublicId, s.Name, errors.ErrNotUnique) + return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("scope %s/%s already exists", scopePublicId, s.Name)) } - return nil, fmt.Errorf("create scope: id %s got error: %w", scopePublicId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", scopePublicId))) } return scopeRaw.(*Scope), nil } @@ -349,15 +350,16 @@ func (r *Repository) CreateScope(ctx context.Context, s *Scope, userId string, o // included in fieldMask. Name and Description are the only updatable fields, // and everything else is ignored. If no updatable fields are included in the // fieldMaskPaths, then an error is returned. -func (r *Repository) UpdateScope(ctx context.Context, scope *Scope, version uint32, fieldMaskPaths []string, opt ...Option) (*Scope, int, error) { +func (r *Repository) UpdateScope(ctx context.Context, scope *Scope, version uint32, fieldMaskPaths []string, _ ...Option) (*Scope, int, error) { + const op = "iam.(Repository).UpdateScope" if scope == nil { - return nil, db.NoRowsAffected, fmt.Errorf("update scope: missing scope: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing scope") } if scope.PublicId == "" { - return nil, db.NoRowsAffected, fmt.Errorf("update scope: missing public id: %w", errors.ErrInvalidParameter) + return nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } if contains(fieldMaskPaths, "ParentId") { - return nil, db.NoRowsAffected, fmt.Errorf("update scope: you cannot change a scope's parent: %w", errors.ErrInvalidFieldMask) + return nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, "you cannot change a scope's parent") } var dbMask, nullFields []string dbMask, nullFields = dbcommon.BuildUpdatePaths( @@ -370,24 +372,25 @@ func (r *Repository) UpdateScope(ctx context.Context, scope *Scope, version uint ) // nada to update, so reload scope from db and return it if len(dbMask) == 0 && len(nullFields) == 0 { - return nil, db.NoRowsAffected, fmt.Errorf("update scope: %w", errors.ErrEmptyFieldMask) + return nil, db.NoRowsAffected, errors.E(errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op)) } resource, rowsUpdated, err := r.update(ctx, scope, version, dbMask, nullFields) if err != nil { if errors.IsUniqueError(err) { - return nil, db.NoRowsAffected, fmt.Errorf("update scope: %s name %s already exists: %w", scope.PublicId, scope.Name, errors.ErrNotUnique) + return nil, db.NoRowsAffected, errors.New(errors.NotUnique, op, fmt.Sprintf("%s name %s already exists", scope.PublicId, scope.Name)) } - return nil, db.NoRowsAffected, fmt.Errorf("update scope: failed for public id %s: %w", scope.PublicId, err) + return nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for public id %s", scope.PublicId))) } - return resource.(*Scope), rowsUpdated, err + return resource.(*Scope), rowsUpdated, nil } // LookupScope will look up a scope in the repository. If the scope is not // found, it will return nil, nil. -func (r *Repository) LookupScope(ctx context.Context, withPublicId string, opt ...Option) (*Scope, error) { +func (r *Repository) LookupScope(ctx context.Context, withPublicId string, _ ...Option) (*Scope, error) { + const op = "iam.(Repository).LookupScope" if withPublicId == "" { - return nil, fmt.Errorf("lookup scope: missing public id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing public id") } scope := allocScope() scope.PublicId = withPublicId @@ -395,18 +398,19 @@ func (r *Repository) LookupScope(ctx context.Context, withPublicId string, opt . if errors.IsNotFoundError(err) { return nil, nil } - return nil, fmt.Errorf("lookup scope: failed %w fo %s", err, withPublicId) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId))) } return &scope, nil } // DeleteScope will delete a scope from the repository -func (r *Repository) DeleteScope(ctx context.Context, withPublicId string, opt ...Option) (int, error) { +func (r *Repository) DeleteScope(ctx context.Context, withPublicId string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteScope" if withPublicId == "" { - return db.NoRowsAffected, fmt.Errorf("delete scope: missing public id %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } if withPublicId == scope.Global.String() { - return db.NoRowsAffected, fmt.Errorf("delete scope: invalid to delete global scope: %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "invalid to delete global scope") } scope := allocScope() scope.PublicId = withPublicId @@ -415,30 +419,32 @@ func (r *Repository) DeleteScope(ctx context.Context, withPublicId string, opt . if errors.Is(err, ErrMetadataScopeNotFound) { return 0, nil } - return db.NoRowsAffected, fmt.Errorf("delete scope: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId))) } return rowsDeleted, nil } // ListProjects in an org and supports the WithLimit option. func (r *Repository) ListProjects(ctx context.Context, withOrgId string, opt ...Option) ([]*Scope, error) { + const op = "iam.(Repository).ListProjects" if withOrgId == "" { - return nil, fmt.Errorf("list projects: missing org id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing org id") } var projects []*Scope err := r.list(ctx, &projects, "parent_id = ? and type = ?", []interface{}{withOrgId, scope.Project.String()}, opt...) if err != nil { - return nil, fmt.Errorf("list projects: %w", err) + return nil, errors.Wrap(err, op) } return projects, nil } // ListOrgs and supports the WithLimit option. func (r *Repository) ListOrgs(ctx context.Context, opt ...Option) ([]*Scope, error) { + const op = "iam.(Repository).ListOrgs" var orgs []*Scope err := r.list(ctx, &orgs, "parent_id = ? and type = ?", []interface{}{"global", scope.Org.String()}, opt...) if err != nil { - return nil, fmt.Errorf("list orgs: %w", err) + return nil, errors.Wrap(err, op) } return orgs, nil } diff --git a/internal/iam/repository_scope_test.go b/internal/iam/repository_scope_test.go index 6af72f43c..490f0daa5 100644 --- a/internal/iam/repository_scope_test.go +++ b/internal/iam/repository_scope_test.go @@ -197,7 +197,7 @@ func Test_Repository_Scope_Update(t *testing.T) { require.Error(err) assert.Nil(project) assert.Equal(0, updatedRows) - assert.Contains(err.Error(), "update scope: you cannot change a scope's parent: invalid field mask") + assert.Contains(err.Error(), "iam.(Repository).UpdateScope: you cannot change a scope's parent: parameter violation: error #103") }) } @@ -315,7 +315,7 @@ func TestRepository_UpdateScope(t *testing.T) { }, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "update scope: missing scope: invalid parameter", + wantErrMsg: "iam.(Repository).UpdateScope: missing scope: parameter violation: error #100", wantNullFields: nil, }, { @@ -334,7 +334,7 @@ func TestRepository_UpdateScope(t *testing.T) { }, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "update scope: empty field mask", + wantErrMsg: "iam.(Repository).UpdateScope: empty field mask, parameter violation: error #104", wantNullFields: nil, }, { @@ -398,7 +398,7 @@ func TestRepository_UpdateScope(t *testing.T) { wantDescription: "orig-" + id, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "update scope: you cannot change a scope's parent: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateScope: you cannot change a scope's parent: parameter violation: error #103", wantNullFields: nil, }, { @@ -418,7 +418,7 @@ func TestRepository_UpdateScope(t *testing.T) { }, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "update scope: empty field mask", + wantErrMsg: "am.(Repository).UpdateScope: empty field mask, parameter violation: error #104", wantNullFields: nil, }, } diff --git a/internal/iam/repository_test.go b/internal/iam/repository_test.go index b8f524707..4696735d6 100644 --- a/internal/iam/repository_test.go +++ b/internal/iam/repository_test.go @@ -61,7 +61,7 @@ func TestNewRepository(t *testing.T) { }, want: nil, wantErr: true, - wantErrString: "error creating db repository with nil kms", + wantErrString: "iam.NewRepository: nil kms: parameter violation: error #100", }, { name: "nil-writer", @@ -72,7 +72,7 @@ func TestNewRepository(t *testing.T) { }, want: nil, wantErr: true, - wantErrString: "error creating db repository with nil writer", + wantErrString: "iam.NewRepository: nil writer: parameter violation: error #100", }, { name: "nil-reader", @@ -83,7 +83,7 @@ func TestNewRepository(t *testing.T) { }, want: nil, wantErr: true, - wantErrString: "error creating db repository with nil reader", + wantErrString: "iam.NewRepository: nil reader: parameter violation: error #100", }, } for _, tt := range tests { @@ -92,7 +92,7 @@ func TestNewRepository(t *testing.T) { got, err := NewRepository(tt.args.r, tt.args.w, tt.args.kms) if tt.wantErr { require.Error(err) - assert.Equal(err.Error(), tt.wantErrString) + assert.Equal(tt.wantErrString, err.Error()) return } require.NoError(err) @@ -136,7 +136,7 @@ func Test_Repository_create(t *testing.T) { resource, err := repo.create(context.Background(), nil) require.Error(err) assert.Nil(resource) - assert.Equal(err.Error(), "error creating resource that is nil") + assert.Equal("iam.(Repository).create: missing resource: parameter violation: error #100", err.Error()) }) } @@ -165,7 +165,7 @@ func Test_Repository_delete(t *testing.T) { deletedRows, err := repo.delete(context.Background(), nil, nil) require.Error(err) assert.Equal(0, deletedRows) - assert.Equal(err.Error(), "error deleting resource that is nil") + assert.Equal("iam.(Repository).delete: missing resource: parameter violation: error #100", err.Error()) }) } @@ -223,7 +223,7 @@ func TestRepository_update(t *testing.T) { }, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "error updating resource that is nil", + wantErrMsg: "iam.(Repository).update: missing resource: parameter violation: error #100", }, { name: "intersection", @@ -242,7 +242,7 @@ func TestRepository_update(t *testing.T) { }, wantUpdatedRows: 0, wantErr: true, - wantErrMsg: "db.DoTx: db.Update: getting update fields failed: common.UpdateFields: fieldMashPaths and setToNullPaths cannot intersect: parameter violation: error #100", + wantErrMsg: "iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: getting update fields failed: common.UpdateFields: fieldMashPaths and setToNullPaths cannot intersect: parameter violation: error #100", }, { name: "only-field-masks", diff --git a/internal/iam/repository_user.go b/internal/iam/repository_user.go index 30ba9b0cf..2d9b6ba75 100644 --- a/internal/iam/repository_user.go +++ b/internal/iam/repository_user.go @@ -2,7 +2,6 @@ package iam import ( "context" - stdErrors "errors" "fmt" "strings" @@ -16,11 +15,12 @@ import ( // CreateUser will create a user in the repository and return the written user func (r *Repository) CreateUser(ctx context.Context, user *User, opt ...Option) (*User, error) { + const op = "iam.(Repository).CreateUser" if user == nil { - return nil, fmt.Errorf("create user: missing user %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user") } if user.PublicId != "" { - return nil, fmt.Errorf("create user: public id is not empty %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "public id is not empty") } u := user.Clone().(*User) @@ -28,13 +28,13 @@ func (r *Repository) CreateUser(ctx context.Context, user *User, opt ...Option) if opts.withPublicId != "" { if !strings.HasPrefix(opts.withPublicId, UserPrefix+"_") { - return nil, fmt.Errorf("create user: passed-in public ID %q has wrong prefix, should be %q: %w", opts.withPublicId, UserPrefix, errors.ErrInvalidPublicId) + return nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("passed-in public ID %q has wrong prefix, should be %q", opts.withPublicId, UserPrefix)) } u.PublicId = opts.withPublicId } else { id, err := newUserId() if err != nil { - return nil, fmt.Errorf("create user: %w", err) + return nil, errors.Wrap(err, op) } u.PublicId = id } @@ -42,11 +42,11 @@ func (r *Repository) CreateUser(ctx context.Context, user *User, opt ...Option) resource, err := r.create(ctx, u) if err != nil { if errors.IsUniqueError(err) { - return nil, fmt.Errorf("create user: user %s already exists in org %s: %w", user.Name, user.ScopeId, err) + return nil, errors.New(errors.NotUnique, op, fmt.Sprintf("user %s already exists in org %s", user.Name, user.ScopeId)) } - return nil, fmt.Errorf("create user: %w for %s", err, u.PublicId) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", u.PublicId))) } - return resource.(*User), err + return resource.(*User), nil } // UpdateUser will update a user in the repository and return the written user @@ -56,18 +56,19 @@ func (r *Repository) CreateUser(ctx context.Context, user *User, opt ...Option) // only updatable fields, if no updatable fields are included in the // fieldMaskPaths, then an error is returned. func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32, fieldMaskPaths []string, opt ...Option) (*User, []string, int, error) { + const op = "iam.(Repository).UpdateUser" if user == nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: missing user %w", errors.ErrInvalidParameter) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing user") } if user.PublicId == "" { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: missing user public id %w", errors.ErrInvalidParameter) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } for _, f := range fieldMaskPaths { switch { case strings.EqualFold("name", f): case strings.EqualFold("description", f): default: - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: field: %s: %w", f, errors.ErrInvalidFieldMask) + return nil, nil, db.NoRowsAffected, errors.New(errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) } } var dbMask, nullFields []string @@ -80,13 +81,13 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32, nil, ) if len(dbMask) == 0 && len(nullFields) == 0 { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: %w", errors.ErrEmptyFieldMask) + return nil, nil, db.NoRowsAffected, errors.E(errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op)) } u := user.Clone().(*User) metadata, err := r.stdMetadata(ctx, u) if err != nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: error getting metadata for update: %w", err) + return nil, nil, db.NoRowsAffected, errors.Wrap(err, op) } metadata["op-type"] = []string{oplog.OpType_OP_TYPE_UPDATE.String()} @@ -100,12 +101,12 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32, scope, err := u.GetScope(ctx, r.reader) if err != nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: unable to get scope: %w", err) + return nil, nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get scope")) } oplogWrapper, err := r.kms.GetWrapper(ctx, scope.GetPublicId(), kms.KeyPurposeOplog) if err != nil { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: unable to get oplog wrapper: %w", err) + return nil, nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } dbOpts = append(dbOpts, db.WithOplog(oplogWrapper, metadata)) @@ -125,12 +126,12 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32, nullFields, dbOpts..., ) - if err == nil && rowsUpdated > 1 { - // return err, which will result in a rollback of the update - return stdErrors.New("error more than 1 resource would have been updated ") - } if err != nil { - return err + return errors.Wrap(err, op) + } + if rowsUpdated > 1 { + // return err, which will result in a rollback of the update + return errors.New(errors.MultipleRecords, op, "error more than 1 resource would have been updated") } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -142,25 +143,26 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, version uint32, } currentAccountIds, err = txRepo.ListUserAccounts(ctx, user.PublicId) if err != nil { - return fmt.Errorf("unable to retrieve current account ids after update: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids after update")) } return nil }, ) if err != nil { if errors.IsUniqueError(err) { - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: user %s already exists in org %s", user.Name, user.ScopeId) + return nil, nil, db.NoRowsAffected, errors.New(errors.NotUnique, op, fmt.Sprintf("user %s already exists in org %s", user.Name, user.ScopeId)) } - return nil, nil, db.NoRowsAffected, fmt.Errorf("update user: %w for %s", err, user.PublicId) + return nil, nil, db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", user.PublicId))) } - return returnedUser, currentAccountIds, rowsUpdated, err + return returnedUser, currentAccountIds, rowsUpdated, nil } // LookupUser will look up a user and its associated account ids in the // repository. If the user is not found, it will return nil, nil, nil. -func (r *Repository) LookupUser(ctx context.Context, userId string, opt ...Option) (*User, []string, error) { +func (r *Repository) LookupUser(ctx context.Context, userId string, _ ...Option) (*User, []string, error) { + const op = "iam.(Repository).LookupUser" if userId == "" { - return nil, nil, fmt.Errorf("lookup user: missing public id %w", errors.ErrInvalidParameter) + return nil, nil, errors.New(errors.InvalidParameter, op, "missing public id") } user := allocUser() @@ -169,41 +171,43 @@ func (r *Repository) LookupUser(ctx context.Context, userId string, opt ...Optio if errors.IsNotFoundError(err) { return nil, nil, nil } - return nil, nil, fmt.Errorf("lookup user: failed %w for %s", err, userId) + return nil, nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", userId))) } currentAccountIds, err := r.ListUserAccounts(ctx, userId) if err != nil { - return nil, nil, fmt.Errorf("lookup user: unable to retrieve current account ids: %w", err) + return nil, nil, errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids")) } return &user, currentAccountIds, nil } // DeleteUser will delete a user from the repository -func (r *Repository) DeleteUser(ctx context.Context, withPublicId string, opt ...Option) (int, error) { +func (r *Repository) DeleteUser(ctx context.Context, withPublicId string, _ ...Option) (int, error) { + const op = "iam.(Repository).DeleteUser" if withPublicId == "" { - return db.NoRowsAffected, fmt.Errorf("delete user: missing public id %w", errors.ErrInvalidParameter) + return db.NoRowsAffected, errors.New(errors.InvalidParameter, op, "missing public id") } user := allocUser() user.PublicId = withPublicId if err := r.reader.LookupByPublicId(ctx, &user); err != nil { - return db.NoRowsAffected, fmt.Errorf("delete user: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", withPublicId))) } rowsDeleted, err := r.delete(ctx, &user) if err != nil { - return db.NoRowsAffected, fmt.Errorf("delete user: failed %w for %s", err, withPublicId) + return db.NoRowsAffected, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("for %s", withPublicId))) } return rowsDeleted, nil } // ListUsers in an org and supports the WithLimit option. func (r *Repository) ListUsers(ctx context.Context, withOrgId string, opt ...Option) ([]*User, error) { + const op = "iam.(Repository).ListUsers" if withOrgId == "" { - return nil, fmt.Errorf("list users: missing org id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing org id") } var users []*User err := r.list(ctx, &users, "scope_id = ?", []interface{}{withOrgId}, opt...) if err != nil { - return nil, fmt.Errorf("list users: %w", err) + return nil, errors.Wrap(err, op) } return users, nil } @@ -215,26 +219,27 @@ func (r *Repository) ListUsers(ctx context.Context, withOrgId string, opt ...Opt // account. If a new user is auto vivified, then the WithName and // WithDescription options are supported as well. func (r *Repository) LookupUserWithLogin(ctx context.Context, accountId string, opt ...Option) (*User, error) { + const op = "iam.(Repository).LookupUserWithLogin" opts := getOpts(opt...) if accountId == "" { - return nil, fmt.Errorf("lookup user with login: missing account id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing account id") } u, err := r.getUserWithAccount(ctx, accountId) if err != nil { - return nil, fmt.Errorf("lookup user with login: %w", err) + return nil, errors.Wrap(err, op) } if u != nil { return u, nil } if !opts.withAutoVivify { - return nil, fmt.Errorf("lookup user with login: user not found for account %s: %w", accountId, errors.ErrRecordNotFound) + return nil, errors.New(errors.RecordNotFound, op, fmt.Sprintf("user not found for account %s", accountId)) } acct := allocAccount() acct.PublicId = accountId err = r.reader.LookupByPublicId(context.Background(), &acct) if err != nil { - return nil, fmt.Errorf("lookup user with login: unable to lookup account %s: %w", accountId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup account %s", accountId))) } metadata := oplog.Metadata{ @@ -246,7 +251,7 @@ func (r *Repository) LookupUserWithLogin(ctx context.Context, accountId string, oplogWrapper, err := r.kms.GetWrapper(ctx, acct.GetScopeId(), kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("lookup user with login: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } // We will create a new user and associate the user with the account @@ -260,21 +265,21 @@ func (r *Repository) LookupUserWithLogin(ctx context.Context, accountId string, msgs := make([]*oplog.Message, 0, 2) ticket, err := w.GetTicket(&acct) if err != nil { - return err + return errors.Wrap(err, op) } obtainedUser, err = NewUser(acct.ScopeId, opt...) if err != nil { - return err + return errors.Wrap(err, op) } id, err := newUserId() if err != nil { - return err + return errors.Wrap(err, op) } var createMsg oplog.Message obtainedUser.PublicId = id err = w.Create(ctx, obtainedUser, db.NewOplogMsg(&createMsg)) if err != nil { - return err + return errors.Wrap(err, op) } msgs = append(msgs, &createMsg) @@ -283,60 +288,59 @@ func (r *Repository) LookupUserWithLogin(ctx context.Context, accountId string, updateAcct.IamUserId = id updatedRows, err := w.Update(ctx, updateAcct, []string{"IamUserId"}, nil, db.NewOplogMsg(&updateMsg)) if err != nil { - return err + return errors.Wrap(err, op) } if updatedRows != 1 { - return fmt.Errorf("account update affected %d rows", updatedRows) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("account update affected %d rows", updatedRows)) } msgs = append(msgs, &updateMsg) if err := w.WriteOplogEntryWith(ctx, oplogWrapper, ticket, metadata, msgs); err != nil { - return err + return errors.Wrap(err, op) } return nil }, ) if err != nil { - return nil, fmt.Errorf("lookup user with login: unable to associate user and account: %w", err) + return nil, errors.Wrap(err, op) } return obtainedUser, nil } -func (r *Repository) getUserWithAccount(ctx context.Context, withAccountId string, opt ...Option) (*User, error) { +func (r *Repository) getUserWithAccount(ctx context.Context, withAccountId string, _ ...Option) (*User, error) { + const op = "iam.(Repository).getUserWithAccount" if withAccountId == "" { - return nil, fmt.Errorf("missing account id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing account id") } rows, err := r.reader.Query(ctx, whereUserAccount, []interface{}{withAccountId}) if err != nil { - return nil, fmt.Errorf("unable to query account %s", withAccountId) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to query account %s", withAccountId))) } defer rows.Close() u := allocUser() if rows.Next() { err = r.reader.ScanRows(rows, &u) if err != nil { - return nil, fmt.Errorf("unable to scan rows for account %s: %w", withAccountId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to scan rows for account %s", withAccountId))) } } else { if err := rows.Err(); err != nil { - return nil, fmt.Errorf("unable to get next account: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get next account")) } return nil, nil } - if err != nil { - return nil, fmt.Errorf("unable to get next row for accounts %s: %w", withAccountId, err) - } return &u, nil } // ListUserAccounts returns the account ids for the userId and supports the // WithLimit option. Returns nil, nil when no associated accounts are found. func (r *Repository) ListUserAccounts(ctx context.Context, userId string, opt ...Option) ([]string, error) { + const op = "iam.(Repository).ListUserAccounts" if userId == "" { - return nil, fmt.Errorf("list auth account ids: missing user id: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user id") } var accounts []*authAccount if err := r.list(ctx, &accounts, "iam_user_id = ?", []interface{}{userId}, opt...); err != nil { - return nil, fmt.Errorf("list auth account ids: %w", err) + return nil, errors.Wrap(err, op) } if len(accounts) == 0 { return nil, nil @@ -352,15 +356,16 @@ func (r *Repository) ListUserAccounts(ctx context.Context, userId string, opt .. // return a list of all associated account ids for the user. The accounts must // not already be associated with different users. No options are currently // supported. -func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, opt ...Option) ([]string, error) { +func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, _ ...Option) ([]string, error) { + const op = "iam.(Repository).AddUserAccounts" if userId == "" { - return nil, fmt.Errorf("associate accounts: missing user public id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user id") } if userVersion == 0 { - return nil, fmt.Errorf("associate accounts: missing user version %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user version") } if len(accountIds) == 0 { - return nil, fmt.Errorf("associate accounts: missing account id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing account ids") } user := allocUser() @@ -368,11 +373,11 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer err := r.reader.LookupById(ctx, &user) if err != nil { - return nil, fmt.Errorf("associate accounts: unable to lookup user %s: %w", userId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup user %s", userId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, user.ScopeId, kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("associate accounts: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentAccountIds []string @@ -383,7 +388,7 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer func(reader db.Reader, w db.Writer) error { userTicket, err := w.GetTicket(&user) if err != nil { - return fmt.Errorf("associate accounts: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedUser := allocUser() updatedUser.PublicId = userId @@ -391,13 +396,13 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer var userOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedUser, []string{"Version"}, nil, db.NewOplogMsg(&userOplogMsg), db.WithVersion(&userVersion)) if err != nil { - return fmt.Errorf("associate accounts: unable to update user version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get user version")) } if rowsUpdated != 1 { - return fmt.Errorf("associate accounts: updated user and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated user and %d rows updated", rowsUpdated)) } if err := associateUserWithAccounts(ctx, r.kms, reader, w, user.PublicId, accountIds); err != nil { - return fmt.Errorf("associate accounts: %w", err) + return errors.Wrap(err, op) } metadata := oplog.Metadata{ "op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()}, @@ -406,7 +411,7 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer "resource-public-id": []string{user.PublicId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, userTicket, metadata, []*oplog.Message{&userOplogMsg}); err != nil { - return fmt.Errorf("associate accounts: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -418,13 +423,13 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer } currentAccountIds, err = txRepo.ListUserAccounts(ctx, user.PublicId) if err != nil { - return fmt.Errorf("associate accounts: unable to retrieve current account ids after adds: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids after adds")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("associate accounts: error associating account ids: %w", err) + return nil, errors.Wrap(err, op) } return currentAccountIds, nil } @@ -433,26 +438,27 @@ func (r *Repository) AddUserAccounts(ctx context.Context, userId string, userVer // return a list of all associated account ids for the user. The accounts must // not be associated with different users. No options are currently // supported. -func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, opt ...Option) ([]string, error) { +func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, _ ...Option) ([]string, error) { + const op = "iam.(Repository).DeleteUserAccounts" if userId == "" { - return nil, fmt.Errorf("disassociate accounts: missing user public id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing public id") } if userVersion == 0 { - return nil, fmt.Errorf("disassociate accounts: missing user version %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing user version") } if len(accountIds) == 0 { - return nil, fmt.Errorf("disassociate accounts: missing account id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing account ids") } user := allocUser() user.PublicId = userId err := r.reader.LookupById(ctx, &user) if err != nil { - return nil, fmt.Errorf("disassociate accounts: unable to lookup user %s: %w", userId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup user %s", userId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, user.ScopeId, kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("disassociate accounts: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentAccountIds []string @@ -463,7 +469,7 @@ func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, user func(reader db.Reader, w db.Writer) error { userTicket, err := w.GetTicket(&user) if err != nil { - return fmt.Errorf("disassociate accounts: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedUser := allocUser() updatedUser.PublicId = userId @@ -471,13 +477,13 @@ func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, user var userOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedUser, []string{"Version"}, nil, db.NewOplogMsg(&userOplogMsg), db.WithVersion(&userVersion)) if err != nil { - return fmt.Errorf("disassociate accounts: unable to update user version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update user version")) } if rowsUpdated != 1 { - return fmt.Errorf("disassociate accounts: updated user and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated user and %d rows updated", rowsUpdated)) } if err := dissociateUserFromAccounts(ctx, r.kms, reader, w, user.PublicId, accountIds); err != nil { - return fmt.Errorf("disassociate accounts: %w", err) + return errors.Wrap(err, op) } metadata := oplog.Metadata{ "op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()}, @@ -486,7 +492,7 @@ func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, user "resource-public-id": []string{user.PublicId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, userTicket, metadata, []*oplog.Message{&userOplogMsg}); err != nil { - return fmt.Errorf("disassociate accounts: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -498,13 +504,13 @@ func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, user } currentAccountIds, err = txRepo.ListUserAccounts(ctx, user.PublicId) if err != nil { - return fmt.Errorf("disassociate accounts: unable to retrieve current account ids after adds: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids after adds")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("disassociate accounts: error associating account ids: %w", err) + return nil, errors.Wrap(err, op) } return currentAccountIds, nil } @@ -513,23 +519,24 @@ func (r *Repository) DeleteUserAccounts(ctx context.Context, userId string, user // return a list of all associated account ids for the user. The accounts must // not already be associated with different users. No options are currently // supported. -func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, opt ...Option) ([]string, error) { +func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVersion uint32, accountIds []string, _ ...Option) ([]string, error) { + const op = "iam.(Repository).SetUserAccounts" if userId == "" { - return nil, fmt.Errorf("set associated accounts: missing user public id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing public id") } if userVersion == 0 { - return nil, fmt.Errorf("set associated accounts: missing user version %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing version") } user := allocUser() user.PublicId = userId err := r.reader.LookupById(ctx, &user) if err != nil { - return nil, fmt.Errorf("set associated accounts: unable to lookup user %s: %w", userId, err) + return nil, errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup user %s", userId))) } oplogWrapper, err := r.kms.GetWrapper(ctx, user.ScopeId, kms.KeyPurposeOplog) if err != nil { - return nil, fmt.Errorf("set associated accounts: unable to get oplog wrapper: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } var currentAccountIds []string _, err = r.writer.DoTx( @@ -539,7 +546,7 @@ func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVer func(reader db.Reader, w db.Writer) error { associateIds, disassociateIds, err := associationChanges(ctx, reader, userId, accountIds) if err != nil { - return fmt.Errorf("set associated accounts: unable to determine changes: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to determine changes")) } // just in case we've got nothing to do... @@ -554,13 +561,13 @@ func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVer } currentAccountIds, err = txRepo.ListUserAccounts(ctx, userId) if err != nil { - return fmt.Errorf("set associated accounts: unable to retrieve current account ids after set: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids after set")) } return nil } userTicket, err := w.GetTicket(&user) if err != nil { - return fmt.Errorf("set associated accounts: unable to get ticket: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get ticket")) } updatedUser := allocUser() updatedUser.PublicId = userId @@ -568,21 +575,21 @@ func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVer var userOplogMsg oplog.Message rowsUpdated, err := w.Update(ctx, &updatedUser, []string{"Version"}, nil, db.NewOplogMsg(&userOplogMsg), db.WithVersion(&userVersion)) if err != nil { - return fmt.Errorf("set associated accounts: unable to update user version: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to update user version")) } if rowsUpdated != 1 { - return fmt.Errorf("set associated accounts: updated user and %d rows updated", rowsUpdated) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("updated user and %d rows updated", rowsUpdated)) } if len(associateIds) > 0 { if err := associateUserWithAccounts(ctx, r.kms, reader, w, userId, associateIds); err != nil { - return fmt.Errorf("set associated accounts: unable to associate ids: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to associate ids")) } } if len(disassociateIds) > 0 { if err := dissociateUserFromAccounts(ctx, r.kms, reader, w, userId, disassociateIds); err != nil { - return fmt.Errorf("set associated accounts: unable to disassociate ids: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to disassociate ids")) } } @@ -593,7 +600,7 @@ func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVer "resource-public-id": []string{userId}, } if err := w.WriteOplogEntryWith(ctx, oplogWrapper, userTicket, metadata, []*oplog.Message{&userOplogMsg}); err != nil { - return fmt.Errorf("set associated accounts: unable to write oplog: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to write oplog")) } // we need a new repo, that's using the same reader/writer as this TxHandler txRepo := &Repository{ @@ -605,34 +612,35 @@ func (r *Repository) SetUserAccounts(ctx context.Context, userId string, userVer } currentAccountIds, err = txRepo.ListUserAccounts(ctx, user.PublicId) if err != nil { - return fmt.Errorf("set associated accounts: unable to retrieve current account ids after set: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to retrieve current account ids after set")) } return nil }, ) if err != nil { - return nil, fmt.Errorf("set associated accounts: %w", err) + return nil, errors.Wrap(err, op) } return currentAccountIds, nil } // associateUserWithAccounts will associate the accounts (accountIds) with // the user (userId) within the writer's database -func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db.Reader, writer db.Writer, userId string, accountIds []string, opt ...Option) error { +func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db.Reader, writer db.Writer, userId string, accountIds []string, _ ...Option) error { + const op = "iam.associateUserWithAccounts" if repoKms == nil { - return fmt.Errorf("associate user with accounts: kms is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil kms") } if reader == nil { - return fmt.Errorf("associate user with accounts: db reader is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil reader") } if writer == nil { - return fmt.Errorf("associate user with accounts: db writer is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil writer") } if userId == "" { - return fmt.Errorf("associate user with accounts: user id is empty: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing user id") } if len(accountIds) == 0 { - return fmt.Errorf("associate user with accounts: missing account id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing account ids") } authAccounts := make([]*authAccount, 0, len(accountIds)) for _, accountId := range accountIds { @@ -640,10 +648,10 @@ func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db. acct.PublicId = accountId err := reader.LookupByPublicId(context.Background(), &acct) if err != nil { - return fmt.Errorf("associate user with accounts: unable to lookup account %s: %w", accountId, err) + return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup account %s", accountId))) } if acct.IamUserId != "" && acct.IamUserId != userId { - return fmt.Errorf("associate user with accounts: %s account is associated with a user %s: %w", accountId, acct.IamUserId, errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s account is associated with a user %s", accountId, acct.IamUserId)) } authAccounts = append(authAccounts, &acct) } @@ -652,7 +660,7 @@ func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db. // wrapper could be different for each authAccount depending on it's scope oplogWrapper, err := repoKms.GetWrapper(ctx, aa.GetScopeId(), kms.KeyPurposeOplog) if err != nil { - return fmt.Errorf("associate user with accounts: unable to get oplog wrapper: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } metadata := oplog.Metadata{ @@ -666,13 +674,13 @@ func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db. updatedAcct.IamUserId = userId updatedRows, err = writer.Update(ctx, updatedAcct, []string{"IamUserId"}, nil, db.WithOplog(oplogWrapper, metadata), db.WithWhere("iam_user_id is NULL or iam_user_id = ?", userId)) if err != nil { - return fmt.Errorf("associate user with accouts: failed to associate %s account: %w", aa.PublicId, err) + return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed to associate %s account", aa.PublicId))) } if updatedRows == 0 { - return fmt.Errorf("associate user with accounts: failed to associate %s account: it is already associated with another user", aa.PublicId) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("failed to associate %s account: it is already associated with another user", aa.PublicId)) } if updatedRows > 1 { - return fmt.Errorf("associate user with accounts: failed to associate %s account: would have updated too many accounts %d", aa.PublicId, updatedRows) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("failed to associate %s account: would have updated too many accounts %d", aa.PublicId, updatedRows)) } } return nil @@ -681,21 +689,22 @@ func associateUserWithAccounts(ctx context.Context, repoKms *kms.Kms, reader db. // dissociateUserFromAccounts will dissociate a user with its existing accounts // (accountIds). An error is returned if account is associated with a different // user. No options are currently supported. -func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db.Reader, writer db.Writer, userId string, accountIds []string, opt ...Option) error { +func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db.Reader, writer db.Writer, userId string, accountIds []string, _ ...Option) error { + const op = "iam.dissociateUserFromAccounts" if repoKms == nil { - return fmt.Errorf("dissociate user from accounts: kms is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil kms") } if reader == nil { - return fmt.Errorf("dissociate user from accounts: db reader is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil reader") } if writer == nil { - return fmt.Errorf("dissociate user from accounts: db writer is nil: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "nil writer") } if userId == "" { - return fmt.Errorf("dissociate user from accounts: missing user public id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing public id") } if len(accountIds) == 0 { - return fmt.Errorf("dissociate user from accounts: missing account id %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing account ids") } authAccounts := make([]*authAccount, 0, len(accountIds)) for _, accountId := range accountIds { @@ -703,10 +712,10 @@ func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db acct.PublicId = accountId err := reader.LookupByPublicId(context.Background(), &acct) if err != nil { - return fmt.Errorf("dissociate user from accounts: unable to lookup account %s: %w", accountId, err) + return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("unable to lookup account %s", accountId))) } if acct.IamUserId != userId { - return fmt.Errorf("dissociate user from accounts: %s account is not associated with user %s: %w", accountId, userId, errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, fmt.Sprintf("%s account is not associated with user %s", accountId, userId)) } authAccounts = append(authAccounts, &acct) } @@ -715,7 +724,7 @@ func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db // wrapper could be different for each authAccount depending on it's scope oplogWrapper, err := repoKms.GetWrapper(ctx, aa.GetScopeId(), kms.KeyPurposeOplog) if err != nil { - return fmt.Errorf("dissociate user from accounts: unable to get oplog wrapper: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to get oplog wrapper")) } metadata := oplog.Metadata{ "resource-public-id": []string{aa.PublicId}, @@ -729,13 +738,13 @@ func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db // update IamUserId to null updatedRows, err = writer.Update(ctx, updatedAcct, nil, []string{"IamUserId"}, db.WithOplog(oplogWrapper, metadata), db.WithWhere("iam_user_id is NULL or iam_user_id = ?", userId)) if err != nil { - return fmt.Errorf("dissociate user from accounts: failed to disassociate %s account: %w", aa.PublicId, err) + return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("failed to disassociate %s account", aa.PublicId))) } if updatedRows == 0 { - return fmt.Errorf("dissociate user from accounts: failed to disassociate %s account: it is associated with another user", aa.PublicId) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("failed to disassociate %s account: it is already associated with another user", aa.PublicId)) } if updatedRows > 1 { - return fmt.Errorf("dissociate user from accounts: failed to disassociate %s account: would have updated too many accounts %d", aa.PublicId, updatedRows) + return errors.New(errors.MultipleRecords, op, fmt.Sprintf("failed to disassociate %s account: would have updated too many accounts %d", aa.PublicId, updatedRows)) } } return nil @@ -743,6 +752,7 @@ func dissociateUserFromAccounts(ctx context.Context, repoKms *kms.Kms, reader db // associationChanges returns two slices: accounts to associate and disassociate func associationChanges(ctx context.Context, reader db.Reader, userId string, accountIds []string) ([]string, []string, error) { + const op = "iam.associationChanges" var inClauseSpots []string // starts at 2 because there is already a $1 in the query for i := 2; i < len(accountIds)+2; i++ { @@ -761,7 +771,7 @@ func associationChanges(ctx context.Context, reader db.Reader, userId string, ac } rows, err := reader.Query(ctx, query, params) if err != nil { - return nil, nil, fmt.Errorf("changes: query failed: %w", err) + return nil, nil, errors.Wrap(err, op) } defer rows.Close() @@ -773,14 +783,14 @@ func associationChanges(ctx context.Context, reader db.Reader, userId string, ac for rows.Next() { var chg change if err := reader.ScanRows(rows, &chg); err != nil { - return nil, nil, fmt.Errorf("changes: scan row failed: %w", err) + return nil, nil, errors.Wrap(err, op) } changes = append(changes, &chg) } var associateIds, disassociateIds []string for _, c := range changes { if c.AccountId == "" { - return nil, nil, fmt.Errorf("changes: missing account id in change result") + return nil, nil, errors.New(errors.InvalidParameter, op, "missing account id in change result") } switch c.Action { case "associate": @@ -788,9 +798,8 @@ func associationChanges(ctx context.Context, reader db.Reader, userId string, ac case "disassociate": disassociateIds = append(disassociateIds, c.AccountId) default: - return nil, nil, fmt.Errorf("changes: unknown action %s for %s", c.Action, c.AccountId) + return nil, nil, errors.New(errors.InvalidParameter, op, fmt.Sprintf("unknown action %s for %s", c.Action, c.AccountId)) } - } return associateIds, disassociateIds, nil } diff --git a/internal/iam/repository_user_test.go b/internal/iam/repository_user_test.go index 9f763850a..94ee4a897 100644 --- a/internal/iam/repository_user_test.go +++ b/internal/iam/repository_user_test.go @@ -59,7 +59,7 @@ func TestRepository_CreateUser(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "create user: error getting metadata for create: unable to get scope for standard metadata: db.LookupWhere: record not found", + wantErrMsg: "iam.(Repository).create: error getting metadata: iam.(Repository).stdMetadata: unable to get scope: iam.LookupScope: db.LookupWhere: record not found", }, { name: "dup-name", @@ -72,7 +72,7 @@ func TestRepository_CreateUser(t *testing.T) { }, wantDup: true, wantErr: true, - wantErrMsg: "create user: user %s already exists in org %s", + wantErrMsg: "iam.(Repository).CreateUser: user %s already exists in org %s: integrity violation: error #1002", }, } for _, tt := range tests { @@ -170,7 +170,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update user: db.DoTx: db.Update: db.lookupAfterWrite: db.LookupById: record not found", + wantErrMsg: "iam.(Repository).UpdateUser: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100", wantIsErr: errors.RecordNotFound, }, { @@ -204,7 +204,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update user: empty field mask", + wantErrMsg: "iam.(Repository).UpdateUser: empty field mask, parameter violation: error #104", }, { name: "nil-fieldmask", @@ -215,7 +215,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update user: empty field mask", + wantErrMsg: "iam.(Repository).UpdateUser: empty field mask, parameter violation: error #104", }, { name: "read-only-fields", @@ -226,7 +226,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update user: field: CreateTime: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateUser: invalid field mask: CreateTime: parameter violation: error #103", }, { name: "unknown-fields", @@ -237,7 +237,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "update user: field: Alice: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateUser: invalid field mask: Alice: parameter violation: error #103", }, { name: "no-public-id", @@ -248,7 +248,7 @@ func TestRepository_UpdateUser(t *testing.T) { PublicId: pubId(""), }, wantErr: true, - wantErrMsg: "update user: missing user public id invalid parameter", + wantErrMsg: "iam.(Repository).UpdateUser: missing public id: parameter violation: error #100", wantRowsUpdate: 0, }, { @@ -259,7 +259,7 @@ func TestRepository_UpdateUser(t *testing.T) { ScopeId: proj.PublicId, }, wantErr: true, - wantErrMsg: "update user: field: ScopeId: invalid field mask", + wantErrMsg: "iam.(Repository).UpdateUser: invalid field mask: ScopeId: parameter violation: error #103", }, { name: "empty-scope-id-with-name-mask", @@ -280,7 +280,7 @@ func TestRepository_UpdateUser(t *testing.T) { }, wantErr: true, wantDup: true, - wantErrMsg: `update user: user %s already exists in org %s`, + wantErrMsg: `iam.(Repository).UpdateUser: user %s already exists in org %s: integrity violation: error #1002`, }, { name: "modified-scope", @@ -291,7 +291,7 @@ func TestRepository_UpdateUser(t *testing.T) { opt: []Option{WithSkipVetForWrite(true)}, }, wantErr: true, - wantErrMsg: `db.DoTx: db.Update: immutable column: iam_user.scope_id:`, + wantErrMsg: `iam.(Repository).update: db.DoTx: iam.(Repository).update: db.Update: immutable column: iam_user.scope_id: integrity violation: error #1003`, directUpdate: true, }, } @@ -418,7 +418,7 @@ func TestRepository_DeleteUser(t *testing.T) { }, wantRowsDeleted: 0, wantErr: true, - wantErrMsg: "delete user: missing public id invalid parameter", + wantErrMsg: "iam.(Repository).DeleteUser: missing public id: parameter violation: error #100", }, { name: "not-found", @@ -434,7 +434,7 @@ func TestRepository_DeleteUser(t *testing.T) { }, wantRowsDeleted: 1, wantErr: true, - wantErrMsg: "delete user: failed db.LookupById: record not found", + wantErrMsg: "db.LookupById: record not found, search issue: error #1100", }, } for _, tt := range tests { diff --git a/internal/iam/resource.go b/internal/iam/resource.go index 8a5df0f3f..9401557b2 100644 --- a/internal/iam/resource.go +++ b/internal/iam/resource.go @@ -2,7 +2,6 @@ package iam import ( "context" - stderrors "errors" "fmt" "github.com/hashicorp/boundary/internal/db" @@ -49,28 +48,29 @@ type ResourceWithScope interface { // LookupScope looks up the resource's scope func LookupScope(ctx context.Context, reader db.Reader, resource ResourceWithScope) (*Scope, error) { + const op = "iam.LookupScope" if reader == nil { - return nil, stderrors.New("error reader is nil for LookupScope") + return nil, errors.New(errors.InvalidParameter, op, "nil reader") } if resource == nil { - return nil, stderrors.New("error resource is nil for LookupScope") + return nil, errors.New(errors.InvalidParameter, op, "missing resource") } if resource.GetPublicId() == "" { - return nil, fmt.Errorf("LookupScope: scope id is unset %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing public id") } if resource.GetScopeId() == "" { // try to retrieve it from db with it's scope id if err := reader.LookupById(ctx, resource); err != nil { - return nil, fmt.Errorf("unable to get resource by public id: %w", err) + return nil, errors.Wrap(err, op) } // if it's still not set after getting it from the db... if resource.GetScopeId() == "" { - return nil, stderrors.New("error scope is unset for LookupScope") + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } } var p Scope if err := reader.LookupWhere(ctx, &p, "public_id = ?", resource.GetScopeId()); err != nil { - return nil, err + return nil, errors.Wrap(err, op) } return &p, nil } @@ -89,7 +89,7 @@ func validateScopeForWrite(ctx context.Context, r db.Reader, resource ResourceWi if errors.IsNotFoundError(err) { return errors.New(errors.RecordNotFound, op, "scope is not found") } - return err + return errors.Wrap(err, op) } validScopeType := false for _, t := range resource.validScopeTypes() { diff --git a/internal/iam/resource_test.go b/internal/iam/resource_test.go index 7b587b0fb..8bd9f69f8 100644 --- a/internal/iam/resource_test.go +++ b/internal/iam/resource_test.go @@ -40,15 +40,15 @@ func Test_LookupScope(t *testing.T) { s, err := LookupScope(context.Background(), nil, user) require.Error(err) assert.Nil(s) - assert.Equal("error reader is nil for LookupScope", err.Error()) + assert.Equal("iam.LookupScope: nil reader: parameter violation: error #100", err.Error()) s, err = LookupScope(context.Background(), w, nil) assert.Nil(s) - assert.Equal("error resource is nil for LookupScope", err.Error()) + assert.Equal("iam.LookupScope: missing resource: parameter violation: error #100", err.Error()) user2 := allocUser() s, err = LookupScope(context.Background(), w, &user2) assert.Nil(s) - assert.Contains(err.Error(), "LookupScope: scope id is unset invalid parameter") + assert.Contains(err.Error(), "iam.LookupScope: missing public id: parameter violation: error #100") }) } diff --git a/internal/iam/role.go b/internal/iam/role.go index c2fd19db0..32fc12b54 100644 --- a/internal/iam/role.go +++ b/internal/iam/role.go @@ -2,8 +2,6 @@ package iam import ( "context" - stderrors "errors" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -32,8 +30,9 @@ var _ db.VetForWriter = (*Role)(nil) // NewRole creates a new in memory role with a scope (project/org) // allowed options include: withDescripion, WithName, withGrantScopeId. func NewRole(scopeId string, opt ...Option) (*Role, error) { + const op = "iam.NewRole" if scopeId == "" { - return nil, fmt.Errorf("new role: missing scope id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } opts := getOpts(opt...) r := &Role{ @@ -63,11 +62,12 @@ func (r *Role) Clone() interface{} { // VetForWrite implements db.VetForWrite() interface. func (role *Role) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(Role).VetForWrite" if role.PublicId == "" { - return stderrors.New("error public id is empty string for role write") + return errors.New(errors.InvalidParameter, op, "missing public id") } if err := validateScopeForWrite(ctx, r, role, opType, opt...); err != nil { - return err + return errors.Wrap(err, op) } return nil } diff --git a/internal/iam/role_grant.go b/internal/iam/role_grant.go index f83fbdbcd..33f325bfd 100644 --- a/internal/iam/role_grant.go +++ b/internal/iam/role_grant.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -24,12 +23,13 @@ var _ Cloneable = (*RoleGrant)(nil) var _ db.VetForWriter = (*RoleGrant)(nil) // NewRoleGrant creates a new in memory role grant -func NewRoleGrant(roleId string, grant string, opt ...Option) (*RoleGrant, error) { +func NewRoleGrant(roleId string, grant string, _ ...Option) (*RoleGrant, error) { + const op = "iam.NewRoleGrant" if roleId == "" { - return nil, fmt.Errorf("new role grant: role id is not set: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing role id") } if grant == "" { - return nil, fmt.Errorf("new role grant: grant is empty: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing grant") } // Validate that the grant parses successfully. Note that we fake the scope @@ -37,7 +37,7 @@ func NewRoleGrant(roleId string, grant string, opt ...Option) (*RoleGrant, error // checking time and we just care that it parses correctly. perm, err := perms.Parse("o_abcd1234", grant) if err != nil { - return nil, fmt.Errorf("new role grant: error parsing grant string: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("parsing grant string")) } rg := &RoleGrant{ RoleGrant: &store.RoleGrant{ @@ -64,9 +64,10 @@ func (g *RoleGrant) Clone() interface{} { } // VetForWrite implements db.VetForWrite() interface -func (g *RoleGrant) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { +func (g *RoleGrant) VetForWrite(_ context.Context, _ db.Reader, _ db.OpType, _ ...db.Option) error { + const op = "iam.(RoleGrant).VetForWrite" if g.RawGrant == "" { - return fmt.Errorf("vet role grant for writing: grant is empty: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing grant") } // Validate that the grant parses successfully. Note that we fake the scope @@ -76,11 +77,11 @@ func (g *RoleGrant) VetForWrite(ctx context.Context, r db.Reader, opType db.OpTy // anyways because it should still be part of the vetting process. perm, err := perms.Parse("o_abcd1234", g.RawGrant) if err != nil { - return fmt.Errorf("vet role grant for writing: error parsing grant string: %w", err) + return errors.Wrap(err, op, errors.WithMsg("parsing grant string")) } canonical := perm.CanonicalString() if g.CanonicalGrant != "" && g.CanonicalGrant != canonical { - return fmt.Errorf("vet role grant for writing: existing canonical grant and derived one do not match: %w", err) + return errors.Wrap(err, op, errors.WithMsg("existing canonical grant and derived one do not match")) } g.CanonicalGrant = canonical diff --git a/internal/iam/role_grant_test.go b/internal/iam/role_grant_test.go index 0f7571ab0..c67f01ca2 100644 --- a/internal/iam/role_grant_test.go +++ b/internal/iam/role_grant_test.go @@ -31,7 +31,7 @@ func TestRoleGrant_Create(t *testing.T) { args args want *RoleGrant wantErr bool - wantIsErr error + wantIsErr errors.Code create bool }{ { @@ -41,7 +41,7 @@ func TestRoleGrant_Create(t *testing.T) { grant: "id=*;type=*;actions=*", }, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, }, { name: "nil-grant", @@ -50,7 +50,7 @@ func TestRoleGrant_Create(t *testing.T) { grant: "", }, wantErr: true, - wantIsErr: errors.ErrInvalidParameter, + wantIsErr: errors.InvalidParameter, create: true, }, { @@ -91,7 +91,7 @@ func TestRoleGrant_Create(t *testing.T) { got, err := NewRoleGrant(tt.args.roleId, tt.args.grant, tt.args.opt...) if tt.wantErr { require.Error(err) - assert.True(errors.Is(err, tt.wantIsErr)) + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) return } require.NoError(err) diff --git a/internal/iam/role_test.go b/internal/iam/role_test.go index 0c8e66931..3ee0e2e5e 100644 --- a/internal/iam/role_test.go +++ b/internal/iam/role_test.go @@ -34,7 +34,7 @@ func TestNewRole(t *testing.T) { args args wantErr bool wantErrMsg string - wantIsErr error + wantIsErr errors.Code wantName string wantDescription string }{ @@ -71,8 +71,8 @@ func TestNewRole(t *testing.T) { opt: []Option{WithName(id)}, }, wantErr: true, - wantErrMsg: "new role: missing scope id invalid parameter:", - wantIsErr: errors.ErrInvalidParameter, + wantErrMsg: "iam.NewRole: missing scope id: parameter violation: error #100", + wantIsErr: errors.InvalidParameter, }, } for _, tt := range tests { @@ -82,8 +82,8 @@ func TestNewRole(t *testing.T) { if tt.wantErr { require.Error(err) assert.Contains(err.Error(), tt.wantErrMsg) - if tt.wantIsErr != nil { - assert.True(errors.Is(err, tt.wantIsErr)) + if tt.wantIsErr != 0 { + assert.True(errors.Match(errors.T(tt.wantIsErr), err)) } return } @@ -171,7 +171,7 @@ func Test_RoleCreate(t *testing.T) { }(), }, wantErr: true, - wantErrMsg: "db.Create: vet for write failed: iam.validateScopeForWrite: scope is not found: search issue: error #1100", + wantErrMsg: "db.Create: iam.(Role).VetForWrite: iam.validateScopeForWrite: scope is not found: search issue: error #1100", }, } @@ -252,7 +252,7 @@ func Test_RoleUpdate(t *testing.T) { scopeIdOverride: org.PublicId, }, wantErr: true, - wantErrMsg: "db.Update: vet for write failed: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", + wantErrMsg: "db.Update: iam.(Role).VetForWrite: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", }, { name: "proj-scope-id-not-in-mask", diff --git a/internal/iam/scope.go b/internal/iam/scope.go index 596d5b249..839ecddc2 100644 --- a/internal/iam/scope.go +++ b/internal/iam/scope.go @@ -2,8 +2,6 @@ package iam import ( "context" - stderrors "errors" - "fmt" "strings" "github.com/hashicorp/boundary/internal/db" @@ -41,11 +39,12 @@ func NewOrg(opt ...Option) (*Scope, error) { } func NewProject(orgPublicId string, opt ...Option) (*Scope, error) { + const op = "iam.NewProject" org := allocScope() org.PublicId = orgPublicId p, err := newScope(&org, opt...) if err != nil { - return nil, fmt.Errorf("error creating new project: %w", err) + return nil, errors.Wrap(err, op) } return p, nil } @@ -55,8 +54,9 @@ func NewProject(orgPublicId string, opt ...Option) (*Scope, error) { // specifies the Scope's parent and must be filled in. The type of the parent is // used to determine the type of the child. func newScope(parent *Scope, opt ...Option) (*Scope, error) { + const op = "iam.newScope" if parent == nil || parent.PublicId == "" { - return nil, fmt.Errorf("new scope: child scope is missing its parent: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "child scope is missing its parent") } var typ scope.Type switch { @@ -66,7 +66,7 @@ func newScope(parent *Scope, opt ...Option) (*Scope, error) { typ = scope.Project } if typ == scope.Unknown { - return nil, fmt.Errorf("new scope: unknown type of scope to create: %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "unknown scope type") } opts := getOpts(opt...) @@ -100,41 +100,42 @@ func (s *Scope) Clone() interface{} { // this function is intended to be callled by a db.Writer (Create and Update) to validate // the scope before writing it to the db. func (s *Scope) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(Scope).VetForWrite" if s.Type == scope.Unknown.String() { - return stderrors.New("unknown scope type for scope write") + return errors.New(errors.InvalidParameter, op, "unknown scope type") } if s.PublicId == "" { - return stderrors.New("public id is empty string for scope write") + return errors.New(errors.InvalidParameter, op, "missing public id") } if opType == db.UpdateOp { dbOptions := db.GetOpts(opt...) for _, path := range dbOptions.WithFieldMaskPaths { switch path { case "ParentId": - return stderrors.New("you cannot change a scope's parent") + return errors.New(errors.InvalidParameter, op, "you cannot change a scope's parent") case "Type": - return stderrors.New("you cannot change a scope's type") + return errors.New(errors.InvalidParameter, op, "you cannot change a scope's type") } } } if opType == db.CreateOp { switch { case s.Type == scope.Global.String(): - return stderrors.New("global scope cannot be created") + return errors.New(errors.InvalidParameter, op, "you cannot create a global scope") case s.ParentId == "": - return stderrors.New("scope must have a parent") + return errors.New(errors.InvalidParameter, op, "scope must have a parent") case s.Type == scope.Org.String(): if s.ParentId != "global" { - return stderrors.New(`org's parent must be "global"`) + return errors.New(errors.InvalidParameter, op, `org's parent must be "global"`) } case s.Type == scope.Project.String(): parentScope := allocScope() parentScope.PublicId = s.ParentId if err := r.LookupByPublicId(ctx, &parentScope, opt...); err != nil { - return fmt.Errorf("unable to verify project's org scope: %w", err) + return errors.Wrap(err, op, errors.WithMsg("unable to verify project's org scope")) } if parentScope.Type != scope.Org.String() { - return stderrors.New("project parent scope is not an org") + return errors.New(errors.InvalidParameter, op, "project parent scope is not an org") } } } @@ -153,15 +154,16 @@ func (*Scope) Actions() map[string]action.Type { // GetScope returns the scope for the "scope" if there is one defined func (s *Scope) GetScope(ctx context.Context, r db.Reader) (*Scope, error) { + const op = "iam.(Scope).GetScope" if r == nil { - return nil, stderrors.New("error db is nil for get scope") + return nil, errors.New(errors.InvalidParameter, op, "nil reader") } if s.PublicId == "" { - return nil, stderrors.New("unable to get scope with unset public id") + return nil, errors.New(errors.InvalidParameter, op, "missing public id") } if s.Type == "" && s.ParentId == "" { if err := r.LookupByPublicId(ctx, s); err != nil { - return nil, fmt.Errorf("unable to get scope by public id: %w", err) + return nil, errors.Wrap(err, op) } } // HANDLE_GLOBAL @@ -179,11 +181,11 @@ func (s *Scope) GetScope(ctx context.Context, r db.Reader) (*Scope, error) { // handled at HANDLE_GLOBAL where := "public_id in (select parent_id from iam_scope where public_id = ?)" if err := r.LookupWhere(ctx, &p, where, s.PublicId); err != nil { - return nil, fmt.Errorf("unable to lookup parent public id from public id: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to lookup parent public id from public id")) } default: if err := r.LookupWhere(ctx, &p, "public_id = ?", s.ParentId); err != nil { - return nil, fmt.Errorf("unable to lookup parent from public id: %w", err) + return nil, errors.Wrap(err, op, errors.WithMsg("unable to lookup parent from public id")) } } return &p, nil diff --git a/internal/iam/scope_test.go b/internal/iam/scope_test.go index 2494a5229..a49dd7a0e 100644 --- a/internal/iam/scope_test.go +++ b/internal/iam/scope_test.go @@ -42,14 +42,14 @@ func TestScope_New(t *testing.T) { s, err := newScope(nil) require.Error(err) require.Nil(s) - assert.Contains(err.Error(), "new scope: child scope is missing its parent: invalid parameter") + assert.Contains(err.Error(), "iam.newScope: child scope is missing its parent: parameter violation: error #100") }) t.Run("proj-scope-with-no-org", func(t *testing.T) { assert, require := assert.New(t), require.New(t) s, err := NewProject("") require.Error(err) require.Nil(s) - assert.Contains(err.Error(), "error creating new project: new scope: child scope is missing its parent: invalid parameter") + assert.Contains(err.Error(), "iam.NewProject: iam.newScope: child scope is missing its parent: parameter violation: error #100") }) } func TestScope_Create(t *testing.T) { @@ -216,7 +216,7 @@ func TestScope_GlobalErrors(t *testing.T) { s.PublicId = "global" err := s.VetForWrite(context.Background(), nil, db.CreateOp) require.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "global scope cannot be created")) + assert.True(t, strings.Contains(err.Error(), "iam.(Scope).VetForWrite: you cannot create a global scope: parameter violation: error #100")) }) t.Run("check org parent at vet time", func(t *testing.T) { // Org must have global parent diff --git a/internal/iam/user.go b/internal/iam/user.go index b66726d56..830069ce7 100644 --- a/internal/iam/user.go +++ b/internal/iam/user.go @@ -2,7 +2,6 @@ package iam import ( "context" - "fmt" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -32,9 +31,10 @@ var _ db.VetForWriter = (*User)(nil) // WithName - to specify the user's friendly name and WithDescription - to // specify a user description func NewUser(scopeId string, opt ...Option) (*User, error) { + const op = "iam.NewUser" opts := getOpts(opt...) if scopeId == "" { - return nil, fmt.Errorf("new user: missing scope id %w", errors.ErrInvalidParameter) + return nil, errors.New(errors.InvalidParameter, op, "missing scope id") } u := &User{ User: &store.User{ @@ -63,11 +63,12 @@ func (u *User) Clone() interface{} { // VetForWrite implements db.VetForWrite() interface and validates the user // before it's written. func (u *User) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error { + const op = "iam.(User).VetForWrite" if u.PublicId == "" { - return fmt.Errorf("user vet for write: missing public id: %w", errors.ErrInvalidParameter) + return errors.New(errors.InvalidParameter, op, "missing public id") } if err := validateScopeForWrite(ctx, r, u, opType, opt...); err != nil { - return err + return errors.Wrap(err, op) } return nil } diff --git a/internal/iam/user_test.go b/internal/iam/user_test.go index 28f67d7ac..0a267ed93 100644 --- a/internal/iam/user_test.go +++ b/internal/iam/user_test.go @@ -55,7 +55,7 @@ func TestNewUser(t *testing.T) { opt: []Option{WithName(id)}, }, wantErr: true, - wantErrMsg: "new user: missing scope id invalid parameter", + wantErrMsg: "iam.NewUser: missing scope id: parameter violation: error #100", }, } for _, tt := range tests { @@ -123,7 +123,7 @@ func Test_UserCreate(t *testing.T) { user.PublicId = id err = w.Create(context.Background(), user) require.Error(err) - assert.Equal("db.Create: vet for write failed: iam.validateScopeForWrite: scope is not found: search issue: error #1100", err.Error()) + assert.Equal("db.Create: iam.(User).VetForWrite: iam.validateScopeForWrite: scope is not found: search issue: error #1100", err.Error()) }) } @@ -168,7 +168,7 @@ func Test_UserUpdate(t *testing.T) { ScopeId: proj.PublicId, }, wantErr: true, - wantErrMsg: "db.Update: vet for write failed: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", + wantErrMsg: "db.Update: iam.(User).VetForWrite: iam.validateScopeForWrite: not allowed to change a resource's scope: parameter violation: error #100", }, { name: "proj-scope-id-not-in-mask",