Skip to content

Commit fc1d8ab

Browse files
bosorawisdkanney
andauthored
Bosorawis domain iam implement role grant scopes all (#5701)
* implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * split iam_role_global_individual_grant_scope to have separate tables for org and project * small comment change * small comment change * WIP: add tests * remove grant_scope as immutable column * add trigger to delete individual grant scope when grant_scope changes * add a test that covers changing grant_scope * rename function and trigger in iam_role_global * improve assertion in sqltest for iam_role_global * update iam_role_org to delete redundant grants scope * minor comment fix * no longer handle individual grant scope deletion with triggers and rename some functions * rename test * add all subtype definitions * remove unnecessary baseRole subtype * add clone, setTableName, and GetScope tests * add ResourceType and Actions test * add create and delete tests for globalROle * finish create and delete tests * add trigger for deleting base role * add trigger to sync update_time back to base iam_role table * add update tests * fix missing err checks * fix iam_role delete subtype trigger function name and use new.update_time instead of now() * add struct documentation to role subtypes * add version update check * implement getRoleScopeId * implement getRoleScopeId * save * remove struct embedding from iam.Role * fix tests to use new iam.Role definition * repository_role_test.go move to new iam.Role model * repository_principal_role_test.go use new iam.Role model * repository_role_grant_test.go use new iam.Role model in test * add oplog info to sql schema * internal/iam/testing.go use new role schema in TestRole * add toRole helper function to all role subtype * remove tests that are no longer relevant * internal/iam/repository_scope.go use new iam model * internal/iam/repository_role_grant.go use new iam model * internal/iam/repository_principal_role.go use new iam model * internal/iam/repository_role_test.go add test case for global scoped role * internal/iam/repository_grant_scope.go use new iam model * fix query * make create and lookup role work and add tests * add role id to getRoleScopeId error message * make DeleteRole work with new model and add tests * fix update * ensure oplog.ReplayableMessage is implemented on all role subtypes * internal/iam/repository_role_grant.go fix slugging version properly * internal/iam/repository_role.go minor correction to error message saying org instead of scope * internal/iam/repository_role_test.go add more update tests * add immutable_fields tests * fix rebase * change error code to RecordNotFound * refactor to use getScopeType * fix delete test * add getRoleScope utility function * repository_principal_role.go: refactor to remove multiple switch statements * repository_role_grant.go: refactor to reduce LOC * repository_role.go small refactor to use alloc func * repository_grant_scope.go refactor * review comments * implement getRoleScopeId * move query to query.go * improve notfound err message * improve other err messages * use named parameter and move getRoleScopeId implementation * moved getRoleScopeId test * rename getRoleScopeId to getRoleScopeType * fix public_id ambiguous error * undo unintended change to getUserWithAccount * fix the correct query * rename test * change error code to RecordNotFound * Update internal/iam/repository_role.go Co-authored-by: David Kanney <[email protected]> * switch to slice instead of counter * fix merge mistakes * handling special scopes in test function * fix TestRoleWithGrants * fix minor typo * make gen * fix comment typos * Bosorawis domain iam role use new model list role (#5676) * add and use new list roles query * run make gen * tweaked returned error * move ListRoleGrantScopes to repository_grant_scope.go * rename repository_grant_scope to repository_role_grant_scope * add proto definition for global role individual grant scope tables * fix test from removing embeded struct from RoleGrantScope * add grant_scope to proto definition * implement GlobalRoleIndividualOrgGrantScope and GlobalRoleIndividualProjectGrantScope * update comment * run make gen to update comment * implement OrgRoleIndividualGrantScope and add tests * implement part of ListRoleGrantScopes * Add more test * add more test cases and remove add-grants test * unexport listRoleGrantScopes * use reader from function parameter instead of struct method * rename test to match actual function * run make gen * unexport individual grants structs * unexport individual grants structs - missed one file * change TestRole and TestRoleGrantScope function to support new model * add validation for special scopes * add role_org_individual_grant_scope.pb.go to protobuild make target * remove dead code from listRoleGrantScopes * fix testRoleGrantScopeSpecial not handling org role special scope properly * change proto grant_this default to true * make TestRole readback the role to get updated version * implement toRoleGrantScope function on the subtypes * implement conversion function * add tests and AddRoleGrantScope before refactor * working delete grant scope before refactor * remove unused functions * refactor repository_role_grant_scope.go * add tests for SetRoleGrantScopes * all tests passing * refactor repository_role_grant_scope.go again * run make gen * no longer embed Resource in roleScopeGranter interface and make interface all internal functions * add additional test case * fix minor typo * add a constraint check for iam_role_org.grant_scope * refactor and comment repository_role_grant_scope.go and add test cases * remove unused code * rename roleScopeGranter to roleGrantScopeUpdater * interface and function rename * address PR comments * remove redundant constraint on iam_role_org * address pr comments * grant this scope to role by default when creating a role * tweak comments and variable names --------- Co-authored-by: David Kanney <[email protected]>
1 parent d8ff03b commit fc1d8ab

File tree

11 files changed

+1405
-193
lines changed

11 files changed

+1405
-193
lines changed

internal/iam/repository_role.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option)
4848
ScopeId: role.ScopeId,
4949
Name: role.Name,
5050
Description: role.Description,
51-
GrantThisRoleScope: false,
51+
GrantThisRoleScope: true,
5252
GrantScope: globals.GrantScopeIndividual,
5353
},
5454
}
@@ -59,7 +59,7 @@ func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option)
5959
ScopeId: role.ScopeId,
6060
Name: role.Name,
6161
Description: role.Description,
62-
GrantThisRoleScope: false,
62+
GrantThisRoleScope: true,
6363
GrantScope: globals.GrantScopeIndividual,
6464
},
6565
}
@@ -70,6 +70,7 @@ func (r *Repository) CreateRole(ctx context.Context, role *Role, opt ...Option)
7070
ScopeId: role.ScopeId,
7171
Name: role.Name,
7272
Description: role.Description,
73+
// GrantThisRoleScope: true, will be set in https://github.com/hashicorp/boundary/pull/5738 since the field doesn't exist yet
7374
},
7475
}
7576
default:

