Skip to content

Commit 48b5a49

Browse files
authored
Merge pull request #161 from uselagoon/multi-project-project-default-groups
Update roles and rolesmapping handling to match Lagoon permission logic
2 parents bcb8704 + c205f15 commit 48b5a49

File tree

4 files changed

+264
-69
lines changed

4 files changed

+264
-69
lines changed

internal/sync/roles.go

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package sync
33
import (
44
"context"
55
"fmt"
6-
"strings"
76

87
"github.com/uselagoon/lagoon-opensearch-sync/internal/keycloak"
98
"github.com/uselagoon/lagoon-opensearch-sync/internal/opensearch"
@@ -69,40 +68,13 @@ func isLagoonGroup(
6968
return ok
7069
}
7170

72-
// projectGroupRoleName generates the name of a project group role from the
73-
// ID of the group's project.
74-
func projectGroupRoleName(
75-
group keycloak.Group,
76-
groupProjectsMap map[string][]int,
77-
) (string, error) {
78-
projectIDs, ok := groupProjectsMap[group.ID]
79-
if !ok {
80-
return "", fmt.Errorf("missing project group ID %s in groupProjectsMap",
81-
group.ID)
82-
}
83-
if len(projectIDs) != 1 {
84-
return "", fmt.Errorf("too many projects in group ID %s: %d", group.ID,
85-
len(projectIDs))
86-
}
87-
if projectIDs[0] < 0 {
88-
return "", fmt.Errorf("invalid project ID in group ID %s: %d", group.ID,
89-
projectIDs[0])
90-
}
91-
return fmt.Sprintf("p%d", projectIDs[0]), nil
92-
}
93-
94-
// generateProjectGroupRole constructs an opensearch.Role from the given
95-
// keycloak group corresponding to a Lagoon project group.
96-
func generateProjectGroupRole(
97-
group keycloak.Group,
98-
groupProjectsMap map[string][]int,
99-
) (string, *opensearch.Role, error) {
100-
name, err := projectGroupRoleName(group, groupProjectsMap)
101-
if err != nil {
102-
return "", nil,
103-
fmt.Errorf("couldn't generate project group role name: %v", err)
104-
}
105-
return name, &opensearch.Role{
71+
// generateProjectRole constructs an opensearch.Role from the given
72+
// project ID and project name.
73+
func generateProjectRole(
74+
id int,
75+
name string,
76+
) (string, *opensearch.Role) {
77+
return fmt.Sprintf("p%d", id), &opensearch.Role{
10678
RolePermissions: opensearch.RolePermissions{
10779
// use an empty slice instead of omitting this entirely because the
10880
// Opensearch API errors if this field is omitted.
@@ -115,8 +87,7 @@ func generateProjectGroupRole(
11587
},
11688
IndexPatterns: []string{
11789
fmt.Sprintf(
118-
`/^(application|container|lagoon|router)-logs-%s-_-.+/`,
119-
strings.TrimPrefix(group.Name, "project-")),
90+
`/^(application|container|lagoon|router)-logs-%s-_-.+/`, name),
12091
},
12192
},
12293
},
@@ -127,7 +98,7 @@ func generateProjectGroupRole(
12798
},
12899
},
129100
},
130-
}, nil
101+
}
131102
}
132103

133104
// generateRegularGroupRole constructs an opensearch.Role from the given
@@ -184,10 +155,12 @@ func generateRegularGroupRole(
184155
}
185156

