ICU-739/iam domain error refactor (#841)
* Refactor iam errors * Update db and host doTx error ops * Changelog
This commit is contained in:
1 parent
bfbb179741
commit
70a5899e99
41 files changed
+825
-714
No files matched your search
@@ -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))
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
+6
-5
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+58
-38
@@ -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()},
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
+136
-127
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
+21
-19
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user