@pnp/cli-microsoft365 10.0.0-beta.7dfc31a → 10.0.0-beta.977d78b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/allCommands.json +1 -1
- package/allCommandsFull.json +1 -1
- package/dist/Auth.js +14 -13
- package/dist/Command.js +4 -1
- package/dist/cli/cli.js +14 -0
- package/dist/config.js +60 -5
- package/dist/m365/base/PowerAutomateCommand.js +1 -1
- package/dist/m365/base/SpoCommand.js +1 -1
- package/dist/m365/cli/commands/cli-consent.js +2 -2
- package/dist/m365/cli/commands/cli-doctor.js +2 -2
- package/dist/m365/cli/commands/cli-reconsent.js +2 -3
- package/dist/m365/cli/commands/config/config-set.js +12 -4
- package/dist/m365/commands/login.js +28 -9
- package/dist/m365/commands/setup.js +256 -33
- package/dist/m365/commands/setupPresets.js +2 -4
- package/dist/m365/connection/commands/connection-list.js +4 -4
- package/dist/m365/entra/commands/app/app-add.js +52 -288
- package/dist/m365/file/commands/file-move.js +135 -0
- package/dist/m365/file/commands.js +2 -1
- package/dist/m365/flow/commands/environment/environment-get.js +1 -1
- package/dist/m365/flow/commands/environment/environment-list.js +1 -1
- package/dist/m365/flow/commands/flow-disable.js +1 -1
- package/dist/m365/flow/commands/flow-enable.js +1 -1
- package/dist/m365/flow/commands/flow-export.js +17 -16
- package/dist/m365/flow/commands/flow-get.js +1 -1
- package/dist/m365/flow/commands/flow-list.js +1 -1
- package/dist/m365/flow/commands/flow-remove.js +1 -1
- package/dist/m365/flow/commands/owner/owner-ensure.js +1 -1
- package/dist/m365/flow/commands/owner/owner-list.js +1 -1
- package/dist/m365/flow/commands/owner/owner-remove.js +1 -1
- package/dist/m365/flow/commands/recyclebinitem/recyclebinitem-list.js +47 -0
- package/dist/m365/flow/commands/recyclebinitem/recyclebinitem-restore.js +48 -0
- package/dist/m365/flow/commands/run/run-cancel.js +1 -1
- package/dist/m365/flow/commands/run/run-get.js +1 -1
- package/dist/m365/flow/commands/run/run-list.js +1 -1
- package/dist/m365/flow/commands/run/run-resubmit.js +2 -2
- package/dist/m365/flow/commands.js +2 -0
- package/dist/m365/spfx/commands/project/project-doctor/doctor-1.20.0-rc.1.js +25 -0
- package/dist/m365/spfx/commands/project/project-doctor.js +2 -1
- package/dist/m365/spfx/commands/project/project-upgrade/upgrade-1.20.0-rc.1.js +57 -0
- package/dist/m365/spfx/commands/project/project-upgrade.js +16 -13
- package/dist/m365/spfx/commands/spfx-doctor.js +15 -0
- package/dist/m365/spo/commands/contenttype/contenttype-field-remove.js +8 -8
- package/dist/m365/spo/commands/contenttype/contenttype-field-set.js +2 -2
- package/dist/m365/spo/commands/file/file-roleassignment-add.js +17 -54
- package/dist/m365/spo/commands/file/file-roleassignment-remove.js +13 -40
- package/dist/m365/spo/commands/file/file-roleinheritance-break.js +5 -13
- package/dist/m365/spo/commands/file/file-roleinheritance-reset.js +5 -13
- package/dist/m365/spo/commands/folder/folder-sharinglink-add.js +143 -0
- package/dist/m365/spo/commands/folder/folder-sharinglink-clear.js +111 -0
- package/dist/m365/spo/commands/folder/folder-sharinglink-remove.js +95 -0
- package/dist/m365/spo/commands/list/list-get.js +17 -4
- package/dist/m365/spo/commands/page/page-section-add.js +185 -34
- package/dist/m365/spo/commands/site/SiteAdmin.js +2 -0
- package/dist/m365/spo/commands/site/site-admin-add.js +252 -0
- package/dist/m365/spo/commands/site/site-admin-list.js +2 -27
- package/dist/m365/spo/commands/site/site-admin-remove.js +193 -0
- package/dist/m365/spo/commands/sitescript/sitescript-get.js +3 -2
- package/dist/m365/spo/commands/user/user-get.js +67 -9
- package/dist/m365/spo/commands.js +5 -0
- package/dist/m365/spp/commands/contentcenter/contentcenter-list.js +56 -0
- package/dist/m365/spp/commands.js +5 -0
- package/dist/m365/teams/MeetingTranscript.js +2 -0
- package/dist/m365/teams/commands/meeting/meeting-transcript-get.js +152 -0
- package/dist/m365/teams/commands.js +1 -0
- package/dist/settingsNames.js +6 -1
- package/dist/utils/driveUtil.js +51 -0
- package/dist/utils/entraApp.js +283 -0
- package/dist/utils/spo.js +75 -7
- package/dist/utils/zod.js +1 -1
- package/docs/docs/_clisettings.mdx +6 -1
- package/docs/docs/cmd/entra/m365group/m365group-report-activitystorage.mdx +2 -2
- package/docs/docs/cmd/file/file-move.mdx +79 -0
- package/docs/docs/cmd/flow/recyclebinitem/recyclebinitem-list.mdx +132 -0
- package/docs/docs/cmd/flow/recyclebinitem/recyclebinitem-restore.mdx +55 -0
- package/docs/docs/cmd/setup.mdx +17 -6
- package/docs/docs/cmd/spo/cdn/cdn-get.mdx +1 -1
- package/docs/docs/cmd/spo/cdn/cdn-origin-add.mdx +1 -1
- package/docs/docs/cmd/spo/cdn/cdn-origin-list.mdx +1 -1
- package/docs/docs/cmd/spo/cdn/cdn-origin-remove.mdx +1 -1
- package/docs/docs/cmd/spo/cdn/cdn-policy-list.mdx +1 -1
- package/docs/docs/cmd/spo/cdn/cdn-policy-set.mdx +1 -1
- package/docs/docs/cmd/spo/contenttype/contenttype-field-remove.mdx +7 -7
- package/docs/docs/cmd/spo/contenttype/contenttype-field-set.mdx +2 -2
- package/docs/docs/cmd/spo/externaluser/externaluser-list.mdx +1 -1
- package/docs/docs/cmd/spo/folder/folder-sharinglink-add.mdx +125 -0
- package/docs/docs/cmd/spo/folder/folder-sharinglink-clear.mdx +50 -0
- package/docs/docs/cmd/spo/folder/folder-sharinglink-remove.mdx +50 -0
- package/docs/docs/cmd/spo/hidedefaultthemes/hidedefaultthemes-get.mdx +1 -1
- package/docs/docs/cmd/spo/hidedefaultthemes/hidedefaultthemes-set.mdx +1 -1
- package/docs/docs/cmd/spo/homesite/homesite-remove.mdx +1 -1
- package/docs/docs/cmd/spo/knowledgehub/knowledgehub-get.mdx +1 -1
- package/docs/docs/cmd/spo/knowledgehub/knowledgehub-remove.mdx +1 -1
- package/docs/docs/cmd/spo/knowledgehub/knowledgehub-set.mdx +1 -1
- package/docs/docs/cmd/spo/orgassetslibrary/orgassetslibrary-add.mdx +1 -1
- package/docs/docs/cmd/spo/orgassetslibrary/orgassetslibrary-list.mdx +1 -1
- package/docs/docs/cmd/spo/orgassetslibrary/orgassetslibrary-remove.mdx +1 -1
- package/docs/docs/cmd/spo/orgnewssite/orgnewssite-list.mdx +1 -1
- package/docs/docs/cmd/spo/orgnewssite/orgnewssite-remove.mdx +1 -1
- package/docs/docs/cmd/spo/orgnewssite/orgnewssite-set.mdx +1 -1
- package/docs/docs/cmd/spo/page/page-section-add.mdx +57 -2
- package/docs/docs/cmd/spo/site/site-admin-add.mdx +67 -0
- package/docs/docs/cmd/spo/site/site-admin-remove.mdx +67 -0
- package/docs/docs/cmd/spo/site/site-appcatalog-add.mdx +1 -1
- package/docs/docs/cmd/spo/site/site-appcatalog-remove.mdx +1 -1
- package/docs/docs/cmd/spo/site/site-commsite-enable.mdx +1 -1
- package/docs/docs/cmd/spo/site/site-list.mdx +6 -4
- package/docs/docs/cmd/spo/site/site-set.mdx +1 -1
- package/docs/docs/cmd/spo/sitescript/sitescript-get.mdx +14 -1
- package/docs/docs/cmd/spo/storageentity/storageentity-remove.mdx +1 -1
- package/docs/docs/cmd/spo/storageentity/storageentity-set.mdx +1 -1
- package/docs/docs/cmd/spo/tenant/tenant-appcatalog-add.mdx +1 -1
- package/docs/docs/cmd/spo/tenant/tenant-appcatalogurl-get.mdx +1 -1
- package/docs/docs/cmd/spo/tenant/tenant-recyclebinitem-list.mdx +1 -1
- package/docs/docs/cmd/spo/tenant/tenant-recyclebinitem-remove.mdx +1 -1
- package/docs/docs/cmd/spo/tenant/tenant-settings-list.mdx +1 -1
- package/docs/docs/cmd/spo/theme/theme-apply.mdx +1 -1
- package/docs/docs/cmd/spo/theme/theme-get.mdx +1 -1
- package/docs/docs/cmd/spo/theme/theme-list.mdx +1 -1
- package/docs/docs/cmd/spo/theme/theme-remove.mdx +1 -1
- package/docs/docs/cmd/spo/theme/theme-set.mdx +1 -1
- package/docs/docs/cmd/spo/user/user-get.mdx +35 -9
- package/docs/docs/cmd/spp/contentcenter/contentcenter-list.mdx +287 -0
- package/docs/docs/cmd/teams/meeting/meeting-transcript-get.mdx +132 -0
- package/npm-shrinkwrap.json +203 -375
- package/package.json +16 -17
package/dist/settingsNames.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const settingsNames = {
|
|
2
2
|
authType: 'authType',
|
|
3
3
|
autoOpenLinksInBrowser: 'autoOpenLinksInBrowser',
|
|
4
|
+
clientId: 'clientId',
|
|
5
|
+
clientSecret: 'clientSecret',
|
|
6
|
+
clientCertificateFile: 'clientCertificateFile',
|
|
7
|
+
clientCertificateBase64Encoded: 'clientCertificateBase64Encoded',
|
|
8
|
+
clientCertificatePassword: 'clientCertificatePassword',
|
|
4
9
|
copyDeviceCodeToClipboard: 'copyDeviceCodeToClipboard',
|
|
5
10
|
csvEscape: 'csvEscape',
|
|
6
11
|
csvHeader: 'csvHeader',
|
|
@@ -16,7 +21,7 @@ const settingsNames = {
|
|
|
16
21
|
prompt: 'prompt',
|
|
17
22
|
promptListPageSize: 'promptListPageSize',
|
|
18
23
|
showHelpOnFailure: 'showHelpOnFailure',
|
|
19
|
-
|
|
24
|
+
tenantId: 'tenantId'
|
|
20
25
|
};
|
|
21
26
|
export { settingsNames };
|
|
22
27
|
//# sourceMappingURL=settingsNames.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import request from "../request.js";
|
|
2
|
+
export const driveUtil = {
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves the Drive associated with the specified site and URL.
|
|
5
|
+
* @param siteId Site ID
|
|
6
|
+
* @param url Drive URL
|
|
7
|
+
* @returns The Drive associated with the drive URL.
|
|
8
|
+
*/
|
|
9
|
+
async getDriveByUrl(siteId, url) {
|
|
10
|
+
const requestOptions = {
|
|
11
|
+
url: `https://graph.microsoft.com/v1.0/sites/${siteId}/drives?$select=webUrl,id`,
|
|
12
|
+
headers: {
|
|
13
|
+
accept: 'application/json;odata.metadata=none'
|
|
14
|
+
},
|
|
15
|
+
responseType: 'json'
|
|
16
|
+
};
|
|
17
|
+
const drives = await request.get(requestOptions);
|
|
18
|
+
const lowerCaseFolderUrl = url.href.toLowerCase();
|
|
19
|
+
const drive = drives.value
|
|
20
|
+
.sort((a, b) => b.webUrl.localeCompare(a.webUrl))
|
|
21
|
+
.find((d) => {
|
|
22
|
+
const driveUrl = d.webUrl.toLowerCase();
|
|
23
|
+
return lowerCaseFolderUrl.startsWith(driveUrl) &&
|
|
24
|
+
(driveUrl.length === lowerCaseFolderUrl.length ||
|
|
25
|
+
lowerCaseFolderUrl[driveUrl.length] === '/');
|
|
26
|
+
});
|
|
27
|
+
if (!drive) {
|
|
28
|
+
throw `Drive '${url.href}' not found`;
|
|
29
|
+
}
|
|
30
|
+
return drive;
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves the ID of a drive item (file, folder, etc.) associated with the given drive and item URL.
|
|
34
|
+
* @param drive The Drive object containing the item
|
|
35
|
+
* @param itemUrl Item URL
|
|
36
|
+
* @returns Drive item ID
|
|
37
|
+
*/
|
|
38
|
+
async getDriveItemId(drive, itemUrl) {
|
|
39
|
+
const relativeItemUrl = itemUrl.href.replace(new RegExp(`${drive.webUrl}`, 'i'), '').replace(/\/+$/, '');
|
|
40
|
+
const requestOptions = {
|
|
41
|
+
url: `https://graph.microsoft.com/v1.0/drives/${drive.id}/root${relativeItemUrl ? `:${relativeItemUrl}` : ''}?$select=id`,
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json;odata.metadata=none'
|
|
44
|
+
},
|
|
45
|
+
responseType: 'json'
|
|
46
|
+
};
|
|
47
|
+
const driveItem = await request.get(requestOptions);
|
|
48
|
+
return driveItem?.id;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=driveUtil.js.map
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import request from '../request.js';
|
|
3
|
+
import { odata } from './odata.js';
|
|
4
|
+
async function getCertificateBase64Encoded({ options, logger, debug }) {
|
|
5
|
+
if (options.certificateBase64Encoded) {
|
|
6
|
+
return options.certificateBase64Encoded;
|
|
7
|
+
}
|
|
8
|
+
if (debug) {
|
|
9
|
+
await logger.logToStderr(`Reading existing ${options.certificateFile}...`);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return fs.readFileSync(options.certificateFile, { encoding: 'base64' });
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
throw new Error(`Error reading certificate file: ${e}. Please add the certificate using base64 option '--certificateBase64Encoded'.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function createServicePrincipal(appId) {
|
|
19
|
+
const requestOptions = {
|
|
20
|
+
url: `https://graph.microsoft.com/v1.0/myorganization/servicePrincipals`,
|
|
21
|
+
headers: {
|
|
22
|
+
'content-type': 'application/json'
|
|
23
|
+
},
|
|
24
|
+
data: {
|
|
25
|
+
appId: appId
|
|
26
|
+
},
|
|
27
|
+
responseType: 'json'
|
|
28
|
+
};
|
|
29
|
+
return request.post(requestOptions);
|
|
30
|
+
}
|
|
31
|
+
async function grantOAuth2Permission({ appId, resourceId, scopeName }) {
|
|
32
|
+
const grantAdminConsentApplicationRequestOptions = {
|
|
33
|
+
url: `https://graph.microsoft.com/v1.0/myorganization/oauth2PermissionGrants`,
|
|
34
|
+
headers: {
|
|
35
|
+
accept: 'application/json;odata.metadata=none'
|
|
36
|
+
},
|
|
37
|
+
responseType: 'json',
|
|
38
|
+
data: {
|
|
39
|
+
clientId: appId,
|
|
40
|
+
consentType: "AllPrincipals",
|
|
41
|
+
principalId: null,
|
|
42
|
+
resourceId: resourceId,
|
|
43
|
+
scope: scopeName
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return request.post(grantAdminConsentApplicationRequestOptions);
|
|
47
|
+
}
|
|
48
|
+
async function addRoleToServicePrincipal({ objectId, resourceId, appRoleId }) {
|
|
49
|
+
const requestOptions = {
|
|
50
|
+
url: `https://graph.microsoft.com/v1.0/myorganization/servicePrincipals/${objectId}/appRoleAssignments`,
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/json'
|
|
53
|
+
},
|
|
54
|
+
responseType: 'json',
|
|
55
|
+
data: {
|
|
56
|
+
appRoleId: appRoleId,
|
|
57
|
+
principalId: objectId,
|
|
58
|
+
resourceId: resourceId
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return request.post(requestOptions);
|
|
62
|
+
}
|
|
63
|
+
async function getRequiredResourceAccessForApis({ servicePrincipals, apis, scopeType, logger, debug }) {
|
|
64
|
+
if (!apis) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const resolvedApis = [];
|
|
68
|
+
const requestedApis = apis.split(',').map(a => a.trim());
|
|
69
|
+
for (const api of requestedApis) {
|
|
70
|
+
const pos = api.lastIndexOf('/');
|
|
71
|
+
const permissionName = api.substring(pos + 1);
|
|
72
|
+
const servicePrincipalName = api.substring(0, pos);
|
|
73
|
+
if (debug) {
|
|
74
|
+
await logger.logToStderr(`Resolving ${api}...`);
|
|
75
|
+
await logger.logToStderr(`Permission name: ${permissionName}`);
|
|
76
|
+
await logger.logToStderr(`Service principal name: ${servicePrincipalName}`);
|
|
77
|
+
}
|
|
78
|
+
const servicePrincipal = servicePrincipals.find(sp => (sp.servicePrincipalNames.indexOf(servicePrincipalName) > -1 ||
|
|
79
|
+
sp.servicePrincipalNames.indexOf(`${servicePrincipalName}/`) > -1));
|
|
80
|
+
if (!servicePrincipal) {
|
|
81
|
+
throw `Service principal ${servicePrincipalName} not found`;
|
|
82
|
+
}
|
|
83
|
+
const scopesOfType = scopeType === 'Scope' ? servicePrincipal.oauth2PermissionScopes : servicePrincipal.appRoles;
|
|
84
|
+
const permission = scopesOfType.find(scope => scope.value === permissionName);
|
|
85
|
+
if (!permission) {
|
|
86
|
+
throw `Permission ${permissionName} for service principal ${servicePrincipalName} not found`;
|
|
87
|
+
}
|
|
88
|
+
let resolvedApi = resolvedApis.find(a => a.resourceAppId === servicePrincipal.appId);
|
|
89
|
+
if (!resolvedApi) {
|
|
90
|
+
resolvedApi = {
|
|
91
|
+
resourceAppId: servicePrincipal.appId,
|
|
92
|
+
resourceAccess: []
|
|
93
|
+
};
|
|
94
|
+
resolvedApis.push(resolvedApi);
|
|
95
|
+
}
|
|
96
|
+
const resourceAccessPermission = {
|
|
97
|
+
id: permission.id,
|
|
98
|
+
type: scopeType
|
|
99
|
+
};
|
|
100
|
+
resolvedApi.resourceAccess.push(resourceAccessPermission);
|
|
101
|
+
updateAppPermissions({
|
|
102
|
+
spId: servicePrincipal.id,
|
|
103
|
+
resourceAccessPermission,
|
|
104
|
+
oAuth2PermissionValue: permission.value
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return resolvedApis;
|
|
108
|
+
}
|
|
109
|
+
function updateAppPermissions({ spId, resourceAccessPermission, oAuth2PermissionValue }) {
|
|
110
|
+
// During API resolution, we store globally both app role assignments and oauth2permissions
|
|
111
|
+
// So that we'll be able to parse them during the admin consent process
|
|
112
|
+
let existingPermission = entraApp.appPermissions.find(oauth => oauth.resourceId === spId);
|
|
113
|
+
if (!existingPermission) {
|
|
114
|
+
existingPermission = {
|
|
115
|
+
resourceId: spId,
|
|
116
|
+
resourceAccess: [],
|
|
117
|
+
scope: []
|
|
118
|
+
};
|
|
119
|
+
entraApp.appPermissions.push(existingPermission);
|
|
120
|
+
}
|
|
121
|
+
if (resourceAccessPermission.type === 'Scope' && oAuth2PermissionValue && !existingPermission.scope.find(scp => scp === oAuth2PermissionValue)) {
|
|
122
|
+
existingPermission.scope.push(oAuth2PermissionValue);
|
|
123
|
+
}
|
|
124
|
+
if (!existingPermission.resourceAccess.find(res => res.id === resourceAccessPermission.id)) {
|
|
125
|
+
existingPermission.resourceAccess.push(resourceAccessPermission);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export const entraApp = {
|
|
129
|
+
appPermissions: [],
|
|
130
|
+
createAppRegistration: async ({ options, apis, logger, verbose, debug }) => {
|
|
131
|
+
const applicationInfo = {
|
|
132
|
+
displayName: options.name,
|
|
133
|
+
signInAudience: options.multitenant ? 'AzureADMultipleOrgs' : 'AzureADMyOrg'
|
|
134
|
+
};
|
|
135
|
+
if (apis.length > 0) {
|
|
136
|
+
applicationInfo.requiredResourceAccess = apis;
|
|
137
|
+
}
|
|
138
|
+
if (options.redirectUris) {
|
|
139
|
+
applicationInfo[options.platform] = {
|
|
140
|
+
redirectUris: options.redirectUris.split(',').map(u => u.trim())
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (options.implicitFlow) {
|
|
144
|
+
if (!applicationInfo.web) {
|
|
145
|
+
applicationInfo.web = {};
|
|
146
|
+
}
|
|
147
|
+
applicationInfo.web.implicitGrantSettings = {
|
|
148
|
+
enableAccessTokenIssuance: true,
|
|
149
|
+
enableIdTokenIssuance: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (options.certificateFile || options.certificateBase64Encoded) {
|
|
153
|
+
const certificateBase64Encoded = await getCertificateBase64Encoded({ options, logger, debug });
|
|
154
|
+
const newKeyCredential = {
|
|
155
|
+
type: 'AsymmetricX509Cert',
|
|
156
|
+
usage: 'Verify',
|
|
157
|
+
displayName: options.certificateDisplayName,
|
|
158
|
+
key: certificateBase64Encoded
|
|
159
|
+
};
|
|
160
|
+
applicationInfo.keyCredentials = [newKeyCredential];
|
|
161
|
+
}
|
|
162
|
+
if (options.allowPublicClientFlows) {
|
|
163
|
+
applicationInfo.isFallbackPublicClient = true;
|
|
164
|
+
}
|
|
165
|
+
if (verbose) {
|
|
166
|
+
await logger.logToStderr(`Creating Microsoft Entra app registration...`);
|
|
167
|
+
}
|
|
168
|
+
const createApplicationRequestOptions = {
|
|
169
|
+
url: `https://graph.microsoft.com/v1.0/myorganization/applications`,
|
|
170
|
+
headers: {
|
|
171
|
+
accept: 'application/json;odata.metadata=none'
|
|
172
|
+
},
|
|
173
|
+
responseType: 'json',
|
|
174
|
+
data: applicationInfo
|
|
175
|
+
};
|
|
176
|
+
return request.post(createApplicationRequestOptions);
|
|
177
|
+
},
|
|
178
|
+
grantAdminConsent: async ({ appInfo, appPermissions, adminConsent, logger, debug }) => {
|
|
179
|
+
if (!adminConsent || appPermissions.length === 0) {
|
|
180
|
+
return appInfo;
|
|
181
|
+
}
|
|
182
|
+
const sp = await createServicePrincipal(appInfo.appId);
|
|
183
|
+
if (debug) {
|
|
184
|
+
await logger.logToStderr("Service principal created, returned object id: " + sp.id);
|
|
185
|
+
}
|
|
186
|
+
const tasks = [];
|
|
187
|
+
appPermissions.forEach(async (permission) => {
|
|
188
|
+
if (permission.scope.length > 0) {
|
|
189
|
+
tasks.push(grantOAuth2Permission({
|
|
190
|
+
appId: sp.id,
|
|
191
|
+
resourceId: permission.resourceId,
|
|
192
|
+
scopeName: permission.scope.join(' ')
|
|
193
|
+
}));
|
|
194
|
+
if (debug) {
|
|
195
|
+
await logger.logToStderr(`Admin consent granted for following resource ${permission.resourceId}, with delegated permissions: ${permission.scope.join(',')}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
permission.resourceAccess.filter(access => access.type === "Role").forEach(async (access) => {
|
|
199
|
+
tasks.push(addRoleToServicePrincipal({
|
|
200
|
+
objectId: sp.id,
|
|
201
|
+
resourceId: permission.resourceId,
|
|
202
|
+
appRoleId: access.id
|
|
203
|
+
}));
|
|
204
|
+
if (debug) {
|
|
205
|
+
await logger.logToStderr(`Admin consent granted for following resource ${permission.resourceId}, with application permission: ${access.id}`);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
await Promise.all(tasks);
|
|
210
|
+
return appInfo;
|
|
211
|
+
},
|
|
212
|
+
resolveApis: async ({ options, manifest, logger, verbose, debug }) => {
|
|
213
|
+
if (!options.apisDelegated && !options.apisApplication
|
|
214
|
+
&& (typeof manifest?.requiredResourceAccess === 'undefined' || manifest.requiredResourceAccess.length === 0)) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
if (verbose) {
|
|
218
|
+
await logger.logToStderr('Resolving requested APIs...');
|
|
219
|
+
}
|
|
220
|
+
const servicePrincipals = await odata.getAllItems(`https://graph.microsoft.com/v1.0/myorganization/servicePrincipals?$select=appId,appRoles,id,oauth2PermissionScopes,servicePrincipalNames`);
|
|
221
|
+
let resolvedApis = [];
|
|
222
|
+
if (options.apisDelegated || options.apisApplication) {
|
|
223
|
+
resolvedApis = await getRequiredResourceAccessForApis({
|
|
224
|
+
servicePrincipals,
|
|
225
|
+
apis: options.apisDelegated,
|
|
226
|
+
scopeType: 'Scope',
|
|
227
|
+
logger,
|
|
228
|
+
debug
|
|
229
|
+
});
|
|
230
|
+
if (verbose) {
|
|
231
|
+
await logger.logToStderr(`Resolved delegated permissions: ${JSON.stringify(resolvedApis, null, 2)}`);
|
|
232
|
+
}
|
|
233
|
+
const resolvedApplicationApis = await getRequiredResourceAccessForApis({
|
|
234
|
+
servicePrincipals,
|
|
235
|
+
apis: options.apisApplication,
|
|
236
|
+
scopeType: 'Role',
|
|
237
|
+
logger,
|
|
238
|
+
debug
|
|
239
|
+
});
|
|
240
|
+
if (verbose) {
|
|
241
|
+
await logger.logToStderr(`Resolved application permissions: ${JSON.stringify(resolvedApplicationApis, null, 2)}`);
|
|
242
|
+
}
|
|
243
|
+
// merge resolved application APIs onto resolved delegated APIs
|
|
244
|
+
resolvedApplicationApis.forEach(resolvedRequiredResource => {
|
|
245
|
+
const requiredResource = resolvedApis.find(api => api.resourceAppId === resolvedRequiredResource.resourceAppId);
|
|
246
|
+
if (requiredResource) {
|
|
247
|
+
requiredResource.resourceAccess.push(...resolvedRequiredResource.resourceAccess);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
resolvedApis.push(resolvedRequiredResource);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const manifestApis = manifest.requiredResourceAccess;
|
|
256
|
+
manifestApis.forEach(manifestApi => {
|
|
257
|
+
resolvedApis.push(manifestApi);
|
|
258
|
+
const app = servicePrincipals.find(servicePrincipals => servicePrincipals.appId === manifestApi.resourceAppId);
|
|
259
|
+
if (app) {
|
|
260
|
+
manifestApi.resourceAccess.forEach((res => {
|
|
261
|
+
const resourceAccessPermission = {
|
|
262
|
+
id: res.id,
|
|
263
|
+
type: res.type
|
|
264
|
+
};
|
|
265
|
+
const oAuthValue = app.oauth2PermissionScopes.find(scp => scp.id === res.id)?.value;
|
|
266
|
+
updateAppPermissions({
|
|
267
|
+
spId: app.id,
|
|
268
|
+
resourceAccessPermission,
|
|
269
|
+
oAuth2PermissionValue: oAuthValue
|
|
270
|
+
});
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (verbose) {
|
|
276
|
+
await logger.logToStderr(`Merged delegated and application permissions: ${JSON.stringify(resolvedApis, null, 2)}`);
|
|
277
|
+
await logger.logToStderr(`App role assignments: ${JSON.stringify(entraApp.appPermissions.flatMap(permission => permission.resourceAccess.filter(access => access.type === "Role")), null, 2)}`);
|
|
278
|
+
await logger.logToStderr(`OAuth2 permissions: ${JSON.stringify(entraApp.appPermissions.flatMap(permission => permission.scope), null, 2)}`);
|
|
279
|
+
}
|
|
280
|
+
return resolvedApis;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
//# sourceMappingURL=entraApp.js.map
|
package/dist/utils/spo.js
CHANGED
|
@@ -500,10 +500,10 @@ export const spo = {
|
|
|
500
500
|
* @param webUrl Web url
|
|
501
501
|
* @param email The email of the user
|
|
502
502
|
* @param logger the Logger object
|
|
503
|
-
* @param verbose set
|
|
503
|
+
* @param verbose set for verbose logging
|
|
504
504
|
*/
|
|
505
505
|
async getUserByEmail(webUrl, email, logger, verbose) {
|
|
506
|
-
if (verbose) {
|
|
506
|
+
if (verbose && logger) {
|
|
507
507
|
await logger.logToStderr(`Retrieving the spo user by email ${email}`);
|
|
508
508
|
}
|
|
509
509
|
const requestUrl = `${webUrl}/_api/web/siteusers/GetByEmail('${formatting.encodeQueryParameter(email)}')`;
|
|
@@ -574,10 +574,10 @@ export const spo = {
|
|
|
574
574
|
* @param webUrl Web url
|
|
575
575
|
* @param name The name of the group
|
|
576
576
|
* @param logger the Logger object
|
|
577
|
-
* @param verbose set
|
|
577
|
+
* @param verbose set for verbose logging
|
|
578
578
|
*/
|
|
579
579
|
async getGroupByName(webUrl, name, logger, verbose) {
|
|
580
|
-
if (verbose) {
|
|
580
|
+
if (verbose && logger) {
|
|
581
581
|
await logger.logToStderr(`Retrieving the group by name ${name}`);
|
|
582
582
|
}
|
|
583
583
|
const requestUrl = `${webUrl}/_api/web/sitegroups/GetByName('${formatting.encodeQueryParameter(name)}')`;
|
|
@@ -596,10 +596,10 @@ export const spo = {
|
|
|
596
596
|
* @param webUrl Web url
|
|
597
597
|
* @param name the name of the role definition
|
|
598
598
|
* @param logger the Logger object
|
|
599
|
-
* @param
|
|
599
|
+
* @param verbose set for verbose logging
|
|
600
600
|
*/
|
|
601
|
-
async getRoleDefinitionByName(webUrl, name, logger,
|
|
602
|
-
if (
|
|
601
|
+
async getRoleDefinitionByName(webUrl, name, logger, verbose) {
|
|
602
|
+
if (verbose && logger) {
|
|
603
603
|
await logger.logToStderr(`Retrieving the role definitions for ${name}`);
|
|
604
604
|
}
|
|
605
605
|
const roledefinitions = await odata.getAllItems(`${webUrl}/_api/web/roledefinitions`);
|
|
@@ -1527,6 +1527,29 @@ export const spo = {
|
|
|
1527
1527
|
const itemsResponse = await request.get(requestOptionsItems);
|
|
1528
1528
|
return (itemsResponse);
|
|
1529
1529
|
},
|
|
1530
|
+
/**
|
|
1531
|
+
* Retrieves the file by id.
|
|
1532
|
+
* Returns a FileProperties object
|
|
1533
|
+
* @param webUrl Web url
|
|
1534
|
+
* @param id the id of the file
|
|
1535
|
+
* @param logger the Logger object
|
|
1536
|
+
* @param verbose set for verbose logging
|
|
1537
|
+
*/
|
|
1538
|
+
async getFileById(webUrl, id, logger, verbose) {
|
|
1539
|
+
if (verbose && logger) {
|
|
1540
|
+
await logger.logToStderr(`Retrieving the file with id ${id}`);
|
|
1541
|
+
}
|
|
1542
|
+
const requestUrl = `${webUrl}/_api/web/GetFileById('${formatting.encodeQueryParameter(id)}')`;
|
|
1543
|
+
const requestOptions = {
|
|
1544
|
+
url: requestUrl,
|
|
1545
|
+
headers: {
|
|
1546
|
+
'accept': 'application/json;odata=nometadata'
|
|
1547
|
+
},
|
|
1548
|
+
responseType: 'json'
|
|
1549
|
+
};
|
|
1550
|
+
const file = await request.get(requestOptions);
|
|
1551
|
+
return file;
|
|
1552
|
+
},
|
|
1530
1553
|
/**
|
|
1531
1554
|
* Create a SharePoint copy job to copy a file/folder to another location.
|
|
1532
1555
|
* @param webUrl Absolute web URL where the source file/folder is located.
|
|
@@ -1590,6 +1613,51 @@ export const spo = {
|
|
|
1590
1613
|
// Get the destination object information
|
|
1591
1614
|
const objectInfo = logs.find(l => l.Event === 'JobFinishedObjectInfo');
|
|
1592
1615
|
return objectInfo;
|
|
1616
|
+
},
|
|
1617
|
+
/**
|
|
1618
|
+
* Gets the site collection URL for a given web URL using SP Admin site.
|
|
1619
|
+
* @param adminUrl The SharePoint admin URL
|
|
1620
|
+
* @param siteId The site ID
|
|
1621
|
+
* @param logger The logger object
|
|
1622
|
+
* @param verbose If in verbose mode
|
|
1623
|
+
* @returns Owner login name
|
|
1624
|
+
*/
|
|
1625
|
+
async getPrimaryAdminLoginNameAsAdmin(adminUrl, siteId, logger, verbose) {
|
|
1626
|
+
if (verbose) {
|
|
1627
|
+
await logger.logToStderr('Getting the primary admin login name...');
|
|
1628
|
+
}
|
|
1629
|
+
const requestOptions = {
|
|
1630
|
+
url: `${adminUrl}/_api/SPO.Tenant/sites('${siteId}')?$select=OwnerLoginName`,
|
|
1631
|
+
headers: {
|
|
1632
|
+
accept: 'application/json;odata=nometadata',
|
|
1633
|
+
'content-type': 'application/json;charset=utf-8'
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
const response = await request.get(requestOptions);
|
|
1637
|
+
const responseContent = JSON.parse(response);
|
|
1638
|
+
return responseContent.OwnerLoginName;
|
|
1639
|
+
},
|
|
1640
|
+
/**
|
|
1641
|
+
* Gets the primary owner login from a site.
|
|
1642
|
+
* @param siteUrl The site URL
|
|
1643
|
+
* @param logger The logger object
|
|
1644
|
+
* @param verbose If in verbose mode
|
|
1645
|
+
* @returns Owner login name
|
|
1646
|
+
*/
|
|
1647
|
+
async getPrimaryOwnerLoginFromSite(siteUrl, logger, verbose) {
|
|
1648
|
+
if (verbose) {
|
|
1649
|
+
await logger.logToStderr('Getting the primary admin login name...');
|
|
1650
|
+
}
|
|
1651
|
+
const requestOptions = {
|
|
1652
|
+
url: `${siteUrl}/_api/site/owner`,
|
|
1653
|
+
method: 'GET',
|
|
1654
|
+
headers: {
|
|
1655
|
+
'accept': 'application/json;odata=nometadata'
|
|
1656
|
+
},
|
|
1657
|
+
responseType: 'json'
|
|
1658
|
+
};
|
|
1659
|
+
const responseContent = await request.get(requestOptions);
|
|
1660
|
+
return responseContent?.LoginName;
|
|
1593
1661
|
}
|
|
1594
1662
|
};
|
|
1595
1663
|
//# sourceMappingURL=spo.js.map
|
package/dist/utils/zod.js
CHANGED
|
@@ -60,7 +60,7 @@ function parseDefault(def, _options, currentOption) {
|
|
|
60
60
|
function parseEnum(def, _options, currentOption) {
|
|
61
61
|
if (currentOption) {
|
|
62
62
|
currentOption.type = 'string';
|
|
63
|
-
currentOption.autocomplete = def.values;
|
|
63
|
+
currentOption.autocomplete = [...def.values];
|
|
64
64
|
}
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
@@ -2,6 +2,11 @@ Setting name|Definition|Default value
|
|
|
2
2
|
------------|----------|-------------
|
|
3
3
|
`authType`|Default login method to use when running `m365 login` without the `--authType` option.|`deviceCode`
|
|
4
4
|
`autoOpenLinksInBrowser`|Automatically open the browser for all commands which return a url and expect the user to copy paste this to the browser. For example when logging in, using `m365 login` in device code mode.|`false`
|
|
5
|
+
`clientId`|ID of the default Entra ID app use by the CLI to authenticate|``
|
|
6
|
+
`clientSecret`|Secret of the default Entra ID app use by the CLI to authenticate|``
|
|
7
|
+
`clientCertificateFile`|Path to the file containing the client certificate to use for authentication|``
|
|
8
|
+
`clientCertificateBase64Encoded`|Base64-encoded client certificate contents|``
|
|
9
|
+
`clientCertificatePassword`|Password to the client certificate file|``
|
|
5
10
|
`copyDeviceCodeToClipboard`|Automatically copy the device code to the clipboard when running `m365 login` command in device code mode|`false`
|
|
6
11
|
`csvEscape`|Single character used for escaping; only apply to characters matching the quote and the escape options|`"`
|
|
7
12
|
`csvHeader`|Display the column names on the first line|`true`
|
|
@@ -17,4 +22,4 @@ Setting name|Definition|Default value
|
|
|
17
22
|
`prompt`|Prompts for missing values in required options and enables interactive selection when multiple values are available for a command that requires a specific value to be retrieved.|`true`
|
|
18
23
|
`promptListPageSize`|By default, lists of choices longer than 7 will be paginated. Use this option to control how many choices will appear on the screen at once.|7
|
|
19
24
|
`showHelpOnFailure`|Automatically display help when executing a command failed|`true`
|
|
20
|
-
`
|
|
25
|
+
`tenantId`|ID of the default tenant to use when authenticating with|``
|
|
@@ -9,13 +9,13 @@ Get the total storage used across all group mailboxes and group sites
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
m365 entra m365group report activitystorage
|
|
12
|
+
m365 entra m365group report activitystorage [options]
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Alias
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
m365 aad m365group report activitystorage
|
|
18
|
+
m365 aad m365group report activitystorage [options]
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Options
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import Global from '/docs/cmd/_global.mdx';
|
|
2
|
+
|
|
3
|
+
# file move
|
|
4
|
+
|
|
5
|
+
Moves a file to another location using the Microsoft Graph
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
m365 file move [options]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Options
|
|
14
|
+
|
|
15
|
+
```md definition-list
|
|
16
|
+
`-u, --webUrl <webUrl>`
|
|
17
|
+
: The URL of the site where the file is located.
|
|
18
|
+
|
|
19
|
+
`-s, --sourceUrl <sourceUrl>`
|
|
20
|
+
: Server-relative or absolute URL of the file.
|
|
21
|
+
|
|
22
|
+
`-t, --targetUrl <targetUrl>`
|
|
23
|
+
: Server-relative or absolute URL of the location.
|
|
24
|
+
|
|
25
|
+
`--newName [newName]`
|
|
26
|
+
: New name of the destination file.
|
|
27
|
+
|
|
28
|
+
`--nameConflictBehavior [nameConflictBehavior]`
|
|
29
|
+
: Behavior when a document with the same name is already present at the destination. Possible values: `fail`, `replace`, `rename`. Default is `fail`.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
<Global />
|
|
33
|
+
|
|
34
|
+
## Remarks
|
|
35
|
+
|
|
36
|
+
- If the source and target locations are within the same document library or drive, the command will utilize the Move DriveItem API, preserving the version history of the file.
|
|
37
|
+
- If the source and target locations are in different document libraries or drives, the command will use a copy-and-delete combination to move the file. Please note that in this case, version history will not be retained.
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
Move a file by server-relative URL to a folder in the same document library
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
m365 file move --webUrl "https://contoso.sharepoint.com/sites/project" --sourceUrl "/sites/project/Shared Documents/Document.pdf" --targetUrl "/sites/project/Shared Documents/NewFolder"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Move a file by server-relative URL to a document library in another site collection with server relative URL
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
m365 file move --webUrl "https://contoso.sharepoint.com/sites/project" --sourceUrl "/sites/project/Shared Documents/Document.pdf" --targetUrl "/sites/IT/Shared Documents"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Move a file by absolute URL to a document library in another site collection with absolute URL
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
m365 file move --webUrl "https://contoso.sharepoint.com" --sourceUrl "https://contoso.sharepoint.com/Shared Documents/Document.pdf" --targetUrl "https://contoso.sharepoint.com/sites/IT/Shared Documents"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Move a file to a document library in another site collection and rename the file
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
m365 file move --webUrl "https://contoso.sharepoint.com" --sourceUrl "/Shared Documents/Document.pdf" --targetUrl "/sites/IT/Shared Documents" --newName "newName"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Move a file to a document library in another site collection and rename the file if a file with the same name already exists.
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
m365 file move --webUrl "https://contoso.sharepoint.com" --sourceUrl "/Shared Documents/Document.pdf" --targetUrl "/sites/IT/Shared Documents" --nameConflictBehavior rename
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Move a file to Onedrive for business
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
m365 file move --webUrl "https://contoso.sharepoint.com" --sourceUrl "/Shared Documents/Document.pdf" --targetUrl "https://contoso-my.sharepoint.com/personal/john_contoso_onmicrosoft_com/documents"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Response
|
|
78
|
+
|
|
79
|
+
The command won't return a response on success.
|