UNPKG

ruchy-syntax-tools

Version:

Comprehensive syntax highlighting and language support for the Ruchy programming language

315 lines (281 loc) 12.1 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { AuthError, Authority, ClientAuthErrorCodes, Constants, HeaderNames, INetworkModule, Logger, NetworkRequestOptions, NetworkResponse, ResponseHandler, ServerAuthorizationTokenResponse, TimeUtils, createClientAuthError, AuthenticationResult, UrlString, } from "@azure/msal-common/node"; import { ManagedIdentityId } from "../../config/ManagedIdentityId.js"; import { ManagedIdentityRequestParameters } from "../../config/ManagedIdentityRequestParameters.js"; import { CryptoProvider } from "../../crypto/CryptoProvider.js"; import { ManagedIdentityRequest } from "../../request/ManagedIdentityRequest.js"; import { HttpMethod, ManagedIdentityIdType, ManagedIdentityQueryParameters, } from "../../utils/Constants.js"; import { ManagedIdentityTokenResponse } from "../../response/ManagedIdentityTokenResponse.js"; import { NodeStorage } from "../../cache/NodeStorage.js"; import { ManagedIdentityErrorCodes, createManagedIdentityError, } from "../../error/ManagedIdentityError.js"; import { isIso8601 } from "../../utils/TimeUtils.js"; import { HttpClientWithRetries } from "../../network/HttpClientWithRetries.js"; /** * Managed Identity User Assigned Id Query Parameter Names */ export const ManagedIdentityUserAssignedIdQueryParameterNames = { MANAGED_IDENTITY_CLIENT_ID_2017: "clientid", // 2017-09-01 API version MANAGED_IDENTITY_CLIENT_ID: "client_id", // 2019+ API versions MANAGED_IDENTITY_OBJECT_ID: "object_id", MANAGED_IDENTITY_RESOURCE_ID_IMDS: "msi_res_id", MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS: "mi_res_id", } as const; export type ManagedIdentityUserAssignedIdQueryParameterNames = (typeof ManagedIdentityUserAssignedIdQueryParameterNames)[keyof typeof ManagedIdentityUserAssignedIdQueryParameterNames]; export abstract class BaseManagedIdentitySource { protected logger: Logger; private nodeStorage: NodeStorage; private networkClient: INetworkModule; private cryptoProvider: CryptoProvider; private disableInternalRetries: boolean; constructor( logger: Logger, nodeStorage: NodeStorage, networkClient: INetworkModule, cryptoProvider: CryptoProvider, disableInternalRetries: boolean ) { this.logger = logger; this.nodeStorage = nodeStorage; this.networkClient = networkClient; this.cryptoProvider = cryptoProvider; this.disableInternalRetries = disableInternalRetries; } abstract createRequest( request: string, managedIdentityId: ManagedIdentityId ): ManagedIdentityRequestParameters; public async getServerTokenResponseAsync( response: NetworkResponse<ManagedIdentityTokenResponse>, // eslint-disable-next-line @typescript-eslint/no-unused-vars _networkClient: INetworkModule, // eslint-disable-next-line @typescript-eslint/no-unused-vars _networkRequest: ManagedIdentityRequestParameters, // eslint-disable-next-line @typescript-eslint/no-unused-vars _networkRequestOptions: NetworkRequestOptions ): Promise<ServerAuthorizationTokenResponse> { return this.getServerTokenResponse(response); } public getServerTokenResponse( response: NetworkResponse<ManagedIdentityTokenResponse> ): ServerAuthorizationTokenResponse { let refreshIn, expiresIn: number | undefined; if (response.body.expires_on) { // if the expires_on field in the response body is a string and in ISO 8601 format, convert it to a Unix timestamp (seconds since epoch) if (isIso8601(response.body.expires_on)) { response.body.expires_on = new Date(response.body.expires_on).getTime() / 1000; } expiresIn = response.body.expires_on - TimeUtils.nowSeconds(); // compute refresh_in as 1/2 of expires_in, but only if expires_in > 2h if (expiresIn > 2 * 3600) { refreshIn = expiresIn / 2; } } const serverTokenResponse: ServerAuthorizationTokenResponse = { status: response.status, // success access_token: response.body.access_token, expires_in: expiresIn, scope: response.body.resource, token_type: response.body.token_type, refresh_in: refreshIn, // error correlation_id: response.body.correlation_id || response.body.correlationId, error: typeof response.body.error === "string" ? response.body.error : response.body.error?.code, error_description: response.body.message || (typeof response.body.error === "string" ? response.body.error_description : response.body.error?.message), error_codes: response.body.error_codes, timestamp: response.body.timestamp, trace_id: response.body.trace_id, }; return serverTokenResponse; } public async acquireTokenWithManagedIdentity( managedIdentityRequest: ManagedIdentityRequest, managedIdentityId: ManagedIdentityId, fakeAuthority: Authority, refreshAccessToken?: boolean ): Promise<AuthenticationResult> { const networkRequest: ManagedIdentityRequestParameters = this.createRequest( managedIdentityRequest.resource, managedIdentityId ); if (managedIdentityRequest.revokedTokenSha256Hash) { this.logger.info( `[Managed Identity] The following claims are present in the request: ${managedIdentityRequest.claims}` ); networkRequest.queryParameters[ ManagedIdentityQueryParameters.SHA256_TOKEN_TO_REFRESH ] = managedIdentityRequest.revokedTokenSha256Hash; } if (managedIdentityRequest.clientCapabilities?.length) { const clientCapabilities: string = managedIdentityRequest.clientCapabilities.toString(); this.logger.info( `[Managed Identity] The following client capabilities are present in the request: ${clientCapabilities}` ); networkRequest.queryParameters[ ManagedIdentityQueryParameters.XMS_CC ] = clientCapabilities; } const headers: Record<string, string> = networkRequest.headers; headers[HeaderNames.CONTENT_TYPE] = Constants.URL_FORM_CONTENT_TYPE; const networkRequestOptions: NetworkRequestOptions = { headers }; if (Object.keys(networkRequest.bodyParameters).length) { networkRequestOptions.body = networkRequest.computeParametersBodyString(); } /** * Initializes the network client helper based on the retry policy configuration. * If internal retries are disabled, it uses the provided network client directly. * Otherwise, it wraps the network client with an HTTP client that supports retries. */ const networkClientHelper: INetworkModule = this.disableInternalRetries ? this.networkClient : new HttpClientWithRetries( this.networkClient, networkRequest.retryPolicy, this.logger ); const reqTimestamp = TimeUtils.nowSeconds(); let response: NetworkResponse<ManagedIdentityTokenResponse>; try { // Sources that send POST requests: Cloud Shell if (networkRequest.httpMethod === HttpMethod.POST) { response = await networkClientHelper.sendPostRequestAsync<ManagedIdentityTokenResponse>( networkRequest.computeUri(), networkRequestOptions ); // Sources that send GET requests: App Service, Azure Arc, IMDS, Service Fabric } else { response = await networkClientHelper.sendGetRequestAsync<ManagedIdentityTokenResponse>( networkRequest.computeUri(), networkRequestOptions ); } } catch (error) { if (error instanceof AuthError) { throw error; } else { throw createClientAuthError(ClientAuthErrorCodes.networkError); } } const responseHandler = new ResponseHandler( managedIdentityId.id, this.nodeStorage, this.cryptoProvider, this.logger, null, null ); const serverTokenResponse: ServerAuthorizationTokenResponse = await this.getServerTokenResponseAsync( response, networkClientHelper, networkRequest, networkRequestOptions ); responseHandler.validateTokenResponse( serverTokenResponse, refreshAccessToken ); // caches the token return responseHandler.handleServerTokenResponse( serverTokenResponse, fakeAuthority, reqTimestamp, managedIdentityRequest ); } public getManagedIdentityUserAssignedIdQueryParameterKey( managedIdentityIdType: ManagedIdentityIdType, isImds?: boolean, usesApi2017?: boolean ): string { switch (managedIdentityIdType) { case ManagedIdentityIdType.USER_ASSIGNED_CLIENT_ID: this.logger.info( `[Managed Identity] [API version ${ usesApi2017 ? "2017+" : "2019+" }] Adding user assigned client id to the request.` ); // The Machine Learning source uses the 2017-09-01 API version, which uses "clientid" instead of "client_id" return usesApi2017 ? ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017 : ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID; case ManagedIdentityIdType.USER_ASSIGNED_RESOURCE_ID: this.logger.info( "[Managed Identity] Adding user assigned resource id to the request." ); return isImds ? ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_IMDS : ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS; case ManagedIdentityIdType.USER_ASSIGNED_OBJECT_ID: this.logger.info( "[Managed Identity] Adding user assigned object id to the request." ); return ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_OBJECT_ID; default: throw createManagedIdentityError( ManagedIdentityErrorCodes.invalidManagedIdentityIdType ); } } public static getValidatedEnvVariableUrlString = ( envVariableStringName: string, envVariable: string, sourceName: string, logger: Logger ): string => { try { return new UrlString(envVariable).urlString; } catch (error) { logger.info( `[Managed Identity] ${sourceName} managed identity is unavailable because the '${envVariableStringName}' environment variable is malformed.` ); throw createManagedIdentityError( ManagedIdentityErrorCodes .MsiEnvironmentVariableUrlMalformedErrorCodes[ envVariableStringName ] ); } }; }