@pnp/cli-microsoft365 4.3.0-beta.c761547 → 5.0.0-beta.117f66f
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/dist/appInsights.js +2 -1
- package/dist/cli/Cli.js +22 -3
- package/dist/m365/aad/commands/oauth2grant/oauth2grant-list.js +7 -7
- package/dist/m365/aad/commands/oauth2grant/oauth2grant-remove.js +36 -12
- package/dist/m365/aad/commands/sp/sp-get.js +44 -21
- package/dist/m365/app/commands/permission/permission-list.js +266 -0
- package/dist/m365/app/commands.js +7 -0
- package/dist/m365/base/AppCommand.js +76 -0
- package/dist/m365/pa/commands/app/app-list.js +28 -1
- package/dist/request.js +9 -4
- package/docs/docs/cmd/_global.md +2 -2
- package/docs/docs/cmd/aad/oauth2grant/oauth2grant-list.md +4 -3
- package/docs/docs/cmd/aad/oauth2grant/oauth2grant-remove.md +9 -0
- package/docs/docs/cmd/aad/sp/sp-get.md +2 -1
- package/docs/docs/cmd/app/permission/permission-list.md +36 -0
- package/docs/docs/cmd/pa/app/app-list.md +17 -1
- package/docs/docs/cmd/spfx/project/project-externalize.md +1 -1
- package/docs/docs/cmd/spfx/project/project-rename.md +1 -1
- package/docs/docs/cmd/spfx/spfx-doctor.md +1 -1
- package/npm-shrinkwrap.json +13 -2
- package/package.json +2 -1
package/dist/appInsights.js
CHANGED
|
@@ -20,7 +20,8 @@ const env = process.env.CLIMICROSOFT365_ENV !== undefined ? process.env.CLIMICRO
|
|
|
20
20
|
appInsights.defaultClient.commonProperties = {
|
|
21
21
|
version: version,
|
|
22
22
|
node: process.version,
|
|
23
|
-
env: env
|
|
23
|
+
env: env,
|
|
24
|
+
ci: Boolean(process.env.CI).toString()
|
|
24
25
|
};
|
|
25
26
|
appInsights.defaultClient.context.tags['ai.session.id'] = crypto.randomBytes(24).toString('base64');
|
|
26
27
|
appInsights.defaultClient.context.tags['ai.cloud.roleInstance'] = crypto.createHash('sha256').update(appInsights.defaultClient.context.tags['ai.cloud.roleInstance']).digest('hex');
|
package/dist/cli/Cli.js
CHANGED
|
@@ -17,6 +17,7 @@ const path = require("path");
|
|
|
17
17
|
const appInsights_1 = require("../appInsights");
|
|
18
18
|
const Command_1 = require("../Command");
|
|
19
19
|
const config_1 = require("../config");
|
|
20
|
+
const request_1 = require("../request");
|
|
20
21
|
const settingsNames_1 = require("../settingsNames");
|
|
21
22
|
const Utils_1 = require("../Utils");
|
|
22
23
|
const packageJSON = require('../../package.json');
|
|
@@ -204,16 +205,20 @@ class Cli {
|
|
|
204
205
|
}
|
|
205
206
|
};
|
|
206
207
|
if (args.options.debug) {
|
|
207
|
-
|
|
208
|
+
logErr.push(`Executing command ${command.name} with options ${JSON.stringify(args)}`);
|
|
208
209
|
}
|
|
209
210
|
// store the current command name, if any and set the name to the name of
|
|
210
211
|
// the command to execute
|
|
211
212
|
const cli = Cli.getInstance();
|
|
212
213
|
const parentCommandName = cli.currentCommandName;
|
|
213
214
|
cli.currentCommandName = command.getCommandName();
|
|
215
|
+
// store the current logger if any
|
|
216
|
+
const currentLogger = request_1.default.logger;
|
|
214
217
|
command.action(logger, args, (err) => {
|
|
215
218
|
// restore the original command name
|
|
216
219
|
cli.currentCommandName = parentCommandName;
|
|
220
|
+
// restore the original logger
|
|
221
|
+
request_1.default.logger = currentLogger;
|
|
217
222
|
if (err) {
|
|
218
223
|
return reject({
|
|
219
224
|
error: err,
|
|
@@ -416,11 +421,11 @@ class Cli {
|
|
|
416
421
|
if (arrayType !== 'object') {
|
|
417
422
|
return logStatement.join(os.EOL);
|
|
418
423
|
}
|
|
419
|
-
// if output type has been set to 'text', process the retrieved
|
|
424
|
+
// if output type has been set to 'text' or 'csv', process the retrieved
|
|
420
425
|
// data so that returned objects contain only default properties specified
|
|
421
426
|
// on the current command. If there is no current command or the
|
|
422
427
|
// command doesn't specify default properties, return original data
|
|
423
|
-
if (options.output === 'text') {
|
|
428
|
+
if (options.output === 'text' || options.output === 'csv') {
|
|
424
429
|
const cli = Cli.getInstance();
|
|
425
430
|
const currentCommand = cli.commandToExecute;
|
|
426
431
|
if (arrayType === 'object' &&
|
|
@@ -440,6 +445,20 @@ class Cli {
|
|
|
440
445
|
}
|
|
441
446
|
}
|
|
442
447
|
}
|
|
448
|
+
if (options.output === 'csv') {
|
|
449
|
+
const { stringify } = require('csv-stringify/sync');
|
|
450
|
+
/*
|
|
451
|
+
https://csv.js.org/stringify/options/
|
|
452
|
+
header: Display the column names on the first line if the columns option is provided or discovered.
|
|
453
|
+
escape: Single character used for escaping; only apply to characters matching the quote and the escape options default to ".
|
|
454
|
+
quote: The quote characters surrounding a field, defaults to the " (double quotation marks), an empty quote value will preserve the original field, whether it contains quotation marks or not.
|
|
455
|
+
quoted: Boolean, default to false, quote all the non-empty fields even if not required.
|
|
456
|
+
quotedEmpty: Quote empty strings and overrides quoted_string on empty strings when defined; default is false.
|
|
457
|
+
*/
|
|
458
|
+
return stringify(logStatement, {
|
|
459
|
+
header: true
|
|
460
|
+
});
|
|
461
|
+
}
|
|
443
462
|
// display object as a list of key-value pairs
|
|
444
463
|
if (logStatement.length === 1) {
|
|
445
464
|
const obj = logStatement[0];
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const request_1 = require("../../../../request");
|
|
4
4
|
const Utils_1 = require("../../../../Utils");
|
|
5
|
-
const
|
|
5
|
+
const GraphCommand_1 = require("../../../base/GraphCommand");
|
|
6
6
|
const commands_1 = require("../../commands");
|
|
7
|
-
class AadOAuth2GrantListCommand extends
|
|
7
|
+
class AadOAuth2GrantListCommand extends GraphCommand_1.default {
|
|
8
8
|
get name() {
|
|
9
9
|
return commands_1.default.OAUTH2GRANT_LIST;
|
|
10
10
|
}
|
|
@@ -19,9 +19,9 @@ class AadOAuth2GrantListCommand extends AadCommand_1.default {
|
|
|
19
19
|
logger.logToStderr(`Retrieving list of OAuth grants for the service principal...`);
|
|
20
20
|
}
|
|
21
21
|
const requestOptions = {
|
|
22
|
-
url: `${this.resource}/
|
|
22
|
+
url: `${this.resource}/v1.0/oauth2PermissionGrants?$filter=clientId eq '${encodeURIComponent(args.options.spObjectId)}'`,
|
|
23
23
|
headers: {
|
|
24
|
-
accept: 'application/json;odata=
|
|
24
|
+
accept: 'application/json;odata.metadata=none'
|
|
25
25
|
},
|
|
26
26
|
responseType: 'json'
|
|
27
27
|
};
|
|
@@ -37,15 +37,15 @@ class AadOAuth2GrantListCommand extends AadCommand_1.default {
|
|
|
37
37
|
options() {
|
|
38
38
|
const options = [
|
|
39
39
|
{
|
|
40
|
-
option: '-i, --
|
|
40
|
+
option: '-i, --spObjectId <spObjectId>'
|
|
41
41
|
}
|
|
42
42
|
];
|
|
43
43
|
const parentOptions = super.options();
|
|
44
44
|
return options.concat(parentOptions);
|
|
45
45
|
}
|
|
46
46
|
validate(args) {
|
|
47
|
-
if (!Utils_1.default.isValidGuid(args.options.
|
|
48
|
-
return `${args.options.
|
|
47
|
+
if (!Utils_1.default.isValidGuid(args.options.spObjectId)) {
|
|
48
|
+
return `${args.options.spObjectId} is not a valid GUID`;
|
|
49
49
|
}
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cli_1 = require("../../../../cli");
|
|
3
4
|
const request_1 = require("../../../../request");
|
|
4
5
|
const GraphCommand_1 = require("../../../base/GraphCommand");
|
|
5
6
|
const commands_1 = require("../../commands");
|
|
@@ -11,24 +12,47 @@ class AadOAuth2GrantRemoveCommand extends GraphCommand_1.default {
|
|
|
11
12
|
return 'Remove specified service principal OAuth2 permissions';
|
|
12
13
|
}
|
|
13
14
|
commandAction(logger, args, cb) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const removeOauth2Grant = () => {
|
|
16
|
+
if (this.verbose) {
|
|
17
|
+
logger.logToStderr(`Removing OAuth2 permissions...`);
|
|
18
|
+
}
|
|
19
|
+
const requestOptions = {
|
|
20
|
+
url: `${this.resource}/v1.0/oauth2PermissionGrants/${encodeURIComponent(args.options.grantId)}`,
|
|
21
|
+
headers: {
|
|
22
|
+
'accept': 'application/json;odata.metadata=none'
|
|
23
|
+
},
|
|
24
|
+
responseType: 'json'
|
|
25
|
+
};
|
|
26
|
+
request_1.default
|
|
27
|
+
.delete(requestOptions)
|
|
28
|
+
.then(_ => cb(), (rawRes) => this.handleRejectedODataJsonPromise(rawRes, logger, cb));
|
|
23
29
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
if (args.options.confirm) {
|
|
31
|
+
removeOauth2Grant();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
cli_1.Cli.prompt({
|
|
35
|
+
type: 'confirm',
|
|
36
|
+
name: 'continue',
|
|
37
|
+
default: false,
|
|
38
|
+
message: `Are you sure you want to remove the OAuth2 permissions for ${args.options.grantId}?`
|
|
39
|
+
}, (result) => {
|
|
40
|
+
if (!result.continue) {
|
|
41
|
+
cb();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
removeOauth2Grant();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
27
48
|
}
|
|
28
49
|
options() {
|
|
29
50
|
const options = [
|
|
30
51
|
{
|
|
31
52
|
option: '-i, --grantId <grantId>'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
option: '--confirm'
|
|
32
56
|
}
|
|
33
57
|
];
|
|
34
58
|
const parentOptions = super.options();
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const request_1 = require("../../../../request");
|
|
4
4
|
const Utils_1 = require("../../../../Utils");
|
|
5
|
-
const
|
|
5
|
+
const GraphCommand_1 = require("../../../base/GraphCommand");
|
|
6
6
|
const commands_1 = require("../../commands");
|
|
7
|
-
class AadSpGetCommand extends
|
|
7
|
+
class AadSpGetCommand extends GraphCommand_1.default {
|
|
8
8
|
get name() {
|
|
9
9
|
return commands_1.default.SP_GET;
|
|
10
10
|
}
|
|
@@ -18,35 +18,58 @@ class AadSpGetCommand extends AadCommand_1.default {
|
|
|
18
18
|
telemetryProps.objectId = (!(!args.options.objectId)).toString();
|
|
19
19
|
return telemetryProps;
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
21
|
+
getSpId(args) {
|
|
22
|
+
if (args.options.objectId) {
|
|
23
|
+
return Promise.resolve(args.options.objectId);
|
|
24
24
|
}
|
|
25
25
|
let spMatchQuery = '';
|
|
26
|
-
if (args.options.
|
|
27
|
-
spMatchQuery = `appId eq '${encodeURIComponent(args.options.appId)}'`;
|
|
28
|
-
}
|
|
29
|
-
else if (args.options.objectId) {
|
|
30
|
-
spMatchQuery = `objectId eq '${encodeURIComponent(args.options.objectId)}'`;
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
26
|
+
if (args.options.displayName) {
|
|
33
27
|
spMatchQuery = `displayName eq '${encodeURIComponent(args.options.displayName)}'`;
|
|
34
28
|
}
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
else if (args.options.appId) {
|
|
30
|
+
spMatchQuery = `appId eq '${encodeURIComponent(args.options.appId)}'`;
|
|
31
|
+
}
|
|
32
|
+
const idRequestOptions = {
|
|
33
|
+
url: `${this.resource}/v1.0/servicePrincipals?$filter=${spMatchQuery}`,
|
|
37
34
|
headers: {
|
|
38
|
-
accept: 'application/json;odata=
|
|
35
|
+
accept: 'application/json;odata.metadata=none'
|
|
39
36
|
},
|
|
40
37
|
responseType: 'json'
|
|
41
38
|
};
|
|
42
|
-
request_1.default
|
|
43
|
-
.get(
|
|
44
|
-
.then(
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
return request_1.default
|
|
40
|
+
.get(idRequestOptions)
|
|
41
|
+
.then(response => {
|
|
42
|
+
const spItem = response.value[0];
|
|
43
|
+
if (!spItem) {
|
|
44
|
+
return Promise.reject(`The specified Azure AD app does not exist`);
|
|
45
|
+
}
|
|
46
|
+
if (response.value.length > 1) {
|
|
47
|
+
return Promise.reject(`Multiple Azure AD apps with name ${args.options.displayName} found: ${response.value.map(x => x.id)}`);
|
|
47
48
|
}
|
|
49
|
+
return Promise.resolve(spItem.id);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
commandAction(logger, args, cb) {
|
|
53
|
+
if (this.verbose) {
|
|
54
|
+
logger.logToStderr(`Retrieving service principal information...`);
|
|
55
|
+
}
|
|
56
|
+
this
|
|
57
|
+
.getSpId(args)
|
|
58
|
+
.then((id) => {
|
|
59
|
+
const requestOptions = {
|
|
60
|
+
url: `${this.resource}/v1.0/servicePrincipals/${id}`,
|
|
61
|
+
headers: {
|
|
62
|
+
accept: 'application/json;odata.metadata=none',
|
|
63
|
+
'content-type': 'application/json;odata.metadata=none'
|
|
64
|
+
},
|
|
65
|
+
responseType: 'json'
|
|
66
|
+
};
|
|
67
|
+
return request_1.default.get(requestOptions);
|
|
68
|
+
})
|
|
69
|
+
.then((res) => {
|
|
70
|
+
logger.log(res);
|
|
48
71
|
cb();
|
|
49
|
-
}, (
|
|
72
|
+
}, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
50
73
|
}
|
|
51
74
|
options() {
|
|
52
75
|
const options = [
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const cli_1 = require("../../../../cli");
|
|
13
|
+
const request_1 = require("../../../../request");
|
|
14
|
+
const appGetCommand = require("../../../aad/commands/app/app-get");
|
|
15
|
+
const AppCommand_1 = require("../../../base/AppCommand");
|
|
16
|
+
const commands_1 = require("../../commands");
|
|
17
|
+
var GetServicePrincipal;
|
|
18
|
+
(function (GetServicePrincipal) {
|
|
19
|
+
GetServicePrincipal[GetServicePrincipal["withPermissions"] = 0] = "withPermissions";
|
|
20
|
+
GetServicePrincipal[GetServicePrincipal["withPermissionDefinitions"] = 1] = "withPermissionDefinitions";
|
|
21
|
+
})(GetServicePrincipal || (GetServicePrincipal = {}));
|
|
22
|
+
class AppPermissionListCommand extends AppCommand_1.default {
|
|
23
|
+
get name() {
|
|
24
|
+
return commands_1.default.PERMISSION_LIST;
|
|
25
|
+
}
|
|
26
|
+
get description() {
|
|
27
|
+
return 'Lists API permissions for the current AAD app';
|
|
28
|
+
}
|
|
29
|
+
commandAction(logger, args, cb) {
|
|
30
|
+
this
|
|
31
|
+
.getServicePrincipal({ appId: this.appId }, logger, GetServicePrincipal.withPermissions)
|
|
32
|
+
.then(servicePrincipal => {
|
|
33
|
+
if (servicePrincipal) {
|
|
34
|
+
// service principal found, get permissions from the service principal
|
|
35
|
+
return this.getServicePrincipalPermissions(servicePrincipal, logger);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// service principal not found, get permissions from app registration
|
|
39
|
+
return this.getAppRegPermissions(this.appId, logger);
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
.then(permissions => {
|
|
43
|
+
logger.log(permissions);
|
|
44
|
+
cb();
|
|
45
|
+
}, err => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
46
|
+
}
|
|
47
|
+
getServicePrincipal(servicePrincipalInfo, logger, mode) {
|
|
48
|
+
var _a;
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
if (this.verbose) {
|
|
51
|
+
logger.logToStderr(`Retrieving service principal ${(_a = servicePrincipalInfo.appId) !== null && _a !== void 0 ? _a : servicePrincipalInfo.id}`);
|
|
52
|
+
}
|
|
53
|
+
const lookupUrl = servicePrincipalInfo.appId ? `?$filter=appId eq '${servicePrincipalInfo.appId}'&` : `/${servicePrincipalInfo.id}?`;
|
|
54
|
+
const requestOptions = {
|
|
55
|
+
url: `${this.resource}/v1.0/servicePrincipals${lookupUrl}$select=appId,id,displayName`,
|
|
56
|
+
headers: {
|
|
57
|
+
accept: 'application/json;odata.metadata=none'
|
|
58
|
+
},
|
|
59
|
+
responseType: 'json'
|
|
60
|
+
};
|
|
61
|
+
const response = yield request_1.default.get(requestOptions);
|
|
62
|
+
if ((servicePrincipalInfo.id && !response) ||
|
|
63
|
+
(servicePrincipalInfo.appId && response.value.length === 0)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const servicePrincipal = servicePrincipalInfo.appId ?
|
|
67
|
+
response.value[0] :
|
|
68
|
+
response;
|
|
69
|
+
if (this.verbose) {
|
|
70
|
+
logger.logToStderr(`Retrieving permissions for service principal ${servicePrincipal.id}...`);
|
|
71
|
+
}
|
|
72
|
+
const permissionsPromises = [];
|
|
73
|
+
switch (mode) {
|
|
74
|
+
case GetServicePrincipal.withPermissions:
|
|
75
|
+
const appRoleAssignmentsRequestOptions = {
|
|
76
|
+
url: `${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/appRoleAssignments`,
|
|
77
|
+
headers: {
|
|
78
|
+
accept: 'application/json;odata.metadata=none'
|
|
79
|
+
},
|
|
80
|
+
responseType: 'json'
|
|
81
|
+
};
|
|
82
|
+
const oauth2PermissionGrantsRequestOptions = {
|
|
83
|
+
url: `${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/oauth2PermissionGrants`,
|
|
84
|
+
headers: {
|
|
85
|
+
accept: 'application/json;odata.metadata=none'
|
|
86
|
+
},
|
|
87
|
+
responseType: 'json'
|
|
88
|
+
};
|
|
89
|
+
permissionsPromises.push(...[
|
|
90
|
+
request_1.default.get(appRoleAssignmentsRequestOptions),
|
|
91
|
+
request_1.default.get(oauth2PermissionGrantsRequestOptions)
|
|
92
|
+
]);
|
|
93
|
+
break;
|
|
94
|
+
case GetServicePrincipal.withPermissionDefinitions:
|
|
95
|
+
const oauth2PermissionScopesRequestOptions = {
|
|
96
|
+
url: `${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/oauth2PermissionScopes`,
|
|
97
|
+
headers: {
|
|
98
|
+
accept: 'application/json;odata.metadata=none'
|
|
99
|
+
},
|
|
100
|
+
responseType: 'json'
|
|
101
|
+
};
|
|
102
|
+
const appRolesRequestOptions = {
|
|
103
|
+
url: `${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/appRoles`,
|
|
104
|
+
headers: {
|
|
105
|
+
accept: 'application/json;odata.metadata=none'
|
|
106
|
+
},
|
|
107
|
+
responseType: 'json'
|
|
108
|
+
};
|
|
109
|
+
permissionsPromises.push(...[
|
|
110
|
+
request_1.default.get(oauth2PermissionScopesRequestOptions),
|
|
111
|
+
request_1.default.get(appRolesRequestOptions)
|
|
112
|
+
]);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
const permissions = yield Promise.all(permissionsPromises);
|
|
116
|
+
switch (mode) {
|
|
117
|
+
case GetServicePrincipal.withPermissions:
|
|
118
|
+
servicePrincipal.appRoleAssignments = permissions[0].value;
|
|
119
|
+
servicePrincipal.oauth2PermissionGrants = permissions[1].value;
|
|
120
|
+
break;
|
|
121
|
+
case GetServicePrincipal.withPermissionDefinitions:
|
|
122
|
+
servicePrincipal.oauth2PermissionScopes = permissions[0].value;
|
|
123
|
+
servicePrincipal.appRoles = permissions[1].value;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
return servicePrincipal;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
getServicePrincipalPermissions(servicePrincipal, logger) {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
if (this.verbose) {
|
|
132
|
+
logger.logToStderr(`Resolving permissions for the service principal...`);
|
|
133
|
+
}
|
|
134
|
+
const apiPermissions = [];
|
|
135
|
+
// hash table for resolving resource IDs to names
|
|
136
|
+
const resourceLookup = {};
|
|
137
|
+
// list of service principals for which to load permissions
|
|
138
|
+
const servicePrincipalsToResolve = [];
|
|
139
|
+
const appRoleAssignments = servicePrincipal.appRoleAssignments;
|
|
140
|
+
apiPermissions.push(...appRoleAssignments.map(appRoleAssignment => {
|
|
141
|
+
// store resource name for resolving OAuth2 grants
|
|
142
|
+
resourceLookup[appRoleAssignment.resourceId] = appRoleAssignment.resourceDisplayName;
|
|
143
|
+
// add to the list of service principals to load to get the app role
|
|
144
|
+
// display name
|
|
145
|
+
if (!servicePrincipalsToResolve.find(r => r.id === appRoleAssignment.resourceId)) {
|
|
146
|
+
servicePrincipalsToResolve.push({ id: appRoleAssignment.resourceId });
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
resource: appRoleAssignment.resourceDisplayName,
|
|
150
|
+
// we store the app role ID temporarily and will later resolve to display name
|
|
151
|
+
permission: appRoleAssignment.appRoleId,
|
|
152
|
+
type: 'Application'
|
|
153
|
+
};
|
|
154
|
+
}));
|
|
155
|
+
const oauth2Grants = servicePrincipal.oauth2PermissionGrants;
|
|
156
|
+
oauth2Grants.forEach(oauth2Grant => {
|
|
157
|
+
var _a;
|
|
158
|
+
// see if we can resolve the resource name from the resources
|
|
159
|
+
// retrieved from app role assignments
|
|
160
|
+
const resource = (_a = resourceLookup[oauth2Grant.resourceId]) !== null && _a !== void 0 ? _a : oauth2Grant.resourceId;
|
|
161
|
+
if (resource === oauth2Grant.resourceId &&
|
|
162
|
+
!servicePrincipalsToResolve.find(r => r.id === oauth2Grant.resourceId)) {
|
|
163
|
+
// resource name not found in the resources
|
|
164
|
+
// add it to the list of resources to resolve
|
|
165
|
+
servicePrincipalsToResolve.push({ id: oauth2Grant.resourceId });
|
|
166
|
+
}
|
|
167
|
+
const scopes = oauth2Grant.scope.split(' ');
|
|
168
|
+
scopes.forEach(scope => {
|
|
169
|
+
apiPermissions.push({
|
|
170
|
+
resource,
|
|
171
|
+
permission: scope,
|
|
172
|
+
type: 'Delegated'
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
if (servicePrincipalsToResolve.length > 0) {
|
|
177
|
+
const servicePrincipals = yield Promise
|
|
178
|
+
.all(servicePrincipalsToResolve
|
|
179
|
+
.map(servicePrincipalInfo => this.getServicePrincipal(servicePrincipalInfo, logger, GetServicePrincipal.withPermissionDefinitions)));
|
|
180
|
+
servicePrincipals.forEach(servicePrincipal => {
|
|
181
|
+
apiPermissions.forEach(apiPermission => {
|
|
182
|
+
var _a, _b;
|
|
183
|
+
if (apiPermission.resource === servicePrincipal.id) {
|
|
184
|
+
apiPermission.resource = servicePrincipal.displayName;
|
|
185
|
+
}
|
|
186
|
+
if (apiPermission.resource === servicePrincipal.displayName &&
|
|
187
|
+
apiPermission.type === 'Application') {
|
|
188
|
+
apiPermission.permission = (_b = (_a = servicePrincipal.appRoles
|
|
189
|
+
.find(appRole => appRole.id === apiPermission.permission)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : apiPermission.permission;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return apiPermissions;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
getAppRegistration(appId, logger) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
if (this.verbose) {
|
|
200
|
+
logger.logToStderr(`Retrieving Azure AD application registration ${appId}`);
|
|
201
|
+
}
|
|
202
|
+
const options = {
|
|
203
|
+
appId: appId,
|
|
204
|
+
output: 'json',
|
|
205
|
+
debug: this.debug,
|
|
206
|
+
verbose: this.verbose
|
|
207
|
+
};
|
|
208
|
+
const output = yield cli_1.Cli.executeCommandWithOutput(appGetCommand, { options: Object.assign(Object.assign({}, options), { _: [] }) });
|
|
209
|
+
if (this.debug) {
|
|
210
|
+
logger.logToStderr(output.stderr);
|
|
211
|
+
}
|
|
212
|
+
return JSON.parse(output.stdout);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
getAppRegPermissions(appId, logger) {
|
|
216
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
217
|
+
const application = yield this.getAppRegistration(appId, logger);
|
|
218
|
+
if (application.requiredResourceAccess.length === 0) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const servicePrincipalsToResolve = application.requiredResourceAccess
|
|
222
|
+
.map(resourceAccess => {
|
|
223
|
+
return {
|
|
224
|
+
appId: resourceAccess.resourceAppId
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
const servicePrincipals = yield Promise
|
|
228
|
+
.all(servicePrincipalsToResolve.map(servicePrincipalInfo => this.getServicePrincipal(servicePrincipalInfo, logger, GetServicePrincipal.withPermissionDefinitions)));
|
|
229
|
+
const apiPermissions = [];
|
|
230
|
+
application.requiredResourceAccess.forEach(requiredResourceAccess => {
|
|
231
|
+
var _a;
|
|
232
|
+
const servicePrincipal = servicePrincipals
|
|
233
|
+
.find(servicePrincipal => (servicePrincipal === null || servicePrincipal === void 0 ? void 0 : servicePrincipal.appId) === requiredResourceAccess.resourceAppId);
|
|
234
|
+
const resourceName = (_a = servicePrincipal === null || servicePrincipal === void 0 ? void 0 : servicePrincipal.displayName) !== null && _a !== void 0 ? _a : requiredResourceAccess.resourceAppId;
|
|
235
|
+
requiredResourceAccess.resourceAccess.forEach(permission => {
|
|
236
|
+
apiPermissions.push({
|
|
237
|
+
resource: resourceName,
|
|
238
|
+
permission: this.getPermissionName(permission.id, permission.type, servicePrincipal),
|
|
239
|
+
type: permission.type === 'Role' ? 'Application' : 'Delegated'
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
return apiPermissions;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
getPermissionName(permissionId, permissionType, servicePrincipal) {
|
|
247
|
+
var _a, _b, _c, _d;
|
|
248
|
+
if (!servicePrincipal) {
|
|
249
|
+
return permissionId;
|
|
250
|
+
}
|
|
251
|
+
switch (permissionType) {
|
|
252
|
+
case 'Role':
|
|
253
|
+
return (_b = (_a = servicePrincipal.appRoles
|
|
254
|
+
.find(appRole => appRole.id === permissionId)) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : permissionId;
|
|
255
|
+
case 'Scope':
|
|
256
|
+
return (_d = (_c = servicePrincipal.oauth2PermissionScopes
|
|
257
|
+
.find(permissionScope => permissionScope.id === permissionId)) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : permissionId;
|
|
258
|
+
}
|
|
259
|
+
/* c8 ignore next 4 */
|
|
260
|
+
// permissionType is either 'Scope' or 'Role' but we need a safe default
|
|
261
|
+
// to avoid building errors. This code will never be reached.
|
|
262
|
+
return permissionId;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
module.exports = new AppPermissionListCommand();
|
|
266
|
+
//# sourceMappingURL=permission-list.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const cli_1 = require("../../cli");
|
|
5
|
+
const Command_1 = require("../../Command");
|
|
6
|
+
const Utils_1 = require("../../Utils");
|
|
7
|
+
class AppCommand extends Command_1.default {
|
|
8
|
+
get resource() {
|
|
9
|
+
return 'https://graph.microsoft.com';
|
|
10
|
+
}
|
|
11
|
+
action(logger, args, cb) {
|
|
12
|
+
const m365rcJsonPath = '.m365rc.json';
|
|
13
|
+
if (!fs.existsSync(m365rcJsonPath)) {
|
|
14
|
+
return cb(new Command_1.CommandError(`Could not find file: ${m365rcJsonPath}`));
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const m365rcJsonContents = fs.readFileSync(m365rcJsonPath, 'utf8');
|
|
18
|
+
if (!m365rcJsonContents) {
|
|
19
|
+
return cb(new Command_1.CommandError(`File ${m365rcJsonPath} is empty`));
|
|
20
|
+
}
|
|
21
|
+
this.m365rcJson = JSON.parse(m365rcJsonContents);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return cb(new Command_1.CommandError(`Could not parse file: ${m365rcJsonPath}`));
|
|
25
|
+
}
|
|
26
|
+
if (!this.m365rcJson.apps ||
|
|
27
|
+
this.m365rcJson.apps.length === 0) {
|
|
28
|
+
return cb(new Command_1.CommandError(`No Azure AD apps found in ${m365rcJsonPath}`));
|
|
29
|
+
}
|
|
30
|
+
if (args.options.appId) {
|
|
31
|
+
if (!this.m365rcJson.apps.some(app => app.appId === args.options.appId)) {
|
|
32
|
+
return cb(new Command_1.CommandError(`App ${args.options.appId} not found in ${m365rcJsonPath}`));
|
|
33
|
+
}
|
|
34
|
+
this.appId = args.options.appId;
|
|
35
|
+
return super.action(logger, args, cb);
|
|
36
|
+
}
|
|
37
|
+
if (this.m365rcJson.apps.length === 1) {
|
|
38
|
+
this.appId = this.m365rcJson.apps[0].appId;
|
|
39
|
+
return super.action(logger, args, cb);
|
|
40
|
+
}
|
|
41
|
+
if (this.m365rcJson.apps.length > 1) {
|
|
42
|
+
cli_1.Cli.prompt({
|
|
43
|
+
message: `Multiple Azure AD apps found in ${m365rcJsonPath}. Which app would you like to use?`,
|
|
44
|
+
type: 'list',
|
|
45
|
+
choices: this.m365rcJson.apps.map((app, i) => {
|
|
46
|
+
return {
|
|
47
|
+
name: `${app.name} (${app.appId})`,
|
|
48
|
+
value: i
|
|
49
|
+
};
|
|
50
|
+
}),
|
|
51
|
+
default: 0,
|
|
52
|
+
name: 'appIdIndex'
|
|
53
|
+
}, (result) => {
|
|
54
|
+
this.appId = this.m365rcJson.apps[result.appIdIndex].appId;
|
|
55
|
+
super.action(logger, args, cb);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
options() {
|
|
60
|
+
const options = [
|
|
61
|
+
{
|
|
62
|
+
option: '--appId [appId]'
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
const parentOptions = super.options();
|
|
66
|
+
return options.concat(parentOptions);
|
|
67
|
+
}
|
|
68
|
+
validate(args) {
|
|
69
|
+
if (args.options.appId && !Utils_1.default.isValidGuid(args.options.appId)) {
|
|
70
|
+
return `${args.options.appId} is not a valid GUID`;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.default = AppCommand;
|
|
76
|
+
//# sourceMappingURL=AppCommand.js.map
|
|
@@ -12,8 +12,14 @@ class PaAppListCommand extends AzmgmtItemsListCommand_1.AzmgmtItemsListCommand {
|
|
|
12
12
|
defaultProperties() {
|
|
13
13
|
return ['name', 'displayName'];
|
|
14
14
|
}
|
|
15
|
+
getTelemetryProperties(args) {
|
|
16
|
+
const telemetryProps = super.getTelemetryProperties(args);
|
|
17
|
+
telemetryProps.asAdmin = args.options.asAdmin === true;
|
|
18
|
+
telemetryProps.environment = typeof args.options.environment !== 'undefined';
|
|
19
|
+
return telemetryProps;
|
|
20
|
+
}
|
|
15
21
|
commandAction(logger, args, cb) {
|
|
16
|
-
const url = `${this.resource}providers/Microsoft.PowerApps/apps?api-version=2017-08-01`;
|
|
22
|
+
const url = `${this.resource}providers/Microsoft.PowerApps${args.options.asAdmin ? '/scopes/admin' : ''}${args.options.environment ? '/environments/' + encodeURIComponent(args.options.environment) : ''}/apps?api-version=2017-08-01`;
|
|
17
23
|
this
|
|
18
24
|
.getAllItems(url, logger, true)
|
|
19
25
|
.then(() => {
|
|
@@ -31,6 +37,27 @@ class PaAppListCommand extends AzmgmtItemsListCommand_1.AzmgmtItemsListCommand {
|
|
|
31
37
|
cb();
|
|
32
38
|
}, (rawRes) => this.handleRejectedODataJsonPromise(rawRes, logger, cb));
|
|
33
39
|
}
|
|
40
|
+
options() {
|
|
41
|
+
const options = [
|
|
42
|
+
{
|
|
43
|
+
option: '-e, --environment [environment]'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
option: '--asAdmin'
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
const parentOptions = super.options();
|
|
50
|
+
return options.concat(parentOptions);
|
|
51
|
+
}
|
|
52
|
+
validate(args) {
|
|
53
|
+
if (args.options.asAdmin && !args.options.environment) {
|
|
54
|
+
return 'When specifying the asAdmin option the environment option is required as well';
|
|
55
|
+
}
|
|
56
|
+
if (args.options.environment && !args.options.asAdmin) {
|
|
57
|
+
return 'When specifying the environment option the asAdmin option is required as well';
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
34
61
|
}
|
|
35
62
|
module.exports = new PaAppListCommand();
|
|
36
63
|
//# sourceMappingURL=app-list.js.map
|
package/dist/request.js
CHANGED
|
@@ -16,7 +16,9 @@ class Request {
|
|
|
16
16
|
decompress: true,
|
|
17
17
|
responseType: 'text',
|
|
18
18
|
/* c8 ignore next */
|
|
19
|
-
transformResponse: [data => data]
|
|
19
|
+
transformResponse: [data => data],
|
|
20
|
+
maxBodyLength: Infinity,
|
|
21
|
+
maxContentLength: Infinity
|
|
20
22
|
});
|
|
21
23
|
// since we're stubbing requests, request interceptor is never called in
|
|
22
24
|
// tests, so let's exclude it from coverage
|
|
@@ -64,7 +66,7 @@ class Request {
|
|
|
64
66
|
});
|
|
65
67
|
// since we're stubbing requests, response interceptor is never called in
|
|
66
68
|
// tests, so let's exclude it from coverage
|
|
67
|
-
/* c8 ignore next
|
|
69
|
+
/* c8 ignore next 26 */
|
|
68
70
|
this.req.interceptors.response.use((response) => {
|
|
69
71
|
if (this._logger) {
|
|
70
72
|
this._logger.logToStderr('Response:');
|
|
@@ -73,19 +75,22 @@ class Request {
|
|
|
73
75
|
response.headers['content-type'].indexOf('json') > -1) {
|
|
74
76
|
properties.push('data');
|
|
75
77
|
}
|
|
76
|
-
this._logger.logToStderr(JSON.stringify(Utils_1.default.filterObject(response, properties), null, 2));
|
|
78
|
+
this._logger.logToStderr(JSON.stringify(Object.assign({ url: response.config.url }, Utils_1.default.filterObject(response, properties)), null, 2));
|
|
77
79
|
}
|
|
78
80
|
return response;
|
|
79
81
|
}, (error) => {
|
|
80
82
|
if (this._logger) {
|
|
81
83
|
const properties = ['status', 'statusText', 'headers'];
|
|
82
84
|
this._logger.logToStderr('Request error:');
|
|
83
|
-
this._logger.logToStderr(JSON.stringify(Object.assign(Object.assign({}, Utils_1.default.filterObject(error.response, properties)), { error: error.error }), null, 2));
|
|
85
|
+
this._logger.logToStderr(JSON.stringify(Object.assign(Object.assign({ url: error.config.url }, Utils_1.default.filterObject(error.response, properties)), { error: error.error }), null, 2));
|
|
84
86
|
}
|
|
85
87
|
throw error;
|
|
86
88
|
});
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
get logger() {
|
|
92
|
+
return this._logger;
|
|
93
|
+
}
|
|
89
94
|
set logger(logger) {
|
|
90
95
|
this._logger = logger;
|
|
91
96
|
}
|
package/docs/docs/cmd/_global.md
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
: JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
|
|
6
6
|
|
|
7
7
|
`-o, --output [output]`
|
|
8
|
-
: Output type. `json,text`. Default `
|
|
8
|
+
: Output type. `json,text,csv`. Default `json`
|
|
9
9
|
|
|
10
10
|
`--verbose`
|
|
11
11
|
: Runs command with verbose logging
|
|
12
12
|
|
|
13
13
|
`--debug`
|
|
14
|
-
: Runs command with debug logging
|
|
14
|
+
: Runs command with debug logging
|
|
@@ -10,7 +10,7 @@ m365 aad oauth2grant list [options]
|
|
|
10
10
|
|
|
11
11
|
## Options
|
|
12
12
|
|
|
13
|
-
`-i, --
|
|
13
|
+
`-i, --spObjectId <spObjectId>`
|
|
14
14
|
: objectId of the service principal for which the configured OAuth2 permission grants should be retrieved
|
|
15
15
|
|
|
16
16
|
--8<-- "docs/cmd/_global.md"
|
|
@@ -26,9 +26,10 @@ When using the text output type (default), the command lists only the values of
|
|
|
26
26
|
List OAuth2 permissions granted to service principal with `objectId` _b2307a39-e878-458b-bc90-03bc578531d6_.
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
m365 aad oauth2grant list --
|
|
29
|
+
m365 aad oauth2grant list --spObjectId b2307a39-e878-458b-bc90-03bc578531d6
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## More information
|
|
33
33
|
|
|
34
|
-
- Application and service principal objects in Azure Active Directory (Azure AD): [https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects)
|
|
34
|
+
- Application and service principal objects in Azure Active Directory (Azure AD): [https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects)
|
|
35
|
+
- List oauth2PermissionGrants: [https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-list?view=graph-rest-1.0](https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-list?view=graph-rest-1.0)
|
|
@@ -13,6 +13,9 @@ m365 aad oauth2grant remove [options]
|
|
|
13
13
|
`-i, --grantId <grantId>`
|
|
14
14
|
: `objectId` of OAuth2 permission grant to remove
|
|
15
15
|
|
|
16
|
+
`--confirm`
|
|
17
|
+
: Do not prompt for confirmation before removing OAuth2 permission grant
|
|
18
|
+
|
|
16
19
|
--8<-- "docs/cmd/_global.md"
|
|
17
20
|
|
|
18
21
|
## Remarks
|
|
@@ -33,6 +36,12 @@ Remove the OAuth2 permission grant with ID _YgA60KYa4UOPSdc-lpxYEnQkr8KVLDpCsOXk
|
|
|
33
36
|
m365 aad oauth2grant remove --grantId YgA60KYa4UOPSdc-lpxYEnQkr8KVLDpCsOXkiV8i-ek
|
|
34
37
|
```
|
|
35
38
|
|
|
39
|
+
Remove the OAuth2 permission grant with ID _YgA60KYa4UOPSdc-lpxYEnQkr8KVLDpCsOXkiV8i-ek_ without being asked for confirmation
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
m365 aad oauth2grant remove --grantId YgA60KYa4UOPSdc-lpxYEnQkr8KVLDpCsOXkiV8i-ek --confirm
|
|
43
|
+
```
|
|
44
|
+
|
|
36
45
|
## More information
|
|
37
46
|
|
|
38
47
|
- Application and service principal objects in Azure Active Directory (Azure AD): [https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects)
|
|
@@ -47,4 +47,5 @@ m365 aad sp get --objectId b2307a39-e878-458b-bc90-03bc578531dd
|
|
|
47
47
|
|
|
48
48
|
## More information
|
|
49
49
|
|
|
50
|
-
- Application and service principal objects in Azure Active Directory (Azure AD): [https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects)
|
|
50
|
+
- Application and service principal objects in Azure Active Directory (Azure AD): [https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects)
|
|
51
|
+
- Get servicePrincipal: [https://docs.microsoft.com/en-us/graph/api/serviceprincipal-get?view=graph-rest-1.0](https://docs.microsoft.com/en-us/graph/api/serviceprincipal-get?view=graph-rest-1.0)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# app permission list
|
|
2
|
+
|
|
3
|
+
Lists API permissions for the current AAD app
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
m365 app permission list [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Options
|
|
12
|
+
|
|
13
|
+
`--appId [appId]`
|
|
14
|
+
: Client ID of the Azure AD app registered in the .m365rc.json file to retrieve API permissions for
|
|
15
|
+
|
|
16
|
+
--8<-- "docs/cmd/_global.md"
|
|
17
|
+
|
|
18
|
+
## Remarks
|
|
19
|
+
|
|
20
|
+
Use this command to quickly look up API permissions for the Azure AD application registration registered in the .m365rc.json file in your current project (folder).
|
|
21
|
+
|
|
22
|
+
If you have multiple apps registered in your .m365rc.json file, you can specify the app for which you'd like to retrieve permissions using the `--appId` option. If you don't specify the app using the `--appId` option, you'll be prompted to select one of the applications from your .m365rc.json file.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
Retrieve API permissions for your current Azure AD app
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
m365 app permission list
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Retrieve API permissions for the Azure AD app with client ID _e23d235c-fcdf-45d1-ac5f-24ab2ee0695d_ specified in the _.m365rc.json_ file
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
m365 app permission list --appId e23d235c-fcdf-45d1-ac5f-24ab2ee0695d
|
|
36
|
+
```
|
|
@@ -10,6 +10,12 @@ pa app list [options]
|
|
|
10
10
|
|
|
11
11
|
## Options
|
|
12
12
|
|
|
13
|
+
`-e, --environment [environment]`
|
|
14
|
+
: The name of the environment for which to retrieve available apps
|
|
15
|
+
|
|
16
|
+
`--asAdmin`
|
|
17
|
+
: Set, to list all Power Apps as admin. Otherwise will return only your own apps
|
|
18
|
+
|
|
13
19
|
--8<-- "docs/cmd/_global.md"
|
|
14
20
|
|
|
15
21
|
## Remarks
|
|
@@ -17,10 +23,20 @@ pa app list [options]
|
|
|
17
23
|
!!! attention
|
|
18
24
|
This command is based on an API that is currently in preview and is subject to change once the API reaches general availability.
|
|
19
25
|
|
|
26
|
+
If the environment with the name you specified doesn't exist, you will get the `Access to the environment 'xyz' is denied.` error.
|
|
27
|
+
|
|
28
|
+
By default, the `app list` command returns only your apps. To list all apps, use the `asAdmin` option and make sure to specify the `environment` option. You cannot specify only one of the options, when specifying the `environment` option the `asAdmin` option has to be present as well.
|
|
29
|
+
|
|
20
30
|
## Examples
|
|
21
31
|
|
|
22
|
-
List all apps
|
|
32
|
+
List all your apps
|
|
23
33
|
|
|
24
34
|
```sh
|
|
25
35
|
m365 pa app list
|
|
26
36
|
```
|
|
37
|
+
|
|
38
|
+
List all apps in a given environment
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
m365 pa app list --environment Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --asAdmin
|
|
42
|
+
```
|
|
@@ -20,7 +20,7 @@ m365 spfx project externalize [options]
|
|
|
20
20
|
: JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
|
|
21
21
|
|
|
22
22
|
`-o, --output [output]`
|
|
23
|
-
: Output type. `json,text,md`. Default `
|
|
23
|
+
: Output type. `json,text,csv,md`. Default `json`
|
|
24
24
|
|
|
25
25
|
`--verbose`
|
|
26
26
|
: Runs command with verbose logging
|
|
@@ -23,7 +23,7 @@ m365 spfx project rename [options]
|
|
|
23
23
|
: JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
|
|
24
24
|
|
|
25
25
|
`-o, --output [output]`
|
|
26
|
-
: Output type. `json,text,md`. Default `
|
|
26
|
+
: Output type. `json,text,csv,md`. Default `json`
|
|
27
27
|
|
|
28
28
|
`--verbose`
|
|
29
29
|
: Runs command with verbose logging
|
|
@@ -20,7 +20,7 @@ m365 spfx doctor [options]
|
|
|
20
20
|
: JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
|
|
21
21
|
|
|
22
22
|
`-o, --output [output]`
|
|
23
|
-
: Output type. `json,text,md`. Default `
|
|
23
|
+
: Output type. `json,text,csv,md`. Default `json`
|
|
24
24
|
|
|
25
25
|
`--verbose`
|
|
26
26
|
: Runs command with verbose logging
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnp/cli-microsoft365",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@pnp/cli-microsoft365",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "5.0.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@azure/msal-node": "^1.4.0",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"applicationinsights": "^2.2.0",
|
|
18
18
|
"axios": "^0.24.0",
|
|
19
19
|
"chalk": "^4.1.2",
|
|
20
|
+
"csv-stringify": "^6.0.4",
|
|
20
21
|
"easy-table": "^1.2.0",
|
|
21
22
|
"inquirer": "^8.2.0",
|
|
22
23
|
"jmespath": "^0.15.0",
|
|
@@ -1785,6 +1786,11 @@
|
|
|
1785
1786
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
|
|
1786
1787
|
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
|
|
1787
1788
|
},
|
|
1789
|
+
"node_modules/csv-stringify": {
|
|
1790
|
+
"version": "6.0.4",
|
|
1791
|
+
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.0.4.tgz",
|
|
1792
|
+
"integrity": "sha512-Z3EbRQWwkOV3Qc2fQnJmfjrxRgAwH9AncnNK2jmtTvBvFjj/hESZUGm42YvTh9kMw2OOGPHQn5Yt0EbqoRtUVQ=="
|
|
1793
|
+
},
|
|
1788
1794
|
"node_modules/d3-format": {
|
|
1789
1795
|
"version": "1.4.5",
|
|
1790
1796
|
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
|
|
@@ -7553,6 +7559,11 @@
|
|
|
7553
7559
|
}
|
|
7554
7560
|
}
|
|
7555
7561
|
},
|
|
7562
|
+
"csv-stringify": {
|
|
7563
|
+
"version": "6.0.4",
|
|
7564
|
+
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.0.4.tgz",
|
|
7565
|
+
"integrity": "sha512-Z3EbRQWwkOV3Qc2fQnJmfjrxRgAwH9AncnNK2jmtTvBvFjj/hESZUGm42YvTh9kMw2OOGPHQn5Yt0EbqoRtUVQ=="
|
|
7566
|
+
},
|
|
7556
7567
|
"d3-format": {
|
|
7557
7568
|
"version": "1.4.5",
|
|
7558
7569
|
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnp/cli-microsoft365",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-beta.117f66f",
|
|
4
4
|
"description": "Manage Microsoft 365 and SharePoint Framework projects on any platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -177,6 +177,7 @@
|
|
|
177
177
|
"applicationinsights": "^2.2.0",
|
|
178
178
|
"axios": "^0.24.0",
|
|
179
179
|
"chalk": "^4.1.2",
|
|
180
|
+
"csv-stringify": "^6.0.4",
|
|
180
181
|
"easy-table": "^1.2.0",
|
|
181
182
|
"inquirer": "^8.2.0",
|
|
182
183
|
"jmespath": "^0.15.0",
|