UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

1,112 lines (1,110 loc) 53.2 kB
import { of, Subject, throwError } from 'rxjs'; import { map, mergeMap, take } from 'rxjs/operators'; import { Crypto } from '../data/crypto'; import { headerConstants } from '../data/http-constants'; import { Net } from '../data/net'; import { Logging } from '../diagnostics/logging'; import { RpcForwarder, RpcServiceForwarder } from '../rpc/rpc-forwarder'; import { SignOnManager } from './sign-on-manager'; /** * Authorization Manager class. Handles SME authentication for service requests. */ export class AuthorizationManager extends RpcServiceForwarder { authorize; /** * Cache key for session storage of authorization cache */ static cacheKey = 'AuthorizationManager.cache.v1'; /** * If no laps local admin name is defined, it will default to 'administrator' */ static defaultLapsLocalAdminName = 'administrator'; static rpcCommands = { setNodeToken: 'setNodeToken', setNodeTokens: 'setNodeTokens', manageAsToken: 'manageAsToken', setNodeTokenError: 'setNodeTokenError', setJeaContext: 'setJeaContext', clearNodeTokens: 'clearNodeTokens', getNewToken: 'getNewToken', secureToken: 'secureToken', encrypt: 'encrypt', getSignOnToken: 'getSignOnToken', setSignOnToken: 'setSignOnToken', setSignOnTokenError: 'setSignOnTokenError' }; /** * Create a map of nodeNames to token objects to hold node specific tokens. */ nodeTokens; /** * The sign on operation manager. */ signOnManager; /** * The backing store fro the manageAsToken */ token; /** * The JSON Web Key. Single string with JSON.stringify format. */ jwk; /** * Credentials expiration time, used to determine the lifetime of new tokens. Value in milliseconds. */ credentialsExpirationTimeInMs; /** * Underlying implementation subject for credentialExpiration observable */ credentialExpirationChangedSubject; /** * Observable that emits whenever the credentials expiration has changed */ credentialExpirationChanged; /** * Set the admin configured expiration time in milliseconds * If this is not set it will be defaulted to 365 days. */ set authExpirationInMs(expirationTimeInMs) { this.credentialsExpirationTimeInMs = expirationTimeInMs; this.credentialExpirationChangedSubject.next(expirationTimeInMs); } /** * The logon user returned by gateway's api/user */ logOnUser; /** * Sets logon user and caches the value */ set shellLogOnUser(username) { this.logOnUser = username; this.updateCache(); } /** * The mapping of connections to PowershellEndpoints */ nodePowershellEndpoints; /** * The token awaiter subject. */ tokenAwaiter; /** * The subject observable of sign on token awaiter. */ signOnTokenAwaiter; /** * Sets the current manage as token * If running with an Rpc child, notify of the change */ set manageAsToken(token) { this.token = token; this.nodePowershellEndpoints = {}; this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.manageAsToken, token); this.updateCache(); } /** * Gets the current manage as token */ get manageAsToken() { return this.token; } /** * Sets the gateway encryption certificate JWK. * If this is not set by Shell/Add-Connection, it will send clear text password. */ set gatewayJwk(jwk) { if (this.jwk !== jwk) { this.resetAllTokens(); } this.jwk = jwk; this.gatewayJwkChangedSubject.next(jwk); this.updateCache(); } /** * Observable that emits whenever the gateways Jwk has changed */ gatewayJwkChanged; /** * Underlying implementation subject for gatewayJwkChanged observable */ gatewayJwkChangedSubject; /** * Gets the gateway encryption certificate JWK. */ get gatewayJwk() { return this.jwk; } /** * Gets an observable the emits when the authorization token is ready */ get authAwaiter() { if (this.tokenAwaiter && !this.tokenAwaiter.closed) { // return the global token return this.tokenAwaiter; } // return the global token return of({ appliesTo: null, token: this.manageAsToken }); } /** * Initializes a new instance of the Authorization Manager class * @param authorize An AuthorizationHandler with which to retrieve user credentials * @param rpc The rpc to forward auth requests to a parent window */ constructor(authorize, rpc) { super('authorization-manager', rpc); this.authorize = authorize; this.gatewayJwkChangedSubject = new Subject(); this.gatewayJwkChanged = this.gatewayJwkChangedSubject.asObservable(); this.credentialExpirationChangedSubject = new Subject(); this.credentialExpirationChanged = this.credentialExpirationChangedSubject.asObservable(); this.nodeTokens = {}; this.nodePowershellEndpoints = {}; if (MsftSme.isShell()) { // attempt to restore from storage if this is a shell instance const cachedDataString = MsftSme.SessionStorageHandler.getItem(AuthorizationManager.cacheKey); if (!MsftSme.isNullOrWhiteSpace(cachedDataString)) { try { const cachedData = JSON.parse(cachedDataString); // verify data and set properties this.jwk = cachedData.jwk || null; this.manageAsToken = cachedData.manageAsToken || null; this.nodeTokens = cachedData.nodeTokens || {}; this.nodePowershellEndpoints = cachedData.nodePowershellEndpoints || {}; this.logOnUser = cachedData.logOnUser; } catch (error) { MsftSme.SessionStorageHandler.removeItem(AuthorizationManager.cacheKey); } } } this.signOnManager = new SignOnManager(() => this.refreshSignOnToken()); } /** * defines the conditions under which the AuthorizationManager can handle an ajax error */ canHandleAjaxFailure(code, error) { // we can handle ajax errors if we have a getter defined, and the code is Unauthorized (401) or we get a cim authorization failure return this.authorize && Net.isUnauthorized(error); } /** * When canHandle returns true for an ajax error, this method can be called to handle that error. */ handleAjaxFailure(code, request, error, nodeName) { const errorData = this.getErrorData(code, error); return this.getNewToken(nodeName, errorData) .pipe(mergeMap(response => { // There may be multiple nodes requesting authentication, but we can only ask the user for one. // check if the result if for our node, otherwise try again. // It looks for inside of array only for first name. const names = response.appliesTo; const isTokenForNode = !names || names === nodeName || (Array.isArray(names) && names[0] === nodeName); if (isTokenForNode) { // this token applies to our node, so continue this.addAuthorizationRequestHeader(request, nodeName); return of(request); } else { // this token did not apply to our node. Ask again. return this.handleAjaxFailure(code, request, error, nodeName); } })); } /** * Check if it can handle the error. * * @param response the response of CIM stream query. */ canHandleStreamFailure(response) { // access denied case. return response && response.response && response.response.statusCode === 401 /* HttpStatusCode.Unauthorized */; } /** * When canHandle returns true for an ajax error, this method can be called to handle that error. */ handleStreamFailure(nodeName, options, response) { let message = response.response.error && response.response.error.message; if (!message) { const errors = response.response.errors; if (errors && errors.length > 0) { message = errors[0].message; } } const errorData = { statusCode: response.response.statusCode, errorCode: 'Unauthorized', errorMessage: message }; return this.getNewToken(nodeName, errorData) .pipe(mergeMap(result => { const names = result.appliesTo; const isTokenForNode = !names || names === nodeName || (Array.isArray(names) && names[0] === nodeName); if (isTokenForNode) { const token = this.getSavedNodeToken(nodeName); if (token) { if (options) { options.authToken = token; } else { options = { authToken: token }; } } return of(options); } else { return this.handleStreamFailure(nodeName, options, response); } })); } saveJeaContext(nodeName, endpoint) { Logging.trace({ view: 'sme-ui-control', instance: 'saveJeaContext', action: 'command-click', data: { message: 'Establishing session using JEA endpoint' } }); this.setJeaEndpoint(nodeName, endpoint); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setJeaContext, { nodeName: nodeName, endpoint: endpoint }); } getJeaEndpoint(nodeName) { return this.nodePowershellEndpoints[nodeName.toLocaleLowerCase()]; } /** * Associates a node to use a specified JEA endpoint */ setJeaEndpoint(nodeName, endpoint) { this.nodePowershellEndpoints[nodeName.toLocaleLowerCase()] = endpoint; this.updateCache(); } getNewToken(nodeNames, errorData) { // if we are already awaiting a token, then hook into the current request and try using that token if (this.tokenAwaiter && !this.tokenAwaiter.closed) { return this.tokenAwaiter; } // ensure input is an array only if not null. if (nodeNames && !Array.isArray(nodeNames)) { nodeNames = [nodeNames]; } // define a new subject for multiple requests to wait on this.tokenAwaiter = new Subject(); // try to forward execute getNewToken from our parent const parentExecuter = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.getNewToken, [nodeNames, errorData]); if (parentExecuter) { return this.tokenAwaiter; } // Need to clear the endpoints before we acquire a new token, // because we must try new credentials in an admin context before any fallback happens. if (nodeNames) { for (const node of nodeNames) { this.setJeaEndpoint(node, null); } } // since we could not forward the request We must ask for the auth token ourselves this.authorize(nodeNames, errorData) .pipe(mergeMap(credentials => { if (credentials.applyToAllNodes) { this.nodeTokens = {}; this.updateCache(); const forward = this.forwardExecute(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, []); if (forward) { return forward.pipe(map(() => credentials)); } } return of(credentials); }), mergeMap(credentials => { const appliesTo = credentials.applyToAllNodes ? null : nodeNames || null; return this.createSecureToken({ username: credentials.username, password: credentials.password, useLaps: credentials.useLaps, lapsLocalAdminName: credentials.lapsLocalAdminName }).pipe(map(token => { return { token: token, appliesTo: appliesTo }; })); }), // take the next 1 result and use it for our token. // We can now notify anyone waiting for auth and save the token take(1)) .subscribe({ next: result => { this.completeTokenAwaiter(result); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, result); }, error: error => { this.completeTokenAwaiter(null, error); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, RpcForwarder.ensureSerializable(error)); } }); return this.tokenAwaiter; } refreshSignOnToken() { // if we are already awaiting a token, then hook into the current request and try using that token if (this.signOnTokenAwaiter) { return this.signOnTokenAwaiter; } // define a new subject for multiple requests to wait on this.signOnTokenAwaiter = new Subject(); // try to forward execute getSignOnToken from our parent. const now = Date.now(); const parentExecuter = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.getSignOnToken, [now]); if (parentExecuter) { return this.signOnTokenAwaiter; } // since we could not forward the request We must ask for the signon token ourselves this.signOnManager.requestSignOnRefresh() .subscribe({ next: response => { this.completeSignOnTokenAwaiter(response); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnToken, RpcForwarder.ensureSerializable(response)); }, error: error => { this.completeSignOnTokenAwaiter(null, error); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnTokenError, RpcForwarder.ensureSerializable(error)); } }); return this.signOnTokenAwaiter; } /** * Forward Sign On Token to child frame. */ forwardSignOnToken() { const data = { time: Date.now(), token: this.signOnManager.signedHttpRequestToken }; this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnToken, data); } /** * @deprecated use createSecureToken() after configured gatewayJwk property. * * Creates a token from the given options that may be used for node authentication * @param options The token creation options */ createToken(options, passwordEncryptedWith = null) { // ensure a valid value for laps local admin name if (MsftSme.isNullOrWhiteSpace(options.lapsLocalAdminName)) { options.lapsLocalAdminName = AuthorizationManager.defaultLapsLocalAdminName; } // ensure username has a valid value if (MsftSme.isNullOrWhiteSpace(options.username)) { options.username = null; } if (options.useLaps || !options.username) { return { value: null, username: null, useLaps: options.useLaps, lapsLocalAdminName: options.lapsLocalAdminName }; } // ensure password has a valid value if (MsftSme.isNullOrWhiteSpace(options.password)) { options.password = null; } let username; if (options.username.indexOf('@') >= 0) { // domain is empty if UPN is used. username = ["", options.username]; } else { username = options.username.split('\\'); } const token = Net.createEncodedAuthenticationHeader(username, options.password, passwordEncryptedWith); return { value: token, username: options.username, useLaps: false, lapsLocalAdminName: null }; } /** * Creates a secure token from the given options that may be used for node authentication * * @param options The token creation options */ createSecureToken(options) { const forward = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.secureToken, [options]); if (forward) { return forward; } else { return this.secureToken(options); } } /** * Get the saved Auth token for a node. * * @param nodeName The nodeName to get token for. */ getSavedNodeToken(nodeName) { if (nodeName && this.nodeTokens[nodeName.toLocaleLowerCase()]) { return this.nodeTokens[nodeName.toLocaleLowerCase()]; } return this.manageAsToken; } /** * Sets a token on the given nodes * @param token The token to use for the given nodes * @param nodeName The names of the nodes to set the token for. If empty or null, the token will be used as the global manageAs token * @returns an Observable indicating that the token has been set. */ setNodeTokens(token, nodeNames) { // if we have a parent instance, just forward it and return the result const forward = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.setNodeTokens, [token, nodeNames]); if (forward) { return forward; } // otherwise process the token const childSetTokenResponse = { token: token }; if (!nodeNames || !Array.isArray(nodeNames) || nodeNames.length === 0) { this.manageAsToken = token; } else { childSetTokenResponse.appliesTo = nodeNames; nodeNames.forEach(nodeName => this.nodeTokens[nodeName.toLocaleLowerCase()] = token); } this.updateCache(); // notify all child instances of the token change. this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, childSetTokenResponse, true); // return a null observable, indicating we are done. return of(null); } /** * Adds a authorization header to a request given a node with a manageAsToken * @param request The request to add headers to * @param nodeName optional. The node to add headers for if not provided, the global token will be used * @param token optional. The token to use for the headers. if provided, the nodeName is not used. */ addAuthorizationRequestHeader(request, nodeName, token) { if (!token) { token = this.getSavedNodeToken(nodeName); } if (token) { if (token.value) { // If username and password are explicitly provided, we only add the Authorization header. request.headers[headerConstants.SME_AUTHORIZATION] = token.value; } else { // If not, we add useLaps header. request.headers[headerConstants.USE_LAPS] = token.useLaps; request.headers[headerConstants.LAPS_LOCALADMINNAME] = token.lapsLocalAdminName; } } } /** * Create token headers. * * @param nodeName the node name. * @param token the token to override current setting (optional). */ createTokenHeaders(nodeName, token) { const headers = {}; token = token || this.getSavedNodeToken(nodeName); if (token) { if (token.value) { // If username and password are explicitly provided, we only add the Authorization header. headers[headerConstants.SME_AUTHORIZATION] = token.value; } else { // If not, we add useLaps header. headers[headerConstants.USE_LAPS] = token.useLaps ? 'true' : 'false'; headers[headerConstants.LAPS_LOCALADMINNAME] = token.lapsLocalAdminName; } } return headers; } /** * Adds a authorization header to a request given a node with a manageAsToken */ addAuthorizationTokensToMultiPartBody(body, nodeName, token) { if (!token) { token = this.getSavedNodeToken(nodeName); } if (token) { if (token.value) { // If username and password are explicitly provided, we only add the Authorization header. body.push(headerConstants.SME_AUTHORIZATION + ': ' + token.value); } else { // If not, we add useLaps header. body.push(headerConstants.USE_LAPS + ': ' + token.useLaps); body.push(headerConstants.LAPS_LOCALADMINNAME + ': ' + token.lapsLocalAdminName); } } } resetAllTokens() { this.manageAsToken = null; this.nodePowershellEndpoints = {}; this.nodeTokens = {}; this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.manageAsToken, null); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, []); this.updateCache(); } /** * Encrypts a string value using the jwk from the gateway */ encrypt(value) { const forward = this.forwardExecute(0 /* RpcRelationshipType.Parent */, AuthorizationManager.rpcCommands.encrypt, [value]); if (forward) { return forward; } return Crypto.encryptRsaSha1(this.jwk, value); } /** * Called on a child service instance when onForwardInit returns from the parent * @param data The response from the forwardInit call */ onForwardInitResponse(data) { if (data && data.error) { // if there is an error, we cannot continue, so throw it throw data.error; } this.manageAsToken = data.result.manageAsToken; this.nodeTokens = data.result.nodeTokens; this.nodePowershellEndpoints = data.result.nodePowershellEndpoints; this.signOnManager.signedHttpRequestToken = data.result.signOnToken; } /** * Called when a new instance of the service in another window is initialized and needs to synchronize with its parent * @param from The RpcRelationshipType that this request is from * @returns an observable for the all the values needed to initialize the service */ onForwardInit() { // authorization manager doesn't pass any properties to child services at this time. return of({ manageAsToken: this.manageAsToken, nodeTokens: this.nodeTokens, nodePowershellEndpoints: this.nodePowershellEndpoints, signOnToken: this.signOnManager.signedHttpRequestToken }); } /** * Called when the forwarded services counterpart wants to get data from the parent * @param from The RpcRelationshipType that this request is from * @param name The name of the method to forward to * @param args The arguments of the method * @returns an observable for the result of the method call */ onForwardExecute(from, name, args) { // command comes from child if (from === 1 /* RpcRelationshipType.Child */) { switch (name) { case AuthorizationManager.rpcCommands.getNewToken: { // start getting the new token, but return immediately to avoid timeout const subscription = this.getNewToken(args ? args[0] : null, args ? args[1] : null) .subscribe({ next: () => subscription.unsubscribe(), error: error => { subscription.unsubscribe(); this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, RpcForwarder.ensureSerializable(error)); } }); return of(null); } case AuthorizationManager.rpcCommands.secureToken: { const options = args && args[0]; if (!options) { return of(null); } return this.secureToken(options); } case AuthorizationManager.rpcCommands.encrypt: { const options = args && args[0]; if (!options) { return of(null); } return this.encrypt(options); } case AuthorizationManager.rpcCommands.setNodeTokens: { const token = args && args[0]; const nodes = args && args[1]; if (!token) { return of(null); } return this.setNodeTokens(token, nodes); } case AuthorizationManager.rpcCommands.getSignOnToken: { const subscription = this.refreshSignOnToken() .subscribe({ next: () => subscription.unsubscribe(), error: error => { subscription.unsubscribe(), this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnTokenError, RpcForwarder.ensureSerializable(error)); } }); return of(null); } } } // command comes from parent if (from === 0 /* RpcRelationshipType.Parent */) { if (name === AuthorizationManager.rpcCommands.clearNodeTokens) { this.nodeTokens = {}; this.nodePowershellEndpoints = {}; // if we also have children, forward the request on const forward = this.forwardExecute(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.clearNodeTokens, args); return forward || of(null); } } // command not implemented return this.nameNotFound(name); } /** * Called when the forwarded services counterpart sends a notify message * @param from The RpcRelationshipType that this request is from * @param name The name of the property to change * @param value The new value of the property * @returns an observable that completes when the property has been changed. */ onForwardNotify(from, name, value) { // allow our parent to give us a new token. if (from === 0 /* RpcRelationshipType.Parent */) { if (name === AuthorizationManager.rpcCommands.manageAsToken) { this.manageAsToken = value; // if we also have children, forward the notification on const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.manageAsToken, value); return forward || of(null); } if (name === AuthorizationManager.rpcCommands.setNodeToken) { if (!value) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message; return throwError(() => message.format(AuthorizationManager.rpcCommands.setNodeToken)); } this.completeTokenAwaiter(value); const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeToken, value); return forward || of(null); } if (name === AuthorizationManager.rpcCommands.setNodeTokenError) { if (!value) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message; return throwError(() => message.format(AuthorizationManager.rpcCommands.setNodeTokenError)); } this.completeTokenAwaiter(null, value); const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setNodeTokenError, value); return forward || of(null); } if (name === AuthorizationManager.rpcCommands.setJeaContext) { if (!value) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message; return throwError(() => message.format(AuthorizationManager.rpcCommands.setJeaContext)); } this.saveJeaContext(value.nodeName, value.endpoint); const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setJeaContext, value); return forward || of(null); } if (name === AuthorizationManager.rpcCommands.setSignOnToken) { if (!value) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message; return throwError(() => message.format(AuthorizationManager.rpcCommands.setNodeToken)); } this.completeSignOnTokenAwaiter(value); const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnToken, value); return forward || of(null); } if (name === AuthorizationManager.rpcCommands.setSignOnTokenError) { if (!value) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.InvalidValue.message; return throwError(() => message.format(AuthorizationManager.rpcCommands.setNodeTokenError)); } this.completeSignOnTokenAwaiter(null, value); const forward = this.forwardNotify(1 /* RpcRelationshipType.Child */, AuthorizationManager.rpcCommands.setSignOnTokenError, value); return forward || of(null); } } return this.nameNotFound(name); } /** * Creates a secure token from the given options that may be used for node authentication * * @param options The token creation options */ secureToken(options) { // ensure a valid value for laps local admin name if (MsftSme.isNullOrWhiteSpace(options.lapsLocalAdminName)) { options.lapsLocalAdminName = AuthorizationManager.defaultLapsLocalAdminName; } // ensure username has a valid value if (MsftSme.isNullOrWhiteSpace(options.username)) { options.username = null; } if (options.useLaps || !options.username) { return of({ value: null, username: null, useLaps: options.useLaps, lapsLocalAdminName: options.lapsLocalAdminName }); } // ensure password has a valid value if (MsftSme.isNullOrWhiteSpace(options.password)) { options.password = null; } let username; if (options.username.indexOf('@') >= 0) { // domain is empty if UPN is used. username = ["", options.username]; } else { username = options.username.split('\\'); } const tokenData = { value: undefined, username: options.username, useLaps: false, lapsLocalAdminName: null }; if (this.jwk) { return Net.createEncryptedAuthenticationHeader(this.jwk, username, options.password, this.logOnUser, this.credentialsExpirationTimeInMs) .pipe(map(newToken => { tokenData.value = newToken; return tokenData; })); } tokenData.value = Net.createEncodedAuthenticationHeader(username, options.password); return of(tokenData); } /** * Completes the token awaiter */ completeTokenAwaiter(result, error) { if (error) { if (this.tokenAwaiter) { const awaiter = this.tokenAwaiter; this.tokenAwaiter = null; awaiter.error(error); } } else { if (!result.appliesTo) { this.manageAsToken = result.token; } else if (Array.isArray(result.appliesTo)) { result.appliesTo.forEach(nodeName => { this.nodeTokens[nodeName.toLocaleLowerCase()] = result.token; }); this.updateCache(); } else { this.nodeTokens[result.appliesTo.toLocaleLowerCase()] = result.token; this.updateCache(); } if (this.tokenAwaiter) { const awaiter = this.tokenAwaiter; this.tokenAwaiter = null; awaiter.next(result); awaiter.complete(); } } } /** * Completes the sign on token awaiter */ completeSignOnTokenAwaiter(result, error) { const awaiter = this.signOnTokenAwaiter; if (awaiter) { this.signOnTokenAwaiter = null; } if (error) { if (awaiter) { awaiter.error(error); } return; } if (result.token) { this.signOnManager.signedHttpRequestToken = result.token; } else { Logging.logError('Shell.AuthorizationManager', 'Null token was passed.'); } if (awaiter) { awaiter.next(result); awaiter.complete(); } } /** * Gets the error data. * @param code the http status code. * @param error the AJAX error object. */ getErrorData(code, error) { return { statusCode: code, errorCode: error && error.xhr && error.xhr.response && error.xhr.response.error && error.xhr.response.error.code, errorMessage: Net.getErrorMessage(error) }; } /** * Updates the authorization cache. Does nothing if we are not running in shell */ updateCache() { if (!MsftSme.isShell()) { // ignore if not shell return; } // update cached data const cacheData = { jwk: this.jwk, manageAsToken: this.manageAsToken, nodePowershellEndpoints: this.nodePowershellEndpoints, nodeTokens: this.nodeTokens }; MsftSme.SessionStorageHandler.setItem(AuthorizationManager.cacheKey, JSON.stringify(cacheData)); } } //# sourceMappingURL=authorization-manager.js.map // SIG // Begin signature block // SIG // MIIoNwYJKoZIhvcNAQcCoIIoKDCCKCQCAQExDzANBglg // SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor // SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC // SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg // SIG // efJ8cdj/pvxVJxde6/jWb3uBZMT5R9I7nBkFrR6kEJeg // SIG // gg2FMIIGAzCCA+ugAwIBAgITMwAABAO91ZVdDzsYrQAA // SIG // AAAEAzANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV // SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH // SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv // SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT // SIG // aWduaW5nIFBDQSAyMDExMB4XDTI0MDkxMjIwMTExM1oX // SIG // DTI1MDkxMTIwMTExM1owdDELMAkGA1UEBhMCVVMxEzAR // SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v // SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv // SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA // SIG // n3RnXcCDp20WFMoNNzt4s9fV12T5roRJlv+bshDfvJoM // SIG // ZfhyRnixgUfGAbrRlS1St/EcXFXD2MhRkF3CnMYIoeMO // SIG // MuMyYtxr2sC2B5bDRMUMM/r9I4GP2nowUthCWKFIS1RP // SIG // lM0YoVfKKMaH7bJii29sW+waBUulAKN2c+Gn5znaiOxR // SIG // qIu4OL8f9DCHYpME5+Teek3SL95sH5GQhZq7CqTdM0fB // SIG // w/FmLLx98SpBu7v8XapoTz6jJpyNozhcP/59mi/Fu4tT // SIG // 2rI2vD50Vx/0GlR9DNZ2py/iyPU7DG/3p1n1zluuRp3u // SIG // XKjDfVKH7xDbXcMBJid22a3CPbuC2QJLowIDAQABo4IB // SIG // gjCCAX4wHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB // SIG // BQUHAwMwHQYDVR0OBBYEFOpuKgJKc+OuNYitoqxfHlrE // SIG // gXAZMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQLEyRNaWNy // SIG // b3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQx // SIG // FjAUBgNVBAUTDTIzMDAxMis1MDI5MjYwHwYDVR0jBBgw // SIG // FoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0w // SIG // SzBJoEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29t // SIG // L3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx // SIG // LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYB // SIG // BQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w // SIG // a2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8yMDEx // SIG // LTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3 // SIG // DQEBCwUAA4ICAQBRaP+hOC1+dSKhbqCr1LIvNEMrRiOQ // SIG // EkPc7D6QWtM+/IbrYiXesNeeCZHCMf3+6xASuDYQ+AyB // SIG // TX0YlXSOxGnBLOzgEukBxezbfnhUTTk7YB2/TxMUcuBC // SIG // P45zMM0CVTaJE8btloB6/3wbFrOhvQHCILx41jTd6kUq // SIG // 4bIBHah3NG0Q1H/FCCwHRGTjAbyiwq5n/pCTxLz5XYCu // SIG // 4RTvy/ZJnFXuuwZynowyju90muegCToTOwpHgE6yRcTv // SIG // Ri16LKCr68Ab8p8QINfFvqWoEwJCXn853rlkpp4k7qzw // SIG // lBNiZ71uw2pbzjQzrRtNbCFQAfmoTtsHFD2tmZvQIg1Q // SIG // VkzM/V1KCjHL54ItqKm7Ay4WyvqWK0VIEaTbdMtbMWbF // SIG // zq2hkRfJTNnFr7RJFeVC/k0DNaab+bpwx5FvCUvkJ3z2 // SIG // wfHWVUckZjEOGmP7cecefrF+rHpif/xW4nJUjMUiPsyD // SIG // btY2Hq3VMLgovj+qe0pkJgpYQzPukPm7RNhbabFNFvq+ // SIG // kXWBX/z/pyuo9qLZfTb697Vi7vll5s/DBjPtfMpyfpWG // SIG // 0phVnAI+0mM4gH09LCMJUERZMgu9bbCGVIQR7cT5YhlL // SIG // t+tpSDtC6XtAzq4PJbKZxFjpB5wk+SRJ1gm87olbfEV9 // SIG // SFdO7iL3jWbjgVi1Qs1iYxBmvh4WhLWr48uouzCCB3ow // SIG // ggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQEL // SIG // BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo // SIG // aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK // SIG // ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMT // SIG // KU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv // SIG // cml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcw // SIG // ODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgT // SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc // SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYG // SIG // A1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Eg // SIG // MjAxMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC // SIG // ggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq // SIG // uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJ // SIG // DXlkh36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/X // SIG // llnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3WraPPL // SIG // bfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5 // SIG // pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt // SIG // 7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7La4zWMW3 // SIG // Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwP // SIG // rRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDgl // SIG // mDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLE // SIG // tVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9 // SIG // G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/C // SIG // HFfbg43sTUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L50kp // SIG // pxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9A // SIG // N0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE // SIG // 2rCIF96eTvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB // SIG // 6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k // SIG // 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAwe // SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB // SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h // SIG // 6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov // SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj // SIG // dHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNy // SIG // bDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 // SIG // dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv // SIG // TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDCB // SIG // nwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYI // SIG // KwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv // SIG // bS9wa2lvcHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggr // SIG // BgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABp // SIG // AGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkq // SIG // hkiG9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuW // SIG // EeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H // SIG // qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS // SIG // 0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32m // SIG // kHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPyprWElj // SIG // HwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMO // SIG // r5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsat // SIG // ruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiyWYlobm+n // SIG // t3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVA // SIG // KmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0Hj // SIG // Wwechz4GdwbRBrF1HxS+YWG18NzGGwS+30HHDiju3mUv // SIG // 7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3 // SIG // HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs // SIG // 6jeZeRhL/9azI2h15q/6/IvrC4DqaTuv/DDtBEyO3991 // SIG // bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYib // SIG // V3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0x // SIG // ghoKMIIaBgIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEG // SIG // A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u // SIG // ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n // SIG // IFBDQSAyMDExAhMzAAAEA73VlV0POxitAAAAAAQDMA0G // SIG // CWCGSAFlAwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgor // SIG // BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE // SIG // AYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDHHORPOjw+JOL8 // SIG // SgtL2LlX1Ed5+Z1eMR/Rn0ZJjMmAkDBCBgorBgEEAYI3 // SIG // AgEMMTQwMqAUgBIATQBpAGMAcgBvAHMAbwBmAHShGoAY // SIG // aHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3 // SIG // DQEBAQUABIIBAIdhN3IJYjkwCYnlnHqFQsMSJ9HTqZt6 // SIG // acKFISgNzjMBZgtXpooOgnyRDecfuqzb8U71d8yx7iHQ // SIG // wTy4pLlThZayUHaIVZIBJqY/EHAtQJG3Xje94mVKSqkx // SIG // XkrefmGKD5oIZg2IS48nYjQe0GmVz9aT2hBFnrU+Onoy // SIG // csCZNvSseYx3ls3QzCeaYfkLdK52fanv2n+elZh2h9w8 // SIG // YVJ+99XCLAke2vEyGynwTdKQFDkIz+EcPD+j234abqR/ // SIG // zGt8BeXdBT1G62Wzhnq0r4K/zqTdSnlW7dgbHgdNfA6c // SIG // Nq+qpH0Kndc+EbT5Lq9am8OcfCCwB1k9EnA4PQOgdEmq // SIG // H/yhgheUMIIXkAYKKwYBBAGCNwMDATGCF4Awghd8Bgkq // SIG // hkiG9w0BBwKgghdtMIIXaQIBAzEPMA0GCWCGSAFlAwQC // SIG // AQUAMIIBUgYLKoZIhvcNAQkQAQSgggFBBIIBPTCCATkC // SIG // AQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg // SIG // 3vo9d5vg3IC4hOnt5BbJ1atIK6Vx7w36s15otddZKusC // SIG // BmeuC227ARgTMjAyNTAyMjAxNTI4MzYuOTUxWjAEgAIB // SIG // 9KCB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgT // SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc // SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMG // SIG // A1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9u // SIG // czEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAt // SIG // MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt // SIG // ZS1TdGFtcCBTZXJ2aWNloIIR6jCCByAwggUIoAMCAQIC // SIG // EzMAAAHwKnwdWTvmH60AAQAAAfAwDQYJKoZIhvcNAQEL // SIG // BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp // SIG // bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT // SIG // FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd // SIG // TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN // SIG // MjMxMjA2MTg0NTUxWhcNMjUwMzA1MTg0NTUxWjCByzEL // SIG // MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x // SIG // EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv // SIG // c29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z // SIG // b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMe // SIG // blNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUw // SIG // IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2 // SIG // aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC // SIG // AgEAtR4tU6M4dztHMxPMX0Z68ppSTQmhlqJpj8tHiDX/ // SIG // uBCa+63/LUs5YBPCJeGY+PE+X00zgIepyE0X9pSu/rqX // SIG // J2f8YNACqA2KQUDlqy/TmxUocpSB36/w0OD7EV/BkbSJ // SIG // 7ibDJMEoOt23weENlIpDBD/wrWR4vMVuV7QCqfeXLN5r // SIG // 1AjLyAAPNya/1MgAwAiV1VOJmmIrHM1M+ydddXg9Sqxv // SIG // ZkPiE4J0Uf19sUwzAs/oqPGWRRxsBGYPnN75j6fO5uDq // SIG // jilsoXKjFLqT73jv4EAvUb+LMyzRg2qHj3iuiFNCanBo // SIG // 16sW3BKEv7NYQoD3e1MemFnlq1F2sW2/iaLIDms1IFBr // SIG // NWqqZy489GCn1Kp/IuU25kdXahJUeEAPjmX3lYaU6J6z // SIG // OLBPzJSdSS6UdhcACB1HjH6LVzUIsrWH0QVDiRxXiWBH // SIG // 5WYFZNF8f+JGQXc4BUDzln1XdjaM15QtnqRrVI2pbgNq // SIG // LDr0B2cxjqCk71lD1/fTLLBjmztMSR3dA8oD/yQFafm0 // SIG // RiamtruwKi5tvrQE9usiOb3nHA5jWbIN7w4dR3KQiWvU // SIG // KUVvzA92vgBdsoFdbEZxlVFRt1Tg19wYRk/EzFPxolMt // SIG // 4ewpVzsZmfsHgriDswpEw2yuTpQEvKkj6YKflxpTixD2 // SIG // jN9TIH+mE6aQGnj9KKYcd+OaIbcCAwEAAaOCAUkwggFF // SIG // MB0GA1UdDgQWBBTQgrKnV0cvzJo2RlUwL8e2BVqbJTAf // SIG // BgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf // SIG // BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv // SIG // c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU // SIG // aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI // SIG // KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8v // SIG // d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p // SIG // Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEw // SIG // KDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM // SIG // MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkq // SIG // hkiG9w0BAQsFAAOCAgEAOCL0m56+IxD4KOVuMY9//tHQ // SIG // hsLM/Ot0BmdpgfWeEsLRhizL8H7EVLNzSJTw/y7FgMXV // SIG // WB5JQM8C08EOTPj0Xcvdgxn7chDhjB37rexqvC90VHL6 // SIG // 769AC/zTbXxKWwBJAhc7HnsbWObN4c49619sL6AWZtsr // SIG // YcHC3mZjIB0Apo2af9tHx1iYK4z2I7HukQybVE5b1LI6 // SIG // /vO/P7fr60BCKpZnmwnhIvlUFcXO8BdC7jE8P4AlnXKh // SIG // 6Ki+diaLcSs2PI2UkO3HDR4QuHhxhUaWinokkNBl7ZWx // SIG // iGz+JFtTtyc5So38ButjQkr35jNYjw/dF2IhZu//JoEd // SIG // egJqnnw7H4wQlsH96oXiDH4Gc1qnhM/JWhZjPA3ZF47j // SIG // gkBP9i9E8Ya41LXTJAE313WY2EZbHAQ8q/MxjJaaxQuy // SIG // 3Magl5YcYbXdgjPpqXE3PEQdg9xKK9FHaD9+kPa+F1gl // SIG // Vf9ip9AF7b1sbyH8jhZuWi5dHhM5IX7/15lJQXjghJUu // SIG // 43XXVqQZUIybhx1B4zlRl5ayU+1+IYnBdaNt8eVPo+6j // SIG // ygXHq8j9v9aMX5h3OrgV5VwSrpFf0AmQVQIgTGCYZ5LW // SIG // pFh6aPbiHkp2E+kMe8H9kWrmByBEfEi0Zm5TMzzrPiR0 // SIG // M674e8Urcd9dCzqftA2jl7PMY2b4aZ/lrmYo+UZmYoww // SIG // ggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAV // SIG // MA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET // SIG // MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk // SIG // bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 // SIG // aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0 // SIG // aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAx // SIG // ODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYT // SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH // SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y // SIG // cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l // SIG // LVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEF // SIG // AAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7V // SIG // gtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H // SIG // ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKD // SIG // RLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gm // SIG // U3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36M // SIG // EBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+y // SIG // OSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoF // SIG // VZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJi // SIG // ss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGa // SIG // RnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+Autuqfjbs // SIG // Nkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afo // SIG // mXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9 // SIG // ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk // SIG // i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y // SIG // 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV // SIG // 2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0C // SIG // AwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEw // SIG // IwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/ // SIG // LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnp // SIG // cjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8G // SIG // CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j // SIG // b20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYD // SIG // VR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAwe // SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB // SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9 // SIG // lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov // SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj // SIG // dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoG // SIG // CCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov // SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS // SIG // b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcN // SIG // AQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pc // SIG // FLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHk // SIG // wo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF // SIG // vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4 // SIG // U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2 // SIG // EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8Atq // SIG // gcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mj // SIG // dAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZM // SIG // cm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQE // SIG // cb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2f // SIG // pCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBM // SIG // drVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L // SIG // +DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJ // SIG // C4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU // SIG // 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/ // SIG // 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDTTCC // SIG // AjUCAQEwgfmhgdGkgc4wgc