Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .woodpecker.star
Original file line number Diff line number Diff line change
Expand Up @@ -1492,11 +1492,6 @@ def keycloakService():
}] + waitForServices("keycloak", ["keycloak:8443"])

def e2eTestsOnKeycloak(ctx):
e2e_Keycloak_tests = [
"admin-settings/spaces.feature:25",
"admin-settings/spaces.feature:60",
]

steps = restoreBuildArtifactCache(ctx, "pnpm", ".pnpm-store") + \
installPnpm() + \
restoreBrowsersCache() + \
Expand Down Expand Up @@ -1560,7 +1555,7 @@ def e2eTestsOnKeycloak(ctx):
},
"commands": [
"cd tests/e2e",
"bash run-e2e.sh %s" % " ".join(["cucumber/features/" + tests for tests in e2e_Keycloak_tests]),
"bash run-e2e.sh cucumber/features/keycloak",
],
},
] + \
Expand Down
11 changes: 8 additions & 3 deletions tests/e2e/cucumber/environment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,14 @@ function filterTracingReports(status: string) {
const cleanUpUser = async (createdUserStore, adminUser: User) => {
const requests: Promise<User>[] = []
createdUserStore.forEach((user) => {
requests.push(api.provision.deleteUser({ user, admin: adminUser }))
if (config.keycloak) {
requests.push(api.keycloak.deleteUser({ user }))
} else {
requests.push(api.graph.deleteUser({ user, admin: adminUser }))
}
})
await Promise.all(requests)
createdUserStore.clear()
store.keycloakCreatedUser.clear()
}

const cleanUpSpaces = async (adminUser: User) => {
Expand Down Expand Up @@ -228,7 +231,9 @@ const cleanUpSpaces = async (adminUser: User) => {
const cleanUpGroup = async (adminUser: User) => {
const requests: Promise<Group>[] = []
store.createdGroupStore.forEach((group) => {
if (!group.id.startsWith('keycloak')) {
if (config.keycloak) {
requests.push(api.keycloak.deleteGroup({ group }))
} else {
requests.push(api.graph.deleteGroup({ group, admin: adminUser }))
}
})
Expand Down
67 changes: 0 additions & 67 deletions tests/e2e/cucumber/features/keycloak/groups.feature

This file was deleted.

76 changes: 76 additions & 0 deletions tests/e2e/cucumber/features/keycloak/smoke.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
Feature: keycloak integration
As a user
I want to use Keycloak users and groups in OpenCloud
So that I can verify that Keycloak-created entities are accessible and functional in OpenCloud


Scenario: keycloak integration
Given admin creates following users using keycloak API
| id |
| Alice |
| Brian |
| Carol |
And admin assigns following roles to the users using keycloak API
| id | role |
| Alice | Space Admin |
# Group role assignment - all members of the group inherit the assigned role
And admin creates following groups using keycloak API
| id | role |
| sales | |
| finance | Space Admin |
| security | User |
And admin adds users to the group using keycloak API
| user | group |
| Alice | sales |
| Brian | finance |
| Carol | security |
| Carol | finance |

When "Alice" logs in
Then "Alice" should have self info:
| key | value |
| username | alice |
| displayname | Alice Hansen |
| email | alice@example.org |
| groups | sales |
And "Alice" opens the "files" app
And "Alice" navigates to the projects space page
And "Alice" creates the following project spaces
| name | id |
| teamSpace | teamSpace.1 |
And "Alice" navigates to the project space "teamSpace.1"
And "Alice" creates the following resources
| resource | type |
| security-folder | folder |
| finance-folder | folder |

And "Alice" shares the following resource using the sidebar panel
| resource | recipient | type | role | resourceType |
| finance-folder | finance | group | Can edit | folder |
| finance-folder | Brian | user | Can edit | file |
| security-folder | security | group | Can view | folder |
| security-folder | Carol | user | Can view | file |
And "Alice" logs out

And "Brian" logs in
And "Brian" navigates to the projects space page
And "Brian" creates the following project spaces
| name | id |
| brianSpace | brianSpace.1 |
And "Brian" navigates to the project space "brianSpace.1"
And "Brian" adds following users to the project space
| user | role | kind |
| Carol | Can edit | user |
| security | Can view | group |
And "Brian" logs out

When "Carol" logs in
Then "Carol" should have self info:
| key | value |
| username | carol |
| displayname | Carol King |
| email | carol@example.org |
| groups | finance, security |
And "Carol" opens the "files" app
And "Carol" navigates to the project space "brianSpace.1"
And "Carol" logs out
67 changes: 56 additions & 11 deletions tests/e2e/cucumber/steps/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@ Given(
email: `${uniqueId}@example.org`
}

await api.provision.createUser({ user, admin })
await api.graph.createUser({ user, admin })
}
}
)

Given(
'admin creates following user(s) using keycloak API',
async function (this: World, stepTable: DataTable): Promise<void> {
for (const info of stepTable.hashes()) {
const uniqueId = `${info.id}-${this.uniquePrefix}`
// use a unique user name
const user = {
...this.usersEnvironment.getUser({ key: info.id }),
id: info.id,
username: uniqueId,
email: `${uniqueId}@example.org`
}

await api.keycloak.createUser({ user })
}
}
)
Expand All @@ -30,16 +48,17 @@ Given(
const admin = this.usersEnvironment.getUser({ key: stepUser })
for await (const info of stepTable.hashes()) {
const user = this.usersEnvironment.getCreatedUser({ key: info.id })
/**
The OpenCloud API request for assigning roles allows only one role per user,
whereas the Keycloak API request can assign multiple roles to a user.
If multiple roles are assigned to a user in Keycloak,
OpenCloud map the highest priority role among Keycloak assigned roles.
Therefore, we need to unassign the previous role before
assigning a new one when using the Keycloak API.
*/
await api.provision.unAssignRole({ admin, user })
await api.provision.assignRole({ admin, user, role: info.role })
await api.graph.assignRole(admin, user.uuid, info.role)
}
}
)

Given(
'admin assigns following roles to the user(s) using keycloak API',
async function (this: World, stepTable: DataTable): Promise<void> {
for await (const info of stepTable.hashes()) {
const user = this.usersEnvironment.getCreatedUser({ key: info.id })
await api.keycloak.assignRole({ uuid: user.keycloakUuid, role: info.role })
}
}
)
Expand All @@ -61,6 +80,21 @@ Given(
}
)

