/*! @azure/msal-common v14.4.0 2023-11-07 */ 'use strict'; import { createClientConfigurationError } from '../error/ClientConfigurationError.mjs'; import { StringUtils } from '../utils/StringUtils.mjs'; import { createClientAuthError } from '../error/ClientAuthError.mjs'; import { Constants, OIDC_SCOPES } from '../utils/Constants.mjs'; import { emptyInputScopesError } from '../error/ClientConfigurationErrorCodes.mjs'; import { cannotAppendScopeSet, cannotRemoveEmptyScope, emptyInputScopeSet } from '../error/ClientAuthErrorCodes.mjs'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** * The ScopeSet class creates a set of scopes. Scopes are case-insensitive, unique values, so the Set object in JS makes * the most sense to implement for this class. All scopes are trimmed and converted to lower case strings in intersection and union functions * to ensure uniqueness of strings. */ class ScopeSet { constructor(inputScopes) { // Filter empty string and null/undefined array items const scopeArr = inputScopes ? StringUtils.trimArrayEntries([...inputScopes]) : []; const filteredInput = scopeArr ? StringUtils.removeEmptyStringsFromArray(scopeArr) : []; // Validate and filter scopes (validate function throws if validation fails) this.validateInputScopes(filteredInput); this.scopes = new Set(); // Iterator in constructor not supported by IE11 filteredInput.forEach((scope) => this.scopes.add(scope)); } /** * Factory method to create ScopeSet from space-delimited string * @param inputScopeString * @param appClientId * @param scopesRequired */ static fromString(inputScopeString) { const scopeString = inputScopeString || Constants.EMPTY_STRING; const inputScopes = scopeString.split(" "); return new ScopeSet(inputScopes); } /** * Creates the set of scopes to search for in cache lookups * @param inputScopeString * @returns */ static createSearchScopes(inputScopeString) { const scopeSet = new ScopeSet(inputScopeString); if (!scopeSet.containsOnlyOIDCScopes()) { scopeSet.removeOIDCScopes(); } else { scopeSet.removeScope(Constants.OFFLINE_ACCESS_SCOPE); } return scopeSet; } /** * Used to validate the scopes input parameter requested by the developer. * @param {Array} inputScopes - Developer requested permissions. Not all scopes are guaranteed to be included in the access token returned. * @param {boolean} scopesRequired - Boolean indicating whether the scopes array is required or not */ validateInputScopes(inputScopes) { // Check if scopes are required but not given or is an empty array if (!inputScopes || inputScopes.length < 1) { throw createClientConfigurationError(emptyInputScopesError); } } /** * Check if a given scope is present in this set of scopes. * @param scope */ containsScope(scope) { const lowerCaseScopes = this.printScopesLowerCase().split(" "); const lowerCaseScopesSet = new ScopeSet(lowerCaseScopes); // compare lowercase scopes return scope ? lowerCaseScopesSet.scopes.has(scope.toLowerCase()) : false; } /** * Check if a set of scopes is present in this set of scopes. * @param scopeSet */ containsScopeSet(scopeSet) { if (!scopeSet || scopeSet.scopes.size <= 0) { return false; } return (this.scopes.size >= scopeSet.scopes.size && scopeSet.asArray().every((scope) => this.containsScope(scope))); } /** * Check if set of scopes contains only the defaults */ containsOnlyOIDCScopes() { let defaultScopeCount = 0; OIDC_SCOPES.forEach((defaultScope) => { if (this.containsScope(defaultScope)) { defaultScopeCount += 1; } }); return this.scopes.size === defaultScopeCount; } /** * Appends single scope if passed * @param newScope */ appendScope(newScope) { if (newScope) { this.scopes.add(newScope.trim()); } } /** * Appends multiple scopes if passed * @param newScopes */ appendScopes(newScopes) { try { newScopes.forEach((newScope) => this.appendScope(newScope)); } catch (e) { throw createClientAuthError(cannotAppendScopeSet); } } /** * Removes element from set of scopes. * @param scope */ removeScope(scope) { if (!scope) { throw createClientAuthError(cannotRemoveEmptyScope); } this.scopes.delete(scope.trim()); } /** * Removes default scopes from set of scopes * Primarily used to prevent cache misses if the default scopes are not returned from the server */ removeOIDCScopes() { OIDC_SCOPES.forEach((defaultScope) => { this.scopes.delete(defaultScope); }); } /** * Combines an array of scopes with the current set of scopes. * @param otherScopes */ unionScopeSets(otherScopes) { if (!otherScopes) { throw createClientAuthError(emptyInputScopeSet); } const unionScopes = new Set(); // Iterator in constructor not supported in IE11 otherScopes.scopes.forEach((scope) => unionScopes.add(scope.toLowerCase())); this.scopes.forEach((scope) => unionScopes.add(scope.toLowerCase())); return unionScopes; } /** * Check if scopes intersect between this set and another. * @param otherScopes */ intersectingScopeSets(otherScopes) { if (!otherScopes) { throw createClientAuthError(emptyInputScopeSet); } // Do not allow OIDC scopes to be the only intersecting scopes if (!otherScopes.containsOnlyOIDCScopes()) { otherScopes.removeOIDCScopes(); } const unionScopes = this.unionScopeSets(otherScopes); const sizeOtherScopes = otherScopes.getScopeCount(); const sizeThisScopes = this.getScopeCount(); const sizeUnionScopes = unionScopes.size; return sizeUnionScopes < sizeThisScopes + sizeOtherScopes; } /** * Returns size of set of scopes. */ getScopeCount() { return this.scopes.size; } /** * Returns the scopes as an array of string values */ asArray() { const array = []; this.scopes.forEach((val) => array.push(val)); return array; } /** * Prints scopes into a space-delimited string */ printScopes() { if (this.scopes) { const scopeArr = this.asArray(); return scopeArr.join(" "); } return Constants.EMPTY_STRING; } /** * Prints scopes into a space-delimited lower-case string (used for caching) */ printScopesLowerCase() { return this.printScopes().toLowerCase(); } } export { ScopeSet }; //# sourceMappingURL=ScopeSet.mjs.map