Skip to content

Commit

Permalink
Merge pull request #349 from MerginMaps/use-v2-update-project-access
Browse files Browse the repository at this point in the history
Use v2 update project access
  • Loading branch information
MarcelGeo authored Dec 20, 2024
2 parents df13893 + e61f7b5 commit e84681e
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 106 deletions.
3 changes: 2 additions & 1 deletion server/mergin/sync/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ class ProjectAccessDetail:
role: str
username: str
name: Optional[str]
project_permission: str
workspace_role: str
project_role: Optional[ProjectRole]
type: str


Expand Down
44 changes: 26 additions & 18 deletions server/mergin/sync/private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,7 @@ components:
- id
- type
- email
- project_permission
- role
- workspace_role
properties:
id:
description: User/Invitation (uu)id
Expand All @@ -569,16 +568,9 @@ components:
type: string
format: email
example: [email protected]
role:
workspace_role:
description: Workspace role
type: string
enum:
- owner
- admin
- writer
- editor
- reader
- guest
$ref: "#/components/schemas/WorkspaceRole"
username:
description: Present only for type `member`
type: string
Expand All @@ -587,13 +579,13 @@ components:
description: Present only for type `member`
type: string
example: John Doe
project_permission:
type: string
enum:
- owner
- writer
- editor
- reader
role:
description: Project role defined as combination of project and workspace roles
$ref: "#/components/schemas/ProjectRole"
project_role:
nullable: true
description: Project role defined in database, not calculated version
$ref: "#/components/schemas/ProjectRole"
invitation:
description: Present only for type `invitation`
type: object
Expand Down Expand Up @@ -658,3 +650,19 @@ components:
items:
type: integer
example: [1]
WorkspaceRole:
type: string
enum:
- owner
- admin
- writer
- editor
- reader
- guest
ProjectRole:
type: string
enum:
- owner
- writer
- editor
- reader
1 change: 0 additions & 1 deletion server/mergin/sync/private_api_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
AdminProjectSchema,
ProjectAccessSchema,
ProjectAccessDetailSchema,
ProjectVersionListSchema,
)
from .permissions import (
require_project_by_uuid,
Expand Down
3 changes: 2 additions & 1 deletion server/mergin/sync/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,8 @@ class ProjectAccessDetailSchema(Schema):
role = fields.String()
username = fields.String()
name = fields.String()
project_permission = fields.String()
workspace_role = fields.String()
project_role = fields.String()
type = fields.String()
invitation = fields.Nested(ProjectInvitationAccessSchema())

Expand Down
13 changes: 8 additions & 5 deletions server/mergin/sync/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,17 +323,19 @@ def project_access(self, project: Project) -> List[ProjectAccessDetail]:

direct_members_ids = [u.user_id for u in project.project_users]
users = User.query.filter(User.active.is_(True)).order_by(User.email)
direct_members = users.filter(User.id.in_(direct_members_ids)).all()
direct_members: list[User] = users.filter(User.id.in_(direct_members_ids)).all()

for dm in direct_members:
project_role = ProjectPermissions.get_user_project_role(project, dm)
project_permission = ProjectPermissions.get_user_project_role(project, dm)
project_role = project.get_role(dm.id)
member = ProjectAccessDetail(
id=dm.id,
username=dm.username,
role=ws.get_user_role(dm).value,
workspace_role=ws.get_user_role(dm).value,
name=dm.profile.name(),
email=dm.email,
project_permission=project_role and project_role.value,
role=project_permission and project_permission.value,
project_role=project_role.value if project_role else None,
type="member",
)
result.append(member)
Expand All @@ -345,8 +347,9 @@ def project_access(self, project: Project) -> List[ProjectAccessDetail]:
username=gm.username,
name=gm.profile.name(),
email=gm.email,
workspace_role=global_role,
role=global_role,
project_permission=global_role,
project_role=None,
type="member",
)
result.append(member)
Expand Down
28 changes: 14 additions & 14 deletions server/mergin/tests/test_private_project_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,45 +490,45 @@ def test_get_project_access(client):
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 1
assert resp.json[0]["project_permission"] == "owner"
assert resp.json[0]["role"] == "owner"
project.set_role(users[0].id, ProjectRole.OWNER)
project.set_role(users[1].id, ProjectRole.WRITER)
project.set_role(users[2].id, ProjectRole.READER)
db.session.commit()
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 4
assert sum(map(lambda x: int(x["project_permission"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["project_permission"] == "writer"), resp.json)) == 1
assert sum(map(lambda x: int(x["project_permission"] == "reader"), resp.json)) == 1
assert sum(map(lambda x: int(x["role"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["role"] == "writer"), resp.json)) == 1
assert sum(map(lambda x: int(x["role"] == "reader"), resp.json)) == 1
# user3 does not have access to the project
assert not any(users[3].email == access["email"] for access in resp.json)
assert any(users[2].email == access["email"] for access in resp.json)
Configuration.GLOBAL_READ = True
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 6
assert sum(map(lambda x: int(x["project_permission"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["project_permission"] == "writer"), resp.json)) == 1
assert sum(map(lambda x: int(x["project_permission"] == "reader"), resp.json)) == 3
assert sum(map(lambda x: int(x["role"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["role"] == "writer"), resp.json)) == 1
assert sum(map(lambda x: int(x["role"] == "reader"), resp.json)) == 3
Configuration.GLOBAL_WRITE = True
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 6
assert sum(map(lambda x: int(x["project_permission"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["project_permission"] == "writer"), resp.json)) == 4
assert sum(map(lambda x: int(x["project_permission"] == "reader"), resp.json)) == 0
assert sum(map(lambda x: int(x["role"] == "owner"), resp.json)) == 2
assert sum(map(lambda x: int(x["role"] == "writer"), resp.json)) == 4
assert sum(map(lambda x: int(x["role"] == "reader"), resp.json)) == 0
Configuration.GLOBAL_ADMIN = True
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 6
assert sum(map(lambda x: int(x["project_permission"] == "owner"), resp.json)) == 6
assert sum(map(lambda x: int(x["project_permission"] == "writer"), resp.json)) == 0
assert sum(map(lambda x: int(x["project_permission"] == "reader"), resp.json)) == 0
assert sum(map(lambda x: int(x["role"] == "owner"), resp.json)) == 6
assert sum(map(lambda x: int(x["role"] == "writer"), resp.json)) == 0
assert sum(map(lambda x: int(x["role"] == "reader"), resp.json)) == 0
# pretend a user was deleted to test that api can handle it
users[3].inactivate()
users[3].anonymize()
resp = client.get(url)
assert resp.status_code == 200
assert len(resp.json) == 5
assert sum(map(lambda x: int(x["project_permission"] == "owner"), resp.json)) == 5
assert sum(map(lambda x: int(x["role"] == "owner"), resp.json)) == 5
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ const changeStatusDialog = () => {
await adminStore.updateUser({
username: user.value.username,
data: {
active: !user.value.active
active: !user.value.active,
is_admin: user.value.is_admin
}
})
}
Expand Down
18 changes: 7 additions & 11 deletions web-app/packages/lib/src/common/permission_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export enum WorkspaceRole {
}

export enum ProjectRole {
none,
reader,
editor,
writer,
Expand All @@ -37,9 +36,10 @@ export type WorkspaceRoleName =
| 'admin'
| 'owner'

export type ProjectRoleName =
| Extract<WorkspaceRoleName, 'reader' | 'editor' | 'writer' | 'owner'>
| 'none'
export type ProjectRoleName = Extract<
WorkspaceRoleName,
'reader' | 'editor' | 'writer' | 'owner'
>

export type ProjectPermissionName = 'owner' | 'write' | 'edit' | 'read'

Expand All @@ -63,15 +63,13 @@ export const USER_ROLE_BY_NAME: Record<WorkspaceRoleName, WorkspaceRole> = {
}

export const PROJECT_ROLE_NAME_BY_ROLE: Record<ProjectRole, ProjectRoleName> = {
[ProjectRole.none]: 'none',
[ProjectRole.reader]: 'reader',
[ProjectRole.editor]: 'editor',
[ProjectRole.writer]: 'writer',
[ProjectRole.owner]: 'owner'
}

export const PROJECT_ROLE_BY_NAME: Record<ProjectRoleName, ProjectRole> = {
none: ProjectRole.none,
reader: ProjectRole.reader,
editor: ProjectRole.editor,
writer: ProjectRole.writer,
Expand Down Expand Up @@ -191,21 +189,19 @@ export function getProjectAccessKeyByRoleName(
owner: 'ownersnames',
writer: 'writersnames',
editor: 'editorsnames',
reader: 'readersnames',
none: undefined
reader: 'readersnames'
}
return mapper[roleName]
}

export function getProjectPermissionByRoleName(
roleName: ProjectRoleName
): ProjectPermissionName {
const mapper: Record<ProjectRoleName, ProjectPermissionName | undefined> = {
const mapper: Record<ProjectRoleName, ProjectPermissionName> = {
owner: 'owner',
writer: 'write',
editor: 'edit',
reader: 'read',
none: undefined
reader: 'read'
}
return mapper[roleName]
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export default defineComponent({
await this.acceptProjectAccessRequest({
data,
itemId: request.id,
namespace: this.namespace
workspace: this.namespace
})
await this.updatePaginationOrFetch()
} catch (err) {
Expand All @@ -200,7 +200,7 @@ export default defineComponent({
async cancelRequest(request) {
await this.cancelProjectAccessRequest({
itemId: request.id,
namespace: this.namespace
workspace: this.namespace
})
await this.updatePaginationOrFetch()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export default defineComponent({
await this.acceptProjectAccessRequest({
data,
itemId: request.id,
namespace: this.project.namespace
workspace: this.project.namespace
})
await this.updatePaginationOrFetch()
} catch (err) {
Expand All @@ -173,7 +173,7 @@ export default defineComponent({
async cancelRequest(request) {
await this.cancelProjectAccessRequest({
itemId: request.id,
namespace: this.project.namespace
workspace: this.project.namespace
})
await this.updatePaginationOrFetch()
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-MerginMaps-Commercial
<template #col-roles="{ item }">
<AppDropdown
:options="roles"
:model-value="item.project_permission"
:model-value="item.role"
@update:model-value="(e) => roleUpdate(item, e)"
:disabled="item.id === loggedUser.id"
class="w-6 lg:w-full"
Expand Down Expand Up @@ -163,7 +163,7 @@ function removeMember(item: ProjectAccessDetail) {
function roleUpdate(item: ProjectAccessDetail, value: ProjectRoleName) {
projectStore.updateProjectAccess({
projectId: projectStore.project.id,
userId: item.id,
access: item,
data: { role: value }
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ export default defineComponent({
default: false
},
namespace: String,
asAdmin: {
type: Boolean,
default: false
},
public: {
type: Boolean,
default: true
Expand Down Expand Up @@ -136,9 +132,6 @@ export default defineComponent({
if (projectGridState.namespace) {
params.only_namespace = projectGridState.namespace
}
if (this.asAdmin) {
params.as_admin = true
}
if (!this.public) {
params.public = false
}
Expand Down
Loading

0 comments on commit e84681e

Please sign in to comment.