diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6a4f107a168..931de61f390 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -32,6 +32,7 @@ const dictionary = [ 'customizer', 'dataverse', 'default', + 'definition', 'dev', 'details', 'eligibility', diff --git a/docs/docs/cmd/entra/roledefinition/roledefinition-list.mdx b/docs/docs/cmd/entra/roledefinition/roledefinition-list.mdx new file mode 100644 index 00000000000..b41473b1246 --- /dev/null +++ b/docs/docs/cmd/entra/roledefinition/roledefinition-list.mdx @@ -0,0 +1,148 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# entra roledefinition list + +Lists all Microsoft Entra ID role definitions. + +## Usage + +```sh +m365 entra roledefinition list [options] +``` + +## Options + +```md definition-list +`-p, --properties [properties]` +: Comma-separated list of properties to retrieve. + +`-f, --filter [filter]` +: OData filter to apply when retrieving the role definitions. +``` + + + +## Examples + +Retrieve all Microsoft Entra ID role definitions + +```sh +m365 entra roledefinition list +``` + +Retrieve only the names of the role definitions + +```sh +m365 entra roledefinition list --properties 'displayName' +``` + +Retrieve only custom role definitions + +```sh +m365 entra roledefinition list --filter 'isBuiltIn eq false' +``` + +## Response + +### Standard response + + + + + ```json + [ + { + "id": "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", + "description": "Can manage all aspects of the SharePoint service.", + "displayName": "SharePoint Administrator", + "isBuiltIn": true, + "isEnabled": true, + "resourceScopes": [ + "/" + ], + "templateId": "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.azure.serviceHealth/allEntities/allTasks", + "microsoft.azure.supportTickets/allEntities/allTasks", + "microsoft.backup/oneDriveForBusinessProtectionPolicies/allProperties/allTasks", + "microsoft.backup/oneDriveForBusinessRestoreSessions/allProperties/allTasks", + "microsoft.backup/restorePoints/sites/allProperties/allTasks", + "microsoft.backup/restorePoints/userDrives/allProperties/allTasks", + "microsoft.backup/sharePointProtectionPolicies/allProperties/allTasks", + "microsoft.backup/sharePointRestoreSessions/allProperties/allTasks", + "microsoft.backup/siteProtectionUnits/allProperties/allTasks", + "microsoft.backup/siteRestoreArtifacts/allProperties/allTasks", + "microsoft.backup/userDriveProtectionUnits/allProperties/allTasks", + "microsoft.backup/userDriveRestoreArtifacts/allProperties/allTasks", + "microsoft.directory/groups/hiddenMembers/read", + "microsoft.directory/groups.unified/basic/update", + "microsoft.directory/groups.unified/create", + "microsoft.directory/groups.unified/delete", + "microsoft.directory/groups.unified/members/update", + "microsoft.directory/groups.unified/owners/update", + "microsoft.directory/groups.unified/restore", + "microsoft.office365.migrations/allEntities/allProperties/allTasks", + "microsoft.office365.network/performance/allProperties/read", + "microsoft.office365.serviceHealth/allEntities/allTasks", + "microsoft.office365.sharePoint/allEntities/allTasks", + "microsoft.office365.supportTickets/allEntities/allTasks", + "microsoft.office365.usageReports/allEntities/allProperties/read", + "microsoft.office365.webPortal/allEntities/standard/read" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + { + "id": "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + } + ] + } + ] + ``` + + + + + ```text + id displayName isBuiltIn isEnabled + ------------------------------------ --------------------------------------------- --------- --------- + f28a1f50-f6e7-4571-818b-6a12f2af6b6c SharePoint Administrator true true + ``` + + + + + ```csv + id,description,displayName,isBuiltIn,isEnabled,templateId,version + f28a1f50-f6e7-4571-818b-6a12f2af6b6c,Can manage all aspects of the SharePoint service.,SharePoint Administrator,1,1,f28a1f50-f6e7-4571-818b-6a12f2af6b6c,1 + ``` + + + + + ```md + # entra roledefinition list + + Date: 11/7/2024 + + ## SharePoint Administrator (f28a1f50-f6e7-4571-818b-6a12f2af6b6c) + + Property | Value + ---------|------- + id | f28a1f50-f6e7-4571-818b-6a12f2af6b6c + description | Can manage all aspects of the SharePoint service. + displayName | SharePoint Administrator + isBuiltIn | true + isEnabled | true + templateId | f28a1f50-f6e7-4571-818b-6a12f2af6b6c + version | 1 + ``` + + + diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 06ed8558b56..acf13bcd47b 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -627,6 +627,15 @@ const sidebars: SidebarsConfig = { } ] }, + { + roledefinition: [ + { + type: 'doc', + label: 'roledefinition list', + id: 'cmd/entra/roledefinition/roledefinition-list' + } + ] + }, { siteclassification: [ { diff --git a/src/m365/entra/commands.ts b/src/m365/entra/commands.ts index 4b51b5d02e4..bf397c5f31d 100644 --- a/src/m365/entra/commands.ts +++ b/src/m365/entra/commands.ts @@ -88,6 +88,7 @@ export default { PIM_ROLE_ASSIGNMENT_ELIGIBILITY_LIST: `${prefix} pim role assignment eligibility list`, PIM_ROLE_REQUEST_LIST: `${prefix} pim role request list`, POLICY_LIST: `${prefix} policy list`, + ROLEDEFINITION_LIST: `${prefix} roledefinition list`, SITECLASSIFICATION_DISABLE: `${prefix} siteclassification disable`, SITECLASSIFICATION_ENABLE: `${prefix} siteclassification enable`, SITECLASSIFICATION_GET: `${prefix} siteclassification get`, diff --git a/src/m365/entra/commands/roledefinition/roledefinition-list.spec.ts b/src/m365/entra/commands/roledefinition/roledefinition-list.spec.ts new file mode 100644 index 00000000000..e6e1520c7f0 --- /dev/null +++ b/src/m365/entra/commands/roledefinition/roledefinition-list.spec.ts @@ -0,0 +1,241 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import commands from '../../commands.js'; +import request from '../../../../request.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import command from './roledefinition-list.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import { CommandError } from '../../../../Command.js'; + +describe(commands.ROLEDEFINITION_LIST, () => { + const roleDefinitionsResponse = [ + { + "id": "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", + "description": "Can manage all aspects of the SharePoint service.", + "displayName": "SharePoint Administrator", + "isBuiltIn": true, + "isEnabled": true, + "resourceScopes": [ + "/" + ], + "templateId": "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.azure.serviceHealth/allEntities/allTasks", + "microsoft.azure.supportTickets/allEntities/allTasks", + "microsoft.backup/oneDriveForBusinessProtectionPolicies/allProperties/allTasks", + "microsoft.backup/oneDriveForBusinessRestoreSessions/allProperties/allTasks", + "microsoft.backup/restorePoints/sites/allProperties/allTasks", + "microsoft.backup/restorePoints/userDrives/allProperties/allTasks", + "microsoft.backup/sharePointProtectionPolicies/allProperties/allTasks", + "microsoft.backup/sharePointRestoreSessions/allProperties/allTasks", + "microsoft.backup/siteProtectionUnits/allProperties/allTasks", + "microsoft.backup/siteRestoreArtifacts/allProperties/allTasks", + "microsoft.backup/userDriveProtectionUnits/allProperties/allTasks", + "microsoft.backup/userDriveRestoreArtifacts/allProperties/allTasks", + "microsoft.directory/groups/hiddenMembers/read", + "microsoft.directory/groups.unified/basic/update", + "microsoft.directory/groups.unified/create", + "microsoft.directory/groups.unified/delete", + "microsoft.directory/groups.unified/members/update", + "microsoft.directory/groups.unified/owners/update", + "microsoft.directory/groups.unified/restore", + "microsoft.office365.migrations/allEntities/allProperties/allTasks", + "microsoft.office365.network/performance/allProperties/read", + "microsoft.office365.serviceHealth/allEntities/allTasks", + "microsoft.office365.sharePoint/allEntities/allTasks", + "microsoft.office365.supportTickets/allEntities/allTasks", + "microsoft.office365.usageReports/allEntities/allProperties/read", + "microsoft.office365.webPortal/allEntities/standard/read" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + { + "id": "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + } + ] + }, + { + "id": "abcd1234-de71-4623-b4af-96380a352509", + "description": "Can read Bitlocker keys.", + "displayName": "Bitlocker Keys Reader", + "isBuiltIn": false, + "isEnabled": true, + "resourceScopes": [ + "/" + ], + "templateId": "abcd1234-de71-4623-b4af-96380a352509", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.directory/bitlockerKeys/key/read" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + ] + } + ]; + + const roleDefinitionsLimitedResponse = [ + { + "id": "f28a1f50-f6e7-4571-818b-6a12f2af6b6c", + "displayName": "SharePoint Administrator", + "isBuiltIn": true, + "isEnabled": true + }, + { + "id": "abcd1234-de71-4623-b4af-96380a352509", + "displayName": "Bitlocker Keys Reader", + "isBuiltIn": false, + "isEnabled": true + } + ]; + + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.ROLEDEFINITION_LIST); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('defines correct properties for the default output', () => { + assert.deepStrictEqual(command.defaultProperties(), ['id', 'displayName', 'isBuiltIn', 'isEnabled']); + }); + + it(`should get a list of Entra ID role definitions`, async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions`) { + return { + value: roleDefinitionsResponse + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { verbose: true } + }); + + assert(loggerLogSpy.calledWith(roleDefinitionsResponse)); + }); + + it(`should get a list of Entra ID role definitions with specified properties`, async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions?$select=id,displayName,isBuiltIn,isEnabled`) { + return { + value: roleDefinitionsLimitedResponse + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { properties: 'id,displayName,isBuiltIn,isEnabled' } + }); + + assert(loggerLogSpy.calledWith(roleDefinitionsLimitedResponse)); + }); + + it(`should get a list of filtered Entra ID role definitions`, async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions?$filter=isBuiltIn eq false`) { + return { + value: [roleDefinitionsResponse[1]] + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { filter: 'isBuiltIn eq false' } + }); + + assert(loggerLogSpy.calledWith([roleDefinitionsResponse[1]])); + }); + + it(`should get a list of filtered Entra ID role definitions with specified properties`, async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions?$select=id,displayName,isBuiltIn,isEnabled&$filter=isBuiltIn eq false`) { + return { + value: [roleDefinitionsLimitedResponse[1]] + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { properties: 'id,displayName,isBuiltIn,isEnabled', filter: 'isBuiltIn eq false' } + }); + + assert(loggerLogSpy.calledWith([roleDefinitionsLimitedResponse[1]])); + }); + + it('handles error when retrieving role definitions failed', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions`) { + throw { error: { message: 'An error has occurred' } }; + } + throw `Invalid request`; + }); + + await assert.rejects( + command.action(logger, { options: {} } as any), + new CommandError('An error has occurred') + ); + }); +}); \ No newline at end of file diff --git a/src/m365/entra/commands/roledefinition/roledefinition-list.ts b/src/m365/entra/commands/roledefinition/roledefinition-list.ts new file mode 100644 index 00000000000..846291ef071 --- /dev/null +++ b/src/m365/entra/commands/roledefinition/roledefinition-list.ts @@ -0,0 +1,69 @@ +import { UnifiedRoleDefinition } from '@microsoft/microsoft-graph-types'; +import { Logger } from '../../../../cli/Logger.js'; +import { odata } from '../../../../utils/odata.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; +import { z } from 'zod'; +import { globalOptionsZod } from '../../../../Command.js'; +import { zod } from '../../../../utils/zod.js'; + +const options = globalOptionsZod + .extend({ + properties: zod.alias('p', z.string().optional()), + filter: zod.alias('f', z.string().optional()) + }) + .strict(); + +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +class EntraRoleDefinitionListCommand extends GraphCommand { + public get name(): string { + return commands.ROLEDEFINITION_LIST; + } + + public get description(): string { + return 'Lists all Microsoft Entra ID role definitions'; + } + + public defaultProperties(): string[] | undefined { + return ['id', 'displayName', 'isBuiltIn', 'isEnabled']; + } + + public get schema(): z.ZodTypeAny | undefined { + return options; + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (this.verbose) { + await logger.logToStderr('Getting Microsoft Entra ID role definitions...'); + } + + try { + const queryParameters: string[] = []; + + if (args.options.properties) { + queryParameters.push(`$select=${args.options.properties}`); + } + + if (args.options.filter) { + queryParameters.push(`$filter=${args.options.filter}`); + } + + const queryString = queryParameters.length > 0 + ? `?${queryParameters.join('&')}` + : ''; + + const results = await odata.getAllItems(`${this.resource}/v1.0/roleManagement/directory/roleDefinitions${queryString}`); + await logger.log(results); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new EntraRoleDefinitionListCommand(); \ No newline at end of file