|
|
|
|
@ -110,25 +110,250 @@ func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleV
|
|
|
|
|
return principalRoles, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, int, error) {
|
|
|
|
|
panic("not implemented")
|
|
|
|
|
}
|
|
|
|
|
// tests: no change, just delete, just add
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, int, error) {
|
|
|
|
|
if roleId == "" {
|
|
|
|
|
return nil, fmt.Errorf("lookup principal roles: missing role id %w", db.ErrInvalidParameter)
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing role id: %w", db.ErrInvalidParameter)
|
|
|
|
|
}
|
|
|
|
|
var roles []principalRoleView
|
|
|
|
|
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)
|
|
|
|
|
if len(userIds) == 0 && len(groupIds) == 0 {
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing either user or groups to delete %w", db.ErrInvalidParameter)
|
|
|
|
|
}
|
|
|
|
|
principals := make([]PrincipalRole, 0, len(roles))
|
|
|
|
|
for _, r := range roles {
|
|
|
|
|
principals = append(principals, r)
|
|
|
|
|
toSet, err := r.principalsToSet(ctx, roleId, userIds, groupIds)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to determine set: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return principals, nil
|
|
|
|
|
role := allocRole()
|
|
|
|
|
role.PublicId = roleId
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
// handle no change to existing principal roles
|
|
|
|
|
if len(toSet.addUserRoles) == 0 && len(toSet.addGroupRoles) == 0 && len(toSet.deleteUserRoles) == 0 && len(toSet.deleteGroupRoles) == 0 {
|
|
|
|
|
results := make([]PrincipalRole, 0, len(userIds)+len(groupIds))
|
|
|
|
|
for _, id := range userIds {
|
|
|
|
|
role, err := NewUserRole(scope.PublicId, roleId, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("add principal roles: unable to create in memory user role: %w", err)
|
|
|
|
|
}
|
|
|
|
|
results = append(results, role)
|
|
|
|
|
}
|
|
|
|
|
for _, id := range groupIds {
|
|
|
|
|
role, err := NewGroupRole(scope.PublicId, roleId, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("add principal roles: unable to create in memory group role: %w", err)
|
|
|
|
|
}
|
|
|
|
|
results = append(results, role)
|
|
|
|
|
}
|
|
|
|
|
return results, db.NoRowsAffected, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var currentPrincipals []PrincipalRole
|
|
|
|
|
var totalRowsAffected int
|
|
|
|
|
_, err = r.writer.DoTx(
|
|
|
|
|
ctx,
|
|
|
|
|
db.StdRetryCnt,
|
|
|
|
|
db.ExpBackoff{},
|
|
|
|
|
func(reader db.Reader, w db.Writer) error {
|
|
|
|
|
// we need a roleTicket, which won't be redeemed until all the other
|
|
|
|
|
// writes are successful. We can't just use a single ticket because
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
updatedRole := allocRole()
|
|
|
|
|
updatedRole.PublicId = roleId
|
|
|
|
|
updatedRole.Version = uint32(roleVersion) + 1
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
if rowsUpdated != 1 {
|
|
|
|
|
return fmt.Errorf("set principal roles: updated role and %d rows updated", rowsUpdated)
|
|
|
|
|
}
|
|
|
|
|
if len(toSet.deleteUserRoles) > 0 || len(toSet.deleteGroupRoles) > 0 {
|
|
|
|
|
msgs := make([]*oplog.Message, 0, 2)
|
|
|
|
|
// deleteTicket which will be redeemed when we write the oplog
|
|
|
|
|
// for these deletes
|
|
|
|
|
deleteTicket, err := w.GetTicket(&principalRoleView{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to get ticket for principal role deletes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if len(toSet.deleteUserRoles) > 0 {
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
totalRowsAffected += rowsDeleted
|
|
|
|
|
msgs = append(msgs, userOplogMsgs...)
|
|
|
|
|
}
|
|
|
|
|
if len(toSet.deleteGroupRoles) > 0 {
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
totalRowsAffected += rowsDeleted
|
|
|
|
|
msgs = append(msgs, grpOplogMsgs...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
metadata := oplog.Metadata{
|
|
|
|
|
"op-type": []string{oplog.OpType_OP_TYPE_DELETE.String()},
|
|
|
|
|
"scope-id": []string{scope.PublicId},
|
|
|
|
|
"scope-type": []string{scope.Type},
|
|
|
|
|
"resource-public-id": []string{roleId},
|
|
|
|
|
}
|
|
|
|
|
// write the oplog msgs for the deletes
|
|
|
|
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, deleteTicket, metadata, msgs); err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to write oplog for deletes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(toSet.addUserRoles) > 0 || len(toSet.addGroupRoles) > 0 {
|
|
|
|
|
msgs := make([]*oplog.Message, 0, 2)
|
|
|
|
|
// addTicket which will be redeemed when we write the oplog
|
|
|
|
|
// entry for these writes.
|
|
|
|
|
addTicket, err := w.GetTicket(&principalRoleView{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to get ticket for principal role additions: %w", err)
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
msgs = append(msgs, userOplogMsgs...)
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
msgs = append(msgs, grpOplogMsgs...)
|
|
|
|
|
}
|
|
|
|
|
metadata := oplog.Metadata{
|
|
|
|
|
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
|
|
|
|
|
"scope-id": []string{scope.PublicId},
|
|
|
|
|
"scope-type": []string{scope.Type},
|
|
|
|
|
"resource-public-id": []string{roleId},
|
|
|
|
|
}
|
|
|
|
|
// write the oplog msgs for the additions
|
|
|
|
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, addTicket, metadata, msgs); err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to write oplog for additions: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// we're done with all the principal writes, so let's write the
|
|
|
|
|
// role's update oplog message
|
|
|
|
|
metadata := oplog.Metadata{
|
|
|
|
|
"op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()},
|
|
|
|
|
"scope-id": []string{scope.PublicId},
|
|
|
|
|
"scope-type": []string{scope.Type},
|
|
|
|
|
"resource-public-id": []string{roleId},
|
|
|
|
|
}
|
|
|
|
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, roleTicket, metadata, []*oplog.Message{&roleOplogMsg}); err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to write oplog for additions: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentPrincipals, err = r.ListPrincipalRoles(ctx, roleId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("set principal roles: unable to retrieve current principal roles after sets: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to set principals: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return currentPrincipals, totalRowsAffected, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type principalSet struct {
|
|
|
|
|
addUserRoles []interface{}
|
|
|
|
|
addGroupRoles []interface{}
|
|
|
|
|
deleteUserRoles []interface{}
|
|
|
|
|
deleteGroupRoles []interface{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Repository) principalsToSet(ctx context.Context, roleId string, userIds, groupIds []string) (*principalSet, error) {
|
|
|
|
|
existing, err := r.ListPrincipalRoles(ctx, roleId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to list existing principal role %s: %w", roleId, err)
|
|
|
|
|
}
|
|
|
|
|
existingUsers := map[string]PrincipalRole{}
|
|
|
|
|
existingGroups := map[string]PrincipalRole{}
|
|
|
|
|
for _, p := range existing {
|
|
|
|
|
switch p.GetType() {
|
|
|
|
|
case UserRoleType.String():
|
|
|
|
|
existingUsers[p.GetPrincipalId()] = p
|
|
|
|
|
case GroupRoleType.String():
|
|
|
|
|
existingGroups[p.GetPrincipalId()] = p
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("%s is unknown principal type %s", p.GetPrincipalId(), p.GetType())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var newUserRoles []interface{}
|
|
|
|
|
userIdsMap := map[string]struct{}{}
|
|
|
|
|
for _, id := range userIds {
|
|
|
|
|
userIdsMap[id] = struct{}{}
|
|
|
|
|
if p, ok := existingUsers[id]; !ok {
|
|
|
|
|
usrRole, err := NewUserRole(p.GetScopeId(), p.GetRoleId(), id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to create in memory user role for add: %w", err)
|
|
|
|
|
}
|
|
|
|
|
newUserRoles = append(newUserRoles, usrRole)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var newGrpRoles []interface{}
|
|
|
|
|
groupIdsMap := map[string]struct{}{}
|
|
|
|
|
for _, id := range groupIds {
|
|
|
|
|
groupIdsMap[id] = struct{}{}
|
|
|
|
|
if p, ok := existingGroups[id]; !ok {
|
|
|
|
|
grpRole, err := NewGroupRole(p.GetScopeId(), p.GetRoleId(), id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to create in memory group role for add: %w", err)
|
|
|
|
|
}
|
|
|
|
|
newGrpRoles = append(newGrpRoles, grpRole)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var deleteUserRoles []interface{}
|
|
|
|
|
for _, p := range existingUsers {
|
|
|
|
|
if _, ok := userIdsMap[p.GetPrincipalId()]; !ok {
|
|
|
|
|
usrRole, err := NewUserRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to create in memory user role for delete: %w", err)
|
|
|
|
|
}
|
|
|
|
|
deleteUserRoles = append(deleteUserRoles, usrRole)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var deleteGrpRoles []interface{}
|
|
|
|
|
for _, p := range existingGroups {
|
|
|
|
|
if _, ok := userIdsMap[p.GetPrincipalId()]; !ok {
|
|
|
|
|
grpRole, err := NewGroupRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to create in memory group role for delete: %w", err)
|
|
|
|
|
}
|
|
|
|
|
deleteGrpRoles = append(deleteGrpRoles, grpRole)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return &principalSet{
|
|
|
|
|
addUserRoles: newUserRoles,
|
|
|
|
|
addGroupRoles: newGrpRoles,
|
|
|
|
|
deleteUserRoles: deleteUserRoles,
|
|
|
|
|
deleteGroupRoles: deleteGrpRoles,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeletePrincipalRoles principals (userIds and/or groupIds) from a role
|
|
|
|
|
// (roleId). The role's current db version must match the roleVersion or an
|
|
|
|
|
// error will be returned.
|
|
|
|
|
func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) (int, error) {
|
|
|
|
|
if roleId == "" {
|
|
|
|
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing role id: %w", db.ErrInvalidParameter)
|
|
|
|
|
@ -219,7 +444,23 @@ func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, ro
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: error deleting roles: %w", err)
|
|
|
|
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: error deleting principal roles: %w", err)
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
if roleId == "" {
|
|
|
|
|
return nil, fmt.Errorf("lookup principal roles: missing role id %w", db.ErrInvalidParameter)
|
|
|
|
|
}
|
|
|
|
|
var roles []principalRoleView
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
principals := make([]PrincipalRole, 0, len(roles))
|
|
|
|
|
for _, r := range roles {
|
|
|
|
|
principals = append(principals, r)
|
|
|
|
|
}
|
|
|
|
|
return principals, nil
|
|
|
|
|
}
|
|
|
|
|
|