Given(
'admin creates following group(s) using keycloak API',
async function (this: World, stepTable: DataTable): Promise<void> {
for (const info of stepTable.hashes()) {
const uniqueId = `${info.id}-${this.uniquePrefix}`
const group = {
...this.usersEnvironment.getGroup({ key: info.id }),
id: info.id,
displayName: uniqueId
}
await api.keycloak.createGroup({ group, role: info.role })
}
}
)

Given(
'{string} adds user(s) to the group(s) using API',
async function (this: World, stepUser: string, stepTable: DataTable): Promise<void> {
Expand All @@ -74,6 +108,17 @@ Given(
}
)

Given(
'admin adds user(s) to the group(s) using keycloak API',
async function (this: World, stepTable: DataTable): Promise<void> {
for (const info of stepTable.hashes()) {
const user = this.usersEnvironment.getCreatedUser({ key: info.user })
const group = this.usersEnvironment.getCreatedGroup({ key: info.group })
await api.keycloak.addUserToGroup({ user, group })
}
}
)

Given(
'{string} creates the following folder(s) in personal space using API',
async function (this: World, stepUser: string, stepTable: DataTable): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/support/api/graph/userManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const addUserToGroup = async ({
body: body,
user: admin
})
checkResponseStatus(response, 'Failed while adding an user to the group')
checkResponseStatus(response, 'Failed while adding a user to the group')
}

export const assignRole = async (admin: User, id: string, role: string): Promise<void> => {
Expand Down
1 change: 0 additions & 1 deletion tests/e2e/support/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export * as graph from './graph'
export * as dav from './davSpaces'
export * as share from './share'
export * as keycloak from './keycloak'
export * as provision from './provision'
export * as settings from './userSettings'
export * as token from './token'
export * as external from './external'
77 changes: 77 additions & 0 deletions tests/e2e/support/api/keycloak/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import join from 'join-path'
import { request, realmBasePath } from './utils'
import { checkResponseStatus } from '../http'
import { Group, User } from '../../types'
import { UsersEnvironment } from '../../environment'
import { getAdminUser, getRealmRole, openCloudKeycloakUserRoles } from './user'

export const createGroup = async ({
group,
role
}: {
group: Group
role?: string
}): Promise<Group> => {
const creationRes = await request({
method: 'POST',
path: join(realmBasePath, 'groups'),
body: { name: group.displayName },
user: getAdminUser(),
header: { 'Content-Type': 'application/json' }
})
checkResponseStatus(creationRes, 'Failed while creating group')
const groupId = creationRes.headers()['location'].split('/').pop()
const usersEnvironment = new UsersEnvironment()
usersEnvironment.storeCreatedGroup({ group: { ...group, keycloakUuid: groupId } })

if (role) {
const roleData = await getRealmRole(openCloudKeycloakUserRoles[role])
const roleAssignmentRes = await request({
method: 'POST',
path: join(realmBasePath, 'groups', groupId, 'role-mappings/realm'),
body: [
{
id: roleData.id,
name: roleData.name,
description: '',
composite: false,
clientRole: false,
containerId: 'openCloud'
}
],
user: getAdminUser(),
header: { 'Content-Type': 'application/json' }
})
checkResponseStatus(roleAssignmentRes, `Failed while assigning role ${role} to group`)
}
return group
}

export const addUserToGroup = async ({
user,
group
}: {
user: User
group: Group
}): Promise<void> => {
const response = await request({
method: 'PUT',
path: join(realmBasePath, 'users', user.keycloakUuid, 'groups', group.keycloakUuid),
body: {},
user: getAdminUser(),
header: { 'Content-Type': 'application/json' }
})
checkResponseStatus(response, 'Failed while adding a user to the group')
}

export const deleteGroup = async ({ group }: { group: Group }): Promise<Group> => {
const response = await request({
method: 'DELETE',
path: join(realmBasePath, 'groups', group.keycloakUuid),
body: {},
user: getAdminUser(),
header: { 'Content-Type': 'application/json' }
})
checkResponseStatus(response, 'Failed while adding a user to the group')
return group
}
1 change: 1 addition & 0 deletions tests/e2e/support/api/keycloak/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './user'
export * from './utils'
export * from './openCloudUserToken'
export * from './group'
Loading