Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workspace user management #4337

Open
wants to merge 54 commits into
base: main
Choose a base branch
from

Conversation

fortunkam
Copy link

@fortunkam fortunkam commented Feb 7, 2025

Resolves #4169 "Enable Workspace Owners to add multiple users and assign WorkspaceResearcher role"

This pull request introduces a new user management feature to the api_app project, along with several related changes. The key changes include the addition of new routes for managing workspace users, updates to the configuration to enable user management, and the creation of new schemas and models to support user roles and assignments.

The User management feature will only be visible if the user is a TreAdmin, the USER_MANAGEMENT_ENABLED flag is enabled and the Workspace has Entra ID/AAD groups enabled.

User Management Feature:

  • api_app/api/routes/api.py: Added workspace_users to the list of routes and included conditional routing based on the USER_MANAGEMENT_ENABLED configuration. [1] [2]
  • api_app/api/routes/workspace_users.py: Introduced new routes for getting workspace users, assignable users, workspace roles, assigning users to roles, and removing user assignments.

Configuration Updates:

  • api_app/core/config.py: Added a new configuration variable USER_MANAGEMENT_ENABLED to toggle user management features.

Schema and Model Additions:

Other Changes:

Version Update:

@github-actions github-actions bot added the external PR from an external contributor label Feb 7, 2025
Copy link

github-actions bot commented Feb 7, 2025

Unit Test Results

617 tests   617 ✅  6s ⏱️
  1 suites    0 💤
  1 files      0 ❌

Results for commit 404e64a.

♻️ This comment has been updated with latest results.

Copy link
Member

@marrobi marrobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to see this, couple of comments, will likely give it a go and review further next week. Thanks!

sp_graph_data = self._get_app_sp_graph_data(workspace.properties["client_id"])
app_id_to_role_name = {app_role["id"]: app_role["value"] for app_role in sp_graph_data["value"][0]["appRoles"]}

for group in (item for item in roles_graph_data["value"] if item["principalType"] == PrincipalType.Group.value):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not just going to add the user to a random group that has been assigned to the app role?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the terraform uses:

resource "azuread_group" "workspace_owners" {
  count            = var.create_aad_groups ? 1 : 0
  display_name     = "${var.workspace_resource_name_suffix} Workspace Owners"
  owners           = [var.workspace_owner_object_id]
  security_enabled = true
}

We should maybe output and store the group IDs as workspace properties? If that property exists we add to the group, if it doesn't we add direct to the app role?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I think when we created this originally we made an assumption that there would only be one group assigned to the app role (for each role type) since that is what the "Add groups" option did. We can make that more explicit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need to, who knows what group might get added to the app role, and then unknowingly other people get added to it and end up with access to something they shouldn't.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to store this in the Cosmos config somewhere or can we just infer the names from convention?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to add the group IDs in outputs.tf for the workspace, and porter.yaml, then the IDs will be stored in cosmos. I think check for existence of that value - will need to be conditional on groups being enabled - and if is there use the ID of the group.

That make sense?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there are already some properties being exported I might be able to use create_aad_groups and can then lookup based on the name and the app_role_id_* properties.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added an implementation of this, let me know what you think.

