Skip to content

Commit 9322585

Browse files
nicodecleyreAdam-it
authored andcommitted
Enhances group member remove. Closes pnp#4098
1 parent c70465d commit 9322585

File tree

4 files changed

+276
-52
lines changed

4 files changed

+276
-52
lines changed

docs/docs/cmd/spo/group/group-member-remove.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,54 @@ m365 spo group member remove [options]
2020
: Name of the SharePoint group from which user has to be removed. Specify either `groupName` or `groupId`, but not both.
2121

2222
`--userName [userName]`
23-
: The UPN (user principal name, eg. [email protected]) of the user that needs to be removed. Specify either `userName`, `email`, or `userId`, but not multiple.
23+
: The UPN (user principal name, eg. [email protected]) of the user that needs to be removed. Specify either `userName`, `email`, `userId`, `aadGroupId` or `aadGroupName`.
2424

2525
`--email [email]`
26-
: The email of the user to remove as a member. Specify either `userName`, `email`, or `userId`, but not multiple.
26+
: The email of the user to remove as a member. Specify either `userName`, `email`, `userId`, `aadGroupId` or `aadGroupName`.
2727

2828
`--userId [userId]`
29-
: The user Id (Id of the site user, eg. 14) of the user to remove as a member. Specify either `userName`, `email`, or `userId`, but not multiple.
29+
: The user Id (Id of the site user, eg. 14) of the user to remove as a member. Specify either `userName`, `email`, `userId`, `aadGroupId` or `aadGroupName`.
30+
31+
`--aadGroupId [aadGroupId]`
32+
: The object Id of the Azure AD group to remove as a member. Specify either `userName`, `email`, `userId`, `aadGroupId` or `aadGroupName`.
33+
34+
`--aadGroupName [aadGroupName]`
35+
: The name of the Azure AD group to remove as a member. Specify either `userName`, `email`, `userId`, `aadGroupId` or `aadGroupName`.
3036

3137
--8<-- "docs/cmd/_global.md"
3238

3339
## Examples
3440

35-
Remove a user from a SharePoint group by userName.
41+
Remove a user from a SharePoint group based on the id on a given web
3642

3743
```sh
3844
m365 spo group member remove --webUrl https://contoso.sharepoint.com/sites/SiteA --groupId 5 --userName "[email protected]"
3945
```
4046

41-
Remove a user from a SharePoint group by email.
47+
Remove a user from a SharePoint group based on the username on a given web
4248

4349
```sh
4450
m365 spo group member remove --webUrl https://contoso.sharepoint.com/sites/SiteA --groupName "Site A Visitors" --email "[email protected]"
4551
```
4652

47-
Remove a user from a SharePoint group by id.
53+
Remove a user from a SharePoint group by email.
4854

4955
```sh
5056
m365 spo group member remove --webUrl https://contoso.sharepoint.com/sites/SiteA --groupName "Site A Visitors" --userId 14
5157
```
5258

59+
Remove an Azure AD group from a SharePoint group based on the Azure AD group name on a given web
60+
61+
```sh
62+
m365 spo group member remove --webUrl https://contoso.sharepoint.com/sites/SiteA --groupId 5 --aadGroupName "Azure AD Security Group"
63+
```
64+
65+
Remove an Azure AD group from a SharePoint group based on the Azure AD group ID on a given web
66+
67+
```sh
68+
m365 spo group member remove --webUrl https://contoso.sharepoint.com/sites/SiteA --groupName "Site A Visitors" --aadGroupId "5786b8e8-c495-4734-b345-756733960730"
69+
```
70+
5371
## Response
5472

5573
The command won't return a response on success.

src/m365/spo/commands/group/group-member-list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface CommandArgs {
1010
options: Options;
1111
}
1212

