@@ -7,15 +7,113 @@ import (
7
7
)
8
8
9
9
const (
10
- Admin = "admin"
11
- Member = "member"
10
+ admin string = "admin"
11
+ member string = "member"
12
+ auditor string = "auditor"
12
13
13
- OrganizationMember = "organization-member"
14
- OrganizationAdmin = "organization-admin"
14
+ orgAdmin string = "organization-admin"
15
+ orgMember string = "organization-member"
16
+ )
17
+
18
+ // RoleName is a string that represents a registered rbac role. We want to store
19
+ // strings in the database to allow the underlying role permissions to be migrated
20
+ // or modified.
21
+ // All RoleNames should have an entry in 'builtInRoles'.
22
+ // We use functions to retrieve the name incase we need to add a scope.
23
+ type RoleName = string
24
+
25
+ func RoleAdmin () string {
26
+ return roleName (admin , "" )
27
+ }
28
+
29
+ func RoleMember () string {
30
+ return roleName (member , "" )
31
+ }
32
+
33
+ func RoleOrgAdmin (organizationID string ) RoleName {
34
+ return roleName (orgAdmin , organizationID )
35
+ }
36
+
37
+ func RoleOrgMember (organizationID string ) RoleName {
38
+ return roleName (orgMember , organizationID )
39
+ }
40
+
41
+ var (
42
+ // builtInRoles are just a hard coded set for now. Ideally we store these in
43
+ // the database. Right now they are functions because the org id should scope
44
+ // certain roles. If we store them in the database, we will need to store
45
+ // them such that the "org" permissions are dynamically changed by the
46
+ // scopeID passed in. This isn't a hard problem to solve, it's just easier
47
+ // as a function right now.
48
+ builtInRoles = map [string ]func (scopeID string ) Role {
49
+ // admin grants all actions to all resources.
50
+ admin : func (_ string ) Role {
51
+ return Role {
52
+ Name : admin ,
53
+ Site : permissions (map [Object ][]Action {
54
+ ResourceWildcard : {WildcardSymbol },
55
+ }),
56
+ }
57
+ },
58
+
59
+ // member grants all actions to all resources owned by the user
60
+ member : func (_ string ) Role {
61
+ return Role {
62
+ Name : member ,
63
+ User : permissions (map [Object ][]Action {
64
+ ResourceWildcard : {WildcardSymbol },
65
+ }),
66
+ }
67
+ },
68
+
69
+ // auditor provides all permissions required to effectively read and understand
70
+ // audit log events.
71
+ // TODO: Finish the auditor as we add resources.
72
+ auditor : func (_ string ) Role {
73
+ return Role {
74
+ Name : "auditor" ,
75
+ Site : permissions (map [Object ][]Action {
76
+ //ResourceAuditLogs: {ActionRead},
77
+ // Should be able to read all template details, even in orgs they
78
+ // are not in.
79
+ ResourceTemplate : {ActionRead },
80
+ }),
81
+ }
82
+ },
83
+
84
+ // orgAdmin returns a role with all actions allows in a given
85
+ // organization scope.
86
+ orgAdmin : func (organizationID string ) Role {
87
+ return Role {
88
+ Name : roleName (orgAdmin , organizationID ),
89
+ Org : map [string ][]Permission {
90
+ organizationID : {
91
+ {
92
+ Negate : false ,
93
+ ResourceType : "*" ,
94
+ ResourceID : "*" ,
95
+ Action : "*" ,
96
+ },
97
+ },
98
+ },
99
+ }
100
+ },
101
+
102
+ // orgMember has an empty set of permissions, this just implies their membership
103
+ // in an organization.
104
+ orgMember : func (organizationID string ) Role {
105
+ return Role {
106
+ Name : roleName (orgMember , organizationID ),
107
+ Org : map [string ][]Permission {
108
+ organizationID : {},
109
+ },
110
+ }
111
+ },
112
+ }
15
113
)
16
114
17
115
// RoleByName returns the permissions associated with a given role name.
18
- // This allows just the role names to be stored.
116
+ // This allows just the role names to be stored and expanded when required .
19
117
func RoleByName (name string ) (Role , error ) {
20
118
arr := strings .Split (name , ":" )
21
119
if len (arr ) > 2 {
@@ -28,34 +126,53 @@ func RoleByName(name string) (Role, error) {
28
126
scopeID = arr [1 ]
29
127
}
30
128
31
- // If the role requires a scope, the scope will be checked at the end
32
- // of the switch statement.
33
- var scopedRole Role
34
- switch roleName {
35
- case Admin :
36
- return RoleAdmin , nil
37
- case Member :
38
- return RoleMember , nil
39
- case OrganizationMember :
40
- scopedRole = RoleOrgMember (scopeID )
41
- case OrganizationAdmin :
42
- scopedRole = RoleOrgAdmin (scopeID )
43
- default :
129
+ roleFunc , ok := builtInRoles [roleName ]
130
+ if ! ok {
44
131
// No role found
45
132
return Role {}, xerrors .Errorf ("role %q not found" , roleName )
46
133
}
47
134
48
- // Scoped roles should be checked their scope is set
49
- if scopeID == "" {
50
- return Role {}, xerrors .Errorf ("%q requires a scope id" , roleName )
135
+ // Ensure all org roles are properly scoped a non-empty organization id.
136
+ // This is just some defensive programming.
137
+ role := roleFunc (scopeID )
138
+ if len (role .Org ) > 0 && scopeID == "" {
139
+ return Role {}, xerrors .Errorf ("expect a scope id for role %q" , roleName )
51
140
}
52
141
53
- return scopedRole , nil
142
+ return role , nil
54
143
}
55
144
56
- func RoleName (name string , scopeID string ) string {
145
+ // roleName is a quick helper function to return
146
+ // role_name:scopeID
147
+ // If no scopeID is required, only 'role_name' is returned
148
+ func roleName (name string , scopeID string ) string {
57
149
if scopeID == "" {
58
150
return name
59
151
}
60
152
return name + ":" + scopeID
61
153
}
154
+
155
+ // permissions is just a helper function to make building roles that list out resources
156
+ // and actions a bit easier.
157
+ func permissions (perms map [Object ][]Action ) []Permission {
158
+ list := make ([]Permission , 0 , len (perms ))
159
+ for k , actions := range perms {
160
+ for _ , act := range actions {
161
+ act := act
162
+ list = append (list , Permission {
163
+ Negate : false ,
164
+ ResourceType : k .Type ,
165
+ ResourceID : WildcardSymbol ,
166
+ Action : act ,
167
+ })
168
+ }
169
+ }
170
+ return list
171
+ }
172
+
173
+ func must [T any ](value T , err error ) T {
174
+ if err != nil {
175
+ panic (err )
176
+ }
177
+ return value
178
+ }
0 commit comments