Skip to content

Commit

Permalink
feat: view,add,remove platform roles on users
Browse files Browse the repository at this point in the history
  • Loading branch information
shreddedbacon committed Nov 19, 2024
1 parent 7bc11f0 commit ef75e4f
Show file tree
Hide file tree
Showing 14 changed files with 594 additions and 40 deletions.
4 changes: 3 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ services:
api:
image: ${IMAGE_REPO:-lagoon}/api:${IMAGE_REPO_API_TAG:-${IMAGE_REPO_TAG:-latest}}
environment:
- KEYCLOAK_FRONTEND_URL=http://localhost:8088/
- KEYCLOAK_FRONTEND_URL=http://localhost:8088/auth
- NODE_ENV=development
- OPENSEARCH_INTEGRATION_ENABLED=false
- DISABLE_CORE_HARBOR=true
Expand All @@ -97,6 +97,8 @@ services:
- S3_BAAS_SECRET_ACCESS_KEY=minio123
- CONSOLE_LOGGING_LEVEL=debug
- SIDECAR_HANDLER_HOST=apisidecarhandler
- SSH_TOKEN_ENDPOINT=localhost
- SSH_TOKEN_ENDPOINT_PORT=2020
depends_on:
api-lagoon-migrations:
condition: service_started
Expand Down
19 changes: 15 additions & 4 deletions docs/interacting/rbac.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,24 @@ When assigning a user to a group, you need to provide a group role for that user

### Platform-Wide Roles

#### Platform-Wide Admin

The platform-wide admin has access to everything across all of Lagoon. That includes dangerous mutations like deleting all projects. Use very, _very,_ **very** carefully.
Platform-wide roles are typically assigned to people that manage Lagoon.

#### Platform-Wide Owner

The platform-wide owner has access to every Lagoon group, like the group owner role, and can be used if you need a user that needs access to everything but you don't want to assign the user to every group.
The platform-wide owner has access to everything across all of Lagoon.

#### Platform-Wide Viewer

Similar to the platform-wide owner, except this role can only view.

#### Platform-Wide Organization Owner

The platform-wide organization owner role provides permission to create, update, delete, and all other permissions related to changes withing an organization, including existing organizations. It does not grant full platform-wide owner access, this means the ability to access and deploy projects still needs to be granted via a group within an organization.

This role also has the ability to view all the deploytargets (kubernetes clusters) assigned to Lagoon so that they can be assigned to an organization when it is being created.

!!! Warning "NOTE"
By default this role does not allow the creation of environments or the ability to trigger deployments within a project within an organization. They can add themselves to a group with a role that does grant them this permission.

### Organization Roles

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,48 @@ mutation PopulateApi {
id
}

UserExamplePlatformOrgOwner: addUser(
input: {
email: "[email protected]"
comment: "platform organization owner user"
}
) {
id
}

## Assign platform owner role
UserExamplePlatformOwnerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: OWNER
){
email
platformRoles
}

## Assign platform viewer role
UserExamplePlatformViewerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: VIEWER
){
email
platformRoles
}

## Assign platform owner role
UserExamplePlatformOrgOwnerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: ORGANIZATIONOWNER
){
email
platformRoles
}

LagoonDemoGroup: addGroup(
input: {
name: "lagoon-demo-group"
Expand Down
42 changes: 42 additions & 0 deletions local-dev/k3d-seed-data/00-populate-kubernetes.gql
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,48 @@ mutation PopulateApi {
id
}

UserExamplePlatformOrgOwner: addUser(
input: {
email: "[email protected]"
comment: "platform organization owner user"
}
) {
id
}

## Assign platform owner role
UserExamplePlatformOwnerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: OWNER
){
email
platformRoles
}

## Assign platform viewer role
UserExamplePlatformViewerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: VIEWER
){
email
platformRoles
}

## Assign platform organization owner role
UserExamplePlatformOrgOwnerRole: addPlatformRoleToUser(
user:{
email:"[email protected]"
}
role: ORGANIZATIONOWNER
){
email
platformRoles
}

