authui-container/server/api/iap-settings-handler.ts (168 lines of code) (raw):

/* * Copyright 2020 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ import {AuthenticatedRequestHandler} from './authenticated-request-handler'; import {AccessTokenManager} from './token-manager'; import {ApplicationData} from './metadata-server'; /** List compute backend service IDs endpoint. */ const LIST_COMPUTE_BACKEND_SERVICE_IDS_URL = 'https://compute.googleapis.com/compute/v1/projects/{projectId}/global/backendServices'; /** Get IAP settings endpoint. */ const GET_IAP_SETTINGS_URL = 'https://iap.googleapis.com/v1/projects/{projectNumber}/iap_web/{id}:iapSettings'; /** Default error message to show when compute backend service IDs cannot be listed. */ export const DEFAULT_ERROR_COMPUTE_BACKEND_SERIVCE_IDS_LIST = 'Unable to list compute backend service IDs.'; /** Default error message to show when IAP settings for a specified resource cannot be retrieved. */ export const DEFAULT_ERROR_IAP_SETTINGS = 'Unable to get IAP settings for requested resource.'; /** Network request timeout duration. */ const TIMEOUT_DURATION = 10000; // IAP related interfaces. // https://cloud.google.com/iap/docs/reference/rest/v1/IapSettings interface GcipSettings { tenantIds?: string[]; loginPageUri?: string; } interface CorsSettings { allowHttpOptions?: boolean; } interface OAuthSettings { loginHint?: string; clientId?: string; } interface AccessSettings { gcipSettings?: GcipSettings; corsSettings?: CorsSettings; oauthSettings?: OAuthSettings; } interface ApplicationSettings { csmSettings?: { rctokenAud?: string; }; } export interface IapSettings { name: string; accessSettings?: AccessSettings; applicationSettings?: ApplicationSettings; } // Compute backend service IDs interfaces. // https://cloud.google.com/compute/docs/reference/rest/v1/backendServices/list export interface BackendServicesList { id?: string; items?: { id?: string; creationTimestamp?: string; name?: string; description?: string; selfLink?: string; backends?: { group?: string; balancingMode?: string; maxUtilization?: number; capacityScaler?: number; }[]; healthChecks?: string[]; timeoutSec?: number; port?: number; protocol?: string; fingerprint?: string; portName?: string; enableCDN?: boolean; sessionAffinity?: string; affinityCookieTtlSec?: number; loadBalancingScheme?: string; connectionDraining?: { drainingTimeoutSec?: number; }; iap?: { enabled?: boolean; oauth2ClientId?: string; oauth2ClientSecretSha256?: string; }; kind?: string; }[]; selfLink?: string; kind?: string; } /** * Utility used to make IAP API calls. This is currently used to list the IAP settings * of all enabled IAP settings. */ export class IapSettingsHandler { private listComputeBackendServiceIdsHandler: AuthenticatedRequestHandler; private getIapSettingsHandler: AuthenticatedRequestHandler; /** * Instantiates an IAP settings handler. * @param app The application data. * @param accessTokenManager The access token manager. */ constructor( private readonly app: ApplicationData, private readonly accessTokenManager: AccessTokenManager) { this.listComputeBackendServiceIdsHandler = new AuthenticatedRequestHandler({ method: 'GET', url: LIST_COMPUTE_BACKEND_SERVICE_IDS_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); this.getIapSettingsHandler = new AuthenticatedRequestHandler({ method: 'GET', url: GET_IAP_SETTINGS_URL, timeout: TIMEOUT_DURATION, }, this.accessTokenManager, app.log.bind(app)); } /** * Retrieves the IAP settings associated with the provided resource ID. * @param id The IAP resource ID whose IAP settings are to be retrieved. * id can be: * GAE: appengine-APP_ID eg: appengine-ciap-prod-agent * GCE: compute/services/BACKEND_SERVICE_ID or BACKEND_SERVICE_NAME * @return A promise that resolves with the resource's IAP settings, if found. */ getIapSettings(id: string): Promise<IapSettings> { return this.app.getProjectNumber() .then((projectNumber) => { return this.getIapSettingsHandler.send({ urlParams: { projectNumber, id, }, }, DEFAULT_ERROR_IAP_SETTINGS); }) .then((httpResponse) => { const iapSettings: IapSettings = typeof httpResponse.body === 'object' ? httpResponse.body : JSON.parse(httpResponse.body); return iapSettings; }); } /** * @return A promise that resolves with the list of all IAP settings for all enabled * IAP resources. */ listIapSettings(): Promise<IapSettings[]> { const configs: any[] = []; const getSettings = (ids: string[], i: number, settings: IapSettings[]): Promise<IapSettings> => { if (i < ids.length) { return this.getIapSettings(ids[i]) .then((config) => { settings.push(config); }) .catch((error) => { // Ignore. }) .then(() => { return getSettings(ids, i + 1, settings); }); } }; const iapResourceIds: string[] = []; return this.app.getProjectId() .then((projectId) => { iapResourceIds.push(`appengine-${projectId}`); return this.getComputeBackendServiceIds(); }) .then((backendServiceIds) => { backendServiceIds.forEach((backendServiceId) => { iapResourceIds.push(`compute/services/${backendServiceId}`); }); return iapResourceIds; }) .then((ids) => { return getSettings(ids, 0, configs); }) .then(() => { return configs; }); } /** * @return A promise that resolves with the list of Compute backend service IDs. */ private getComputeBackendServiceIds(): Promise<string[]> { return this.app.getProjectId() .then((projectId) => { return this.listComputeBackendServiceIdsHandler.send({ urlParams: { projectId, }, }, DEFAULT_ERROR_COMPUTE_BACKEND_SERIVCE_IDS_LIST); }) .then((httpResponse) => { const backendServiceIds: string[] = []; const computeBackendServiceIdsList: BackendServicesList = typeof httpResponse.body === 'object' ? httpResponse.body : JSON.parse(httpResponse.body); if (computeBackendServiceIdsList.items) { computeBackendServiceIdsList.items.forEach((item) => { backendServiceIds.push(item.id); }); } return backendServiceIds; }) .catch((error) => { // GCE API not enabled. GCE usage is not required. Ignore error. if (error.message.indexOf('Access Not Configured.') !== -1) { return []; } throw error; }); } }