forked from pnp/cli-microsoft365
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds command 'spo site sharingpermission set'. Closes pnp#6266
- Loading branch information
1 parent
77f6320
commit 6f78d79
Showing
5 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import Global from '/docs/cmd/_global.mdx'; | ||
|
||
# spo site sharingpermission set | ||
|
||
Controls how a site and its components can be shared | ||
|
||
## Usage | ||
|
||
```sh | ||
m365 spo site sharingpermission set [options] | ||
``` | ||
|
||
## Options | ||
|
||
```md definition-list | ||
`-u, --url <url>` | ||
: URL of the site. | ||
|
||
`--capability <capability>` | ||
: Define how the site is shared. Possible values: `full`, `limited`, `ownersOnly`. | ||
``` | ||
|
||
<Global /> | ||
|
||
## Remarks | ||
|
||
When specifying `capability`, consider the following: | ||
- `full`: Site owners and members can share files, folders, and the site. People with Edit permissions can share files and folders. | ||
- `limited`: Site owners and members, and people with Edit permissions can share files and folders, but only site owners can share the site. | ||
- `ownersOnly`: Only site owners can share files, folders, and the site. | ||
|
||
## Examples | ||
|
||
Update the sharing permissions for a site so only owners can share files and the site | ||
|
||
```sh | ||
m365 spo site sharingpermission set --siteUrl https://siteaddress.com/sites/sitename --capability ownersOnly | ||
``` | ||
|
||
Update the sharing permissions for a site where so both owners and members can share files and the site | ||
|
||
```sh | ||
m365 spo site sharingpermission set --siteUrl https://siteaddress.com/sites/sitename --capability full | ||
``` | ||
|
||
Update the sharing permissions for a site where so owners can share the site, but members can only share files | ||
|
||
```sh | ||
m365 spo site sharingpermission set --siteUrl https://siteaddress.com/sites/sitename --capability full | ||
``` | ||
|
||
## Response | ||
|
||
The command won't return a response on success. | ||
|
||
## More information | ||
|
||
- Sharing a SharePoint site: [https://support.microsoft.com/office/share-a-site-958771a8-d041-4eb8-b51c-afea2eae3658](https://support.microsoft.com/office/share-a-site-958771a8-d041-4eb8-b51c-afea2eae3658) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
src/m365/spo/commands/site/site-sharingpermission-set.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import assert from 'assert'; | ||
import sinon from 'sinon'; | ||
import auth from '../../../../Auth.js'; | ||
import { CommandError } from '../../../../Command.js'; | ||
import { Logger } from '../../../../cli/Logger.js'; | ||
import request from '../../../../request.js'; | ||
import { telemetry } from '../../../../telemetry.js'; | ||
import { pid } from '../../../../utils/pid.js'; | ||
import { CommandInfo } from '../../../../cli/CommandInfo.js'; | ||
import { session } from '../../../../utils/session.js'; | ||
import { sinonUtil } from '../../../../utils/sinonUtil.js'; | ||
import commands from '../../commands.js'; | ||
import command from './site-sharingpermission-set.js'; | ||
import { z } from 'zod'; | ||
import { cli } from '../../../../cli/cli.js'; | ||
|
||
describe(commands.SITE_SHARINGPERMISSION_SET, () => { | ||
const siteUrl = 'https://contoso.sharepoint.com/sites/marketing'; | ||
|
||
let log: string[]; | ||
let logger: Logger; | ||
let loggerLogSpy: sinon.SinonSpy; | ||
let patchStub: sinon.SinonStub; | ||
let commandInfo: CommandInfo; | ||
let commandOptionsSchema: z.ZodTypeAny; | ||
|
||
before(() => { | ||
sinon.stub(auth, 'restoreAuth').resolves(); | ||
sinon.stub(telemetry, 'trackEvent').returns(); | ||
sinon.stub(pid, 'getProcessName').returns(''); | ||
sinon.stub(session, 'getId').returns(''); | ||
|
||
commandInfo = cli.getCommandInfo(command); | ||
commandOptionsSchema = commandInfo.command.getSchemaToParse()!; | ||
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'); | ||
|
||
patchStub = sinon.stub(request, 'patch').callsFake(async opts => { | ||
if (opts.url === `${siteUrl}/_api/Web`) { | ||
return; | ||
} | ||
if (opts.url === `${siteUrl}/_api/Web/AssociatedMemberGroup`) { | ||
return; | ||
} | ||
|
||
throw 'Invalid request :' + opts.url; | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
sinonUtil.restore([ | ||
request.patch | ||
]); | ||
}); | ||
|
||
after(() => { | ||
sinon.restore(); | ||
auth.connection.active = false; | ||
}); | ||
|
||
it('has correct name', () => { | ||
assert.strictEqual(command.name, commands.SITE_SHARINGPERMISSION_SET); | ||
}); | ||
|
||
it('has a description', () => { | ||
assert.notStrictEqual(command.description, null); | ||
}); | ||
|
||
it('fails validation if siteUrl is not a valid URL', async () => { | ||
const actual = commandOptionsSchema.safeParse({ siteUrl: 'invalid', capability: 'full' }); | ||
assert.strictEqual(actual.success, false); | ||
}); | ||
|
||
it('passes validation when capability is not a valid value', async () => { | ||
const actual = commandOptionsSchema.safeParse({ siteUrl: siteUrl, capability: 'invalid' }); | ||
assert.strictEqual(actual.success, false); | ||
}); | ||
|
||
it('passes validation when the input is correct', async () => { | ||
const actual = commandOptionsSchema.safeParse({ siteUrl: siteUrl, capability: 'limited' }); | ||
assert.strictEqual(actual.success, true); | ||
}); | ||
|
||
it('outputs no command output', async () => { | ||
patchStub.restore(); | ||
sinon.stub(request, 'patch').resolves(); | ||
|
||
await command.action(logger, { | ||
options: { | ||
siteUrl: siteUrl, | ||
capability: 'full', | ||
verbose: true | ||
} | ||
}); | ||
|
||
assert(loggerLogSpy.notCalled); | ||
}); | ||
|
||
it('correctly sets sharing permissions to full', async () => { | ||
await command.action(logger, { | ||
options: { | ||
siteUrl: siteUrl, | ||
capability: 'full' | ||
} | ||
}); | ||
|
||
assert.deepStrictEqual(patchStub.firstCall.args[0].data, { MembersCanShare: true }); | ||
assert.deepStrictEqual(patchStub.secondCall.args[0].data, { AllowMembersEditMembership: true }); | ||
}); | ||
|
||
it('correctly sets sharing permissions to limited', async () => { | ||
await command.action(logger, { | ||
options: { | ||
siteUrl: siteUrl, | ||
capability: 'limited' | ||
} | ||
}); | ||
|
||
assert.deepStrictEqual(patchStub.firstCall.args[0].data, { MembersCanShare: true }); | ||
assert.deepStrictEqual(patchStub.secondCall.args[0].data, { AllowMembersEditMembership: false }); | ||
}); | ||
|
||
it('correctly sets sharing permissions to ownersOnly', async () => { | ||
await command.action(logger, { | ||
options: { | ||
siteUrl: siteUrl, | ||
capability: 'ownersOnly' | ||
} | ||
}); | ||
|
||
assert.deepStrictEqual(patchStub.firstCall.args[0].data, { MembersCanShare: false }); | ||
assert.deepStrictEqual(patchStub.secondCall.args[0].data, { AllowMembersEditMembership: false }); | ||
}); | ||
|
||
it('correctly handles error when updating sharing permissions', async () => { | ||
patchStub.restore(); | ||
const errorMessage = 'Access is denied.'; | ||
|
||
sinon.stub(request, 'patch').rejects({ | ||
error: { | ||
'odata.error': { | ||
message: { | ||
lang: 'en-US', | ||
value: errorMessage | ||
} | ||
} | ||
} | ||
}); | ||
|
||
await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, capability: 'limited' } }), | ||
new CommandError(errorMessage)); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import SpoCommand from '../../../base/SpoCommand.js'; | ||
import { globalOptionsZod } from '../../../../Command.js'; | ||
import { z } from 'zod'; | ||
import { zod } from '../../../../utils/zod.js'; | ||
import { Logger } from '../../../../cli/Logger.js'; | ||
import commands from '../../commands.js'; | ||
import { validation } from '../../../../utils/validation.js'; | ||
import request, { CliRequestOptions } from '../../../../request.js'; | ||
|
||
const options = globalOptionsZod | ||
.extend({ | ||
siteUrl: zod.alias('u', z.string() | ||
.refine(url => validation.isValidSharePointUrl(url) === true, url => ({ | ||
message: `'${url}' is not a valid SharePoint Online site URL.` | ||
})) | ||
), | ||
capability: z.enum(['full', 'limited', 'ownersOnly']) | ||
}) | ||
.strict(); | ||
declare type Options = z.infer<typeof options>; | ||
|
||
interface CommandArgs { | ||
options: Options; | ||
} | ||
|
||
class SpoSiteSharingPermissionSetCommand extends SpoCommand { | ||
public get name(): string { | ||
return commands.SITE_SHARINGPERMISSION_SET; | ||
} | ||
|
||
public get description(): string { | ||
return 'Controls how a site and its components can be shared'; | ||
} | ||
|
||
public get schema(): z.ZodTypeAny { | ||
return options; | ||
} | ||
|
||
public async commandAction(logger: Logger, args: CommandArgs): Promise<void> { | ||
try { | ||
if (this.verbose) { | ||
await logger.logToStderr(`Updating sharing permissions for site '${args.options.siteUrl}'...`); | ||
} | ||
|
||
const { capability } = args.options; | ||
|
||
if (this.verbose) { | ||
await logger.logToStderr(`Updating site sharing permissions...`); | ||
} | ||
const requestOptionsWeb: CliRequestOptions = { | ||
url: `${args.options.siteUrl}/_api/Web`, | ||
headers: { | ||
accept: 'application/json;odata=nometadata' | ||
}, | ||
responseType: 'json', | ||
data: { | ||
MembersCanShare: capability === 'full' || capability === 'limited' | ||
} | ||
}; | ||
await request.patch(requestOptionsWeb); | ||
|
||
if (this.verbose) { | ||
await logger.logToStderr(`Updating associated member group sharing permissions...`); | ||
} | ||
|
||
const requestOptionsMemberGroup: CliRequestOptions = { | ||
url: `${args.options.siteUrl}/_api/Web/AssociatedMemberGroup`, | ||
headers: { | ||
accept: 'application/json;odata=nometadata' | ||
}, | ||
responseType: 'json', | ||
data: { | ||
AllowMembersEditMembership: capability === 'full' | ||
} | ||
}; | ||
|
||
await request.patch(requestOptionsMemberGroup); | ||
} | ||
catch (err: any) { | ||
this.handleRejectedODataJsonPromise(err); | ||
} | ||
} | ||
} | ||
|
||
export default new SpoSiteSharingPermissionSetCommand(); |