<Stack tokens={{ childrenGap: 20 }} styles={{ root: { paddingTop: 20 } }}>
<Stack tokens={{ childrenGap: 10 }} verticalAlign="center">
<Label>User</Label>
<TextField
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have a picker here of some sort, I tried with an email in the tenant and got a 500 back. Didn't dig any deeper.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally I had a picker here but it didn't look great with large numbers in the tenant. Ideally what it needs is some sort of search functionality. For external users to a tenant it needs to be added as the user principal name rather than the email address so it is a bit clunky. (e.g. myemail_microsoft.com#EXT#@MyTenant.onmicrosoft.com).

I can readd the name picker as a short term measure and investigate the search functionality as a longer term solution (possibly a new issue)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the picker looks like... got a snip?

Yes, agree, search would work better.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

This was the initial attempt, for each user we show the Display Name and the User Principal Name (email address for non-guest users)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works fine while the GAL is small.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, does it filter as you type or not?

I think add it, and we can maybe modify as we go on. Is it an existing control/component - https://developer.microsoft.com/en-us/fluentui#/controls/web/peoplepicker ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it is a combobox with an API, will add it now and take a look at that link to see if I can get it in easily?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now replaced with the people picker

@jonnyry
Copy link
Collaborator

jonnyry commented Feb 14, 2025

NOTE: To use this feature the API Service principal needs to have 2 more application MS Graph permissions adding. Groups.ReadWrite.All and AppRoleAssignments.ReadWriteAll

Is there any way to use a narrower permission than AppRoleAssignments.ReadWriteAll? IIUC this is a broad permission with the ability to grant any other permission (e.g. global admin).

@marrobi
Copy link
Member

marrobi commented Feb 14, 2025

NOTE: To use this feature the API Service principal needs to have 2 more application MS Graph permissions adding. Groups.ReadWrite.All and AppRoleAssignments.ReadWriteAll

Is there any way to use a narrower permission than AppRoleAssignments.ReadWriteAll? IIUC this is a broad permission with the ability to grant any other permission (e.g. global admin).

The Resource Processor should already have access to these if it created them, maybe it should give the API access to the specific groups and app roles?

@fortunkam
Copy link
Author

NOTE: To use this feature the API Service principal needs to have 2 more application MS Graph permissions adding. Groups.ReadWrite.All and AppRoleAssignments.ReadWriteAll

Is there any way to use a narrower permission than AppRoleAssignments.ReadWriteAll? IIUC this is a broad permission with the ability to grant any other permission (e.g. global admin).

The Resource Processor should already have access to these if it created them, maybe it should give the API access to the specific groups and app roles?

In theory it should be owner on the groups so should be able to access the data but this was the only way we could get it to work. I think the ms graph layer was getting in the way but it needs more research.

@fortunkam fortunkam marked this pull request as ready for review February 17, 2025 10:06
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.

@fortunkam
Copy link
Author

Following discussion the User Management feature will now only be available on workspaces with AAD groups enabled.
The additional Graph API permissions are now not required.

@fortunkam fortunkam marked this pull request as ready for review February 21, 2025 15:01
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Overview

This PR adds a new workspace user management feature that enables TreAdmins and Workspace Owners to manage user assignments and roles, provided that user management is enabled and the workspace is configured with Entra ID/AAD groups. Key changes include:

  • Introducing new API routes for retrieving workspace users, assignable users, workspace roles, and for assigning/removing roles.
  • Adding new domain models and Pydantic schemas to support role assignments.
  • Updating configuration and tests to support user management.

Reviewed Changes

File Description
api_app/api/routes/workspace_users.py New routes for workspace user management are introduced with appropriate dependency injections.
api_app/models/domain/workspace_users.py New domain models for assignable and assigned users, roles, and assignment types.
api_app/models/schemas/roles.py & users.py New/updated schemas reflecting the new user and role operations.
config.sample.yaml Updated configuration flag to enable user management.
api_app/services/aad_authentication.py Updates to Azure AD integration methods to support the new user management functionality.
Tests files New and updated tests to validate the addition and removal of user assignments and to ensure correct API behavior.
api_app/api/routes/api.py & workspaces.py Conditional inclusion of the user management routes based on configuration.

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

if self._is_user_in_role(user_id, role_id):
return
if not self._is_workspace_role_group_in_use(workspace):
logger.error(f"Unable to remove user {user_id} from group with role {role_id}, Entra ID groups are not in use on this workspace")
Copy link
Preview

Copilot AI Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log message in the assign_workspace_user method is misleading as it uses 'remove' instead of 'assign'. Consider updating the message to reference assignment failure for clarity.

Suggested change
logger.error(f"Unable to remove user {user_id} from group with role {role_id}, Entra ID groups are not in use on this workspace")
logger.error(f"Unable to assign user {user_id} to group with role {role_id}, Entra ID groups are not in use on this workspace")

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
@@ -8,7 +8,7 @@ resource "random_uuid" "app_role_workspace_airlock_manager_id" {}
resource "azuread_application" "workspace" {
display_name = var.workspace_resource_name_suffix
identifier_uris = ["api://${var.workspace_resource_name_suffix}"]
owners = [data.azuread_client_config.current.object_id]
owners = [data.azuread_client_config.current.object_id, data.azuread_service_principal.core_api.object_id]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to handle the case where old workspaces do not have this configured.

Copy link
Member

@marrobi marrobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, providing the comments are addressed. Thanks for great contribution!

@marrobi marrobi changed the title Feature/workspace user management Workspace user management Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
external PR from an external contributor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable Workspace Owners to add multiple users and assign WorkspaceResearcher role
3 participants