210 lines
7.1 KiB
JavaScript
210 lines
7.1 KiB
JavaScript
/*! @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<string>} 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
|