UNPKG

@pnp/cli-microsoft365

Version:

Manage Microsoft 365 and SharePoint Framework projects on any platform

311 lines • 17.4 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _EntraAppPermissionRemoveCommand_instances, _EntraAppPermissionRemoveCommand_initTelemetry, _EntraAppPermissionRemoveCommand_initOptions, _EntraAppPermissionRemoveCommand_initValidators, _EntraAppPermissionRemoveCommand_initOptionSets, _EntraAppPermissionRemoveCommand_initTypes; import { odata } from "../../../../utils/odata.js"; import GraphCommand from "../../../base/GraphCommand.js"; import commands from "../../commands.js"; import request from "../../../../request.js"; import { validation } from "../../../../utils/validation.js"; import { cli } from "../../../../cli/cli.js"; import { formatting } from "../../../../utils/formatting.js"; var ScopeType; (function (ScopeType) { ScopeType["Role"] = "Role"; ScopeType["Scope"] = "Scope"; })(ScopeType || (ScopeType = {})); class EntraAppPermissionRemoveCommand extends GraphCommand { get name() { return commands.APP_PERMISSION_REMOVE; } get description() { return 'Removes the specified application and/or delegated permissions from a specified Microsoft Entra app'; } constructor() { super(); _EntraAppPermissionRemoveCommand_instances.add(this); __classPrivateFieldGet(this, _EntraAppPermissionRemoveCommand_instances, "m", _EntraAppPermissionRemoveCommand_initTelemetry).call(this); __classPrivateFieldGet(this, _EntraAppPermissionRemoveCommand_instances, "m", _EntraAppPermissionRemoveCommand_initOptions).call(this); __classPrivateFieldGet(this, _EntraAppPermissionRemoveCommand_instances, "m", _EntraAppPermissionRemoveCommand_initValidators).call(this); __classPrivateFieldGet(this, _EntraAppPermissionRemoveCommand_instances, "m", _EntraAppPermissionRemoveCommand_initOptionSets).call(this); __classPrivateFieldGet(this, _EntraAppPermissionRemoveCommand_instances, "m", _EntraAppPermissionRemoveCommand_initTypes).call(this); } async commandAction(logger, args) { const removeAppPermissions = async () => { try { if (this.verbose) { await logger.logToStderr(`Removing permissions from application ${args.options.appId || args.options.appObjectId || args.options.appName}...`); } const appObject = await this.getAppObject(args.options); const servicePrincipals = await odata.getAllItems(`${this.resource}/v1.0/servicePrincipals?$select=appId,appRoles,id,oauth2PermissionScopes,servicePrincipalNames`); const appPermissions = []; if (args.options.delegatedPermissions) { const delegatedPermissions = await this.getRequiredResourceAccessForApis(servicePrincipals, args.options.delegatedPermissions, ScopeType.Scope, appPermissions, logger); this.removePermissionsFromResourceArray(delegatedPermissions, appObject.requiredResourceAccess); } if (args.options.applicationPermissions) { const applicationPermissions = await this.getRequiredResourceAccessForApis(servicePrincipals, args.options.applicationPermissions, ScopeType.Role, appPermissions, logger); this.removePermissionsFromResourceArray(applicationPermissions, appObject.requiredResourceAccess); } for (let i = 0; i < appObject.requiredResourceAccess.length; i++) { if (appObject.requiredResourceAccess[i].resourceAccess?.length === 0) { appObject.requiredResourceAccess.splice(i, 1); } } const removePermissionRequestOptions = { url: `${this.resource}/v1.0/applications/${appObject.id}`, headers: { accept: 'application/json;odata.metadata=none' }, responseType: 'json', data: { requiredResourceAccess: appObject.requiredResourceAccess } }; await request.patch(removePermissionRequestOptions); if (args.options.revokeAdminConsent) { const appServicePrincipal = servicePrincipals.find(sp => sp.appId === appObject.appId); if (appServicePrincipal) { await this.revokeAdminConsent(appServicePrincipal, appPermissions, logger); } else { if (this.debug) { await logger.logToStderr(`No service principal found for the appId: ${appObject.appId}. Skipping revoking admin consent.`); } } } } catch (err) { this.handleRejectedODataJsonPromise(err); } }; if (args.options.force) { await removeAppPermissions(); } else { const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove the permissions from the specified application ${args.options.appId || args.options.appObjectId || args.options.appName}?` }); if (result) { await removeAppPermissions(); } } } async getAppObject(options) { const selectProperties = '$select=id,appId,requiredResourceAccess'; if (options.appObjectId) { const requestOptions = { url: `${this.resource}/v1.0/applications/${options.appObjectId}?${selectProperties}`, headers: { 'content-type': 'application/json;odata.metadata=none' }, responseType: 'json' }; return request.get(requestOptions); } const apps = options.appId ? await odata.getAllItems(`${this.resource}/v1.0/applications?$filter=appId eq '${options.appId}'&${selectProperties}`) : await odata.getAllItems(`${this.resource}/v1.0/applications?$filter=displayName eq '${options.appName}'&${selectProperties}`); if (apps.length === 0) { throw `App with ${options.appId ? 'id' : 'name'} ${options.appId || options.appName} not found in Microsoft Entra ID`; } if (apps.length > 1) { const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', apps); return cli.handleMultipleResultsFound(`Multiple apps with name '${options.appName}' found.`, resultAsKeyValuePair); } return apps[0]; } async revokeAdminConsent(servicePrincipal, appPermissions, logger) { // Check if contains app permissions let appRoleAssignments; let oAuth2RoleAssignments; if (appPermissions.some(perm => perm.resourceAccess.filter(acc => acc.type === ScopeType.Role).length > 0)) { // Retrieve app role assignments from service application appRoleAssignments = await odata.getAllItems(`${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/appRoleAssignments?$select=id,appRoleId,resourceId`); } if (appPermissions.filter(perm => perm.scope.length > 0).length > 0) { // Retrieve app role assignments from service application oAuth2RoleAssignments = await odata.getAllItems(`${this.resource}/v1.0/servicePrincipals/${servicePrincipal.id}/oAuth2PermissionGrants?$select=id,resourceId,scope`); } for await (const permission of appPermissions) { if (permission.scope.length > 0) { if (this.verbose) { await logger.logToStderr(`Revoking consent for delegated permission(s) with resourceId ${permission.resourceId} and scope(s) ${permission.scope.join(' ')}`); } const oAuth2RoleAssignment = oAuth2RoleAssignments.find(y => y.resourceId === permission.resourceId); if (oAuth2RoleAssignment) { const scopes = oAuth2RoleAssignment?.scope?.split(' '); permission.scope.forEach(scope => { scopes.splice(scopes.indexOf(scope), 1); }); oAuth2RoleAssignment.scope = scopes.join(' '); await this.revokeOAuth2Permission(oAuth2RoleAssignment); } } for await (const access of permission.resourceAccess.filter(acc => acc.type === ScopeType.Role)) { if (this.verbose) { await logger.logToStderr(`Revoking consent for application permission with resourceId ${permission.resourceId} and appRoleId ${access.id}`); } const appRoleAssignmentToRemove = appRoleAssignments.find(y => y.resourceId === permission.resourceId && y.appRoleId === access.id); if (appRoleAssignmentToRemove) { await this.revokeApplicationPermission(servicePrincipal.id, appRoleAssignmentToRemove.id); } } } } async revokeOAuth2Permission(oAuth2RoleAssignment) { const revokeRequestOptions = { url: `${this.resource}/v1.0/oauth2PermissionGrants/${oAuth2RoleAssignment.id}`, headers: { accept: 'application/json;odata.metadata=none' }, responseType: 'json', data: oAuth2RoleAssignment }; return request.patch(revokeRequestOptions); } async revokeApplicationPermission(servicePrincipalId, id) { const requestOptions = { url: `${this.resource}/v1.0/servicePrincipals/${servicePrincipalId}/appRoleAssignments/${id}`, headers: { accept: 'application/json;odata.metadata=none' }, responseType: 'json' }; return request.delete(requestOptions); } async getRequiredResourceAccessForApis(servicePrincipals, apis, scopeType, appPermissions, logger) { const resolvedApis = []; const requestedApis = apis.split(' ').map(a => a.trim()); for await (const api of requestedApis) { const pos = api.lastIndexOf('/'); const permissionName = api.substring(pos + 1); const servicePrincipalName = api.substring(0, pos); if (this.verbose) { await logger.logToStderr(`Resolving ${api}...`); await logger.logToStderr(`Permission name: ${permissionName}`); await logger.logToStderr(`Service principal name: ${servicePrincipalName}`); } const servicePrincipal = servicePrincipals.find(sp => (sp.servicePrincipalNames.indexOf(servicePrincipalName) > -1 || sp.servicePrincipalNames.indexOf(`${servicePrincipalName}/`) > -1)); if (!servicePrincipal) { throw `Service principal ${servicePrincipalName} not found`; } let permission; if (scopeType === ScopeType.Scope) { permission = servicePrincipal.oauth2PermissionScopes.find(scope => scope.value === permissionName); } else if (scopeType === ScopeType.Role) { permission = servicePrincipal.appRoles.find(scope => scope.value === permissionName); } if (!permission) { throw `Permission ${permissionName} for service principal ${servicePrincipalName} not found`; } let resolvedApi = resolvedApis.find(a => a.resourceAppId === servicePrincipal.appId); if (!resolvedApi) { resolvedApi = { resourceAppId: servicePrincipal.appId, resourceAccess: [] }; resolvedApis.push(resolvedApi); } const resourceAccessPermission = { id: permission.id, type: scopeType }; resolvedApi.resourceAccess.push(resourceAccessPermission); this.updateAppPermissions(servicePrincipal.id, resourceAccessPermission, permission.value, appPermissions); } return resolvedApis; } updateAppPermissions(spId, resourceAccessPermission, oAuth2PermissionValue, appPermissions) { let existingPermission = appPermissions.find(oauth => oauth.resourceId === spId); if (!existingPermission) { existingPermission = { resourceId: spId, resourceAccess: [], scope: [] }; appPermissions.push(existingPermission); } if (resourceAccessPermission.type === ScopeType.Scope && oAuth2PermissionValue && !existingPermission.scope.find(scp => scp === oAuth2PermissionValue)) { existingPermission.scope.push(oAuth2PermissionValue); } if (!existingPermission.resourceAccess.find(res => res.id === resourceAccessPermission.id)) { existingPermission.resourceAccess.push(resourceAccessPermission); } } removePermissionsFromResourceArray(permissions, existingArray) { permissions.forEach(resolvedRequiredResource => { const requiredResource = existingArray?.find(api => api.resourceAppId === resolvedRequiredResource.resourceAppId); if (requiredResource) { resolvedRequiredResource.resourceAccess.forEach(resolvedResourceAccess => { requiredResource.resourceAccess = requiredResource.resourceAccess.filter(ra => ra.id !== resolvedResourceAccess.id); }); } }); } } _EntraAppPermissionRemoveCommand_instances = new WeakSet(), _EntraAppPermissionRemoveCommand_initTelemetry = function _EntraAppPermissionRemoveCommand_initTelemetry() { this.telemetry.push((args) => { Object.assign(this.telemetryProperties, { appId: typeof args.options.appId !== 'undefined', appObjectId: typeof args.options.appObjectId !== 'undefined', appName: typeof args.options.appName !== 'undefined', applicationPermissions: typeof args.options.applicationPermissions !== 'undefined', delegatedPermissions: typeof args.options.delegatedPermissions !== 'undefined', revokeAdminConsent: !!args.options.revokeAdminConsent, force: !!args.options.force }); }); }, _EntraAppPermissionRemoveCommand_initOptions = function _EntraAppPermissionRemoveCommand_initOptions() { this.options.unshift({ option: '-i, --appId [appId]' }, { option: '--appObjectId [appObjectId]' }, { option: '-n, --appName [appName]' }, { option: '-a, --applicationPermissions [applicationPermissions]' }, { option: '-d, --delegatedPermissions [delegatedPermissions]' }, { option: '--revokeAdminConsent' }, { option: '--force' }); }, _EntraAppPermissionRemoveCommand_initValidators = function _EntraAppPermissionRemoveCommand_initValidators() { this.validators.push(async (args) => { if (args.options.appId && !validation.isValidGuid(args.options.appId)) { return `${args.options.appId} is not a valid GUID`; } if (args.options.appObjectId && !validation.isValidGuid(args.options.appObjectId)) { return `${args.options.appObjectId} is not a valid GUID`; } if (args.options.delegatedPermissions) { const invalidPermissions = validation.isValidPermission(args.options.delegatedPermissions); if (Array.isArray(invalidPermissions)) { return `Delegated permission(s) ${invalidPermissions.join(', ')} are not fully-qualified`; } } if (args.options.applicationPermissions) { const invalidPermissions = validation.isValidPermission(args.options.applicationPermissions); if (Array.isArray(invalidPermissions)) { return `Application permission(s) ${invalidPermissions.join(', ')} are not fully-qualified`; } } return true; }); }, _EntraAppPermissionRemoveCommand_initOptionSets = function _EntraAppPermissionRemoveCommand_initOptionSets() { this.optionSets.push({ options: ['appId', 'appObjectId', 'appName'] }, { options: ['applicationPermissions', 'delegatedPermissions'], runsWhen: (args) => args.options.delegatedPermissions === undefined && args.options.applicationPermissions === undefined }); }, _EntraAppPermissionRemoveCommand_initTypes = function _EntraAppPermissionRemoveCommand_initTypes() { this.types.string.push('appId', 'appObjectId', 'appName', 'applicationPermissions', 'delegatedPermissions'); this.types.boolean.push('revokeAdminConsent'); }; export default new EntraAppPermissionRemoveCommand(); //# sourceMappingURL=app-permission-remove.js.map