diff --git a/docs/docs/cmd/spo/file/file-roleassignment-add.mdx b/docs/docs/cmd/spo/file/file-roleassignment-add.mdx index 88cb89ed03e..f4f2a67b989 100644 --- a/docs/docs/cmd/spo/file/file-roleassignment-add.mdx +++ b/docs/docs/cmd/spo/file/file-roleassignment-add.mdx @@ -26,10 +26,16 @@ m365 spo file roleassignment add [options] : The SharePoint Id of the principal. It may be either a user id or group id to add a role assignment for. Specify either `upn`, `groupName`, or `principalId` but not multiple. `--upn [upn]` -: The upn/email of user to assign role to. Specify either `upn`, `groupName`, or `principalId` but not multiple. +: The upn/email of user to assign role to. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. `--groupName [groupName]` -: The group name of the SharePoint group Specify either `upn`, `groupName`, or `principalId` but not multiple. +: The group name of the SharePoint group Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. This option is only used for SharePoint Online groups. + +`--entraGroupId [entraGroupId]` +: ID of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. + +`--entraGroupName [entraGroupName]` +: Display name of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. `--roleDefinitionId [roleDefinitionId]` : ID of role definition. Specify either `roleDefinitionId` or `roleDefinitionName` but not both. @@ -54,12 +60,23 @@ Adds a role assignment to a file with a specified site-relative URL for a specif m365 spo file roleassignment add --webUrl "https://contoso.sharepoint.com/sites/project-x" --fileUrl "Shared Documents/Test1.docx" --upn "testuser@tenant.onmicrosoft.com" --roleDefinitionName "Full Control" ``` -Adds a role assignment to a file with a specified server-relative URL the for a specific group and a role definition name. +Adds a role assignment to a file with a specified server-relative URL the for a specific SharePoint Online group and a role definition name. ```sh -m365 spo file roleassignment add --webUrl "https://contoso.sharepoint.com/sites/project-x" --fileUrl "/sites/project-x/documents/Test1.docx" --upn "testuser@tenant.onmicrosoft.com" --roleDefinitionName "Read" +m365 spo file roleassignment add --webUrl "https://contoso.sharepoint.com/sites/project-x" --fileUrl "/sites/project-x/documents/Test1.docx" --groupName "Sales Group" --roleDefinitionName "Read" ``` +Adds a role assignment to a file with a specified site-relative URL for a specific Entra Group Id and a role definition name. + +```sh +m365 spo file roleassignment add --webUrl "https://contoso.sharepoint.com/sites/project-x" --fileUrl "Shared Documents/Test1.docx" --entraGroupId 3c97e01e-978d-417b-a196-b6a3e37fa873 --roleDefinitionName "Full Control" +``` + +Adds a role assignment to a file with a specified site-relative URL for a specific Entra Group Name and a role definition name. + +```sh +m365 spo file roleassignment add --webUrl "https://contoso.sharepoint.com/sites/project-x" --fileUrl "Shared Documents/Test1.docx" --entraGroupName "Marketing Memebers" --roleDefinitionName "Full Control" +``` ## Response The command won't return a response on success. diff --git a/docs/docs/cmd/spo/file/file-roleassignment-remove.mdx b/docs/docs/cmd/spo/file/file-roleassignment-remove.mdx index b5382d1f618..7aa22b934d4 100644 --- a/docs/docs/cmd/spo/file/file-roleassignment-remove.mdx +++ b/docs/docs/cmd/spo/file/file-roleassignment-remove.mdx @@ -23,13 +23,19 @@ m365 spo file roleassignment remove [options] : The UniqueId (GUID) of the file. Specify either `fileUrl` or `fileId` but not both. `--principalId [principalId]` -: The SharePoint Id of the principal. It may be either a user id or group id. Specify either `upn`, `groupName`, or `principalId` but not multiple. +: The SharePoint Id of the principal. It may be either a user id or group id. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. `--upn [upn]` -: The upn/email of the user. Specify either `upn`, `groupName`, or `principalId` but not multiple. +: The upn/email of the user. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. `--groupName [groupName]` -: The group name of the SharePoint group Specify either `upn`, `groupName`, or `principalId` but not multiple. +: The group name of the SharePoint group Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. This option is only used for SharePoint Online groups. + +`--entraGroupId [entraGroupId]` +: ID of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. + +`--entraGroupName [entraGroupName]` +: Display name of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple. `-f, --force` : Don't prompt for confirmation. @@ -57,6 +63,17 @@ Remove a role assignment by group name from a file by id. m365 spo file roleassignment remove --webUrl "https://contoso.sharepoint.com/sites/contoso-sales" --fileId "b2307a39-e878-458b-bc90-03bc578531d6" --groupName "saleGroup" ``` +Remove a role assignment by entra group id from a file by id without prompt + +```sh +m365 spo file roleassignment remove --webUrl "https://contoso.sharepoint.com/sites/contoso-sales" --fileId "b2307a39-e878-458b-bc90-03bc578531d6" --entraGroupId 3c97e01e-978d-417b-a196-b6a3e37fa873 --force + +Remove a role assignment by entra group name from a file by id. + +```sh +m365 spo file roleassignment remove --webUrl "https://contoso.sharepoint.com/sites/contoso-sales" --fileId "b2307a39-e878-458b-bc90-03bc578531d6" --entraGroupName "Marketing Members" +``` + ## Response The command won't return a response on success. diff --git a/src/m365/spo/commands/file/file-roleassignment-add.spec.ts b/src/m365/spo/commands/file/file-roleassignment-add.spec.ts index 2b2e5a48923..0dad2aac241 100644 --- a/src/m365/spo/commands/file/file-roleassignment-add.spec.ts +++ b/src/m365/spo/commands/file/file-roleassignment-add.spec.ts @@ -14,6 +14,7 @@ import commands from '../../commands.js'; import command from './file-roleassignment-add.js'; import { settingsNames } from '../../../../settingsNames.js'; import { spo } from '../../../../utils/spo.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { const webUrl = 'https://contoso.sharepoint.com/sites/project-x'; @@ -119,6 +120,21 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { UserPrincipalName: 'someaccount@tenant.onmicrosoft.com' }; + const entraGroupResponse = { + Id: 15, + IsHiddenInUI: false, + LoginName: 'c:0o.c|federateddirectoryclaimprovider|27ae47f1-48f1-46f3-980b-d3c1470e398d', + Title: 'Marketing members', + PrincipalType: 1, + Email: '', + Expiration: '', + IsEmailAuthenticationGuestUser: false, + IsShareByEmailGuestUser: false, + IsSiteAdmin: false, + UserId: null, + UserPrincipalName: null + }; + const groupResponse = { Id: 5, IsHiddenInUI: false, @@ -134,11 +150,56 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { RequestToJoinLeaveEmailSetting: null }; + const graphGroup = { + id: '27ae47f1-48f1-46f3-980b-d3c1470e398d', + deletedDateTime: null, + classification: null, + createdDateTime: '2024-03-22T20:18:37Z', + creationOptions: [], + description: null, + displayName: 'Marketing', + expirationDateTime: null, + groupTypes: [ + 'Unified' + ], + isAssignableToRole: null, + mail: 'Marketing@milanhdev.onmicrosoft.com', + mailEnabled: true, + mailNickname: 'Marketing', + membershipRule: null, + membershipRuleProcessingState: null, + onPremisesDomainName: null, + onPremisesLastSyncDateTime: null, + onPremisesNetBiosName: null, + onPremisesSamAccountName: null, + onPremisesSecurityIdentifier: null, + onPremisesSyncEnabled: null, + preferredDataLocation: null, + preferredLanguage: null, + proxyAddresses: [ + 'SPO:SPO_de7704ba-415d-4dd0-9bbd-fa565007a87e@SPO_18c58817-3bc9-489d-ac63-f7264fb357e5', + 'SMTP:Marketing@milanhdev.onmicrosoft.com' + ], + renewedDateTime: '2024-03-22T20:18:37Z', + resourceBehaviorOptions: [], + resourceProvisioningOptions: [], + securityEnabled: true, + securityIdentifier: 'S-1-12-1-665733105-1190349041-3268610968-2369326662', + theme: null, + uniqueName: null, + visibility: 'Private', + onPremisesProvisioningErrors: [], + serviceProvisioningErrors: [] + }; + before(() => { sinon.stub(auth, 'restoreAuth').resolves(); sinon.stub(telemetry, 'trackEvent').returns(); sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); + sinon.stub(entraGroup, 'getGroupById').withArgs(graphGroup.id).resolves(graphGroup); + sinon.stub(entraGroup, 'getGroupByDisplayName').withArgs(graphGroup.displayName).resolves(graphGroup); + sinon.stub(spo, 'ensureEntraGroup').withArgs(webUrl, graphGroup).resolves(entraGroupResponse); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); }); @@ -165,6 +226,7 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { spo.getGroupByName, spo.getUserByEmail, spo.getFileById, + spo.ensureUser, cli.getSettingWithDefaultValue ]); }); @@ -192,6 +254,11 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { assert.notStrictEqual(actual, true); }); + it('fails validation if the entraGroupId option is not a valid GUID', async () => { + const actual = await command.validate({ options: { webUrl: webUrl, fileId: fileId, entraGroupId: 'Invalid Guid', roleDefinitionName: 'Read' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + it('fails validation if the principalId option is not a valid number', async () => { const actual = await command.validate({ options: { webUrl: webUrl, fileId: fileId, principalId: 'NaN', roleDefinitionName: 'Read' } }, commandInfo); assert.notStrictEqual(actual, true); @@ -343,6 +410,46 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => { }); }); + it('correctly adds role assignments for a Microsoft Entra group specified by ID', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url as string === `https://contoso.sharepoint.com/sites/project-x/_api/web/GetFileByServerRelativePath(DecodedUrl='%2Fsites%2Fproject-x%2Fdocuments%2FTest1.docx')/ListItemAllFields/roleassignments/addroleassignment(principalid='15',roledefid='1073741827')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + verbose: true, + webUrl: 'https://contoso.sharepoint.com/sites/project-x', + fileUrl: fileUrl, + entraGroupId: graphGroup.id, + roleDefinitionId: 1073741827 + } + }); + }); + + it('correctly adds role assignments for a Microsoft Entra group specified by display name', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url as string === `https://contoso.sharepoint.com/sites/project-x/_api/web/GetFileByServerRelativePath(DecodedUrl='%2Fsites%2Fproject-x%2Fdocuments%2FTest1.docx')/ListItemAllFields/roleassignments/addroleassignment(principalid='15',roledefid='1073741827')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + verbose: true, + webUrl: 'https://contoso.sharepoint.com/sites/project-x', + fileUrl: fileUrl, + entraGroupName: graphGroup.displayName, + roleDefinitionId: 1073741827 + } + }); + }); + it('correctly handles error when role definition does not exist', async () => { const error = 'no role definition found'; sinon.stub(spo, 'getRoleDefinitionByName').rejects(new Error(error)); diff --git a/src/m365/spo/commands/file/file-roleassignment-add.ts b/src/m365/spo/commands/file/file-roleassignment-add.ts index 13a92026fcc..c578529da5c 100644 --- a/src/m365/spo/commands/file/file-roleassignment-add.ts +++ b/src/m365/spo/commands/file/file-roleassignment-add.ts @@ -1,3 +1,4 @@ +import { Group } from '@microsoft/microsoft-graph-types'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -9,6 +10,7 @@ import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { RoleDefinition } from '../roledefinition/RoleDefinition.js'; import { FileProperties } from './FileProperties.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; interface CommandArgs { options: Options; @@ -21,6 +23,8 @@ interface Options extends GlobalOptions { principalId?: number; upn?: string; groupName?: string; + entraGroupId?: string; + entraGroupName?: string; roleDefinitionId?: number; roleDefinitionName?: string; } @@ -52,6 +56,8 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand { principalId: typeof args.options.principalId !== 'undefined', upn: typeof args.options.upn !== 'undefined', groupName: typeof args.options.groupName !== 'undefined', + entraGroupId: typeof args.options.entraGroupId !== 'undefined', + entraGroupName: typeof args.options.entraGroupName !== 'undefined', roleDefinitionId: typeof args.options.roleDefinitionId !== 'undefined', roleDefinitionName: typeof args.options.roleDefinitionName !== 'undefined' }); @@ -78,6 +84,12 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand { { option: '--groupName [groupName]' }, + { + option: '--entraGroupId [entraGroupId]' + }, + { + option: '--entraGroupName [entraGroupName]' + }, { option: '--roleDefinitionId [roleDefinitionId]' }, @@ -103,6 +115,10 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand { return `Specified principalId ${args.options.principalId} is not a number`; } + if (args.options.entraGroupId && !validation.isValidGuid(args.options.entraGroupId)) { + return `'${args.options.entraGroupId}' is not a valid GUID for option entraGroupId`; + } + if (args.options.roleDefinitionId && isNaN(args.options.roleDefinitionId)) { return `Specified roleDefinitionId ${args.options.roleDefinitionId} is not a number`; } @@ -115,13 +131,13 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand { #initOptionSets(): void { this.optionSets.push( { options: ['fileId', 'fileUrl'] }, - { options: ['principalId', 'upn', 'groupName'] }, + { options: ['principalId', 'upn', 'groupName', 'entraGroupId', 'entraGroupName'] }, { options: ['roleDefinitionId', 'roleDefinitionName'] } ); } #initTypes(): void { - this.types.string.push('webUrl', 'fileUrl', 'fileId', 'upn', 'groupName', 'roleDefinitionName'); + this.types.string.push('webUrl', 'fileUrl', 'fileId', 'upn', 'groupName', 'entraGroupId', 'entraGroupName', 'roleDefinitionName'); } public async commandAction(logger: Logger, args: CommandArgs): Promise { @@ -140,6 +156,22 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand { const groupPrincipalId = await this.getGroupPrincipalId(args.options, logger); await this.addRoleAssignment(fileUrl, args.options.webUrl, groupPrincipalId, roleDefinitionId); } + else if (args.options.entraGroupId || args.options.entraGroupName) { + if (this.verbose) { + await logger.logToStderr('Retrieving group information...'); + } + + let group: Group; + if (args.options.entraGroupId) { + group = await entraGroup.getGroupById(args.options.entraGroupId); + } + else { + group = await entraGroup.getGroupByDisplayName(args.options.entraGroupName!); + } + + const entraSiteUser = await spo.ensureEntraGroup(args.options.webUrl, group); + await this.addRoleAssignment(fileUrl, args.options.webUrl, entraSiteUser.Id, roleDefinitionId); + } else { await this.addRoleAssignment(fileUrl, args.options.webUrl, args.options.principalId!, roleDefinitionId); } diff --git a/src/m365/spo/commands/file/file-roleassignment-remove.spec.ts b/src/m365/spo/commands/file/file-roleassignment-remove.spec.ts index c191ad275b1..7d196a9df02 100644 --- a/src/m365/spo/commands/file/file-roleassignment-remove.spec.ts +++ b/src/m365/spo/commands/file/file-roleassignment-remove.spec.ts @@ -15,6 +15,7 @@ import { urlUtil } from '../../../../utils/urlUtil.js'; import commands from '../../commands.js'; import command from './file-roleassignment-remove.js'; import { spo } from '../../../../utils/spo.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { const webUrl = 'https://contoso.sharepoint.com/sites/contoso-sales'; @@ -69,6 +70,21 @@ describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { UserPrincipalName: 'user1@contoso.onmicrosoft.com' }; + const entraGroupResponse = { + Id: 2, + IsHiddenInUI: false, + LoginName: 'c:0o.c|federateddirectoryclaimprovider|27ae47f1-48f1-46f3-980b-d3c1470e398d', + Title: 'Marketing members', + PrincipalType: 1, + Email: '', + Expiration: '', + IsEmailAuthenticationGuestUser: false, + IsShareByEmailGuestUser: false, + IsSiteAdmin: false, + UserId: null, + UserPrincipalName: null + }; + const groupResponse = { Id: 2, IsHiddenInUI: false, @@ -84,6 +100,48 @@ describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { RequestToJoinLeaveEmailSetting: null }; + const graphGroup = { + id: '27ae47f1-48f1-46f3-980b-d3c1470e398d', + deletedDateTime: null, + classification: null, + createdDateTime: '2024-03-22T20:18:37Z', + creationOptions: [], + description: null, + displayName: 'Marketing', + expirationDateTime: null, + groupTypes: [ + 'Unified' + ], + isAssignableToRole: null, + mail: 'Marketing@milanhdev.onmicrosoft.com', + mailEnabled: true, + mailNickname: 'Marketing', + membershipRule: null, + membershipRuleProcessingState: null, + onPremisesDomainName: null, + onPremisesLastSyncDateTime: null, + onPremisesNetBiosName: null, + onPremisesSamAccountName: null, + onPremisesSecurityIdentifier: null, + onPremisesSyncEnabled: null, + preferredDataLocation: null, + preferredLanguage: null, + proxyAddresses: [ + 'SPO:SPO_de7704ba-415d-4dd0-9bbd-fa565007a87e@SPO_18c58817-3bc9-489d-ac63-f7264fb357e5', + 'SMTP:Marketing@milanhdev.onmicrosoft.com' + ], + renewedDateTime: '2024-03-22T20:18:37Z', + resourceBehaviorOptions: [], + resourceProvisioningOptions: [], + securityEnabled: true, + securityIdentifier: 'S-1-12-1-665733105-1190349041-3268610968-2369326662', + theme: null, + uniqueName: null, + visibility: 'Private', + onPremisesProvisioningErrors: [], + serviceProvisioningErrors: [] + }; + let log: any[]; let logger: Logger; let commandInfo: CommandInfo; @@ -125,6 +183,7 @@ describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { spo.getUserByEmail, spo.getGroupByName, spo.getFileById, + spo.ensureEntraGroup, request.post ]); }); @@ -152,6 +211,11 @@ describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { assert.notStrictEqual(actual, true); }); + it('fails validation if the entraGroupId option is not a valid GUID', async () => { + const actual = await command.validate({ options: { webUrl: webUrl, fileId: fileId, entraGroupId: 'Invalid Guid', force: true } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + it('fails validation if the principalId option is not a number', async () => { const actual = await command.validate({ options: { webUrl: webUrl, fileId: fileId, principalId: 'Hi', force: true } }, commandInfo); assert.notStrictEqual(actual, true); @@ -260,6 +324,56 @@ describe(commands.FILE_ROLEASSIGNMENT_REMOVE, () => { }); }); + it('remove role assignment from the file by Id and entragroup Id', async () => { + sinon.stub(spo, 'getFileById').resolves(fileResponse); + sinon.stub(entraGroup, 'getGroupById').withArgs(graphGroup.id).resolves(graphGroup); + sinon.stub(spo, 'ensureEntraGroup').withArgs(webUrl, graphGroup).resolves(entraGroupResponse); + + sinon.stub(request, 'post').callsFake(async (opts) => { + const serverRelativeUrl: string = urlUtil.getServerRelativePath(webUrl, fileUrl); + if (opts.url === `${webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(serverRelativeUrl)}')/ListItemAllFields/roleassignments/removeroleassignment(principalid='${principalId}')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + debug: true, + webUrl: webUrl, + fileId: fileId, + entraGroupId: graphGroup.id, + force: true + } + }); + }); + + it('remove role assignment from the file by Id and entragroup name', async () => { + sinon.stub(spo, 'getFileById').resolves(fileResponse); + sinon.stub(entraGroup, 'getGroupByDisplayName').withArgs(graphGroup.displayName).resolves(graphGroup); + sinon.stub(spo, 'ensureEntraGroup').withArgs(webUrl, graphGroup).resolves(entraGroupResponse); + + sinon.stub(request, 'post').callsFake(async (opts) => { + const serverRelativeUrl: string = urlUtil.getServerRelativePath(webUrl, fileUrl); + if (opts.url === `${webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(serverRelativeUrl)}')/ListItemAllFields/roleassignments/removeroleassignment(principalid='${principalId}')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + debug: true, + webUrl: webUrl, + fileId: fileId, + entraGroupName: graphGroup.displayName, + force: true + } + }); + }); + it('correctly handles error when removing file role assignment', async () => { const errorMessage = 'request rejected'; sinon.stub(request, 'post').callsFake(async () => { throw errorMessage; }); diff --git a/src/m365/spo/commands/file/file-roleassignment-remove.ts b/src/m365/spo/commands/file/file-roleassignment-remove.ts index 33704bfcb84..590ff3e9b36 100644 --- a/src/m365/spo/commands/file/file-roleassignment-remove.ts +++ b/src/m365/spo/commands/file/file-roleassignment-remove.ts @@ -1,3 +1,4 @@ +import { Group } from '@microsoft/microsoft-graph-types'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; @@ -9,6 +10,7 @@ import { validation } from '../../../../utils/validation.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; import { FileProperties } from './FileProperties.js'; +import { entraGroup } from '../../../../utils/entraGroup.js'; interface CommandArgs { options: Options; @@ -21,6 +23,8 @@ interface Options extends GlobalOptions { principalId?: number; upn?: string; groupName?: string; + entraGroupId?: string; + entraGroupName?: string; force?: boolean; } @@ -51,6 +55,8 @@ class SpoFileRoleAssignmentRemoveCommand extends SpoCommand { principalId: typeof args.options.principalId !== 'undefined', upn: typeof args.options.upn !== 'undefined', groupName: typeof args.options.groupName !== 'undefined', + entraGroupId: typeof args.options.entraGroupId !== 'undefined', + entraGroupName: typeof args.options.entraGroupName !== 'undefined', force: !!args.options.force }); }); @@ -76,6 +82,12 @@ class SpoFileRoleAssignmentRemoveCommand extends SpoCommand { { option: '--groupName [groupName]' }, + { + option: '--entraGroupId [entraGroupId]' + }, + { + option: '--entraGroupName [entraGroupName]' + }, { option: '-f, --force' } @@ -94,6 +106,10 @@ class SpoFileRoleAssignmentRemoveCommand extends SpoCommand { return `Specified principalId ${args.options.principalId} is not a number`; } + if (args.options.entraGroupId && !validation.isValidGuid(args.options.entraGroupId)) { + return `'${args.options.entraGroupId}' is not a valid GUID for option entraGroupId`; + } + if (args.options.fileId && !validation.isValidGuid(args.options.fileId)) { return `${args.options.fileId} is not a valid GUID`; } @@ -106,12 +122,12 @@ class SpoFileRoleAssignmentRemoveCommand extends SpoCommand { #initOptionSets(): void { this.optionSets.push( { options: ['fileUrl', 'fileId'] }, - { options: ['upn', 'groupName', 'principalId'] } + { options: ['upn', 'groupName', 'principalId', 'entraGroupId', 'entraGroupName'] } ); } #initTypes(): void { - this.types.string.push('webUrl', 'fileUrl', 'fileId', 'upn', 'groupName'); + this.types.string.push('webUrl', 'fileUrl', 'fileId', 'upn', 'groupName', 'entraGroupId', 'entraGroupName'); this.types.boolean.push('force'); } @@ -131,6 +147,23 @@ class SpoFileRoleAssignmentRemoveCommand extends SpoCommand { else if (args.options.upn) { principalId = await this.getUserPrincipalId(args.options, logger); } + else if (args.options.entraGroupId || args.options.entraGroupName) { + if (this.verbose) { + await logger.logToStderr('Retrieving group information...'); + } + + let group: Group; + if (args.options.entraGroupId) { + group = await entraGroup.getGroupById(args.options.entraGroupId); + } + else { + group = await entraGroup.getGroupByDisplayName(args.options.entraGroupName!); + } + + const entraSiteUser = await spo.ensureEntraGroup(args.options.webUrl, group); + + principalId = entraSiteUser.Id; + } else { principalId = args.options.principalId!; }