13-
interface Options extends GlobalOptions {
13+
export interface Options extends GlobalOptions {
1414
webUrl: string;
1515
groupId?: number;
1616
groupName?: string;

src/m365/spo/commands/group/group-member-remove.spec.ts

Lines changed: 159 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { formatting } from '../../../../utils/formatting';
1111
import { pid } from '../../../../utils/pid';
1212
import { sinonUtil } from '../../../../utils/sinonUtil';
1313
import commands from '../../commands';
14+
import * as SpoGroupMemberListCommand from './group-member-list';
1415
const command: Command = require('./group-member-remove');
1516

1617
describe(commands.GROUP_MEMBER_REMOVE, () => {
@@ -25,6 +26,7 @@ describe(commands.GROUP_MEMBER_REMOVE, () => {
2526
const email = '[email protected]';
2627
const userId = 14;
2728

29+
const spoGroupMemberListCommandOutput = `[{ "Id": 13, "IsHiddenInUI": false, "LoginName": "c:0t.c|tenant|4b468129-3b44-4414-bd45-aa5bde29df2f", "Title": "Azure AD Security Group 2", "PrincipalType": 1, "Email": "", "Expiration": "", "IsEmailAuthenticationGuestUser": false, "IsShareByEmailGuestUser": false, "IsSiteAdmin": false, "UserId": null, "UserPrincipalName": null },{ "Id": 13, "IsHiddenInUI": false, "LoginName": "c:0t.c|tenant|3f10f4af-8704-4394-80c0-ee8cef5eae27", "Title": "Azure AD Security Group", "PrincipalType": 1, "Email": "", "Expiration": "", "IsEmailAuthenticationGuestUser": false, "IsShareByEmailGuestUser": false, "IsSiteAdmin": false, "UserId": null, "UserPrincipalName": null }, { "Id": 17, "IsHiddenInUI": false, "LoginName": "c:0o.c|federateddirectoryclaimprovider|5786b8e8-c495-4734-b345-756733960730", "Title": "Office 365 Group", "PrincipalType": 4, "Email": "[email protected]", "Expiration": "", "IsEmailAuthenticationGuestUser": false, "IsShareByEmailGuestUser": false, "IsSiteAdmin": false, "UserId": null, "UserPrincipalName": null }]`;
2830
const UserRemovalJSONResponse =
2931
{
3032
"odata.null": true
@@ -70,6 +72,7 @@ describe(commands.GROUP_MEMBER_REMOVE, () => {
7072

7173
afterEach(() => {
7274
sinonUtil.restore([
75+
request.get,
7376
request.post,
7477
Cli.prompt,
7578
Cli.executeCommandWithOutput
@@ -93,15 +96,143 @@ describe(commands.GROUP_MEMBER_REMOVE, () => {
9396
assert.notStrictEqual(command.description, null);
9497
});
9598

96-
it('fails validation if webURL is Invalid', async () => {
97-
const actual = await command.validate({
99+
it('Removes Azure AD group from SharePoint group using Azure AD Group Name', async () => {
100+
sinonUtil.restore(Cli.prompt);
101+
sinon.stub(Cli, 'prompt').callsFake(async () => (
102+
{ continue: true }
103+
));
104+
105+
sinon.stub(Cli, 'executeCommandWithOutput').callsFake(async (command): Promise<any> => {
106+
if (command === SpoGroupMemberListCommand) {
107+
return ({
108+
stdout: spoGroupMemberListCommandOutput
109+
});
110+
}
111+
112+
throw new CommandError('Unknown case');
113+
});
114+
115+
const postStub = sinon.stub(request, 'post').callsFake(opts => {
116+
if ((opts.url as string).indexOf('/_api/web/sitegroups/GetByName') > -1) {
117+
return Promise.resolve(UserRemovalJSONResponse);
118+
}
119+
120+
return Promise.reject(`Invalid request ${JSON.stringify(opts)}`);
121+
});
122+
await command.action(logger, {
98123
options: {
99-
webUrl: "InvalidWEBURL",
100-
groupId: groupId,
101-
userName: userName
124+
debug: true,
125+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
126+
groupName: "Site A Visitors",
127+
aadGroupName: "Azure AD Security Group"
102128
}
103-
}, commandInfo);
104-
assert.notStrictEqual(actual, true);
129+
});
130+
assert(postStub.called);
131+
});
132+
133+
it('Removes Azure AD group from SharePoint group using Azure AD Group ID - Without Confirmation Prompt', async () => {
134+
sinon.stub(Cli, 'executeCommandWithOutput').callsFake(async (command): Promise<any> => {
135+
if (command === SpoGroupMemberListCommand) {
136+
return ({
137+
stdout: spoGroupMemberListCommandOutput
138+
});
139+
}
140+
141+
throw new CommandError('Unknown case');
142+
});
143+
144+
const postStub = sinon.stub(request, 'post').callsFake(opts => {
145+
if ((opts.url as string).indexOf('/_api/web/sitegroups/GetByName') > -1) {
146+
return Promise.resolve(UserRemovalJSONResponse);
147+
}
148+
149+
return Promise.reject(`Invalid request ${JSON.stringify(opts)}`);
150+
});
151+
await command.action(logger, {
152+
options: {
153+
debug: true,
154+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
155+
groupName: "Site A Visitors",
156+
aadGroupId: "5786b8e8-c495-4734-b345-756733960730",
157+
confirm: true
158+
}
159+
});
160+
assert(postStub.called);
161+
});
162+
163+
it('Removes Azure AD group from SharePoint group using Azure AD Group ID and SharePoint Group ID', async () => {
164+
sinonUtil.restore(Cli.prompt);
165+
sinon.stub(Cli, 'prompt').callsFake(async () => (
166+
{ continue: true }
167+
));
168+
169+
sinon.stub(Cli, 'executeCommandWithOutput').callsFake(async (command): Promise<any> => {
170+
if (command === SpoGroupMemberListCommand) {
171+
return ({
172+
stdout: spoGroupMemberListCommandOutput
173+
});
174+
}
175+
176+
throw new CommandError('Unknown case');
177+
});
178+
179+
const postStub = sinon.stub(request, 'post').callsFake(opts => {
180+
if ((opts.url as string).indexOf('/_api/web/sitegroups/GetById') > -1) {
181+
return Promise.resolve(UserRemovalJSONResponse);
182+
}
183+
184+
return Promise.reject(`Invalid request ${JSON.stringify(opts)}`);
185+
});
186+
await command.action(logger, {
187+
options: {
188+
debug: true,
189+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
190+
groupId: 4,
191+
aadGroupId: "4b468129-3b44-4414-bd45-aa5bde29df2f"
192+
}
193+
});
194+
assert(postStub.called);
195+
});
196+
197+
it('Throws error when Azure AD group not found', async () => {
198+
sinon.stub(Cli, 'executeCommandWithOutput').callsFake(async (command): Promise<any> => {
199+
if (command === SpoGroupMemberListCommand) {
200+
return ({
201+
stdout: spoGroupMemberListCommandOutput
202+
});
203+
}
204+
205+
throw new CommandError('Unknown case');
206+
});
207+
208+
await assert.rejects(command.action(logger, {
209+
options: {
210+
debug: true,
211+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
212+
groupName: "Site A Visitors",
213+
aadGroupName: "Not existing group",
214+
confirm: true
215+
}
216+
}), new CommandError('The Azure AD group Not existing group is not found in SharePoint group Site A Visitors'));
217+
});
218+
219+
it('Removes user from SharePoint group using Group ID - Without Confirmation Prompt', async () => {
220+
const postStub = sinon.stub(request, 'post').callsFake(opts => {
221+
if ((opts.url as string).indexOf('/_api/web/sitegroups/GetById') > -1) {
222+
return Promise.resolve(UserRemovalJSONResponse);
223+
}
224+
225+
return Promise.reject(`Invalid request ${JSON.stringify(opts)}`);
226+
});
227+
await command.action(logger, {
228+
options: {
229+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
230+
groupId: 4,
231+
userName: "[email protected]",
232+
confirm: true
233+
}
234+
});
235+
assert(postStub.called);
105236
});
106237

107238
it('fails validation if the userName is not a valid UPN.', async () => {
@@ -440,4 +571,25 @@ describe(commands.GROUP_MEMBER_REMOVE, () => {
440571
}
441572
}), new CommandError('The user does not exist or is not unique.'));
442573
});
574+
575+
it('fails validation if webURL is Invalid', async () => {
576+
const actual = await command.validate({ options: { webUrl: "InvalidWEBURL", groupId: 4, userName: "[email protected]" } }, commandInfo);
577+
assert.notStrictEqual(actual, true);
578+
});
579+
580+
it('fails validation if groupid and groupName is entered', async () => {
581+
const actual = await command.validate({ options: { webUrl: "https://contoso.sharepoint.com/sites/SiteA", groupId: "4", groupName: "Site A Visitors", userName: "[email protected]" } }, commandInfo);
582+
assert.notStrictEqual(actual, true);
583+
});
584+
585+
it('fails validation if aadGroupId is not a valid guid.', async () => {
586+
const actual = await command.validate({
587+
options: {
588+
webUrl: "https://contoso.sharepoint.com/sites/SiteA",
589+
groupId: 3,
590+
aadGroupId: 'Invalid GUID'
591+
}
592+
}, commandInfo);
593+
assert.notStrictEqual(actual, true);
594+
});
443595
});

0 commit comments

Comments
 (0)