@pnp/cli-microsoft365 5.0.0-beta.2e2ba7d → 5.0.0-beta.4aa3f65

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.
Files changed (89) hide show
  1. package/.devcontainer/devcontainer.json +9 -1
  2. package/.eslintrc.js +1 -0
  3. package/README.md +1 -1
  4. package/dist/Utils.js +7 -0
  5. package/dist/api.d.ts +11 -0
  6. package/dist/api.js +17 -0
  7. package/dist/appInsights.js +2 -1
  8. package/dist/cli/Cli.js +38 -6
  9. package/dist/m365/aad/commands/app/app-add.js +43 -7
  10. package/dist/m365/aad/commands/group/group-list.js +41 -0
  11. package/dist/m365/aad/commands/o365group/{Group.js → GroupExtended.js} +1 -1
  12. package/dist/m365/aad/commands/o365group/o365group-user-set.js +3 -3
  13. package/dist/m365/aad/commands/user/user-get.js +33 -6
  14. package/dist/m365/aad/commands/user/user-hibp.js +67 -0
  15. package/dist/m365/aad/commands/user/user-list.js +7 -4
  16. package/dist/m365/aad/commands/user/user-password-validate.js +42 -0
  17. package/dist/m365/aad/commands.js +3 -0
  18. package/dist/m365/app/commands/permission/permission-list.js +266 -0
  19. package/dist/m365/app/commands.js +7 -0
  20. package/dist/m365/base/AppCommand.js +76 -0
  21. package/dist/m365/cli/commands/config/config-set.js +4 -1
  22. package/dist/m365/flow/commands/flow-get.js +2 -2
  23. package/dist/m365/pa/cds-project-mutator.js +1 -1
  24. package/dist/m365/pa/commands/app/app-list.js +28 -1
  25. package/dist/m365/{aad/commands/o365group/GroupUser.js → planner/AppliedCategories.js} +1 -1
  26. package/dist/m365/planner/commands/task/task-add.js +288 -0
  27. package/dist/m365/planner/commands/task/task-details-get.js +39 -0
  28. package/dist/m365/planner/commands/task/task-get.js +37 -0
  29. package/dist/m365/planner/commands/task/task-list.js +37 -7
  30. package/dist/m365/planner/commands/task/task-set.js +357 -0
  31. package/dist/m365/planner/commands.js +5 -1
  32. package/dist/m365/spfx/commands/project/project-upgrade/rules/FN014008_CODE_launch_hostedWorkbench_type.js +62 -0
  33. package/dist/m365/spfx/commands/project/project-upgrade/{upgrade-1.14.0-beta.4.js → upgrade-1.14.0-rc.2.js} +27 -25
  34. package/dist/m365/spfx/commands/project/project-upgrade.js +1 -1
  35. package/dist/m365/spo/commands/group/group-user-add.js +64 -13
  36. package/dist/m365/spo/commands/group/group-user-remove.js +100 -0
  37. package/dist/m365/spo/commands/site/site-ensure.js +1 -1
  38. package/dist/m365/spo/commands/site/site-recyclebinitem-list.js +76 -0
  39. package/dist/m365/spo/commands.js +2 -0
  40. package/dist/m365/teams/commands/app/app-list.js +9 -6
  41. package/dist/m365/teams/commands/chat/chat-list.js +43 -0
  42. package/dist/m365/teams/commands/chat/chat-member-list.js +42 -0
  43. package/dist/m365/teams/commands/chat/chat-message-list.js +60 -0
  44. package/dist/m365/teams/commands/message/message-get.js +1 -1
  45. package/dist/m365/teams/commands/tab/tab-get.js +9 -6
  46. package/dist/m365/teams/commands.js +3 -0
  47. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-health-get.js +57 -0
  48. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-health-list.js +56 -0
  49. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-healthissue-get.js +39 -0
  50. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-healthissue-list.js +38 -0
  51. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-message-get.js +51 -0
  52. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-message-list.js +38 -0
  53. package/dist/m365/tenant/commands.js +6 -0
  54. package/dist/request.js +9 -4
  55. package/dist/settingsNames.js +6 -1
  56. package/docs/docs/cmd/_global.md +2 -2
  57. package/docs/docs/cmd/aad/group/group-list.md +21 -0
  58. package/docs/docs/cmd/aad/user/user-get.md +13 -4
  59. package/docs/docs/cmd/aad/user/user-hibp.md +46 -0
  60. package/docs/docs/cmd/aad/user/user-list.md +9 -0
  61. package/docs/docs/cmd/aad/user/user-password-validate.md +29 -0
  62. package/docs/docs/cmd/app/permission/permission-list.md +36 -0
  63. package/docs/docs/cmd/pa/app/app-list.md +17 -1
  64. package/docs/docs/cmd/planner/task/task-add.md +78 -0
  65. package/docs/docs/cmd/planner/task/task-details-get.md +24 -0
  66. package/docs/docs/cmd/planner/task/task-get.md +24 -0
  67. package/docs/docs/cmd/planner/task/task-list.md +5 -0
  68. package/docs/docs/cmd/planner/task/task-set.md +99 -0
  69. package/docs/docs/cmd/search/externalconnection/externalconnection-add.md +3 -3
  70. package/docs/docs/cmd/spfx/project/project-externalize.md +1 -1
  71. package/docs/docs/cmd/spfx/project/project-rename.md +1 -1
  72. package/docs/docs/cmd/spfx/spfx-doctor.md +1 -1
  73. package/docs/docs/cmd/spo/group/group-user-add.md +24 -6
  74. package/docs/docs/cmd/spo/group/group-user-remove.md +39 -0
  75. package/docs/docs/cmd/spo/site/site-recyclebinitem-list.md +40 -0
  76. package/docs/docs/cmd/teams/channel/channel-get.md +1 -1
  77. package/docs/docs/cmd/teams/chat/chat-list.md +30 -0
  78. package/docs/docs/cmd/teams/chat/chat-member-list.md +24 -0
  79. package/docs/docs/cmd/teams/chat/chat-message-list.md +24 -0
  80. package/docs/docs/cmd/teams/message/message-get.md +0 -3
  81. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-health-get.md +33 -0
  82. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-health-list.md +30 -0
  83. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-healthissue-get.md +24 -0
  84. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-healthissue-list.md +34 -0
  85. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-message-get.md +28 -0
  86. package/docs/docs/cmd/tenant/serviceannouncement/serviceannouncement-message-list.md +34 -0
  87. package/npm-shrinkwrap.json +1298 -1501
  88. package/package.json +34 -27
  89. package/dist/m365/base/AadCommand.js +0 -10
