diff --git a/apps/desktop/src/components/admin/AuthorizationScopeEditor.vue b/apps/desktop/src/components/admin/AuthorizationScopeEditor.vue index b9743c46a7..bd22c3873f 100644 --- a/apps/desktop/src/components/admin/AuthorizationScopeEditor.vue +++ b/apps/desktop/src/components/admin/AuthorizationScopeEditor.vue @@ -28,6 +28,11 @@ const props = defineProps<{ connectionId: string; /** 紧凑布局:用于右侧权限编辑面板,隐藏标题区、压缩列表高度并让表范围控件换行显示 */ compact?: boolean; + /** Oracle exposes owner/object selection; it has no all-schema grant scope. */ + objectScope?: { + loadObjects: (owner: string) => Promise; + privileges: readonly string[]; + }; }>(); const emit = defineEmits<{ @@ -45,7 +50,7 @@ const tablesLoading = ref>({}); const tableErrors = ref>({}); const tableSearch = ref>({}); -const supportsTableGrants = computed(() => !!props.provider?.supportsTableGrantsOnCreate); +const supportsTableGrants = computed(() => !!props.objectScope || !!props.provider?.supportsTableGrantsOnCreate); const filteredDatabases = computed(() => { const query = search.value.trim().toLowerCase(); return query ? props.databases.filter((database) => databaseLabel(database).toLowerCase().includes(query)) : props.databases; @@ -114,11 +119,12 @@ function toggleDatabase(database: AuthorizationDatabaseOption) { commitSelections(currentSelections().filter((selection) => authorizationSelectionKey(selection) !== authorizationOptionKey(database))); return; } - commitSelections([...currentSelections(), { database: database.database, ...(database.catalog ? { catalog: database.catalog } : {}), preset: "readOnly", privileges: ["SELECT"] }]); + commitSelections([...currentSelections(), { database: database.database, ...(database.catalog ? { catalog: database.catalog } : {}), preset: "readOnly", privileges: ["SELECT"], ...(props.objectScope ? { tables: [] } : {}) }]); } // 表范围下 MySQL 不支持 CREATE ROUTINE / EVENT 等库级权限,需要按目标作用域过滤可选权限 function privilegesForSelection(selection?: DatabaseAuthorizationSelection): string[] { + if (props.objectScope) return [...props.objectScope.privileges]; const userProvider = props.provider; if (!userProvider) return []; return authorizationPrivileges(userProvider, selection?.tables === undefined ? "database" : "table"); @@ -153,7 +159,9 @@ async function loadTables(database: AuthorizationDatabaseOption) { tableErrors.value = { ...tableErrors.value, [key]: "" }; try { let result: AuthorizationTableSelection[]; - if (props.provider?.authorizationModel === "postgres") { + if (props.objectScope) { + result = await props.objectScope.loadObjects(database.database); + } else if (props.provider?.authorizationModel === "postgres") { const schemas = (await api.listSchemas(props.connectionId, database.database)).filter(isPostgresGrantSchema); const schemaTables = await Promise.all( schemas.map(async (schema) => { @@ -253,11 +261,14 @@ function databaseLabel(option: AuthorizationDatabaseOption): string { } function authorizationTableKey(table: AuthorizationTableSelection): string { - return JSON.stringify([table.schema ?? "", table.name]); + // Oracle allows a PACKAGE/PROCEDURE to share a name with a TABLE in one + // schema; dropping objectType here would make the table unselectable. + return JSON.stringify([table.schema ?? "", table.name, table.objectType ?? ""]); } function tableLabel(table: AuthorizationTableSelection): string { - return table.schema ? `${table.schema}.${table.name}` : table.name; + const name = table.schema ? `${table.schema}.${table.name}` : table.name; + return props.objectScope && table.objectType ? `${name} · ${table.objectType}` : name; } function uniqueTableOptions(options: AuthorizationTableSelection[]): AuthorizationTableSelection[] { @@ -288,15 +299,15 @@ function isPostgresGrantSchema(schema: string): boolean {
- +
- {{ t("userAdmin.loadingDatabases") }} + {{ t(objectScope ? "common.loading" : "userAdmin.loadingDatabases") }}
-
{{ t("userAdmin.emptyDatabases") }}
+
{{ t(objectScope ? "oracleSecurity.empty" : "userAdmin.emptyDatabases") }}
@@ -315,7 +326,7 @@ function isPostgresGrantSchema(schema: string): boolean {
-
+
{{ t("userAdmin.tableScope") }}
- {{ t("userAdmin.loadingTables") }} + {{ t(objectScope ? "common.loading" : "userAdmin.loadingTables") }}
-

{{ t("userAdmin.loadTablesFailed", { message: tableErrors[authorizationOptionKey(database)] }) }}

+

{{ objectScope ? tableErrors[authorizationOptionKey(database)] : t("userAdmin.loadTablesFailed", { message: tableErrors[authorizationOptionKey(database)] }) }}

- {{ t("userAdmin.emptyTables") }} + {{ t(objectScope ? "oracleSecurity.empty" : "userAdmin.emptyTables") }}
-

+

{{ t("userAdmin.tableSelectionRequired") }}

diff --git a/apps/desktop/src/components/admin/DatabaseUserAdmin.vue b/apps/desktop/src/components/admin/DatabaseUserAdmin.vue index 391019da3c..8d9ced72cb 100644 --- a/apps/desktop/src/components/admin/DatabaseUserAdmin.vue +++ b/apps/desktop/src/components/admin/DatabaseUserAdmin.vue @@ -1,5 +1,5 @@