Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.apache.texera.dao.jooq.generated.tables.pojos.WorkflowUserAccess
import org.apache.texera.web.model.common.AccessEntry
import org.apache.texera.web.resource.dashboard.user.workflow.WorkflowAccessResource.{
context,
getPrivilege,
hasWriteAccess
}
import org.jooq.DSLContext
Expand Down Expand Up @@ -174,10 +175,16 @@ class WorkflowAccessResource() {
@PathParam("privilege") privilege: String,
@Auth user: SessionUser
): Unit = {
if (email.equals(user.getEmail)) {
val isModifyingOwnAccess = email.equals(user.getEmail)
val currentPrivilege = getPrivilege(wid, user.getUid)
val hasExistingAccess = !currentPrivilege.eq(PrivilegeEnum.NONE)

// Users can only modify their own access if they already have access
if (isModifyingOwnAccess && !hasExistingAccess) {
throw new BadRequestException("You cannot grant access to yourself!")
}

// Must have write access to modify access levels (including your own)
if (!hasWriteAccess(wid, user.getUid)) {
throw new ForbiddenException(s"You do not have permission to modify workflow $wid")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class="access-button"
[nzType]="isPublic ? 'default' : 'primary'"
(click)="verifyUnpublish()"
[disabled]="!writeAccess">
[disabled]="!hasWriteAccess">
<div
nz-icon
nzType="lock"
Expand All @@ -42,7 +42,7 @@
class="access-button"
[nzType]="isPublic ? 'primary' : 'default'"
(click)="verifyPublish()"
[disabled]="!writeAccess">
[disabled]="!hasWriteAccess">
<div
nz-icon
nzType="user"
Expand Down Expand Up @@ -123,7 +123,7 @@
</select>
</div>
<button
[disabled]="!writeAccess"
[disabled]="!hasWriteAccess"
style="width: 100%"
nz-button
nzType="primary"
Expand Down Expand Up @@ -151,9 +151,16 @@
id="current-share">
<li><nz-tag nzColor="green">OWNER</nz-tag> {{ owner }}</li>
<li *ngFor="let entry of accessList">
<nz-tag nzColor="blue">{{ entry.privilege }}</nz-tag> {{ entry.email }} ({{ entry.name }})
<select
[value]="entry.privilege"
[disabled]="!hasWriteAccess"
(change)="changeAccessLevel(entry.email, $any($event.target).value)">
<option value="READ">READ</option>
<option value="WRITE">WRITE</option>
</select>
{{ entry.email }} ({{ entry.name }})
<button
[disabled]="!writeAccess && entry.email !== currentEmail"
[disabled]="!hasWriteAccess && entry.email !== currentEmail"
(click)="verifyRevokeAccess(entry.email)"
nz-button
nz-tooltip="revoke access"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { Component, EventEmitter, inject, OnDestroy, OnInit, Output } from "@angular/core";
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
import { ShareAccessService } from "../../../service/user/share-access/share-access.service";
import { ShareAccess } from "../../../type/share-access.interface";
import { Privilege, ShareAccess } from "../../../type/share-access.interface";
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
import { UserService } from "../../../../common/service/user/user.service";
import { GmailService } from "../../../../common/service/gmail/gmail.service";
Expand All @@ -40,7 +40,6 @@ import { WorkflowActionService } from "src/app/workspace/service/workflow-graph/
})
export class ShareAccessComponent implements OnInit, OnDestroy {
readonly nzModalData = inject(NZ_MODAL_DATA);
readonly writeAccess: boolean = this.nzModalData.writeAccess;
readonly type: string = this.nzModalData.type;
readonly id: number = this.nzModalData.id;
readonly allOwners: string[] = this.nzModalData.allOwners;
Expand Down Expand Up @@ -76,6 +75,17 @@ export class ShareAccessComponent implements OnInit, OnDestroy {
this.currentEmail = this.userService.getCurrentUser()?.email;
}

get hasWriteAccess(): boolean {
if (!this.currentEmail) {
return false;
}
if (this.currentEmail === this.owner) {
return true;
}
const currentUserAccess = this.accessList.find(entry => entry.email === this.currentEmail);
return currentUserAccess?.privilege === Privilege.WRITE;
}

ngOnInit(): void {
this.accessService
.getAccessList(this.type, this.id)
Expand Down Expand Up @@ -238,6 +248,57 @@ export class ShareAccessComponent implements OnInit, OnDestroy {
});
}

public changeAccessLevel(email: string, newPrivilege: string): void {
const isOwnAccess = email === this.currentEmail;
const currentUserAccess = this.accessList.find(entry => entry.email === email);
const isDowngrade = currentUserAccess?.privilege === Privilege.WRITE && newPrivilege === "READ";

if (isOwnAccess && isDowngrade) {
const modal: NzModalRef = this.modalService.create({
nzTitle: "Downgrade Your Access",
nzContent: `Are you sure you want to change your own access to READ? You will no longer be able to edit this ${this.type} or manage access.`,
nzFooter: [
{
label: "Cancel",
onClick: () => {
modal.close();
this.ngOnInit();
},
},
{
label: "Confirm",
type: "primary",
danger: true,
onClick: () => {
this.applyAccessLevelChange(email, newPrivilege);
modal.close();
},
},
],
});
} else {
this.applyAccessLevelChange(email, newPrivilege);
}
}

private applyAccessLevelChange(email: string, newPrivilege: string): void {
this.accessService
.grantAccess(this.type, this.id, email, newPrivilege)
.pipe(untilDestroyed(this))
.subscribe({
next: () => {
this.notificationService.success(`Access level for ${email} changed to ${newPrivilege}.`);
this.ngOnInit();
},
error: (error: unknown) => {
if (error instanceof HttpErrorResponse) {
this.notificationService.error(error.error.message);
}
this.ngOnInit();
},
});
}

public verifyPublish(): void {
if (!this.isPublic) {
const modal: NzModalRef = this.modalService.create({
Expand Down
Loading