Skip to content

Commit d18a392

Browse files
author
Matthew Fortunka
committed
refactored to removed unused classes
Added support for multi assignment
1 parent fee832a commit d18a392

File tree

8 files changed

+78
-83
lines changed

8 files changed

+78
-83
lines changed

api_app/api/routes/workspace_users.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from fastapi import APIRouter, Depends, Response, status
22
from api.dependencies.workspaces import get_workspace_by_id_from_path
3-
from models.domain.authentication import AssignmentType
3+
from models.schemas.workspace_users import UserRoleAssignmentRequest
4+
from models.domain.workspace_users import AssignmentType
45
from resources import strings
56
from services.authentication import get_access_service
67
from models.schemas.users import UsersInResponse, AssignableUsersInResponse
@@ -31,13 +32,14 @@ async def get_workspace_roles(workspace=Depends(get_workspace_by_id_from_path),
3132

3233

3334
@workspaces_users_admin_router.post("/workspaces/{workspace_id}/users/assign", status_code=status.HTTP_202_ACCEPTED, name=strings.API_ASSIGN_WORKSPACE_USER)
34-
async def assign_workspace_user(response: Response, user_id: str, role_id: str, workspace=Depends(get_workspace_by_id_from_path), access_service=Depends(get_access_service)) -> UsersInResponse:
35-
36-
access_service.assign_workspace_user(
37-
user_id,
38-
workspace,
39-
role_id
40-
)
35+
async def assign_workspace_user(response: Response, userRoleAssignmentRequest: UserRoleAssignmentRequest, workspace=Depends(get_workspace_by_id_from_path), access_service=Depends(get_access_service)) -> UsersInResponse:
36+
37+
for user_id in userRoleAssignmentRequest.user_ids:
38+
access_service.assign_workspace_user(
39+
user_id,
40+
workspace,
41+
userRoleAssignmentRequest.role_id
42+
)
4143

4244
users = access_service.get_workspace_users(workspace)
4345
return UsersInResponse(users=users)
Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from collections import namedtuple
22
from typing import List
33
from pydantic import BaseModel, Field
4-
from enum import Enum
54

65
RoleAssignment = namedtuple("RoleAssignment", "resource_id, role_id")
76

@@ -11,43 +10,6 @@ class User(BaseModel):
1110
email: str = Field(None)
1211
roles: List[str] = Field([])
1312
roleAssignments: List[RoleAssignment] = Field([])
14-
15-
class Role(BaseModel):
16-
id: str
17-
value: str
18-
isEnabled: bool
19-
email: str = Field(None)
20-
allowedMemberTypes: List[str] = Field([])
21-
description: str
22-
displayName: str
23-
origin: str
24-
roleAssignments: List[RoleAssignment] = Field([])
25-
26-
class AssignableUser(BaseModel):
27-
id: str
28-
displayName: str
29-
userPrincipalName: str
30-
31-
class AssignmentType(Enum):
32-
APP_ROLE = "ApplicationRole"
33-
GROUP = "Group"
34-
35-
class AssignedRole(BaseModel):
36-
id: str
37-
displayName: str
38-
type: AssignmentType
39-
40-
def __eq__(self, other):
41-
return self.id == other.id
42-
43-
def __hash__(self):
44-
return hash(self.id)
45-
46-
class AssignedUser(BaseModel):
47-
id: str
48-
displayName: str
49-
userPrincipalName: str
50-
roles: List[AssignedRole] = Field([])
5113

5214

5315

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import List
2+
from pydantic import BaseModel, Field
3+
from enum import Enum
4+
5+
class AssignableUser(BaseModel):
6+
id: str
7+
displayName: str
8+
userPrincipalName: str
9+
10+
class AssignmentType(Enum):
11+
APP_ROLE = "ApplicationRole"
12+
GROUP = "Group"
13+
14+
class Role(BaseModel):
15+
id: str
16+
displayName: str
17+
type: AssignmentType
18+
19+
def __eq__(self, other):
20+
return self.id == other.id
21+
22+
def __hash__(self):
23+
return hash(self.id)
24+
25+
class AssignedUser(BaseModel):
26+
id: str
27+
displayName: str
28+
userPrincipalName: str
29+
roles: List[Role] = Field([])

api_app/models/schemas/roles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from pydantic import BaseModel, Field
22
from typing import List
3-
from models.domain.authentication import Role
3+
from models.domain.workspace_users import Role
44

55

66
class RolesInResponse(BaseModel):

api_app/models/schemas/users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from pydantic import BaseModel, Field
22
from typing import List
33

4-
from models.domain.authentication import AssignedUser, AssignableUser
4+
from models.domain.workspace_users import AssignedUser, AssignableUser
55

66

77
class UsersInResponse(BaseModel):
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import List
2+
from pydantic import BaseModel, Field
3+
4+
5+
class UserRoleAssignmentRequest(BaseModel):
6+
role_id: str = Field(title="Role Id", description="Role to assign users to")
7+
user_ids: List[str] = Field([], title="List of User Ids", description="List of User Ids to assign the role to")
8+
9+
class Config:
10+
schema_extra = {
11+
"example": {
12+
"role_id": "1234",
13+
"user_ids": ["1", "2"]
14+
}
15+
}

api_app/services/aad_authentication.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from services.access_service import AccessService, AuthConfigValidationError, UserRoleAssignmentError
1313
from core import config
1414
from db.errors import EntityDoesNotExist
15-
from models.domain.authentication import AssignedRole, AssignedUser, AssignmentType, User, AssignableUser, Role, RoleAssignment
15+
from models.domain.authentication import User, RoleAssignment
16+
from models.domain.workspace_users import AssignedUser, AssignmentType, AssignableUser, Role
1617
from models.domain.workspace import Workspace, WorkspaceRole
1718
from resources import strings
1819
from db.repositories.workspaces import WorkspaceRepository
@@ -264,11 +265,11 @@ def _get_user_details(self, roles_graph_data, msgraph_token):
264265

265266
return users_graph_data
266267

267-
def _get_roles_for_principal(self, user_id, roles_graph_data, app_id_to_role_name, assignmentType: AssignmentType = AssignmentType.APP_ROLE) -> List[AssignedRole]:
268+
def _get_roles_for_principal(self, user_id, roles_graph_data, app_id_to_role_name, assignmentType: AssignmentType = AssignmentType.APP_ROLE) -> List[Role]:
268269
roles = []
269270
for role_assignment in roles_graph_data["value"]:
270271
if role_assignment["principalId"] == user_id:
271-
roles.append(AssignedRole(id=role_assignment["appRoleId"], displayName=app_id_to_role_name[role_assignment["appRoleId"]], type=assignmentType))
272+
roles.append(Role(id=role_assignment["appRoleId"], displayName=app_id_to_role_name[role_assignment["appRoleId"]], type=assignmentType))
272273
return roles
273274

274275
def _get_users_inc_groups_from_response(self, users_graph_data, roles_graph_data, app_id_to_role_name) -> List[AssignedUser]:
@@ -340,13 +341,14 @@ def get_workspace_roles(self, workspace: Workspace) -> List[Role]:
340341

341342
roles = []
342343

344+
roleAssignmentType = AssignmentType.APP_ROLE
345+
if self._is_workspace_role_group_in_use(workspace):
346+
roleAssignmentType = AssignmentType.GROUP
347+
343348
for role in graph_data["value"]:
344-
roles.append(Role(id=role["id"], value=role["value"],
345-
isEnabled=role["isEnabled"],
346-
description=role["description"],
349+
roles.append(Role(id=role["id"],
347350
displayName=role["displayName"],
348-
origin=role["origin"],
349-
allowedMemberTypes=role["allowedMemberTypes"]))
351+
type=roleAssignmentType))
350352

351353
return roles
352354

@@ -361,21 +363,6 @@ def get_workspace_user_emails_by_role_assignment(self, workspace: Workspace):
361363
workspace_role_assignments_details[role].append(user.email)
362364
return workspace_role_assignments_details
363365

364-
def get_workspace_role_by_name(self, name: str, workspace: Workspace) -> Role:
365-
app_roles_endpoint = f"{MICROSOFT_GRAPH_URL}/v1.0/servicePrincipals/{workspace.properties['sp_id']}/appRoles"
366-
graph_data = self._ms_graph_query(app_roles_endpoint, "GET")
367-
368-
for role in graph_data["value"]:
369-
if role["value"] == name:
370-
return Role(id=role["id"], value=role["value"],
371-
isEnabled=role["isEnabled"],
372-
description=role["description"],
373-
displayName=role["displayName"],
374-
origin=role["origin"],
375-
allowedMemberTypes=role["allowedMemberTypes"])
376-
377-
return None
378-
379366
def assign_workspace_user(self, user_id: str, workspace: Workspace, role_id: str) -> None:
380367
# User already has the role, do nothing
381368
if self._is_user_in_role(user_id, role_id):
@@ -385,7 +372,7 @@ def assign_workspace_user(self, user_id: str, workspace: Workspace, role_id: str
385372
else:
386373
return self._assign_workspace_user_to_application(user_id, workspace, role_id)
387374

388-
def _is_user_in_role(self, user_id: User, role_id: Role) -> bool:
375+
def _is_user_in_role(self, user_id: str, role_id: str) -> bool:
389376
user_app_role_query = f"{MICROSOFT_GRAPH_URL}/v1.0/users/{user_id}/appRoleAssignments"
390377
user_app_roles = self._ms_graph_query(user_app_role_query, "GET")
391378
return any(r for r in user_app_roles["value"] if r["appRoleId"] == role_id)

ui/app/src/components/workspaces/WorkspaceUsersAssignNew.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,15 @@ export const WorkSpaceUsersAssignNew: React.FunctionComponent<WorkspaceUsersAssi
7575

7676
const onChange = (items?: IPersonaProps[] | undefined): void => {
7777
if (items && items.length > 0) {
78-
setSelectedUser(items[0].key as string);
78+
setSelectedUsers(items.map(item => item.key as string));
7979
}
8080
else {
81-
setSelectedUser(null);
81+
setSelectedUsers(null);
8282
}
8383
};
8484

8585
const [roleOptions, setRoleOptions] = useState<IDropdownOption[]>([]);
86-
const [selectedUser, setSelectedUser] = useState<string | null>(null);
86+
const [selectedUsers, setSelectedUsers] = useState<string[] | null>(null);
8787
const [selectedRole, setSelectedRole] = useState<string | null>(null);
8888
const [assigning, setAssigning] = useState(false);
8989
const [hasAssignmentError, setHasAssignmentError] = useState(false);
@@ -120,11 +120,12 @@ export const WorkSpaceUsersAssignNew: React.FunctionComponent<WorkspaceUsersAssi
120120
const assign = useCallback(async () => {
121121
setAssigning(true);
122122

123-
const encodedUser = selectedUser?.replaceAll('#', '%23');
124-
125123
const scopeId = "";
126124
try {
127-
const response = await apiCall(`${ApiEndpoint.Workspaces}/${workspace.id}/${ApiEndpoint.Users}/assign?user_id=${selectedUser}&role_id=${selectedRole}`, HttpMethod.Post, scopeId);
125+
const response = await apiCall(`${ApiEndpoint.Workspaces}/${workspace.id}/${ApiEndpoint.Users}/assign`, HttpMethod.Post, scopeId, {
126+
role_id: selectedRole,
127+
user_ids: selectedUsers
128+
});
128129
props.onAssignUser(response);
129130
}
130131
catch (err: any) {
@@ -134,21 +135,21 @@ export const WorkSpaceUsersAssignNew: React.FunctionComponent<WorkspaceUsersAssi
134135
}
135136
setAssigning(false);
136137

137-
}, [selectedUser, apiCall, workspace.id, selectedRole, props]);
138+
}, [selectedUsers, apiCall, workspace.id, selectedRole, props]);
138139

139140
const renderFooter = useCallback(() => {
140141
let footer = <></>
141142
footer = <>
142143
<div style={{ textAlign: 'end' }}>
143-
<PrimaryButton onClick={() => assign()} disabled={assigning || (!selectedUser || !selectedRole)}>Assign</PrimaryButton>
144+
<PrimaryButton onClick={() => assign()} disabled={assigning || (!selectedUsers || !selectedRole)}>Assign</PrimaryButton>
144145
</div>
145146
</>
146147
return footer;
147-
}, [selectedUser, selectedRole, assign, assigning]);
148+
}, [selectedUsers, selectedRole, assign, assigning]);
148149

149150
return (
150151
<Panel
151-
headerText="Assign user to a role"
152+
headerText="Assign users to a role"
152153
isOpen={true}
153154
isLightDismiss={true}
154155
onDismiss={dismissPanel}
@@ -174,7 +175,6 @@ export const WorkSpaceUsersAssignNew: React.FunctionComponent<WorkspaceUsersAssi
174175
componentRef={picker}
175176
resolveDelay={300}
176177
required={true}
177-
itemLimit={1}
178178
onChange={onChange}
179179
/>
180180
</Stack>

0 commit comments

Comments
 (0)