@@ -47,29 +47,54 @@ func (names RoleNames) Names() []string {
47
47
return names
48
48
}
49
49
50
+ // UniqueRoleName is formatted as <role_name>[:<org_id/scope_id>] to create globally unique
51
+ // names for a given role. When passing the roles into rego, this helps disambiguate
52
+ // 2 roles with the same common name across organizations.
53
+ // TODO: Would be worth refactoring the rego to take a structure of:
54
+ // {Name string, Scope string} to avoid needing to do this as a single string.
55
+ type UniqueRoleName string
56
+
57
+ func (u UniqueRoleName ) Split () (name string , scope string , err error ) {
58
+ arr := strings .Split (string (u ), ":" )
59
+ if len (arr ) > 2 {
60
+ return "" , "" , xerrors .Errorf ("too many colons in role name" )
61
+ }
62
+ if arr [0 ] == "" {
63
+ return "" , "" , xerrors .Errorf ("role cannot be the empty string" )
64
+ }
65
+ if len (arr ) == 2 {
66
+ return arr [0 ], arr [1 ], nil
67
+ }
68
+ return arr [0 ], "" , nil
69
+ }
70
+
50
71
// The functions below ONLY need to exist for roles that are "defaulted" in some way.
51
72
// Any other roles (like auditor), can be listed and let the user select/assigned.
52
73
// Once we have a database implementation, the "default" roles can be defined on the
53
74
// site and orgs, and these functions can be removed.
54
75
55
- func RoleOwner () string {
76
+ func RoleOwner () UniqueRoleName {
56
77
return RoleName (owner , "" )
57
78
}
58
79
59
- func CustomSiteRole () string { return RoleName (customSiteRole , "" ) }
80
+ func CustomSiteRole () UniqueRoleName { return RoleName (customSiteRole , "" ) }
60
81
61
- func RoleTemplateAdmin () string {
82
+ func RoleTemplateAdmin () UniqueRoleName {
62
83
return RoleName (templateAdmin , "" )
63
84
}
64
85
65
- func RoleUserAdmin () string {
86
+ func RoleUserAdmin () UniqueRoleName {
66
87
return RoleName (userAdmin , "" )
67
88
}
68
89
69
- func RoleMember () string {
90
+ func RoleMember () UniqueRoleName {
70
91
return RoleName (member , "" )
71
92
}
72
93
94
+ func RoleAuditor () UniqueRoleName {
95
+ return RoleName (auditor , "" )
96
+ }
97
+
73
98
func RoleOrgAdmin () string {
74
99
return orgAdmin
75
100
}
@@ -81,14 +106,14 @@ func RoleOrgMember() string {
81
106
// ScopedRoleOrgAdmin is the org role with the organization ID
82
107
// Deprecated This was used before organization scope was included as a
83
108
// field in all user facing APIs. Usage of 'ScopedRoleOrgAdmin()' is preferred.
84
- func ScopedRoleOrgAdmin (organizationID uuid.UUID ) string {
109
+ func ScopedRoleOrgAdmin (organizationID uuid.UUID ) UniqueRoleName {
85
110
return RoleName (orgAdmin , organizationID .String ())
86
111
}
87
112
88
113
// ScopedRoleOrgMember is the org role with the organization ID
89
114
// Deprecated This was used before organization scope was included as a
90
115
// field in all user facing APIs. Usage of 'ScopedRoleOrgMember()' is preferred.
91
- func ScopedRoleOrgMember (organizationID uuid.UUID ) string {
116
+ func ScopedRoleOrgMember (organizationID uuid.UUID ) UniqueRoleName {
92
117
return RoleName (orgMember , organizationID .String ())
93
118
}
94
119
@@ -158,7 +183,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
158
183
// on every authorize call. 'withCachedRegoValue' can be used as well to
159
184
// preallocate the rego value that is used by the rego eval engine.
160
185
ownerRole := Role {
161
- Name : owner ,
186
+ Name : RoleOwner () ,
162
187
DisplayName : "Owner" ,
163
188
Site : append (
164
189
// Workspace dormancy and workspace are omitted.
@@ -174,7 +199,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
174
199
}.withCachedRegoValue ()
175
200
176
201
memberRole := Role {
177
- Name : member ,
202
+ Name : RoleMember () ,
178
203
DisplayName : "Member" ,
179
204
Site : Permissions (map [string ][]policy.Action {
180
205
ResourceAssignRole .Type : {policy .ActionRead },
@@ -200,7 +225,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
200
225
}.withCachedRegoValue ()
201
226
202
227
auditorRole := Role {
203
- Name : auditor ,
228
+ Name : RoleAuditor () ,
204
229
DisplayName : "Auditor" ,
205
230
Site : Permissions (map [string ][]policy.Action {
206
231
// Should be able to read all template details, even in orgs they
@@ -220,7 +245,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
220
245
}.withCachedRegoValue ()
221
246
222
247
templateAdminRole := Role {
223
- Name : templateAdmin ,
248
+ Name : RoleTemplateAdmin () ,
224
249
DisplayName : "Template Admin" ,
225
250
Site : Permissions (map [string ][]policy.Action {
226
251
ResourceTemplate .Type : {policy .ActionCreate , policy .ActionRead , policy .ActionUpdate , policy .ActionDelete , policy .ActionViewInsights },
@@ -241,7 +266,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
241
266
}.withCachedRegoValue ()
242
267
243
268
userAdminRole := Role {
244
- Name : userAdmin ,
269
+ Name : RoleUserAdmin () ,
245
270
DisplayName : "User Admin" ,
246
271
Site : Permissions (map [string ][]policy.Action {
247
272
ResourceAssignRole .Type : {policy .ActionAssign , policy .ActionDelete , policy .ActionRead },
@@ -381,7 +406,7 @@ type ExpandableRoles interface {
381
406
Expand () ([]Role , error )
382
407
// Names is for logging and tracing purposes, we want to know the human
383
408
// names of the expanded roles.
384
- Names () []string
409
+ Names () []UniqueRoleName
385
410
}
386
411
387
412
// Permission is the format passed into the rego.
@@ -424,7 +449,7 @@ func (perm Permission) Valid() error {
424
449
// Users of this package should instead **only** use the role names, and
425
450
// this package will expand the role names into their json payloads.
426
451
type Role struct {
427
- Name string `json:"name"`
452
+ Name UniqueRoleName `json:"name"`
428
453
// DisplayName is used for UI purposes. If the role has no display name,
429
454
// that means the UI should never display it.
430
455
DisplayName string `json:"display_name"`
@@ -474,8 +499,8 @@ func (roles Roles) Expand() ([]Role, error) {
474
499
return roles , nil
475
500
}
476
501
477
- func (roles Roles ) Names () []string {
478
- names := make ([]string , 0 , len (roles ))
502
+ func (roles Roles ) Names () []UniqueRoleName {
503
+ names := make ([]UniqueRoleName , 0 , len (roles ))
479
504
for _ , r := range roles {
480
505
names = append (names , r .Name )
481
506
}
@@ -485,17 +510,17 @@ func (roles Roles) Names() []string {
485
510
// CanAssignRole is a helper function that returns true if the user can assign
486
511
// the specified role. This also can be used for removing a role.
487
512
// This is a simple implementation for now.
488
- func CanAssignRole (expandable ExpandableRoles , assignedRole string ) bool {
513
+ func CanAssignRole (expandable ExpandableRoles , assignedRole UniqueRoleName ) bool {
489
514
// For CanAssignRole, we only care about the names of the roles.
490
515
roles := expandable .Names ()
491
516
492
- assigned , assignedOrg , err := RoleSplit ( assignedRole )
517
+ assigned , assignedOrg , err := assignedRole . Split ( )
493
518
if err != nil {
494
519
return false
495
520
}
496
521
497
522
for _ , longRole := range roles {
498
- role , orgID , err := RoleSplit ( longRole )
523
+ role , orgID , err := longRole . Split ( )
499
524
if err != nil {
500
525
continue
501
526
}
@@ -523,8 +548,8 @@ func CanAssignRole(expandable ExpandableRoles, assignedRole string) bool {
523
548
// This function is exported so that the Display name can be returned to the
524
549
// api. We should maybe make an exported function that returns just the
525
550
// human-readable content of the Role struct (name + display name).
526
- func RoleByName (name string ) (Role , error ) {
527
- roleName , orgID , err := RoleSplit ( name )
551
+ func RoleByName (name UniqueRoleName ) (Role , error ) {
552
+ roleName , orgID , err := name . Split ( )
528
553
if err != nil {
529
554
return Role {}, xerrors .Errorf ("parse role name: %w" , err )
530
555
}
@@ -545,7 +570,7 @@ func RoleByName(name string) (Role, error) {
545
570
return role , nil
546
571
}
547
572
548
- func rolesByNames (roleNames []string ) ([]Role , error ) {
573
+ func rolesByNames (roleNames []UniqueRoleName ) ([]Role , error ) {
549
574
roles := make ([]Role , 0 , len (roleNames ))
550
575
for _ , n := range roleNames {
551
576
r , err := RoleByName (n )
@@ -557,8 +582,8 @@ func rolesByNames(roleNames []string) ([]Role, error) {
557
582
return roles , nil
558
583
}
559
584
560
- func IsOrgRole (roleName string ) (string , bool ) {
561
- _ , orgID , err := RoleSplit ( roleName )
585
+ func IsOrgRole (roleName UniqueRoleName ) (string , bool ) {
586
+ _ , orgID , err := roleName . Split ( )
562
587
if err == nil && orgID != "" {
563
588
return orgID , true
564
589
}
@@ -644,27 +669,15 @@ func ChangeRoleSet(from []string, to []string) (added []string, removed []string
644
669
// role_name:scopeID
645
670
//
646
671
// If no scopeID is required, only 'role_name' is returned
647
- func RoleName (name string , orgID string ) string {
672
+ func RoleName (name string , orgID string ) UniqueRoleName {
648
673
if orgID == "" {
649
- return name
674
+ return UniqueRoleName ( name )
650
675
}
651
- return name + ":" + orgID
676
+ return UniqueRoleName ( name ) + UniqueRoleName ( ":" + orgID )
652
677
}
653
678
654
- func RoleSplit (role string ) (name string , orgID string , err error ) {
655
- arr := strings .Split (role , ":" )
656
- if len (arr ) > 2 {
657
- return "" , "" , xerrors .Errorf ("too many colons in role name" )
658
- }
659
-
660
- if arr [0 ] == "" {
661
- return "" , "" , xerrors .Errorf ("role cannot be the empty string" )
662
- }
663
-
664
- if len (arr ) == 2 {
665
- return arr [0 ], arr [1 ], nil
666
- }
667
- return arr [0 ], "" , nil
679
+ func RoleSplit (role UniqueRoleName ) (name string , orgID string , err error ) {
680
+ return role .Split ()
668
681
}
669
682
670
683
// Permissions is just a helper function to make building roles that list out resources
0 commit comments