LagoonDemoGroup: addGroup(
input: {
name: "lagoon-demo-group"
Expand Down
19 changes: 7 additions & 12 deletions local-dev/k3d-seed-data/seed-users.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ function is_keycloak_running {
function configure_user_passwords {

LAGOON_DEMO_USERS=("[email protected]" "[email protected]" "[email protected]" "[email protected]" "[email protected]")
LAGOON_DEMO_ORG_USERS=("[email protected]" "[email protected]" "[email protected]" "[email protected]" "[email protected]" "[email protected]")
LAGOON_DEMO_ORG_USERS=("[email protected]" "[email protected]" "[email protected]" "[email protected]")
LAGOON_DEMO_PLATFORM_USERS=("[email protected]" "[email protected]" "[email protected]")

for i in ${LAGOON_DEMO_USERS[@]}
do
Expand All @@ -23,16 +24,12 @@ function configure_user_passwords {
echo Configuring password for $i
/opt/keycloak/bin/kcadm.sh set-password --config $CONFIG_PATH --username $i -p $i --target-realm lagoon
done
}

function configure_platformowner {
echo Configuring platform owner role
/opt/keycloak/bin/kcadm.sh add-roles --uusername [email protected] --rolename platform-owner --config $CONFIG_PATH --target-realm lagoon
}

function configure_platformviewer {
echo Configuring platform viewer role
/opt/keycloak/bin/kcadm.sh add-roles --uusername [email protected] --rolename platform-viewer --config $CONFIG_PATH --target-realm lagoon
for i in ${LAGOON_DEMO_PLATFORM_USERS[@]}
do
echo Configuring password for $i
/opt/keycloak/bin/kcadm.sh set-password --config $CONFIG_PATH --username $i -p $i --target-realm lagoon
done
}

function configure_keycloak {
Expand All @@ -49,8 +46,6 @@ function configure_keycloak {
/opt/keycloak/bin/kcadm.sh config credentials --config $CONFIG_PATH --server http://localhost:8080/auth --user $KEYCLOAK_ADMIN_USER --password $KEYCLOAK_ADMIN_PASSWORD --realm master

configure_user_passwords
configure_platformowner
configure_platformviewer

echo "Config of Keycloak users done"
}
Expand Down
105 changes: 104 additions & 1 deletion services/api/src/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface User {
owner?: boolean;
admin?: boolean;
organizationRole?: string;
platformRoles?: [string];
}

interface UserEdit {
Expand All @@ -42,6 +43,7 @@ interface UserEdit {

export interface UserModel {
loadAllUsers: () => Promise<User[]>;
loadAllPlatformUsers: () => Promise<User[]>;
loadUserById: (id: string) => Promise<User>;
loadUserByUsername: (email: string) => Promise<User>;
loadUserByIdOrUsername: (userInput: UserEdit) => Promise<User>;
Expand All @@ -55,6 +57,8 @@ export interface UserModel {
userGroups: Group[]
) => Promise<string[]>;
addUser: (userInput: User, resetPassword?: Boolean) => Promise<User>;
addPlatformRoleToUser: (userInput: User, role: string) => Promise<User>;
removePlatformRoleFromUser: (userInput: User, role: string) => Promise<User>;
updateUser: (userInput: UserEdit) => Promise<User>;
deleteUser: (id: string) => Promise<void>;
resetUserPassword: (id: string) => Promise<void>;
Expand Down Expand Up @@ -165,7 +169,7 @@ export const User = (clients: {
(keycloakUser: UserRepresentation): User =>
// @ts-ignore
R.pipe(
R.pick(['id', 'email', 'username', 'firstName', 'lastName', 'attributes', 'admin', 'owner', 'organizationRole']),
R.pick(['id', 'email', 'username', 'firstName', 'lastName', 'attributes', 'admin', 'owner', 'organizationRole', 'platformRoles']),
// @ts-ignore
R.set(commentLens, R.view(attrCommentLens, keycloakUser))
)(keycloakUser)
Expand Down Expand Up @@ -348,6 +352,102 @@ export const User = (clients: {
return users;
};

const loadAllPlatformUsers = async (): Promise<User[]> => {
let platformUsers = [];
const keycloakPlatformOwners = await keycloakAdminClient.roles.findUsersWithRole({
name: "platform-owner",
max: -1
});
for (const f1 in keycloakPlatformOwners) {
keycloakPlatformOwners[f1].platformRoles = []
const found = platformUsers.findIndex(el => el.email === keycloakPlatformOwners[f1].email);
if (found === -1) {
keycloakPlatformOwners[f1].platformRoles.push("owner")
platformUsers.push(keycloakPlatformOwners[f1])
} else {
platformUsers[found].platformRoles.push("owner")
}
}
const keycloakPlatformViewers = await keycloakAdminClient.roles.findUsersWithRole({
name: "platform-viewer",
max: -1
});
for (const f1 in keycloakPlatformViewers) {
keycloakPlatformViewers[f1].platformRoles = []
const found = platformUsers.findIndex(el => el.email === keycloakPlatformViewers[f1].email);
if (found === -1) {
keycloakPlatformViewers[f1].platformRoles.push("viewer")
platformUsers.push(keycloakPlatformViewers[f1])
} else {
platformUsers[found].platformRoles.push("viewer")
}
}
const keycloakPlatformOrgOwners = await keycloakAdminClient.roles.findUsersWithRole({
name: "platform-organization-owner",
max: -1
});
for (const f1 in keycloakPlatformOrgOwners) {
keycloakPlatformOrgOwners[f1].platformRoles = []
const found = platformUsers.findIndex(el => el.email === keycloakPlatformOrgOwners[f1].email);
if (found === -1) {
keycloakPlatformOrgOwners[f1].platformRoles.push("organizationowner")
platformUsers.push(keycloakPlatformOrgOwners[f1])
} else {
platformUsers[found].platformRoles.push("organizationowner")
}
}
const users = await transformKeycloakUsers(platformUsers);
return users;
};

const platformRoleSwitch = (role) => {
let computedRole = "platform-viewer"
switch (role) {
case "owner":
computedRole = "platform-owner"
break;
case "viewer":
computedRole = "platform-viewer"
break;
case "organizationowner":
computedRole = "platform-organization-owner"
break;
default:
break;
}
return computedRole
}

const addPlatformRoleToUser = async (userInput: User, role: string): Promise<User> => {
let computedRole = platformRoleSwitch(role)
const kcRole = await keycloakAdminClient.roles.findOneByName({
name: computedRole
});
let keycloakUser = await keycloakAdminClient.users.addRealmRoleMappings({
id: userInput.id,
roles: [{
id: kcRole.id,
name: kcRole.name
}]
});
return keycloakUser
}

const removePlatformRoleFromUser = async (userInput: User, role: string): Promise<User> => {
let computedRole = platformRoleSwitch(role)
const kcRole = await keycloakAdminClient.roles.findOneByName({
name: computedRole
});
const keycloakUser = await keycloakAdminClient.users.delRealmRoleMappings({
id: userInput.id,
roles: [{
id: kcRole.id,
name: kcRole.name
}]
});
return keycloakUser
}

const getAllGroupsForUser = async (
userId: string,
organization?: number,
Expand Down Expand Up @@ -707,6 +807,7 @@ export const User = (clients: {

return {
loadAllUsers,
loadAllPlatformUsers,
loadUserById,
loadUserByUsername,
loadUserByIdOrUsername,
Expand All @@ -716,6 +817,8 @@ export const User = (clients: {
getAllProjectsIdsForUser,
getUserRolesForProject,
addUser,
addPlatformRoleToUser,
removePlatformRoleFromUser,
updateUser,
userLastAccessed,
deleteUser,
Expand Down
15 changes: 13 additions & 2 deletions services/api/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ const {
deleteUser,
getAllUsers,
getUserByEmail,
getAllPlatformUsers,
addPlatformRoleToUser,
removePlatformRoleFromUser,
} = require('./resources/user/resolvers');

const {
Expand Down Expand Up @@ -376,6 +379,11 @@ const resolvers = {
ACTIVE: 'active',
SUCCEEDED: 'succeeded',
},
PlatformRole: {
OWNER: 'owner',
VIEWER: 'viewer',
ORGANIZATIONOWNER: 'organizationowner',
},
Openshift: {
projectUser: getProjectUser,
token: getToken,
Expand Down Expand Up @@ -581,7 +589,8 @@ const resolvers = {
getGroupProjectOrganizationAssociation,
getProjectGroupOrganizationAssociation,
getEnvVariablesByProjectEnvironmentName,
checkBulkImportProjectsAndGroupsToOrganization
checkBulkImportProjectsAndGroupsToOrganization,
allPlatformUsers: getAllPlatformUsers,
},
Mutation: {
addProblem,
Expand Down Expand Up @@ -705,7 +714,9 @@ const resolvers = {
removeUserFromOrganizationGroups,
bulkImportProjectsAndGroupsToOrganization,
addOrUpdateEnvironmentService,
deleteEnvironmentService
deleteEnvironmentService,
addPlatformRoleToUser,
removePlatformRoleFromUser,
},
Subscription: {
backupChanged: backupSubscriber,
Expand Down
Loading

0 comments on commit ef75e4f

Please sign in to comment.