+
+
diff --git a/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.scss b/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.scss
new file mode 100644
index 00000000000..c3f5a7fcd35
--- /dev/null
+++ b/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.scss
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.permission-edit-modal {
+ .permission-item {
+ margin-bottom: 20px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .permission-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 4px;
+
+ .permission-label {
+ font-weight: 600;
+ font-size: 14px;
+ color: rgba(0, 0, 0, 0.85);
+ flex: 1;
+ }
+
+ .permission-control {
+ flex-shrink: 0;
+ margin-left: 16px;
+ }
+ }
+
+ .permission-description {
+ font-size: 12px;
+ color: rgba(0, 0, 0, 0.45);
+ line-height: 1.5;
+ margin: 4px 0 0 0;
+ }
+ }
+
+ .no-permissions {
+ padding: 40px 0;
+ text-align: center;
+ }
+}
diff --git a/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.ts b/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.ts
new file mode 100644
index 00000000000..b0de34c8649
--- /dev/null
+++ b/frontend/src/app/dashboard/component/admin/user/permission-edit-modal/permission-edit-modal.component.ts
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Component, EventEmitter, Input, OnChanges, Output } from "@angular/core";
+import { PermissionTemplate } from "../../../../service/admin/user/admin-user.service";
+import { User } from "../../../../../common/type/user";
+
+@Component({
+ selector: "texera-permission-edit-modal",
+ templateUrl: "./permission-edit-modal.component.html",
+ styleUrls: ["./permission-edit-modal.component.scss"],
+})
+export class PermissionEditModalComponent implements OnChanges {
+ @Input() isVisible: boolean = false;
+ @Input() permissionTemplate: PermissionTemplate | null = null;
+ @Input() user: User | null = null;
+ @Output() save = new EventEmitter();
+ @Output() cancel = new EventEmitter();
+
+ editPermission: string = "{}";
+ permissionObject: any = {};
+ modalTitle: string = "Edit Permissions";
+
+ ngOnChanges(): void {
+ if (this.user) {
+ this.modalTitle = `Edit Permissions for ${this.user.name}`;
+ this.editPermission = this.user.permission || "{}";
+ this.permissionObject = this.parsePermission(this.editPermission);
+ }
+ }
+
+ parsePermission(permission: string): any {
+ try {
+ return JSON.parse(permission);
+ } catch {
+ return {};
+ }
+ }
+
+ getPermissionKeys(): string[] {
+ return this.permissionTemplate ? Object.keys(this.permissionTemplate.permissions) : [];
+ }
+
+ updatePermissionValue(permissionKey: string, value: any): void {
+ this.permissionObject[permissionKey] = value;
+ this.editPermission = JSON.stringify(this.permissionObject);
+ }
+
+ handleCancel(): void {
+ this.cancel.emit();
+ }
+
+ handleSave(): void {
+ this.save.emit(this.editPermission);
+ }
+}
diff --git a/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts b/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
index 481c5e5302e..3d01a8e9057 100644
--- a/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
+++ b/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
@@ -24,10 +24,24 @@ import { AppSettings } from "../../../../common/app-setting";
import { ExecutionQuota, File, Role, User, Workflow } from "../../../../common/type/user";
import { DatasetQuota } from "src/app/dashboard/type/quota-statistic.interface";
+// Permission field schema definition
+export interface PermissionFieldSchema {
+ fieldType: string; // "boolean", "number", or "string"
+ possibleValues: any[]; // List of possible values, empty list if not a category field
+ defaultValue: any; // Default value for this permission
+ description: string; // Human-readable description of what this permission does
+}
+
+// Permission template containing all available permissions
+export interface PermissionTemplate {
+ permissions: { [key: string]: PermissionFieldSchema };
+}
+
export const USER_BASE_URL = `${AppSettings.getApiEndpoint()}/admin/user`;
export const USER_LIST_URL = `${USER_BASE_URL}/list`;
export const USER_UPDATE_URL = `${USER_BASE_URL}/update`;
export const USER_ADD_URL = `${USER_BASE_URL}/add`;
+export const USER_PERMISSION_TEMPLATE_URL = `${USER_BASE_URL}/permission`;
export const USER_CREATED_FILES = `${USER_BASE_URL}/uploaded_files`;
export const USER_UPLOADED_DATASE_SIZE = `${USER_BASE_URL}/dataset_size`;
export const USER_UPLOADED_DATASET_COUNT = `${USER_BASE_URL}/uploaded_dataset`;
@@ -48,16 +62,28 @@ export class AdminUserService {
return this.http.get>(`${USER_LIST_URL}`);
}
- public updateUser(uid: number, name: string, email: string, role: Role, comment: string): Observable {
+ public updateUser(
+ uid: number,
+ name: string,
+ email: string,
+ role: Role,
+ comment: string,
+ permission?: string
+ ): Observable {
return this.http.put(`${USER_UPDATE_URL}`, {
uid: uid,
name: name,
email: email,
role: role,
comment: comment,
+ permission: permission,
});
}
+ public getPermissionTemplate(): Observable {
+ return this.http.get(`${USER_PERMISSION_TEMPLATE_URL}`);
+ }
+
public addUser(): Observable {
return this.http.post(`${USER_ADD_URL}/`, {});
}
diff --git a/sql/texera_ddl.sql b/sql/texera_ddl.sql
index 7b0f9b9063d..229e791822b 100644
--- a/sql/texera_ddl.sql
+++ b/sql/texera_ddl.sql
@@ -100,6 +100,7 @@ CREATE TABLE IF NOT EXISTS "user"
google_avatar VARCHAR(100),
role user_role_enum NOT NULL DEFAULT 'INACTIVE',
comment TEXT,
+ permission JSONB NOT NULL DEFAULT '{}'::jsonb,
account_creation_time TIMESTAMPTZ NOT NULL DEFAULT now(),
-- check that either password or google_id is not null
CONSTRAINT ck_nulltest CHECK ((password IS NOT NULL) OR (google_id IS NOT NULL))
diff --git a/sql/updates/16.sql b/sql/updates/16.sql
new file mode 100644
index 00000000000..b5988290388
--- /dev/null
+++ b/sql/updates/16.sql
@@ -0,0 +1,36 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+-- ============================================
+-- 1. Connect to the texera_db database
+-- ============================================
+\c texera_db
+
+SET search_path TO texera_db;
+
+-- ============================================
+-- 2. Update the table schema
+-- ============================================
+BEGIN;
+
+-- Add new column permission to user table.
+-- Using JSONB type for better performance and indexing capabilities.
+-- Default value is an empty JSON object '{}'.
+ALTER TABLE "user"
+ ADD COLUMN permission JSONB NOT NULL DEFAULT '{}'::jsonb;
+
+COMMIT;