internal/iam/repository_role_grant_scope.go

Lines changed: 553 additions & 177 deletions
Large diffs are not rendered by default.

internal/iam/repository_role_grant_scope_test.go

Lines changed: 640 additions & 5 deletions
Large diffs are not rendered by default.

internal/iam/repository_role_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,14 @@ func TestRepository_CreateRole(t *testing.T) {
242242
assert.NotNil(grp.CreateTime)
243243
assert.NotNil(grp.UpdateTime)
244244

245-
foundGrp, _, _, _, err := repo.LookupRole(context.Background(), grp.PublicId)
245+
foundGrp, _, _, grantScopes, err := repo.LookupRole(context.Background(), grp.PublicId)
246246
assert.NoError(err)
247247
assert.Equal(foundGrp, grp)
248248

249+
// by default, all created roles has `this` role grant scope
250+
assert.Len(grantScopes, 1)
251+
assert.Equal(grantScopes[0].ScopeIdOrSpecial, globals.GrantScopeThis)
252+
249253
err = db.TestVerifyOplog(t, rw, grp.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
250254
assert.NoError(err)
251255
})

internal/iam/role.go

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package iam
66
import (
77
"context"
88

9+
"github.com/hashicorp/boundary/globals"
910
"github.com/hashicorp/boundary/internal/db"
1011
"github.com/hashicorp/boundary/internal/db/timestamp"
1112
"github.com/hashicorp/boundary/internal/errors"
@@ -17,12 +18,44 @@ import (
1718
)
1819

1920
const (
20-
defaultRoleTableName = "iam_role"
2121
defaultGlobalRoleTableName = "iam_role_global"
2222
defaultOrgRoleTableName = "iam_role_org"
2323
defaultProjectRoleTableName = "iam_role_project"
2424
)
2525

26+
// roleGrantScopeUpdater represents an internal scope type specific role structs that
27+
// support grant scope columns. Currently this only applies to globalRole and orgRole
28+
// this is used in SetRoleGrantScope, AddRoleGrantScope, DeleteRoleGrantScope
29+
type roleGrantScopeUpdater interface {
30+
// setVersion sets value of `Version` of this role. This is used in
31+
// `repository_grant_scope` operations where version column of the associated role
32+
// has to increase when grant scopes list is changed (add/remove) which is done in a different table.
33+
// This version bump cannot be done by automatically with trigger and is being handled in the application code
34+
setVersion(version uint32)
35+
36+
// setThisGrantScope sets value of `GrantThisRoleScope` of this role which control
37+
// whether this role has 'this' scope granted to it
38+
setGrantThisRoleScope(grantThis bool)
39+
40+
// setGrantScope sets value of `GrantScope` column of this role. The allowed values depends on the scope
41+
// that the role is in
42+
// - global-role: ['descendants', 'children', 'individual']
43+
// - org-role: ['children', 'individual']
44+
// - project-role: [] (None)
45+
// This value controls whether special grant scope is granted to this role
46+
setGrantScope(specialGrant string)
47+
48+
// GrantThisRoleScope return value of `GrantScopeThis` column as *RoleGrantScope.
49+
// Prior to the grants refactor, `this` grant scope is granted to a role by
50+
// inserting a row to 'role_grant_scope' table, but we've moved on to storing
51+
// 'this' grant as a dedicated column in the type-specific role tables
52+
grantThisRoleScope() *RoleGrantScope
53+
54+
// GrantScope returns special grant scopes ['descendants', 'children'] if available.
55+
// returns nil, false if special grant scope is 'individual'
56+
grantScope() (*RoleGrantScope, bool)
57+
}
58+
2659
// Roles are granted permissions and assignable to Users and Groups.
2760
type Role struct {
2861
PublicId string
@@ -86,11 +119,13 @@ func (role *Role) GetVersion() uint32 {
86119

87120
// ensure that Role implements the interfaces of: Resource, Cloneable, and db.VetForWriter.
88121
var (
122+
_ roleGrantScopeUpdater = (*globalRole)(nil)
89123
_ Resource = (*globalRole)(nil)
90124
_ Cloneable = (*globalRole)(nil)
91125
_ db.VetForWriter = (*globalRole)(nil)
92126
_ oplog.ReplayableMessage = (*globalRole)(nil)
93127

128+
_ roleGrantScopeUpdater = (*orgRole)(nil)
94129
_ Resource = (*orgRole)(nil)
95130
_ Cloneable = (*orgRole)(nil)
96131
_ db.VetForWriter = (*orgRole)(nil)
@@ -172,6 +207,55 @@ type globalRole struct {
172207
tableName string `gorm:"-"`
173208
}
174209

210+
func (g *globalRole) setVersion(version uint32) {
211+
if g == nil {
212+
return
213+
}
214+
g.Version = version
215+
}
216+
217+
func (g *globalRole) setGrantThisRoleScope(grantThis bool) {
218+
if g == nil {
219+
return
220+
}
221+
g.GrantThisRoleScope = grantThis
222+
}
223+
224+
func (g *globalRole) setGrantScope(specialGrant string) {
225+
if g == nil {
226+
return
227+
}
228+
g.GrantScope = specialGrant
229+
}
230+
231+
func (g *globalRole) grantThisRoleScope() *RoleGrantScope {
232+
if g == nil {
233+
return &RoleGrantScope{}
234+
}
235+
if !g.GrantThisRoleScope {
236+
return &RoleGrantScope{}
237+
}
238+
return &RoleGrantScope{
239+
CreateTime: g.GrantThisRoleScopeUpdateTime,
240+
RoleId: g.PublicId,
241+
ScopeIdOrSpecial: globals.GrantScopeThis,
242+
}
243+
}
244+
245+
func (g *globalRole) grantScope() (*RoleGrantScope, bool) {
246+
if g == nil {
247+
return nil, false
248+
}
249+
if g.GrantScope == globals.GrantScopeIndividual {
250+
return nil, false
251+
}
252+
return &RoleGrantScope{
253+
CreateTime: g.GrantScopeUpdateTime,
254+
RoleId: g.PublicId,
255+
ScopeIdOrSpecial: g.GrantScope,
256+
}, true
257+
}
258+
175259
func (g *globalRole) TableName() string {
176260
if g.tableName != "" {
177261
return g.tableName
@@ -254,6 +338,55 @@ type orgRole struct {
254338
tableName string `gorm:"-"`
255339
}
256340

341+
func (o *orgRole) setVersion(version uint32) {
342+
if o == nil {
343+
return
344+
}
345+
o.Version = version
346+
}
347+
348+
func (o *orgRole) setGrantThisRoleScope(grantThis bool) {
349+
if o == nil {
350+
return
351+
}
352+
o.GrantThisRoleScope = grantThis
353+
}
354+
355+
func (o *orgRole) setGrantScope(specialGrant string) {
356+
if o == nil {
357+
return
358+
}
359+
o.GrantScope = specialGrant
360+
}
361+
362+
func (o *orgRole) grantThisRoleScope() *RoleGrantScope {
363+
if o == nil {
364+
return &RoleGrantScope{}
365+
}
366+
if !o.GrantThisRoleScope {
367+
return &RoleGrantScope{}
368+
}
369+
return &RoleGrantScope{
370+
CreateTime: o.GrantThisRoleScopeUpdateTime,
371+
RoleId: o.PublicId,
372+
ScopeIdOrSpecial: globals.GrantScopeThis,
373+
}
374+
}
375+
376+
func (o *orgRole) grantScope() (*RoleGrantScope, bool) {
377+
if o == nil {
378+
return nil, false
379+
}
380+
if o.GrantScope == globals.GrantScopeIndividual {
381+
return nil, false
382+
}
383+
return &RoleGrantScope{
384+
CreateTime: o.GrantScopeUpdateTime,
385+
RoleId: o.PublicId,
386+
ScopeIdOrSpecial: o.GrantScope,
387+
}, true
388+
}
389+
257390
func (o *orgRole) TableName() string {
258391
if o.tableName != "" {
259392
return o.tableName

internal/iam/role_grant_scope.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,24 @@ var (
3434
_ Cloneable = (*globalRoleIndividualOrgGrantScope)(nil)
3535
_ db.VetForWriter = (*globalRoleIndividualOrgGrantScope)(nil)
3636
_ oplog.ReplayableMessage = (*globalRoleIndividualOrgGrantScope)(nil)
37+
_ roleGrantScoper = (*globalRoleIndividualOrgGrantScope)(nil)
3738

3839
_ Cloneable = (*globalRoleIndividualProjectGrantScope)(nil)
3940
_ db.VetForWriter = (*globalRoleIndividualProjectGrantScope)(nil)
4041
_ oplog.ReplayableMessage = (*globalRoleIndividualProjectGrantScope)(nil)
42+
_ roleGrantScoper = (*globalRoleIndividualProjectGrantScope)(nil)
4143

4244
_ Cloneable = (*orgRoleIndividualGrantScope)(nil)
4345
_ db.VetForWriter = (*orgRoleIndividualGrantScope)(nil)
4446
_ oplog.ReplayableMessage = (*orgRoleIndividualGrantScope)(nil)
47+
_ roleGrantScoper = (*orgRoleIndividualGrantScope)(nil)
4548
)
4649

50+
// roleGrantScoper is an interface for converting internal grantScopeTypes to exported RoleGrantScope
51+
type roleGrantScoper interface {
52+
roleGrantScope() *RoleGrantScope
53+
}
54+
4755
// RoleGrantScope defines the grant scopes that are assigned to a role
4856
type RoleGrantScope struct {
4957
CreateTime *timestamp.Timestamp
@@ -136,6 +144,17 @@ type globalRoleIndividualOrgGrantScope struct {
136144
tableName string `gorm:"-"`
137145
}
138146

147+
func (g *globalRoleIndividualOrgGrantScope) roleGrantScope() *RoleGrantScope {
148+
if g == nil {
149+
return nil
150+
}
151+
return &RoleGrantScope{
152+
CreateTime: g.CreateTime,
153+
RoleId: g.RoleId,
154+
ScopeIdOrSpecial: g.GetScopeId(),
155+
}
156+
}
157+
139158
func (g *globalRoleIndividualOrgGrantScope) TableName() string {
140159
if g.tableName != "" {
141160
return g.tableName
@@ -176,6 +195,17 @@ type globalRoleIndividualProjectGrantScope struct {
176195
tableName string `gorm:"-"`
177196
}
178197

198+
func (g *globalRoleIndividualProjectGrantScope) roleGrantScope() *RoleGrantScope {
199+
if g == nil {
200+
return nil
201+
}
202+
return &RoleGrantScope{
203+
CreateTime: g.GetCreateTime(),
204+
RoleId: g.GetRoleId(),
205+
ScopeIdOrSpecial: g.GetScopeId(),
206+
}
207+
}
208+
179209
func (g *globalRoleIndividualProjectGrantScope) TableName() string {
180210
if g.tableName != "" {
181211
return g.tableName
@@ -216,6 +246,17 @@ type orgRoleIndividualGrantScope struct {
216246
tableName string `gorm:"-"`
217247
}
218248

249+
func (g *orgRoleIndividualGrantScope) roleGrantScope() *RoleGrantScope {
250+
if g == nil {
251+
return nil
252+
}
253+
return &RoleGrantScope{
254+
CreateTime: g.GetCreateTime(),
255+
RoleId: g.GetRoleId(),
256+
ScopeIdOrSpecial: g.GetScopeId(),
257+
}
258+
}
259+
219260
func (g *orgRoleIndividualGrantScope) TableName() string {
220261
if g.tableName != "" {
221262
return g.tableName

internal/iam/store/role_global.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/iam/store/role_org.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/iam/testing.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,29 @@ func TestRole(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Role {
263263
}
264264
require.Equal(opts.withDescription, role.Description)
265265
require.Equal(opts.withName, role.Name)
266-
return role
266+
267+
var final *Role
268+
switch {
269+
case strings.HasPrefix(scopeId, globals.GlobalPrefix):
270+
g := allocGlobalRole()
271+
g.PublicId = id
272+
require.NoError(rw.LookupByPublicId(ctx, &g))
273+
final = g.toRole()
274+
case strings.HasPrefix(scopeId, globals.OrgPrefix):
275+
o := allocOrgRole()
276+
o.PublicId = id
277+
require.NoError(rw.LookupByPublicId(ctx, &o))
278+
final = o.toRole()
279+
case strings.HasPrefix(scopeId, globals.ProjectPrefix):
280+
p := allocProjectRole()
281+
p.PublicId = id
282+
require.NoError(rw.LookupByPublicId(ctx, &p))
283+
final = p.toRole()
284+
default:
285+
t.Logf("invalid scope id: %s", scopeId)
286+
t.FailNow()
287+
}
288+
return final
267289
}
268290

269291
// TestRoleWithGrants creates a role suitable for testing along with grants

internal/proto/controller/storage/iam/store/v1/role_global.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ message GlobalRole {
3636
}];
3737

3838
// control if this role is granted access to its role scope
39-
// @inject_tag: `gorm:"default:true"`
39+
// @inject_tag: `gorm:"default:false"`
4040
bool grant_this_role_scope = 5;
4141

4242
// control type of grant scope granted to this role ['descendant', 'children', 'individual']

0 commit comments

Comments
 (0)