From a10e39b22638b019504d14f1ee151a323701549a Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Wed, 28 Aug 2024 10:17:32 +0530 Subject: [PATCH 1/5] feat: add custom role support in audit plugin --- packages/contentstack-audit/package.json | 2 +- .../src/audit-base-command.ts | 17 +- .../contentstack-audit/src/config/index.ts | 7 +- .../contentstack-audit/src/messages/index.ts | 2 + .../src/modules/custom-roles.ts | 168 ++++++++++++++++++ .../src/types/custom-role.ts | 84 +++++++++ .../contentstack-audit/src/types/index.ts | 1 + packages/contentstack-import/package.json | 2 +- .../src/import/module-importer.ts | 4 +- .../src/utils/import-config-handler.ts | 2 + packages/contentstack/package.json | 2 +- 11 files changed, 282 insertions(+), 9 deletions(-) create mode 100644 packages/contentstack-audit/src/modules/custom-roles.ts create mode 100644 packages/contentstack-audit/src/types/custom-role.ts diff --git a/packages/contentstack-audit/package.json b/packages/contentstack-audit/package.json index 6854a14795..257c59e215 100644 --- a/packages/contentstack-audit/package.json +++ b/packages/contentstack-audit/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/cli-audit", - "version": "1.6.5", + "version": "1.7.0", "description": "Contentstack audit plugin", "author": "Contentstack CLI", "homepage": "https://github.com/contentstack/cli", diff --git a/packages/contentstack-audit/src/audit-base-command.ts b/packages/contentstack-audit/src/audit-base-command.ts index f7f87fd7d6..86a2ff76f4 100644 --- a/packages/contentstack-audit/src/audit-base-command.ts +++ b/packages/contentstack-audit/src/audit-base-command.ts @@ -20,6 +20,7 @@ import { RefErrorReturnType, WorkflowExtensionsRefErrorReturnType, } from './types'; +import CustomRoles from './modules/custom-roles'; export abstract class AuditBaseCommand extends BaseCommand { private currentCommand!: CommandNames; @@ -56,6 +57,7 @@ export abstract class AuditBaseCommand extends BaseCommand) { + this.log = log; + this.config = config; + this.fix = fix ?? false; + this.customRoleSchema = []; + this.moduleName = this.validateModules(moduleName!, this.config.moduleConfig); + this.fileName = config.moduleConfig[this.moduleName].fileName; + this.folderPath = resolve( + sanitizePath(config.basePath), + sanitizePath(config.moduleConfig[this.moduleName].dirName), + ); + this.missingFieldsInCustomRoles = []; + this.customRolePath = ''; + this.isBranchFixDone = false; + } + validateModules( + moduleName: keyof typeof auditConfig.moduleConfig, + moduleConfig: Record, + ): keyof typeof auditConfig.moduleConfig { + if (Object.keys(moduleConfig).includes(moduleName)) { + return moduleName; + } + return 'custom-roles'; + } + + /** + * Check whether the given path for the custom role exists or not + * If path exist read + * From the ctSchema add all the content type UID into ctUidSet to check whether the content-type is present or not + * @returns Array of object containing the custom role name, uid and content_types that are missing + */ + async run() { + if (!existsSync(this.folderPath)) { + this.log(`Skipping ${this.moduleName} audit`, 'warn'); + this.log($t(auditMsg.NOT_VALID_PATH, { path: this.folderPath }), { color: 'yellow' }); + return {}; + } + + this.customRolePath = join(this.folderPath, this.fileName); + this.customRoleSchema = existsSync(this.customRolePath) + ? values(JSON.parse(readFileSync(this.customRolePath, 'utf8')) as CustomRole[]) + : []; + + for (let index = 0; index < this.customRoleSchema?.length; index++) { + const customRole = this.customRoleSchema[index]; + let branchesToBeRemoved: string[] = []; + if (this.config?.branch) { + customRole?.rules?.filter((rule) => { + if (rule.module === 'branch') { + branchesToBeRemoved = rule?.branches?.filter((branch) => branch !== this.config?.branch) || []; + } + }); + } + + if (branchesToBeRemoved?.length) { + this.isBranchFixDone = true; + const tempCR = cloneDeep(customRole); + + if (customRole?.rules && this.config?.branch) { + tempCR.rules.forEach((rule: Rule) => { + if (rule.module === 'branch') { + rule.branches = branchesToBeRemoved; + } + }); + } + + this.missingFieldsInCustomRoles.push(tempCR); + } + + this.log( + $t(auditMsg.SCAN_CR_SUCCESS_MSG, { + name: customRole.name, + uid: customRole.uid, + }), + 'info', + ); + } + + if (this.fix && (this.missingFieldsInCustomRoles.length || this.isBranchFixDone)) { + await this.fixCustomRoleSchema(); + this.missingFieldsInCustomRoles.forEach((cr) => (cr.fixStatus = 'Fixed')); + } + + return this.missingFieldsInCustomRoles; + } + + async fixCustomRoleSchema() { + const newCustomRoleSchema: Record = existsSync(this.customRolePath) + ? JSON.parse(readFileSync(this.customRolePath, 'utf8')) + : {}; + + if (Object.keys(newCustomRoleSchema).length === 0 || !this.customRoleSchema?.length) { + return; + } + + this.customRoleSchema.forEach((customRole) => { + if (!this.config.branch) return; + + const fixedBranches = customRole.rules + ?.filter((rule) => rule.module === 'branch' && rule.branches?.length) + ?.reduce((acc: string[], rule) => { + const relevantBranches = + rule.branches?.filter((branch) => { + if (branch !== this.config.branch) { + this.log( + $t(commonMsg.CR_BRANCH_REMOVAL, { + uid: customRole.uid, + name: customRole.name, + branch, + }), + { color: 'yellow' }, + ); + return false; + } + return true; + }) || []; + return [...acc, ...relevantBranches]; + }, []); + + if (fixedBranches?.length) { + newCustomRoleSchema[customRole.uid].rules + ?.filter((rule: Rule) => rule.module === 'branch') + ?.forEach((rule) => { + rule.branches = fixedBranches; + }); + } + }); + + await this.writeFixContent(newCustomRoleSchema); + } + + async writeFixContent(newCustomRoleSchema: Record) { + if ( + this.fix && + (this.config.flags['copy-dir'] || + this.config.flags['external-config']?.skipConfirm || + this.config.flags.yes || + (await ux.confirm(commonMsg.FIX_CONFIRMATION))) + ) { + writeFileSync( + join(this.folderPath, this.config.moduleConfig[this.moduleName].fileName), + JSON.stringify(newCustomRoleSchema), + ); + } + } +} diff --git a/packages/contentstack-audit/src/types/custom-role.ts b/packages/contentstack-audit/src/types/custom-role.ts new file mode 100644 index 0000000000..2fa2ff92e2 --- /dev/null +++ b/packages/contentstack-audit/src/types/custom-role.ts @@ -0,0 +1,84 @@ +export interface CustomRole { + name: string; + description: string; + rules: Rule[]; + users: string[]; + uid: string; + created_by: string; + updated_by: string; + created_at: string; + updated_at: string; + owner: string; + stack: Stack; + permissions: Permissions; + SYS_ACL: Record; + fixStatus?: string; + missingRefs?: any; +} + +export interface Rule { + module: string; + environments?: string[]; + locales?: string[]; + branches?: string[]; + acl: ACL; +} + +interface ACL { + read: boolean; +} + +interface Stack { + created_at: string; + updated_at: string; + uid: string; + name: string; + org_uid: string; + api_key: string; + master_locale: string; + is_asset_download_public: boolean; + owner_uid: string; + user_uids: string[]; + settings: Settings; + master_key: string; +} + +interface Settings { + version: string; + rte_version: number; + blockAuthQueryParams: boolean; + allowedCDNTokens: string[]; + branches: boolean; + localesOptimization: boolean; + webhook_enabled: boolean; + stack_variables: Record; + live_preview: Record; + discrete_variables: DiscreteVariables; + language_fallback: boolean; +} + +interface DiscreteVariables { + cms: boolean; + _version: number; + secret_key: string; +} + +interface Permissions { + content_types: ContentType[]; + environments: string[]; + locales: Locale[]; +} + +interface ContentType { + uid: string; + SYS_ACL: SysACL; +} + +interface SysACL { + sub_acl: Record; +} + +interface Locale { + uid: string; + SYS_ACL: ACL; +} \ No newline at end of file diff --git a/packages/contentstack-audit/src/types/index.ts b/packages/contentstack-audit/src/types/index.ts index b4d01a8167..d023a44290 100644 --- a/packages/contentstack-audit/src/types/index.ts +++ b/packages/contentstack-audit/src/types/index.ts @@ -4,3 +4,4 @@ export * from './entries'; export * from './content-types'; export * from './workflow'; export * from './extensions'; +export * from './custom-role'; diff --git a/packages/contentstack-import/package.json b/packages/contentstack-import/package.json index d252aa576d..04e85c6b30 100644 --- a/packages/contentstack-import/package.json +++ b/packages/contentstack-import/package.json @@ -5,7 +5,7 @@ "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { - "@contentstack/cli-audit": "~1.6.5", + "@contentstack/cli-audit": "~1.7.0", "@contentstack/cli-command": "~1.3.0", "@contentstack/cli-utilities": "~1.7.1", "@contentstack/management": "~1.17.0", diff --git a/packages/contentstack-import/src/import/module-importer.ts b/packages/contentstack-import/src/import/module-importer.ts index 85ce4a8820..b22e60b3ef 100755 --- a/packages/contentstack-import/src/import/module-importer.ts +++ b/packages/contentstack-import/src/import/module-importer.ts @@ -49,7 +49,7 @@ class ModuleImporter { if ( !this.importConfig.skipAudit && (!this.importConfig.moduleName || - ['content-types', 'global-fields', 'entries', 'extensions', 'workflows'].includes(this.importConfig.moduleName)) + ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles'].includes(this.importConfig.moduleName)) ) { if (!(await this.auditImportData(logger))) { return { noSuccessMsg: true }; @@ -136,7 +136,7 @@ class ModuleImporter { args.push('--modules', this.importConfig.moduleName); } else if (this.importConfig.modules.types.length) { this.importConfig.modules.types - .filter((val) => ['content-types', 'global-fields', 'entries', 'extensions', 'workflows'].includes(val)) + .filter((val) => ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles'].includes(val)) .forEach((val) => { args.push('--modules', val); }); diff --git a/packages/contentstack-import/src/utils/import-config-handler.ts b/packages/contentstack-import/src/utils/import-config-handler.ts index f266634ba2..568d686f4a 100644 --- a/packages/contentstack-import/src/utils/import-config-handler.ts +++ b/packages/contentstack-import/src/utils/import-config-handler.ts @@ -79,6 +79,8 @@ const setupConfig = async (importCmdFlags: any): Promise => { if (importCmdFlags['branch']) { config.branchName = importCmdFlags['branch']; config.branchDir = path.join(sanitizePath(config.contentDir), sanitizePath(config.branchName)); + } else { + config.branchName = 'main'; } if (importCmdFlags['module']) { config.moduleName = importCmdFlags['module']; diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index fc6d9f807c..3b6014ce11 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -22,7 +22,7 @@ "prepack": "pnpm compile && oclif manifest && oclif readme" }, "dependencies": { - "@contentstack/cli-audit": "~1.6.5", + "@contentstack/cli-audit": "~1.7.0", "@contentstack/cli-auth": "~1.3.20", "@contentstack/cli-cm-bootstrap": "~1.10.0", "@contentstack/cli-cm-branches": "~1.1.2", From ce833d5ca4c242deba6780f515b4ce693b002bb1 Mon Sep 17 00:00:00 2001 From: Aman Kumar Date: Wed, 28 Aug 2024 10:28:52 +0530 Subject: [PATCH 2/5] updated lock file & formatting --- package-lock.json | 12 ++++---- packages/contentstack-audit/README.md | 28 ++++++++++--------- packages/contentstack-import/README.md | 2 +- packages/contentstack-import/package.json | 2 +- .../src/import/module-importer.ts | 14 ++++++++-- packages/contentstack-seed/package.json | 2 +- packages/contentstack/README.md | 28 +++++++++---------- packages/contentstack/package.json | 2 +- pnpm-lock.yaml | 8 +++--- 9 files changed, 54 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 918e1b23bb..9211335d5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26340,7 +26340,7 @@ "version": "1.24.0", "license": "MIT", "dependencies": { - "@contentstack/cli-audit": "~1.6.5", + "@contentstack/cli-audit": "~1.7.0", "@contentstack/cli-auth": "~1.3.20", "@contentstack/cli-cm-bootstrap": "~1.10.0", "@contentstack/cli-cm-branches": "~1.1.2", @@ -26348,7 +26348,7 @@ "@contentstack/cli-cm-clone": "~1.10.7", "@contentstack/cli-cm-export": "~1.11.7", "@contentstack/cli-cm-export-to-csv": "~1.7.2", - "@contentstack/cli-cm-import": "~1.16.6", + "@contentstack/cli-cm-import": "~1.16.7", "@contentstack/cli-cm-migrate-rte": "~1.4.18", "@contentstack/cli-cm-seed": "~1.7.8", "@contentstack/cli-command": "~1.3.0", @@ -26405,7 +26405,7 @@ }, "packages/contentstack-audit": { "name": "@contentstack/cli-audit", - "version": "1.6.5", + "version": "1.7.0", "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.3.0", @@ -28104,10 +28104,10 @@ }, "packages/contentstack-import": { "name": "@contentstack/cli-cm-import", - "version": "1.16.6", + "version": "1.16.7", "license": "MIT", "dependencies": { - "@contentstack/cli-audit": "~1.6.5", + "@contentstack/cli-audit": "~1.7.0", "@contentstack/cli-command": "~1.3.0", "@contentstack/cli-utilities": "~1.7.1", "@contentstack/management": "~1.17.0", @@ -28625,7 +28625,7 @@ "version": "1.7.8", "license": "MIT", "dependencies": { - "@contentstack/cli-cm-import": "~1.16.6", + "@contentstack/cli-cm-import": "~1.16.7", "@contentstack/cli-command": "~1.3.0", "@contentstack/cli-utilities": "~1.7.1", "inquirer": "8.2.4", diff --git a/packages/contentstack-audit/README.md b/packages/contentstack-audit/README.md index 630088644a..6f8c8d9104 100644 --- a/packages/contentstack-audit/README.md +++ b/packages/contentstack-audit/README.md @@ -19,7 +19,7 @@ $ npm install -g @contentstack/cli-audit $ csdx COMMAND running command... $ csdx (--version|-v) -@contentstack/cli-audit/1.6.5 darwin-arm64 node-v22.2.0 +@contentstack/cli-audit/1.7.0 darwin-arm64 node-v22.2.0 $ csdx --help [COMMAND] USAGE $ csdx COMMAND @@ -52,12 +52,13 @@ Perform audits and find possible errors in the exported Contentstack data ``` USAGE - $ csdx audit [--report-path ] [--modules content-types|global-fields|entries|extensions|workflows] - [--columns | ] [--sort ] [--filter ] [--csv | --no-truncate] + $ csdx audit [--report-path ] [--modules + content-types|global-fields|entries|extensions|workflows|custom-roles] [--columns | ] [--sort ] + [--filter ] [--csv | --no-truncate] FLAGS --modules=