Skip to content

Commit 53c4ddb

Browse files
committed
chore(playwright): add full cross service e2e tests
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
1 parent e9bfbe7 commit 53c4ddb

1 file changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import type { APIRequestContext } from '@playwright/test'
2+
import { expect, test } from '@playwright/test'
3+
import { clientURL, signInCloudPiNative, tcolinUser } from '../config/console'
4+
import { faker } from '@faker-js/faker'
5+
6+
test.describe('Cross-Service Role Propagation', () => {
7+
const oidcGroupSuffix = faker.string.alpha(10).toLowerCase()
8+
const oidcGroupPath = `/console/test-admin-${oidcGroupSuffix}`
9+
const roleName = `CrossTestRole-${oidcGroupSuffix}`
10+
11+
test('Should propagate admin role to GitLab, SonarQube, and Keycloak', { tag: '@cross-service-role-propagation' }, async ({ page, request }) => {
12+
// 1. Sign in to Console
13+
await page.goto(clientURL)
14+
await signInCloudPiNative({ page, credentials: tcolinUser })
15+
16+
// 2. Configure Plugins to use the test OIDC group
17+
// Configure GitLab
18+
await page.getByTestId('menuAdministrationBtn').click()
19+
await page.getByTestId('menuAdministrationPlugins').click()
20+
await page.getByTestId('gitlab-plugin-config-btn').click()
21+
22+
// Fill Admin Group Path
23+
const gitlabAdminInput = page.getByLabel('Chemin du groupe OIDC Admin')
24+
await gitlabAdminInput.fill(oidcGroupPath)
25+
await page.getByRole('button', { name: 'Enregistrer' }).click()
26+
await expect(page.getByText('Configuration enregistrée')).toBeVisible()
27+
await page.getByRole('button', { name: 'Fermer' }).click()
28+
29+
// Configure SonarQube
30+
await page.getByTestId('sonarqube-plugin-config-btn').click()
31+
const sonarAdminInput = page.getByLabel('Chemin du groupe OIDC Admin')
32+
await sonarAdminInput.fill(oidcGroupPath)
33+
await page.getByRole('button', { name: 'Enregistrer' }).click()
34+
await expect(page.getByText('Configuration enregistrée')).toBeVisible()
35+
await page.getByRole('button', { name: 'Fermer' }).click()
36+
37+
// 3. Create the Role in Console
38+
await page.getByTestId('menuAdministrationRoles').click()
39+
await page.getByTestId('addRoleBtn').click()
40+
await page.getByTestId('roleNameInput').fill(roleName)
41+
await page.getByTestId('oidcGroupInput').fill(oidcGroupPath)
42+
await page.getByTestId('saveBtn').click()
43+
await expect(page.getByTestId('role-list')).toContainText(roleName)
44+
45+
// 4. Create Subject User in Keycloak (via API for speed/reliability)
46+
const targetUser = { email: 'claire.nollet@test.com', id: 'cb8e5b4b-7b7b-40f5-935f-594f48ae6567' } // ID from config/console.ts
47+
48+
// 5. Add User to Role
49+
// Navigate to the role's members
50+
await page.getByTestId(`role-${roleName}-edit`).click()
51+
await page.getByTestId('members-tab').click()
52+
53+
// Add user
54+
await page.getByTestId('addUserSuggestionInput').locator('input').fill(targetUser.email)
55+
await page.getByTestId('addUserBtn').click()
56+
await expect(page.getByTestId(`user-${targetUser.id}`)).toBeVisible()
57+
58+
// 6. Verify in Keycloak
59+
// Check if user is in group `oidcGroupPath`
60+
// Keycloak API
61+
const kcToken = await getKeycloakToken(request)
62+
const kcGroup = await getKeycloakGroup(request, kcToken, oidcGroupPath)
63+
expect(kcGroup, 'Group should exist in Keycloak').toBeDefined()
64+
const kcMembers = await getKeycloakGroupMembers(request, kcToken, kcGroup.id)
65+
const kcMember = kcMembers.find((m: any) => m.email === targetUser.email)
66+
expect(kcMember, 'User should be in Keycloak group').toBeDefined()
67+
68+
// 7. Verify in GitLab
69+
// Check if user is in group (or is admin if we configured admin path)
70+
// Since we configured `adminGroupPath` to `oidcGroupPath`, the user should become an Admin.
71+
const glUser = await getGitLabUser(request, targetUser.email)
72+
expect(glUser.is_admin, 'User should be GitLab Admin').toBe(true)
73+
74+
// 8. Verify in SonarQube
75+
// Check if user is in group `oidcGroupPath`
76+
const sqUser = await getSonarQubeUserGroups(request, targetUser.email)
77+
// The group name in SonarQube will be `oidcGroupPath`.
78+
expect(sqUser.groups, 'User should be in SonarQube group').toContain(oidcGroupPath)
79+
})
80+
})
81+
82+
// Helpers
83+
async function getKeycloakToken(request: APIRequestContext) {
84+
const rootUrl = `${process.env.KEYCLOAK_PROTOCOL}://${process.env.KEYCLOAK_DOMAIN}`
85+
const adminResponse = await request.post(`${rootUrl}/realms/master/protocol/openid-connect/token`, {
86+
form: {
87+
username: process.env.KEYCLOAK_ADMIN || '',
88+
password: process.env.KEYCLOAK_ADMIN_PASSWORD || '',
89+
grant_type: 'password',
90+
client_id: 'admin-cli',
91+
},
92+
})
93+
const json = await adminResponse.json()
94+
return json.access_token
95+
}
96+
97+
async function getKeycloakGroup(request: APIRequestContext, token: string, path: string) {
98+
const rootUrl = `${process.env.KEYCLOAK_PROTOCOL}://${process.env.KEYCLOAK_DOMAIN}`
99+
const response = await request.get(`${rootUrl}/admin/realms/${process.env.KEYCLOAK_REALM}/groups`, {
100+
headers: { Authorization: `Bearer ${token}` },
101+
params: { search: path },
102+
})
103+
const groups = await response.json()
104+
return groups.find((g: any) => g.path === path)
105+
}
106+
107+
async function getKeycloakGroupMembers(request: APIRequestContext, token: string, groupId: string) {
108+
const rootUrl = `${process.env.KEYCLOAK_PROTOCOL}://${process.env.KEYCLOAK_DOMAIN}`
109+
const response = await request.get(`${rootUrl}/admin/realms/${process.env.KEYCLOAK_REALM}/groups/${groupId}/members`, {
110+
headers: { Authorization: `Bearer ${token}` },
111+
})
112+
return await response.json()
113+
}
114+
115+
async function getGitLabUser(request: APIRequestContext, email: string) {
116+
const response = await request.get(`${process.env.GITLAB_URL}/api/v4/users`, {
117+
headers: { 'PRIVATE-TOKEN': process.env.GITLAB_TOKEN || '' },
118+
params: { search: email },
119+
})
120+
const users = await response.json()
121+
return users[0]
122+
}
123+
124+
async function getSonarQubeUserGroups(request: APIRequestContext, email: string) {
125+
const auth = Buffer.from(`${process.env.SONAR_API_TOKEN}:`).toString('base64')
126+
const response = await request.get(`${process.env.SONARQUBE_URL}/api/users/groups`, {
127+
headers: { Authorization: `Basic ${auth}` },
128+
params: { login: email, ps: 500 },
129+
})
130+
return await response.json()
131+
}

0 commit comments

Comments
 (0)