diff --git a/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue index b87270b07a..b2da2dbb39 100644 --- a/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue +++ b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue @@ -126,7 +126,7 @@ async function onOpenModal({ - +
diff --git a/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue b/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue index 8febd32045..d828b8deb3 100644 --- a/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue +++ b/packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue @@ -56,9 +56,8 @@ const formState = ref({ }, sslUse: SSLUsage.No, extraParameters: [], - meta: { - [SourceRestriction.META_READONLY]: true, - [SourceRestriction.DATA_READONLY]: false, + 'is_schema_readonly': true, + 'is_data_readonly': false, }, }) @@ -249,7 +248,8 @@ const createSource = async () => { config, inflection_column: formState.value.inflection.inflectionColumn, inflection_table: formState.value.inflection.inflectionTable, - meta: formState.value.meta, + is_schema_readonly: formState.value.is_schema_readonly, + is_data_readonly: formState.value.is_data_readonly, }) $poller.subscribe( @@ -401,18 +401,16 @@ const toggleModal = (val: boolean) => { } const allowMetaWrite = computed({ - get: () => !formState.value.meta[SourceRestriction.META_READONLY], + get: () => !formState.value.is_schema_readonly, set: (v) => { - formState.value.meta = formState.value.meta || {} - formState.value.meta[SourceRestriction.META_READONLY] = !v + formState.value.is_schema_readonly = !v }, }) const allowDataWrite = computed({ - get: () => !formState.value.meta[SourceRestriction.DATA_READONLY], + get: () => !formState.value.is_data_readonly, set: (v) => { - formState.value.meta = formState.value.meta || {} - formState.value.meta[SourceRestriction.DATA_READONLY] = !v + formState.value.is_data_readonly = !v }, }) diff --git a/packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue b/packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue index a80fa834fc..43a5a3368b 100644 --- a/packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue +++ b/packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue @@ -61,10 +61,8 @@ const formState = ref({ }, sslUse: SSLUsage.No, extraParameters: [], - meta: { - [SourceRestriction.META_READONLY]: true, - [SourceRestriction.DATA_READONLY]: false, - }, + is_schema_readonly: true, + is_data_readonly: false, }) const customFormState = ref({ @@ -76,10 +74,8 @@ const customFormState = ref({ }, sslUse: SSLUsage.No, extraParameters: [], - meta: { - [SourceRestriction.META_READONLY]: true, - [SourceRestriction.DATA_READONLY]: false, - }, + is_schema_readonly: true, + is_data_readonly: false, }) const validators = computed(() => { @@ -237,7 +233,8 @@ const editBase = async () => { config, inflection_column: formState.value.inflection.inflectionColumn, inflection_table: formState.value.inflection.inflectionTable, - meta: formState.value.meta || {}, + is_schema_readonly: formState.value.is_schema_readonly, + is_data_readonly: formState.value.is_data_readonly, }) $e('a:source:edit:extdb') @@ -349,7 +346,8 @@ onMounted(async () => { }, extraParameters: tempParameters, sslUse: SSLUsage.No, - meta: activeBase.meta || {}, + is_schema_readonly: activeBase.is_schema_readonly, + is_data_readonly: activeBase.is_data_readonly, } updateSSLUse() } @@ -369,18 +367,16 @@ watch( ) const allowMetaWrite = computed({ - get: () => !formState.value.meta[SourceRestriction.META_READONLY], + get: () => !formState.value.is_schema_readonly, set: (v) => { - formState.value.meta = formState.value.meta || {} - formState.value.meta[SourceRestriction.META_READONLY] = !v + formState.value.is_schema_readonly = !v }, }) const allowDataWrite = computed({ - get: () => !formState.value.meta[SourceRestriction.DATA_READONLY], + get: () => !formState.value.is_data_readonly, set: (v) => { - formState.value.meta = formState.value.meta || {} - formState.value.meta[SourceRestriction.DATA_READONLY] = !v + formState.value.is_data_readonly = !v }, }) diff --git a/packages/nc-gui/composables/useRoles/index.ts b/packages/nc-gui/composables/useRoles/index.ts index e64a74183c..79cd906b00 100644 --- a/packages/nc-gui/composables/useRoles/index.ts +++ b/packages/nc-gui/composables/useRoles/index.ts @@ -150,7 +150,7 @@ export const useRolesShared = createSharedComposable(() => { if ( !args.skipSourceCheck && (sourceRestrictions[SourceRestriction.DATA_READONLY][permission] || - sourceRestrictions[SourceRestriction.META_READONLY][permission]) + sourceRestrictions[SourceRestriction.SCHEMA_READONLY][permission]) ) { const source = unref(args.source || null) @@ -159,10 +159,10 @@ export const useRolesShared = createSharedComposable(() => { return false } - if (source?.meta?.[SourceRestriction.DATA_READONLY] && sourceRestrictions[SourceRestriction.DATA_READONLY][permission]) { + if (source?.is_data_readonly && sourceRestrictions[SourceRestriction.DATA_READONLY][permission]) { return false } - if (source?.meta?.[SourceRestriction.META_READONLY] && sourceRestrictions[SourceRestriction.META_READONLY][permission]) { + if (source?.is_schema_readonly && sourceRestrictions[SourceRestriction.SCHEMA_READONLY][permission]) { return false } } @@ -186,11 +186,11 @@ export const useRoles = () => { const useRolesRes = useRolesShared() const isMetaReadOnly = computed(() => { - return currentSource.value?.meta?.[SourceRestriction.META_READONLY] || false + return currentSource.value?.is_schema_readonly || false }) const isDataReadOnly = computed(() => { - return currentSource.value?.meta?.[SourceRestriction.DATA_READONLY] || false + return currentSource.value?.is_schema_readonly || false }) return { diff --git a/packages/nc-gui/lib/acl.ts b/packages/nc-gui/lib/acl.ts index 503c81f052..d0124c0853 100644 --- a/packages/nc-gui/lib/acl.ts +++ b/packages/nc-gui/lib/acl.ts @@ -134,7 +134,7 @@ export const sourceRestrictions = { duplicateModel: true, tableDuplicate: true, }, - [SourceRestriction.META_READONLY]: { + [SourceRestriction.SCHEMA_READONLY]: { tableCreate: true, tableRename: true, tableDelete: true, diff --git a/packages/nocodb-sdk/src/lib/enums.ts b/packages/nocodb-sdk/src/lib/enums.ts index 632d138957..8e358d91a3 100644 --- a/packages/nocodb-sdk/src/lib/enums.ts +++ b/packages/nocodb-sdk/src/lib/enums.ts @@ -329,8 +329,8 @@ export enum APIContext { export enum SourceRestriction { - META_READONLY = 'metaReadOnly', - DATA_READONLY = 'dataReadOnly', + SCHEMA_READONLY = 'is_schema_readonly', + DATA_READONLY = 'is_data_readonly', } diff --git a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts index 8dd0fb24f9..07b9054f2c 100644 --- a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts +++ b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts @@ -37,6 +37,7 @@ import * as nc_047_comment_migration from '~/meta/migrations/v2/nc_047_comment_m import * as nc_048_view_links from '~/meta/migrations/v2/nc_048_view_links'; import * as nc_049_clear_notifications from '~/meta/migrations/v2/nc_049_clear_notifications'; import * as nc_050_tenant_isolation from '~/meta/migrations/v2/nc_050_tenant_isolation'; +import * as nc_051_source_readonly_columns from '~/meta/migrations/v2/nc_051_source_readonly_columns'; // Create a custom migration source class export default class XcMigrationSourcev2 { @@ -85,6 +86,7 @@ export default class XcMigrationSourcev2 { 'nc_048_view_links', 'nc_049_clear_notifications', 'nc_050_tenant_isolation', + 'nc_051_source_readonly_columns', ]); } @@ -172,6 +174,8 @@ export default class XcMigrationSourcev2 { return nc_049_clear_notifications; case 'nc_050_tenant_isolation': return nc_050_tenant_isolation; + case 'nc_051_source_readonly_columns': + return nc_051_source_readonly_columns; } } } diff --git a/packages/nocodb/src/meta/migrations/v2/nc_051_source_readonly_columns.ts b/packages/nocodb/src/meta/migrations/v2/nc_051_source_readonly_columns.ts new file mode 100644 index 0000000000..943f8f11dd --- /dev/null +++ b/packages/nocodb/src/meta/migrations/v2/nc_051_source_readonly_columns.ts @@ -0,0 +1,18 @@ +import type { Knex } from 'knex'; +import { MetaTable } from '~/utils/globals'; + +const up = async (knex: Knex) => { + await knex.schema.alterTable(MetaTable.BASES, (table) => { + table.boolean('is_schema_readonly').defaultTo(false); + table.boolean('is_data_readonly').defaultTo(false); + }); +}; + +const down = async (knex: Knex) => { + await knex.schema.alterTable(MetaTable.BASES, (table) => { + table.dropColumn('is_schema_readonly'); + table.dropColumn('is_data_readonly'); + }); +}; + +export { up, down }; diff --git a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts index 6f2bbaf455..aba3d01af3 100644 --- a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts +++ b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts @@ -471,7 +471,7 @@ export class AclMiddleware implements NestInterceptor { // 1. Check if it's present in the source restriction list // 2. If present, check if write permission is allowed if ( - sourceRestrictions[SourceRestriction.META_READONLY][permissionName] || + sourceRestrictions[SourceRestriction.SCHEMA_READONLY][permissionName] || sourceRestrictions[SourceRestriction.DATA_READONLY][permissionName] ) { let source: Source; @@ -496,14 +496,14 @@ export class AclMiddleware implements NestInterceptor { } if ( - source.meta?.[SourceRestriction.META_READONLY] && - sourceRestrictions[SourceRestriction.META_READONLY][permissionName] + source.is_schema_readonly && + sourceRestrictions[SourceRestriction.SCHEMA_READONLY][permissionName] ) { NcError.sourceMetaReadOnly(source.alias); } if ( - source.meta?.[SourceRestriction.DATA_READONLY] && + source.is_data_readonly && sourceRestrictions[SourceRestriction.DATA_READONLY][permissionName] ) { NcError.sourceDataReadOnly(source.alias); diff --git a/packages/nocodb/src/models/Source.ts b/packages/nocodb/src/models/Source.ts index 09ad96c515..cd77f22547 100644 --- a/packages/nocodb/src/models/Source.ts +++ b/packages/nocodb/src/models/Source.ts @@ -33,6 +33,8 @@ export default class Source implements SourceType { alias?: string; type?: DriverClient; is_meta?: BoolType; + is_schema_readonly?: BoolType; + is_data_readonly?: BoolType; config?: string; inflection_column?: string; inflection_table?: string; @@ -70,6 +72,8 @@ export default class Source implements SourceType { 'order', 'enabled', 'meta', + 'is_schema_readonly', + 'is_data_readonly', ]); insertObj.config = CryptoJS.AES.encrypt( @@ -130,6 +134,8 @@ export default class Source implements SourceType { 'meta', 'deleted', 'fk_sql_executor_id', + 'is_schema_readonly', + 'is_data_readonly', ]); if (updateObj.config) { diff --git a/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts b/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts index 3b3174bfa4..5a2aeda45e 100644 --- a/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts +++ b/packages/nocodb/src/modules/jobs/jobs/export-import/duplicate.controller.ts @@ -214,10 +214,10 @@ export class DuplicateController { const source = await Source.get(context, model.source_id); // if data/schema is readonly, then restrict duplication - if (source.meta?.[SourceRestriction.META_READONLY]) { + if (source.is_schema_readonly) { NcError.sourceMetaReadOnly(source.alias); } - if (source.meta?.[SourceRestriction.DATA_READONLY]) { + if (source.is_data_readonly) { NcError.sourceDataReadOnly(source.alias); } @@ -288,10 +288,10 @@ export class DuplicateController { const source = await Source.get(context, model.source_id); // if data/schema is readonly, then restrict duplication - if (source.meta?.[SourceRestriction.META_READONLY]) { + if (source.is_schema_readonly) { NcError.sourceMetaReadOnly(source.alias); } - if (source.meta?.[SourceRestriction.DATA_READONLY]) { + if (source.is_data_readonly) { NcError.sourceDataReadOnly(source.alias); } diff --git a/packages/nocodb/src/schema/swagger-v2.json b/packages/nocodb/src/schema/swagger-v2.json index 6e2c729b6d..2724fca78a 100644 --- a/packages/nocodb/src/schema/swagger-v2.json +++ b/packages/nocodb/src/schema/swagger-v2.json @@ -12166,6 +12166,14 @@ "$ref": "#/components/schemas/Bool", "description": "Is the data source minimal db" }, + "is_schema_readonly": { + "$ref": "#/components/schemas/Bool", + "description": "Is the data source minimal db" + }, + "is_data_readonly": { + "$ref": "#/components/schemas/Bool", + "description": "Is the data source minimal db" + }, "order": { "description": "The order of the list of sources", "example": 1, diff --git a/packages/nocodb/src/schema/swagger.json b/packages/nocodb/src/schema/swagger.json index 24af650cd1..faa1052e77 100644 --- a/packages/nocodb/src/schema/swagger.json +++ b/packages/nocodb/src/schema/swagger.json @@ -18215,6 +18215,14 @@ "$ref": "#/components/schemas/Bool", "description": "Is the data source minimal db" }, + "is_schema_readonly": { + "$ref": "#/components/schemas/Bool", + "description": "Is the data source minimal db" + }, + "is_data_readonly": { + "$ref": "#/components/schemas/Bool", + "description": "Is the data source minimal db" + }, "order": { "description": "The order of the list of sources", "example": 1, diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index fae07000b7..e3b9300b95 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -201,7 +201,7 @@ export class ColumnsService { // check if source is readonly and column type is not allowed if ( - source?.meta?.[SourceRestriction.META_READONLY] && + source?.is_schema_readonly && (!readonlyMetaAllowedTypes.includes(column.uidt) || (param.column.uidt && !readonlyMetaAllowedTypes.includes(param.column.uidt as UITypes))) @@ -1496,7 +1496,7 @@ export class ColumnsService { // check if source is readonly and column type is not allowed if ( - source?.meta?.[SourceRestriction.META_READONLY] && + source?.is_schema_readonly && !readonlyMetaAllowedTypes.includes(param.column.uidt as UITypes) ) { NcError.sourceMetaReadOnly(source.alias); @@ -2064,7 +2064,7 @@ export class ColumnsService { // check if source is readonly and column type is not allowed if ( - source?.meta?.[SourceRestriction.META_READONLY] && + source?.is_schema_readonly && !readonlyMetaAllowedTypes.includes(column.uidt) ) { NcError.sourceMetaReadOnly(source.alias); diff --git a/packages/nocodb/src/services/forms.service.ts b/packages/nocodb/src/services/forms.service.ts index 9d8b7bb568..fe13a6371e 100644 --- a/packages/nocodb/src/services/forms.service.ts +++ b/packages/nocodb/src/services/forms.service.ts @@ -40,7 +40,7 @@ export class FormsService { const source = await Source.get(context, model.source_id); - if (source.meta?.[SourceRestriction.DATA_READONLY]) { + if (source.is_data_readonly) { NcError.sourceDataReadOnly(source.alias); } diff --git a/packages/nocodb/src/services/public-datas.service.ts b/packages/nocodb/src/services/public-datas.service.ts index 092b0074ff..eb9391d3d3 100644 --- a/packages/nocodb/src/services/public-datas.service.ts +++ b/packages/nocodb/src/services/public-datas.service.ts @@ -299,7 +299,7 @@ export class PublicDatasService { const source = await Source.get(context, model.source_id); - if (source?.meta?.[SourceRestriction.DATA_READONLY]) { + if (source?.is_data_readonly) { NcError.sourceDataReadOnly(source.alias); } diff --git a/packages/nocodb/src/utils/acl.ts b/packages/nocodb/src/utils/acl.ts index e048c70dbb..2d00d814eb 100644 --- a/packages/nocodb/src/utils/acl.ts +++ b/packages/nocodb/src/utils/acl.ts @@ -462,7 +462,7 @@ export const sourceRestrictions = { jsonImport: true, excelImport: true, }, - [SourceRestriction.META_READONLY]: { + [SourceRestriction.SCHEMA_READONLY]: { tableCreate: true, tableRename: true, tableDelete: true,