diff --git a/estela-api/api/serializers/auth.py b/estela-api/api/serializers/auth.py index 57ab6b91..1a264a45 100644 --- a/estela-api/api/serializers/auth.py +++ b/estela-api/api/serializers/auth.py @@ -73,7 +73,8 @@ class UserProfileSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User - fields = ["username", "email", "password"] + fields = ["username", "email", "password", "is_superuser"] + read_only_fields = ["is_superuser"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/estela-api/api/serializers/project.py b/estela-api/api/serializers/project.py index c6830ebd..3d9c43f3 100644 --- a/estela-api/api/serializers/project.py +++ b/estela-api/api/serializers/project.py @@ -121,6 +121,7 @@ class ProjectUpdateSerializer(serializers.ModelSerializer): ("ADMIN", "Admin"), ("DEVELOPER", "Developer"), ("VIEWER", "Viewer"), + ("OWNER", "Owner") ] pid = serializers.UUIDField( read_only=True, help_text="A UUID identifying this project." diff --git a/estela-api/api/views/project.py b/estela-api/api/views/project.py index fa7aaf81..5b42b812 100644 --- a/estela-api/api/views/project.py +++ b/estela-api/api/views/project.py @@ -151,6 +151,19 @@ def update(self, request, *args, **kwargs): instance.users.remove(affected_user) description = f"removed user {user_email}." elif action == "update": + if permission == Permission.OWNER_PERMISSION: + if not is_superuser: + raise PermissionDenied( + {"permission": "You do not have permission to do this."} + ) + old_owner = instance.users.filter( + permission__permission=Permission.OWNER_PERMISSION + ).get() + instance.users.remove(old_owner) + instance.users.add( + old_owner, + through_defaults={"permission": Permission.ADMIN_PERMISSION}, + ) instance.users.remove(affected_user) instance.users.add( affected_user, through_defaults={"permission": permission} diff --git a/estela-api/docs/api.yaml b/estela-api/docs/api.yaml index f2db1c03..0e83dfd4 100644 --- a/estela-api/docs/api.yaml +++ b/estela-api/docs/api.yaml @@ -1709,6 +1709,12 @@ definitions: title: Password type: string minLength: 1 + is_superuser: + title: Superuser status + description: Designates that this user has all permissions without explicitly + assigning them. + type: boolean + readOnly: true User: required: - username @@ -1966,6 +1972,7 @@ definitions: - ADMIN - DEVELOPER - VIEWER + - OWNER data_status: title: Data status description: New data status. diff --git a/estela-web/src/pages/ProjectMemberPage/index.tsx b/estela-web/src/pages/ProjectMemberPage/index.tsx index f8951551..487d6595 100644 --- a/estela-web/src/pages/ProjectMemberPage/index.tsx +++ b/estela-web/src/pages/ProjectMemberPage/index.tsx @@ -40,6 +40,7 @@ interface ProjectMemberPageState { newUser: string; members: MemberState[]; permission: ProjectUpdatePermissionEnum; + changeRolePermissions: ProjectUpdatePermissionEnum[]; } interface RouteParams { @@ -58,6 +59,11 @@ export class ProjectMemberPage extends Component { + const requestParams = { username: AuthService.getUserUsername() || "" }; + this.apiService.apiAuthProfileRead(requestParams).then( + (response) => { + this.setState({ + changeRolePermissions: + response.isSuperuser === true + ? [...this.state.changeRolePermissions, ProjectUpdatePermissionEnum.Owner] + : [...this.state.changeRolePermissions], + }); + }, + (error: unknown) => { + error; + }, + ); + }; + updateInfo = (): void => { const requestParams: ApiProjectsReadRequest = { pid: this.projectId }; this.apiService.apiProjectsRead(requestParams).then( @@ -117,6 +140,7 @@ export class ProjectMemberPage extends Component { + this.verifyIsSuperuser(); this.updateInfo(); } @@ -324,27 +348,15 @@ export class ProjectMemberPage extends Component - - - + {this.state.changeRolePermissions.map((permission, index) => ( + + ))}