Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance spo file roleassignment command #6431

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions docs/docs/cmd/spo/file/file-roleassignment-add.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 "[email protected]" --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 "[email protected]" --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.
23 changes: 20 additions & 3 deletions docs/docs/cmd/spo/file/file-roleassignment-remove.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command is about removing role assignment

Suggested change
: ID of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple.
: ID of the Microsoft Entra group to remove. 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be remove

Suggested change
: Display name of the Microsoft Entra group to add. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple.
: Display name of the Microsoft Entra group to remove. Specify either `upn`, `groupName`, `entraGroupId`, `entraGroupName`, or `principalId` but not multiple.


`-f, --force`
: Don't prompt for confirmation.
Expand Down Expand Up @@ -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

Comment on lines +68 to +70
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like we are missing the closing of code block

Suggested change
```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
```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.
107 changes: 107 additions & 0 deletions src/m365/spo/commands/file/file-roleassignment-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -119,6 +120,21 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => {
UserPrincipalName: '[email protected]'
};

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,
Expand All @@ -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: '[email protected]',
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:[email protected]'
],
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);
});
Expand All @@ -165,6 +226,7 @@ describe(commands.FILE_ROLEASSIGNMENT_ADD, () => {
spo.getGroupByName,
spo.getUserByEmail,
spo.getFileById,
spo.ensureUser,
cli.getSettingWithDefaultValue
]);
});
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
36 changes: 34 additions & 2 deletions src/m365/spo/commands/file/file-roleassignment-add.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand All @@ -21,6 +23,8 @@ interface Options extends GlobalOptions {
principalId?: number;
upn?: string;
groupName?: string;
entraGroupId?: string;
entraGroupName?: string;
roleDefinitionId?: number;
roleDefinitionName?: string;
}
Expand Down Expand Up @@ -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'
});
Expand All @@ -78,6 +84,12 @@ class SpoFileRoleAssignmentAddCommand extends SpoCommand {
{
option: '--groupName [groupName]'
},
{
option: '--entraGroupId [entraGroupId]'
},
{
option: '--entraGroupName [entraGroupName]'
},
{
option: '--roleDefinitionId [roleDefinitionId]'
},
Expand All @@ -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`;
}
Expand All @@ -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<void> {
Expand All @@ -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);
}
Expand Down
Loading
Loading