@@ -2,7 +2,15 @@
2
2
  "name": "CLI for Microsoft 365",
3
3
  "dockerFile": "Dockerfile",
4
4
  "settings": {
5
- "terminal.integrated.shell.linux": "/bin/zsh"
5
+ "terminal.integrated.profiles.linux": {
6
+ "zsh": {
7
+ "path": "/bin/zsh",
8
+ "args": [
9
+ "-l"
10
+ ]
11
+ }
12
+ },
13
+ "terminal.integrated.defaultProfile.linux": "zsh"
6
14
  },
7
15
  "postCreateCommand": "npm i && npm run clean && npm run build && npm link",
8
16
  "extensions": [
package/.eslintrc.js CHANGED
@@ -23,6 +23,7 @@ const dictionary = [
23
23
  'group',
24
24
  'groupify',
25
25
  'guest',
26
+ 'health',
26
27
  'hide',
27
28
  'historical',
28
29
  'home',
package/README.md CHANGED
@@ -179,7 +179,7 @@ m365 spo site get --url https://contoso.sharepoint.com --output text
179
179
 
180
180
  ## Build
181
181
 
182
- To build and run this CLI locally, you will need [`node`](https://nodejs.org) `>= 14.0.0` installed.
182
+ To build and run this CLI locally, you will need [`node`](https://nodejs.org) `>= 16.0.0` installed.
183
183
 
184
184
  ```sh
185
185
  # Clone this repository
package/dist/Utils.js CHANGED
@@ -40,6 +40,9 @@ class Utils {
40
40
  }
41
41
  });
42
42
  }
43
+ static isValidGuidArray(guids) {
44
+ return guids.every(guid => this.isValidGuid(guid));
45
+ }
43
46
  static isValidGuid(guid) {
44
47
  const guidRegEx = new RegExp(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
45
48
  return guidRegEx.test(guid);
@@ -48,6 +51,10 @@ class Utils {
48
51
  const guidRegEx = new RegExp(/^19:[0-9a-zA-Z-_]+@thread\.(skype|tacv2)$/i);
49
52
  return guidRegEx.test(guid);
50
53
  }
54
+ static isValidTeamsChatId(guid) {
55
+ const guidRegEx = new RegExp(/^19:[0-9a-zA-Z-_]+(@thread\.v2|@unq\.gbl\.spaces)$/i);
56
+ return guidRegEx.test(guid);
57
+ }
51
58
  static isValidUserPrincipalName(upn) {
52
59
  const upnRegEx = new RegExp(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i);
53
60
  return upnRegEx.test(upn);
package/dist/api.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export interface CommandOutput {
2
+ error?: {
3
+ message: string;
4
+ code?: number;
5
+ }
6
+ stdout: string;
7
+ stderr: string;
8
+ }
9
+
10
+ export declare function executeCommand(commandName: string, options: any): Promise<CommandOutput>;
11
+ export declare function on(eventName: string, listener: (...args: any[]) => void): void;
package/dist/api.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeCommand = void 0;
4
+ const cli_1 = require("./cli");
5
+ const path = require("path");
6
+ function executeCommand(commandName, options, listener) {
7
+ const cli = cli_1.Cli.getInstance();
8
+ cli.commandsFolder = path.join(__dirname, 'm365');
9
+ cli.commands = [];
10
+ cli.loadCommandFromArgs(commandName.split(' '));
11
+ if (cli.commands.length !== 1) {
12
+ return Promise.reject(`Command not found: ${commandName}`);
13
+ }
14
+ return cli_1.Cli.executeCommandWithOutput(cli.commands[0].command, { options: options !== null && options !== void 0 ? options : {} }, listener);
15
+ }
16
+ exports.executeCommand = executeCommand;
17
+ //# sourceMappingURL=api.js.map
@@ -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');
@@ -188,32 +189,51 @@ class Cli {
188
189
  });
189
190
  });
190
191
  }
191
- static executeCommandWithOutput(command, args) {
192
+ static executeCommandWithOutput(command, args, listener) {
192
193
  return new Promise((resolve, reject) => {
193
194
  const log = [];
194
195
  const logErr = [];
195
196
  const logger = {
196
197
  log: (message) => {
197
- log.push(Cli.formatOutput(message, args.options));
198
+ const formattedMessage = Cli.formatOutput(message, args.options);
199
+ if (listener && listener.stdout) {
200
+ listener.stdout(formattedMessage);
201
+ }
202
+ log.push(formattedMessage);
198
203
  },
199
204
  logRaw: (message) => {
200
- log.push(Cli.formatOutput(message, args.options));
205
+ const formattedMessage = Cli.formatOutput(message, args.options);
206
+ if (listener && listener.stdout) {
207
+ listener.stdout(formattedMessage);
208
+ }
209
+ log.push(formattedMessage);
201
210
  },
202
211
  logToStderr: (message) => {
212
+ if (listener && listener.stderr) {
213
+ listener.stderr(message);
214
+ }
203
215
  logErr.push(message);
204
216
  }
205
217
  };
206
218
  if (args.options.debug) {
207
- Cli.log(`Executing command ${command.name} with options ${JSON.stringify(args)}`);
219
+ const message = `Executing command ${command.name} with options ${JSON.stringify(args)}`;
220
+ if (listener && listener.stderr) {
221
+ listener.stderr(message);
222
+ }
223
+ logErr.push(message);
208
224
  }
209
225
  // store the current command name, if any and set the name to the name of
210
226
  // the command to execute
211
227
  const cli = Cli.getInstance();
212
228
  const parentCommandName = cli.currentCommandName;
213
229
  cli.currentCommandName = command.getCommandName();
230
+ // store the current logger if any
231
+ const currentLogger = request_1.default.logger;
214
232
  command.action(logger, args, (err) => {
215
233
  // restore the original command name
216
234
  cli.currentCommandName = parentCommandName;
235
+ // restore the original logger
236
+ request_1.default.logger = currentLogger;
217
237
  if (err) {
218
238
  return reject({
219
239
  error: err,
@@ -416,11 +436,11 @@ class Cli {
416
436
  if (arrayType !== 'object') {
417
437
  return logStatement.join(os.EOL);
418
438
  }
419
- // if output type has been set to 'text', process the retrieved
439
+ // if output type has been set to 'text' or 'csv', process the retrieved
420
440
  // data so that returned objects contain only default properties specified
421
441
  // on the current command. If there is no current command or the
422
442
  // command doesn't specify default properties, return original data
423
- if (options.output === 'text') {
443
+ if (options.output === 'text' || options.output === 'csv') {
424
444
  const cli = Cli.getInstance();
425
445
  const currentCommand = cli.commandToExecute;
426
446
  if (arrayType === 'object' &&
@@ -440,6 +460,18 @@ class Cli {
440
460
  }
441
461
  }
442
462
  }
463
+ if (options.output === 'csv') {
464
+ const { stringify } = require('csv-stringify/sync');
465
+ const cli = Cli.getInstance();
466
+ // https://csv.js.org/stringify/options/
467
+ return stringify(logStatement, {
468
+ header: cli.getSettingWithDefaultValue(settingsNames_1.settingsNames.csvHeader, true),
469
+ escape: cli.getSettingWithDefaultValue(settingsNames_1.settingsNames.csvEscape, '"'),
470
+ quote: cli.config.get(settingsNames_1.settingsNames.csvQuote),
471
+ quoted: cli.getSettingWithDefaultValue(settingsNames_1.settingsNames.csvQuoted, false),
472
+ quotedEmpty: cli.getSettingWithDefaultValue(settingsNames_1.settingsNames.csvQuotedEmpty, false)
473
+ });
474
+ }
443
475
  // display object as a list of key-value pairs
444
476
  if (logStatement.length === 1) {
445
477
  const obj = logStatement[0];
@@ -105,21 +105,49 @@ class AadAppAddCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
105
105
  if (!args.options.manifest) {
106
106
  return Promise.resolve(appInfo);
107
107
  }
108
- const manifest = JSON.parse(args.options.manifest);
108
+ const v2Manifest = JSON.parse(args.options.manifest);
109
109
  // remove properties that might be coming from the original app that was
110
110
  // used to create the manifest and which can't be updated
111
- delete manifest.id;
112
- delete manifest.appId;
113
- delete manifest.publisherDomain;
111
+ delete v2Manifest.id;
112
+ delete v2Manifest.appId;
113
+ delete v2Manifest.publisherDomain;
114
114
  // Azure Portal returns v2 manifest whereas the Graph API expects a v1.6
115
- const transformedManifest = this.transformManifest(manifest);
115
+ const graphManifest = this.transformManifest(v2Manifest);
116
116
  const updateAppRequestOptions = {
117
117
  url: `${this.resource}/v1.0/myorganization/applications/${appInfo.id}`,
118
118
  headers: {
119
119
  'content-type': 'application/json'
120
120
  },
121
121
  responseType: 'json',
122
- data: transformedManifest
122
+ data: graphManifest
123
+ };
124
+ return request_1.default
125
+ .patch(updateAppRequestOptions)
126
+ .then(_ => this.updatePreAuthorizedAppsFromManifest(v2Manifest, appInfo))
127
+ .then(_ => Promise.resolve(appInfo));
128
+ }
129
+ updatePreAuthorizedAppsFromManifest(manifest, appInfo) {
130
+ if (!manifest ||
131
+ !manifest.preAuthorizedApplications ||
132
+ manifest.preAuthorizedApplications.length === 0) {
133
+ return Promise.resolve(appInfo);
134
+ }
135
+ const graphManifest = {
136
+ api: {
137
+ preAuthorizedApplications: manifest.preAuthorizedApplications
138
+ }
139
+ };
140
+ graphManifest.api.preAuthorizedApplications.forEach((p) => {
141
+ p.delegatedPermissionIds = p.permissionIds;
142
+ delete p.permissionIds;
143
+ });
144
+ const updateAppRequestOptions = {
145
+ url: `${this.resource}/v1.0/myorganization/applications/${appInfo.id}`,
146
+ headers: {
147
+ 'content-type': 'application/json'
148
+ },
149
+ responseType: 'json',
150
+ data: graphManifest
123
151
  };
124
152
  return request_1.default
125
153
  .patch(updateAppRequestOptions)
@@ -180,8 +208,16 @@ class AadAppAddCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
180
208
  delete graphManifest.oauth2AllowIdTokenImplicitFlow;
181
209
  graphManifest.api.oauth2PermissionScopes = v2Manifest.oauth2Permissions;
182
210
  delete graphManifest.oauth2Permissions;
211
+ if (graphManifest.api.oauth2PermissionScopes) {
212
+ graphManifest.api.oauth2PermissionScopes.forEach((scope) => {
213
+ delete scope.lang;
214
+ delete scope.origin;
215
+ });
216
+ }
183
217
  delete graphManifest.oauth2RequiredPostResponse;
184
- graphManifest.api.preAuthorizedApplications = v2Manifest.preAuthorizedApplications;
218
+ // MS Graph doesn't support creating OAuth2 permissions and pre-authorized
219
+ // apps in one request. This is why we need to remove it here and do it in
220
+ // the next request
185
221
  delete graphManifest.preAuthorizedApplications;
186
222
  if (v2Manifest.replyUrlsWithType) {
187
223
  v2Manifest.replyUrlsWithType.forEach((urlWithType) => {
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const GraphItemsListCommand_1 = require("../../../base/GraphItemsListCommand");
4
+ const commands_1 = require("../../commands");
5
+ class AadGroupListCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
6
+ get name() {
7
+ return commands_1.default.GROUP_LIST;
8
+ }
9
+ get description() {
10
+ return 'Lists all groups defined in Azure Active Directory.';
11
+ }
12
+ defaultProperties() {
13
+ return ['id', 'displayName', 'groupType'];
14
+ }
15
+ commandAction(logger, args, cb) {
16
+ this
17
+ .getAllItems(`${this.resource}/v1.0/groups`, logger, true)
18
+ .then(() => {
19
+ if (args.options.output === 'text') {
20
+ this.items.forEach((group) => {
21
+ if (group.groupTypes && group.groupTypes.length > 0 && group.groupTypes[0] === 'Unified') {
22
+ group.groupType = 'Microsoft 365';
23
+ }
24
+ else if (group.mailEnabled && group.securityEnabled) {
25
+ group.groupType = 'Mail enabled security';
26
+ }
27
+ else if (group.securityEnabled) {
28
+ group.groupType = 'Security';
29
+ }
30
+ else if (group.mailEnabled) {
31
+ group.groupType = 'Distribution';
32
+ }
33
+ });
34
+ }
35
+ logger.log(this.items);
36
+ cb();
37
+ }, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
38
+ }
39
+ }
40
+ module.exports = new AadGroupListCommand();
41
+ //# sourceMappingURL=group-list.js.map
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=Group.js.map
3
+ //# sourceMappingURL=GroupExtended.js.map
@@ -37,14 +37,14 @@ class AadO365GroupUserSetCommand extends GraphItemsListCommand_1.GraphItemsListC
37
37
  logger.logToStderr(this.items);
38
38
  logger.logToStderr('');
39
39
  }
40
- if (this.items.filter(i => i.userPrincipalName.toLocaleLowerCase() === args.options.userName.toLocaleLowerCase()).length <= 0) {
40
+ if (this.items.filter(i => args.options.userName.toUpperCase() === i.userPrincipalName.toUpperCase()).length <= 0) {
41
41
  const userNotInGroup = (typeof args.options.groupId !== 'undefined') ?
42
42
  'The specified user does not belong to the given Microsoft 365 Group. Please use the \'o365group user add\' command to add new users.' :
43
43
  'The specified user does not belong to the given Microsoft Teams team. Please use the \'graph teams user add\' command to add new users.';
44
44
  throw new Error(userNotInGroup);
45
45
  }
46
46
  if (args.options.role === "Owner") {
47
- const foundMember = this.items.find(e => e.userPrincipalName.toLocaleLowerCase() === args.options.userName.toLocaleLowerCase() && e.userType === 'Member');
47
+ const foundMember = this.items.find(e => args.options.userName.toUpperCase() === e.userPrincipalName.toUpperCase() && e.userType === 'Member');
48
48
  if (foundMember !== undefined) {
49
49
  const endpoint = `${this.resource}/v1.0/groups/${groupId}/owners/$ref`;
50
50
  const requestOptions = {
@@ -65,7 +65,7 @@ class AadO365GroupUserSetCommand extends GraphItemsListCommand_1.GraphItemsListC
65
65
  }
66
66
  }
67
67
  else {
68
- const foundOwner = this.items.find(e => e.userPrincipalName.toLocaleLowerCase() === args.options.userName.toLocaleLowerCase() && e.userType === 'Owner');
68
+ const foundOwner = this.items.find(e => args.options.userName.toUpperCase() === e.userPrincipalName.toUpperCase() && e.userType === 'Owner');
69
69
  if (foundOwner !== undefined) {
70
70
  const endpoint = `${this.resource}/v1.0/groups/${groupId}/owners/${foundOwner.id}/$ref`;
71
71
  const requestOptions = {
@@ -20,10 +20,20 @@ class AadUserGetCommand extends GraphCommand_1.default {
20
20
  }
21
21
  commandAction(logger, args, cb) {
22
22
  const properties = args.options.properties ?
23
- `?$select=${args.options.properties.split(',').map(p => encodeURIComponent(p.trim())).join(',')}` :
23
+ `&$select=${args.options.properties.split(',').map(p => encodeURIComponent(p.trim())).join(',')}` :
24
24
  '';
25
+ let requestUrl = `${this.resource}/v1.0/users`;
26
+ if (args.options.id) {
27
+ requestUrl += `?$filter=id eq '${encodeURIComponent(args.options.id)}'${properties}`;
28
+ }
29
+ else if (args.options.userName) {
30
+ requestUrl += `?$filter=userPrincipalName eq '${encodeURIComponent(args.options.userName)}'${properties}`;
31
+ }
32
+ else if (args.options.email) {
33
+ requestUrl += `?$filter=mail eq '${encodeURIComponent(args.options.email)}'${properties}`;
34
+ }
25
35
  const requestOptions = {
26
- url: `${this.resource}/v1.0/users/${encodeURIComponent(args.options.id ? args.options.id : args.options.userName)}${properties}`,
36
+ url: requestUrl,
27
37
  headers: {
28
38
  accept: 'application/json;odata.metadata=none'
29
39
  },
@@ -31,6 +41,18 @@ class AadUserGetCommand extends GraphCommand_1.default {
31
41
  };
32
42
  request_1.default
33
43
  .get(requestOptions)
44
+ .then((res) => {
45
+ if (res.value.length === 1) {
46
+ return Promise.resolve(res.value[0]);
47
+ }
48
+ const identifier = args.options.id ? `id ${args.options.id}`
49
+ : args.options.userName ? `user name ${args.options.userName}`
50
+ : `email ${args.options.email}`;
51
+ if (res.value.length === 0) {
52
+ return Promise.reject(`The specified user with ${identifier} does not exist`);
53
+ }
54
+ return Promise.reject(`Multiple users with ${identifier} found. Please disambiguate (user names): ${res.value.map(a => a.userPrincipalName).join(', ')} or (ids): ${res.value.map(a => a.id).join(', ')}`);
55
+ })
34
56
  .then((res) => {
35
57
  logger.log(res);
36
58
  cb();
@@ -44,6 +66,9 @@ class AadUserGetCommand extends GraphCommand_1.default {
44
66
  {
45
67
  option: '-n, --userName [userName]'
46
68
  },
69
+ {
70
+ option: '--email [email]'
71
+ },
47
72
  {
48
73
  option: '-p, --properties [properties]'
49
74
  }
@@ -52,11 +77,13 @@ class AadUserGetCommand extends GraphCommand_1.default {
52
77
  return options.concat(parentOptions);
53
78
  }
54
79
  validate(args) {
55
- if (!args.options.id && !args.options.userName) {
56
- return 'Specify either id or userName';
80
+ if (!args.options.id && !args.options.userName && !args.options.email) {
81
+ return 'Specify id, userName or email, one is required';
57
82
  }
58
- if (args.options.id && args.options.userName) {
59
- return 'Specify either id or userName but not both';
83
+ if ((args.options.id && args.options.email) ||
84
+ (args.options.id && args.options.userName) ||
85
+ (args.options.userName && args.options.email)) {
86
+ return 'Use either id, userName or email, but not all';
60
87
  }
61
88
  if (args.options.id &&
62
89
  !Utils_1.default.isValidGuid(args.options.id)) {
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const request_1 = require("../../../../request");
4
+ const Utils_1 = require("../../../../Utils");
5
+ const AnonymousCommand_1 = require("../../../base/AnonymousCommand");
6
+ const commands_1 = require("../../commands");
7
+ class AadUserHibpCommand extends AnonymousCommand_1.default {
8
+ get name() {
9
+ return commands_1.default.USER_HIBP;
10
+ }
11
+ get description() {
12
+ return 'Allows you to retrieve all accounts that have been pwned with the specified username';
13
+ }
14
+ getTelemetryProperties(args) {
15
+ const telemetryProps = super.getTelemetryProperties(args);
16
+ telemetryProps.domain = args.options.domain;
17
+ return telemetryProps;
18
+ }
19
+ commandAction(logger, args, cb) {
20
+ const requestOptions = {
21
+ url: `https://haveibeenpwned.com/api/v3/breachedaccount/${encodeURIComponent(args.options.userName)}${(args.options.domain ? `?domain=${encodeURIComponent(args.options.domain)}` : '')}`,
22
+ headers: {
23
+ 'accept': 'application/json',
24
+ 'hibp-api-key': args.options.apiKey,
25
+ 'x-anonymous': true
26
+ },
27
+ responseType: 'json'
28
+ };
29
+ request_1.default
30
+ .get(requestOptions)
31
+ .then((res) => {
32
+ logger.log(res);
33
+ cb();
34
+ })
35
+ .catch((err) => {
36
+ if ((err && err.response !== undefined && err.response.status === 404) && (this.debug || this.verbose)) {
37
+ logger.log('No pwnage found');
38
+ cb();
39
+ return;
40
+ }
41
+ return this.handleRejectedODataJsonPromise(err, logger, cb);
42
+ });
43
+ }
44
+ options() {
45
+ const options = [
46
+ {
47
+ option: '-n, --userName <userName>'
48
+ },
49
+ {
50
+ option: '--apiKey, <apiKey>'
51
+ },
52
+ {
53
+ option: '--domain, [domain]'
54
+ }
55
+ ];
56
+ const parentOptions = super.options();
57
+ return options.concat(parentOptions);
58
+ }
59
+ validate(args) {
60
+ if (!Utils_1.default.isValidUserPrincipalName(args.options.userName)) {
61
+ return 'Specify valid userName';
62
+ }
63
+ return true;
64
+ }
65
+ }
66
+ module.exports = new AadUserHibpCommand();
67
+ //# sourceMappingURL=user-hibp.js.map
@@ -15,6 +15,7 @@ class AadUserListCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
15
15
  getTelemetryProperties(args) {
16
16
  const telemetryProps = super.getTelemetryProperties(args);
17
17
  telemetryProps.properties = args.options.properties;
18
+ telemetryProps.deleted = typeof args.options.deleted !== 'undefined';
18
19
  return telemetryProps;
19
20
  }
20
21
  commandAction(logger, args, cb) {
@@ -22,7 +23,8 @@ class AadUserListCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
22
23
  args.options.properties.split(',').map(p => p.trim()) :
23
24
  ['userPrincipalName', 'displayName'];
24
25
  const filter = this.getFilter(args.options);
25
- const url = `${this.resource}/v1.0/users?$select=${properties.join(',')}${(filter.length > 0 ? '&' + filter : '')}&$top=100`;
26
+ const endpoint = args.options.deleted ? 'directory/deletedItems/microsoft.graph.user' : 'users';
27
+ const url = `${this.resource}/v1.0/${endpoint}?$select=${properties.join(',')}${(filter.length > 0 ? '&' + filter : '')}&$top=100`;
26
28
  this
27
29
  .getAllItems(url, logger, true)
28
30
  .then(() => {
@@ -35,6 +37,8 @@ class AadUserListCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
35
37
  const excludeOptions = [
36
38
  'properties',
37
39
  'p',
40
+ 'deleted',
41
+ 'd',
38
42
  'debug',
39
43
  'verbose',
40
44
  'output',
@@ -55,9 +59,8 @@ class AadUserListCommand extends GraphItemsListCommand_1.GraphItemsListCommand {
55
59
  }
56
60
  options() {
57
61
  const options = [
58
- {
59
- option: '-p, --properties [properties]'
60
- }
62
+ { option: '-p, --properties [properties]' },
63
+ { option: '-d, --deleted' }
61
64
  ];
62
65
  const parentOptions = super.options();
63
66
  return options.concat(parentOptions);
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const request_1 = require("../../../../request");
4
+ const GraphCommand_1 = require("../../../base/GraphCommand");
5
+ const commands_1 = require("../../commands");
6
+ class AadUserPasswordValidateCommand extends GraphCommand_1.default {
7
+ get name() {
8
+ return commands_1.default.USER_PASSWORD_VALIDATE;
9
+ }
10
+ get description() {
11
+ return "Check a user's password against the organization's password validation policy";
12
+ }
13
+ commandAction(logger, args, cb) {
14
+ const requestOptions = {
15
+ url: `${this.resource}/beta/users/validatePassword`,
16
+ headers: {
17
+ accept: 'application/json;odata.metadata=none'
18
+ },
19
+ data: {
20
+ password: args.options.password
21
+ },
22
+ responseType: 'json'
23
+ };
24
+ request_1.default
25
+ .post(requestOptions)
26
+ .then((res) => {
27
+ logger.log(res);
28
+ cb();
29
+ }, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
30
+ }
31
+ options() {
32
+ const options = [
33
+ {
34
+ option: '-p, --password <password>'
35
+ }
36
+ ];
37
+ const parentOptions = super.options();
38
+ return options.concat(parentOptions);
39
+ }
40
+ }
41
+ module.exports = new AadUserPasswordValidateCommand();
42
+ //# sourceMappingURL=user-password-validate.js.map
@@ -11,6 +11,7 @@ exports.default = {
11
11
  APPROLEASSIGNMENT_ADD: `${prefix} approleassignment add`,
12
12
  APPROLEASSIGNMENT_LIST: `${prefix} approleassignment list`,
13
13
  APPROLEASSIGNMENT_REMOVE: `${prefix} approleassignment remove`,
14
+ GROUP_LIST: `${prefix} group list`,
14
15
  GROUPSETTING_ADD: `${prefix} groupsetting add`,
15
16
  GROUPSETTING_GET: `${prefix} groupsetting get`,
16
17
  GROUPSETTING_LIST: `${prefix} groupsetting list`,
@@ -50,7 +51,9 @@ exports.default = {
50
51
  SP_ADD: `${prefix} sp add`,
51
52
  SP_GET: `${prefix} sp get`,
52
53
  USER_GET: `${prefix} user get`,
54
+ USER_HIBP: `${prefix} user hibp`,
53
55
  USER_LIST: `${prefix} user list`,
56
+ USER_PASSWORD_VALIDATE: `${prefix} user password validate`,
54
57
  USER_SET: `${prefix} user set`
55
58
  };
56
59
  //# sourceMappingURL=commands.js.map