-
Notifications
You must be signed in to change notification settings - Fork 291
/
Copy pathrepository_role.go
196 lines (189 loc) · 6.63 KB
/
repository_role.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package iam
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/go-dbw"
)
// 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, _ ...Option) (*Role, error) {
const op = "iam.(Repository).CreateRole"
if role == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role")
}
if role.Role == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
}
if role.PublicId != "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
}
if role.ScopeId == "" {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id")
}
id, err := newRoleId()
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
c := role.Clone().(*Role)
c.PublicId = id
resource, err := r.create(ctx, c)
if err != nil {
if errors.IsUniqueError(err) {
return nil, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in scope %s", role.Name, role.ScopeId))
}
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", c.PublicId)))
}
return resource.(*Role), nil
}
// UpdateRole will update a role in the repository and return the written role.
// fieldMaskPaths provides field_mask.proto paths for fields that should be
// updated. Fields will be set to NULL if the field is a zero value and
// 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, _ ...Option) (*Role, []*PrincipalRole, []*RoleGrant, int, error) {
const op = "iam.(Repository).UpdateRole"
if role == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role")
}
if role.Role == nil {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing role store")
}
if role.PublicId == "" {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
}
for _, f := range fieldMaskPaths {
switch {
case strings.EqualFold("name", f):
case strings.EqualFold("description", f):
case strings.EqualFold("grantscopeid", f):
default:
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f))
}
}
var dbMask, nullFields []string
dbMask, nullFields = dbw.BuildUpdatePaths(
map[string]any{
"name": role.Name,
"description": role.Description,
"GrantScopeId": role.GrantScopeId,
},
fieldMaskPaths,
nil,
)
if len(dbMask) == 0 && len(nullFields) == 0 {
return nil, nil, nil, db.NoRowsAffected, errors.E(ctx, errors.WithCode(errors.EmptyFieldMask), errors.WithOp(op))
}
var resource Resource
var rowsUpdated int
var pr []*PrincipalRole
var rg []*RoleGrant
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
var err error
c := role.Clone().(*Role)
resource, rowsUpdated, err = r.update(ctx, c, version, dbMask, nullFields)
if err != nil {
return errors.Wrap(ctx, err, op)
}
repo, err := NewRepository(read, w, r.kms)
if err != nil {
return errors.Wrap(ctx, err, op)
}
pr, err = repo.ListPrincipalRoles(ctx, role.PublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
rg, err = repo.ListRoleGrants(ctx, role.PublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
if errors.IsUniqueError(err) {
return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("role %s already exists in org %s", role.Name, role.ScopeId))
}
return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for %s", role.PublicId)))
}
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, _ ...Option) (*Role, []*PrincipalRole, []*RoleGrant, error) {
const op = "iam.(Repository).LookupRole"
if withPublicId == "" {
return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
}
role := allocRole()
role.PublicId = withPublicId
var pr []*PrincipalRole
var rg []*RoleGrant
_, err := r.writer.DoTx(
ctx,
db.StdRetryCnt,
db.ExpBackoff{},
func(read db.Reader, w db.Writer) error {
if err := read.LookupByPublicId(ctx, &role); err != nil {
return errors.Wrap(ctx, err, op)
}
repo, err := NewRepository(read, w, r.kms)
if err != nil {
return errors.Wrap(ctx, err, op)
}
pr, err = repo.ListPrincipalRoles(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
rg, err = repo.ListRoleGrants(ctx, withPublicId)
if err != nil {
return errors.Wrap(ctx, err, op)
}
return nil
},
)
if err != nil {
if errors.IsNotFoundError(err) {
return nil, nil, nil, nil
}
return nil, nil, nil, errors.Wrap(ctx, 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, _ ...Option) (int, error) {
const op = "iam.(Repository).DeleteRole"
if withPublicId == "" {
return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing public id")
}
role := allocRole()
role.PublicId = withPublicId
if err := r.reader.LookupByPublicId(ctx, &role); err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId)))
}
rowsDeleted, err := r.delete(ctx, &role)
if err != nil {
return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId)))
}
return rowsDeleted, nil
}
// ListRoles lists roles in the given scopes and supports WithLimit option.
func (r *Repository) ListRoles(ctx context.Context, withScopeIds []string, opt ...Option) ([]*Role, error) {
const op = "iam.(Repository).ListRoles"
if len(withScopeIds) == 0 {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope ids")
}
var roles []*Role
err := r.list(ctx, &roles, "scope_id in (?)", []any{withScopeIds}, opt...)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return roles, nil
}