186157
// generateRoles returns a slice of roles generated from the given slice of
187-
// keycloak Groups.
158+
// keycloak Groups, and the projectNames map.
188159
//
189160
// Any groups which are not recognized as either project groups or regular
190161
// Lagoon groups are ignored.
162+
//
163+
// All projectNames map entries generate a single role.
191164
func generateRoles(
192165
log *zap.Logger,
193166
groups []keycloak.Group,
@@ -199,14 +172,7 @@ func generateRoles(
199172
var role *opensearch.Role
200173
var err error
201174
for _, group := range groups {
202-
if isProjectGroup(log, group) {
203-
name, role, err = generateProjectGroupRole(group, groupProjectsMap)
204-
if err != nil {
205-
log.Warn("couldn't generate role for project group",
206-
zap.String("group name", group.Name), zap.Error(err))
207-
continue
208-
}
209-
} else if isLagoonGroup(group, groupProjectsMap) {
175+
if isLagoonGroup(group, groupProjectsMap) && !isProjectGroup(log, group) {
210176
name, role, err =
211177
generateRegularGroupRole(log, group, projectNames, groupProjectsMap)
212178
if err != nil {
@@ -219,6 +185,10 @@ func generateRoles(
219185
roles[name] = *role
220186
}
221187
}
188+
for pid, pname := range projectNames {
189+
name, role = generateProjectRole(pid, pname)
190+
roles[name] = *role
191+
}
222192
return roles
223193
}
224194

internal/sync/roles_test.go

Lines changed: 227 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func TestGenerateRoles(t *testing.T) {
7070
input generateRolesInput
7171
expect generateRolesOutput
7272
}{
73-
"generate roles for regular group": {
73+
"generate roles for regular group and projects": {
7474
input: generateRolesInput{
7575
groups: []keycloak.Group{
7676
{
@@ -121,10 +121,98 @@ func TestGenerateRoles(t *testing.T) {
121121
},
122122
},
123123
},
124+
"p31": {
125+
RolePermissions: opensearch.RolePermissions{
126+
ClusterPermissions: []string{},
127+
IndexPermissions: []opensearch.IndexPermission{
128+
{
129+
AllowedActions: []string{
130+
"read",
131+
"indices:monitor/settings/get",
132+
},
133+
IndexPatterns: []string{
134+
"/^(application|container|lagoon|router)-logs-drupal9-base-_-.+/",
135+
},
136+
},
137+
},
138+
TenantPermissions: []opensearch.TenantPermission{
139+
{
140+
AllowedActions: []string{"kibana_all_read"},
141+
TenantPatterns: []string{"global_tenant"},
142+
},
143+
},
144+
},
145+
},
146+
"p34": {
147+
RolePermissions: opensearch.RolePermissions{
148+
ClusterPermissions: []string{},
149+
IndexPermissions: []opensearch.IndexPermission{
150+
{
151+
AllowedActions: []string{
152+
"read",
153+
"indices:monitor/settings/get",
154+
},
155+
IndexPatterns: []string{
156+
"/^(application|container|lagoon|router)-logs-somelongerprojectname-_-.+/",
157+
},
158+
},
159+
},
160+
TenantPermissions: []opensearch.TenantPermission{
161+
{
162+
AllowedActions: []string{"kibana_all_read"},
163+
TenantPatterns: []string{"global_tenant"},
164+
},
165+
},
166+
},
167+
},
168+
"p35": {
169+
RolePermissions: opensearch.RolePermissions{
170+
ClusterPermissions: []string{},
171+
IndexPermissions: []opensearch.IndexPermission{
172+
{
173+
AllowedActions: []string{
174+
"read",
175+
"indices:monitor/settings/get",
176+
},
177+
IndexPatterns: []string{
178+
"/^(application|container|lagoon|router)-logs-drupal10-prerelease-_-.+/",
179+
},
180+
},
181+
},
182+
TenantPermissions: []opensearch.TenantPermission{
183+
{
184+
AllowedActions: []string{"kibana_all_read"},
185+
TenantPatterns: []string{"global_tenant"},
186+
},
187+
},
188+
},
189+
},
190+
"p36": {
191+
RolePermissions: opensearch.RolePermissions{
192+
ClusterPermissions: []string{},
193+
IndexPermissions: []opensearch.IndexPermission{
194+
{
195+
AllowedActions: []string{
196+
"read",
197+
"indices:monitor/settings/get",
198+
},
199+
IndexPatterns: []string{
200+
"/^(application|container|lagoon|router)-logs-delta-backend-_-.+/",
201+
},
202+
},
203+
},
204+
TenantPermissions: []opensearch.TenantPermission{
205+
{
206+
AllowedActions: []string{"kibana_all_read"},
207+
TenantPatterns: []string{"global_tenant"},
208+
},
209+
},
210+
},
211+
},
124212
},
125213
},
126214
},
127-
"generate roles for project group": {
215+
"generate roles for projects ignoring project group": {
128216
input: generateRolesInput{
129217
groups: []keycloak.Group{
130218
{
@@ -148,6 +236,28 @@ func TestGenerateRoles(t *testing.T) {
148236
},
149237
expect: generateRolesOutput{
150238
roles: map[string]opensearch.Role{
239+
"p26": {
240+
RolePermissions: opensearch.RolePermissions{
241+
ClusterPermissions: []string{},
242+
IndexPermissions: []opensearch.IndexPermission{
243+
{
244+
AllowedActions: []string{
245+
"read",
246+
"indices:monitor/settings/get",
247+
},
248+
IndexPatterns: []string{
249+
"/^(application|container|lagoon|router)-logs-abc-_-.+/",
250+
},
251+
},
252+
},
253+
TenantPermissions: []opensearch.TenantPermission{
254+
{
255+
AllowedActions: []string{"kibana_all_read"},
256+
TenantPatterns: []string{"global_tenant"},
257+
},
258+
},
259+
},
260+
},
151261
"p27": {
152262
RolePermissions: opensearch.RolePermissions{
153263
ClusterPermissions: []string{},
@@ -170,6 +280,121 @@ func TestGenerateRoles(t *testing.T) {
170280
},
171281
},
172282
},
283+
"p48": {
284+
RolePermissions: opensearch.RolePermissions{
285+
ClusterPermissions: []string{},
286+
IndexPermissions: []opensearch.IndexPermission{
287+
{
288+
AllowedActions: []string{
289+
"read",
290+
"indices:monitor/settings/get",
291+
},
292+
IndexPatterns: []string{
293+
"/^(application|container|lagoon|router)-logs-somelongprojectname-_-.+/",
294+
},
295+
},
296+
},
297+
TenantPermissions: []opensearch.TenantPermission{
298+
{
299+
AllowedActions: []string{"kibana_all_read"},
300+
TenantPatterns: []string{"global_tenant"},
301+
},
302+
},
303+
},
304+
},
305+
},
306+
},
307+
},
308+
"generate roles for multi-project project group": {
309+
input: generateRolesInput{
310+
groups: []keycloak.Group{
311+
{
312+
ID: "3fc60c90-b72d-4704-8a57-80438adac98d",
313+
GroupUpdateRepresentation: keycloak.GroupUpdateRepresentation{
314+
Name: "project-beta-ui",
315+
Attributes: map[string][]string{
316+
"type": {`project-default-group`},
317+
},
318+
},
319+
},
320+
},
321+
projectNames: map[int]string{
322+
26: "abc",
323+
27: "beta-ui",
324+
48: "somelongprojectname",
325+
},
326+
groupProjectsMap: map[string][]int{
327+
"3fc60c90-b72d-4704-8a57-80438adac98d": {48, 27, 26},
328+
},
329+
},
330+
expect: generateRolesOutput{
331+
roles: map[string]opensearch.Role{
332+
"p26": {
333+
RolePermissions: opensearch.RolePermissions{
334+
ClusterPermissions: []string{},
335+
IndexPermissions: []opensearch.IndexPermission{
336+
{
337+
AllowedActions: []string{
338+
"read",
339+
"indices:monitor/settings/get",
340+
},
341+
IndexPatterns: []string{
342+
"/^(application|container|lagoon|router)-logs-abc-_-.+/",
343+
},
344+
},
345+
},
346+
TenantPermissions: []opensearch.TenantPermission{
347+
{
348+
AllowedActions: []string{"kibana_all_read"},
349+
TenantPatterns: []string{"global_tenant"},
350+
},
351+
},
352+
},
353+
},
354+
"p27": {
355+
RolePermissions: opensearch.RolePermissions{
356+
ClusterPermissions: []string{},
357+
IndexPermissions: []opensearch.IndexPermission{
358+
{
359+
AllowedActions: []string{
360+
"read",
361+
"indices:monitor/settings/get",
362+
},
363+
IndexPatterns: []string{
364+
"/^(application|container|lagoon|router)-logs-beta-ui-_-.+/",
365+
},
366+
},
367+
},
368+
TenantPermissions: []opensearch.TenantPermission{
369+
{
370+
AllowedActions: []string{"kibana_all_read"},
371+
TenantPatterns: []string{"global_tenant"},
372+
},
373+
},
374+
},
375+
},
376+
"p48": {
377+
RolePermissions: opensearch.RolePermissions{
378+
ClusterPermissions: []string{},
379+
IndexPermissions: []opensearch.IndexPermission{
380+
{
381+
AllowedActions: []string{
382+
"read",
383+
"indices:monitor/settings/get",
384+
},
385+
IndexPatterns: []string{
386+
"/^(application|container|lagoon|router)-logs-somelongprojectname-_-.+/",
387+
},
388+
},
389+
},
390+
TenantPermissions: []opensearch.TenantPermission{
391+
{
392+
AllowedActions: []string{"kibana_all_read"},
393+
TenantPatterns: []string{"global_tenant"},
394+
},
395+
},
396+
},
397+
},
173398
},
174399
},
175400
},

0 commit comments

Comments
 (0)