-
Notifications
You must be signed in to change notification settings - Fork 12
feat: Regional Access Boundary Changes #891
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trust_boundary
Are you sure you want to change the base?
Changes from all commits
7d283f5
c6c1c7b
635bae2
0cfa454
f40a3e1
3427c0e
1bf531a
2856d2f
2b247d4
27fd517
a68acd6
eaf0790
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ import { | |
| SERVICE_ACCOUNT_LOOKUP_ENDPOINT, | ||
| WORKFORCE_LOOKUP_ENDPOINT, | ||
| WORKLOAD_LOOKUP_ENDPOINT, | ||
| } from './trustboundary'; | ||
| } from './regionalaccessboundary'; | ||
|
|
||
| /** | ||
| * The required token exchange grant_type: rfc8693#section-2.1 | ||
|
|
@@ -424,11 +424,12 @@ export abstract class BaseExternalAccountClient extends AuthClient { | |
| * The result has the form: | ||
| * { authorization: 'Bearer <access_token_value>' } | ||
| */ | ||
| async getRequestHeaders(): Promise<Headers> { | ||
| async getRequestHeaders(url?: string | URL): Promise<Headers> { | ||
| const accessTokenResponse = await this.getAccessToken(); | ||
| const headers = new Headers({ | ||
| authorization: `Bearer ${accessTokenResponse.token}`, | ||
| }); | ||
| this.applyRegionalAccessBoundary(headers, url); | ||
| return this.addSharedMetadataHeaders(headers); | ||
| } | ||
|
|
||
|
|
@@ -500,20 +501,22 @@ export abstract class BaseExternalAccountClient extends AuthClient { | |
| * returned response. | ||
| * @param opts The HTTP request options. | ||
| * @param reAuthRetried Whether the current attempt is a retry after a failed attempt due to an auth failure. | ||
| * @param retryWithoutRAB Whether the current attempt is a retry after a failed attempt due to a stale regional access boundary. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused parameter: Since we no longer have the reactive refresh in this PR, this parameter should be removed too. |
||
| * @return A promise that resolves with the successful response. | ||
| */ | ||
| protected async requestAsync<T>( | ||
| opts: GaxiosOptions, | ||
| reAuthRetried = false, | ||
| ): Promise<GaxiosResponse<T>> { | ||
| let response: GaxiosResponse; | ||
| const requestOpts = {...opts}; | ||
| try { | ||
| const requestHeaders = await this.getRequestHeaders(); | ||
| opts.headers = Gaxios.mergeHeaders(opts.headers); | ||
| const requestHeaders = await this.getRequestHeaders(opts.url); | ||
| requestOpts.headers = Gaxios.mergeHeaders(requestOpts.headers); | ||
|
|
||
| this.applyHeadersFromSource(opts.headers, requestHeaders); | ||
| this.applyHeadersFromSource(requestOpts.headers, requestHeaders); | ||
|
|
||
| response = await this.transporter.request<T>(opts); | ||
| response = await this.transporter.request<T>(requestOpts); | ||
| } catch (e) { | ||
| const res = (e as GaxiosError).response; | ||
| if (res) { | ||
|
|
@@ -624,7 +627,6 @@ export abstract class BaseExternalAccountClient extends AuthClient { | |
| Object.assign(this.credentials, this.cachedAccessToken); | ||
| delete (this.credentials as CredentialsWithResponse).res; | ||
|
|
||
| this.trustBoundary = await this.refreshTrustBoundary(this.credentials); | ||
| // Trigger tokens event to notify external listeners. | ||
| this.emit('tokens', { | ||
| refresh_token: null, | ||
|
|
@@ -718,42 +720,43 @@ export abstract class BaseExternalAccountClient extends AuthClient { | |
| return this.tokenUrl; | ||
| } | ||
|
|
||
| protected async getTrustBoundaryUrl(): Promise<string> { | ||
| public async getRegionalAccessBoundaryUrl(): Promise<string> { | ||
| if (this.serviceAccountImpersonationUrl) { | ||
| // When impersonating a service account, the trust boundary is determined | ||
| // When impersonating a service account, the regional access boundary is determined | ||
| // by the security policies of the target service account. | ||
| const email = this.getServiceAccountEmail(); | ||
| if (!email) { | ||
| throw new Error( | ||
| `TrustBoundary: A service account email is required for trust boundary lookups but could not be determined from the serviceAccountImpersonationUrl ${this.serviceAccountImpersonationUrl}.`, | ||
| `RegionalAccessBoundary: A service account email is required for regional access boundary lookups but could not be determined from the serviceAccountImpersonationUrl ${this.serviceAccountImpersonationUrl}.`, | ||
| ); | ||
| } | ||
| return SERVICE_ACCOUNT_LOOKUP_ENDPOINT.replace( | ||
| '{universe_domain}', | ||
| this.universeDomain, | ||
| ).replace('{service_account_email}', encodeURIComponent(email)); | ||
| '{service_account_email}', | ||
| encodeURIComponent(email), | ||
| ); | ||
| } | ||
|
|
||
| // Check if the audience corresponds to a workload identity pool. | ||
| const wfPoolId = getWorkforcePoolIdFromAudience(this.audience); | ||
| if (wfPoolId) { | ||
| return WORKFORCE_LOOKUP_ENDPOINT.replace( | ||
| '{universe_domain}', | ||
| this.universeDomain, | ||
| ).replace('{pool_id}', encodeURIComponent(wfPoolId)); | ||
| '{pool_id}', | ||
| encodeURIComponent(wfPoolId), | ||
| ); | ||
| } | ||
|
|
||
| // Check if the audience corresponds to a workforce identity pool. | ||
| const wlPoolId = getWorkloadPoolIdFromAudience(this.audience); | ||
| const projectNumber = this.getProjectNumber(this.audience); | ||
| if (wlPoolId && projectNumber) { | ||
| return WORKLOAD_LOOKUP_ENDPOINT.replace('{project_id}', projectNumber) | ||
| .replace('{pool_id}', wlPoolId) | ||
| .replace('{universe_domain}', this.universeDomain); | ||
| return WORKLOAD_LOOKUP_ENDPOINT.replace( | ||
| '{project_id}', | ||
| projectNumber, | ||
| ).replace('{pool_id}', wlPoolId); | ||
| } | ||
|
|
||
| throw new RangeError( | ||
| `TrustBoundary: Invalid audience provided: "${this.audience}" does not correspond to a workforce or workload pool.`, | ||
| `RegionalAccessBoundary: Invalid audience provided: "${this.audience}" does not correspond to a workforce or workload pool.`, | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ import { | |
| OAuth2Client, | ||
| OAuth2ClientOptions, | ||
| } from './oauth2client'; | ||
| import {SERVICE_ACCOUNT_LOOKUP_ENDPOINT} from './trustboundary'; | ||
| import {SERVICE_ACCOUNT_LOOKUP_ENDPOINT} from './regionalaccessboundary'; | ||
|
|
||
| export interface ComputeOptions extends OAuth2ClientOptions { | ||
| /** | ||
|
|
@@ -90,7 +90,6 @@ export class Compute extends OAuth2Client { | |
| tokens.expiry_date = new Date().getTime() + data.expires_in * 1000; | ||
| delete (tokens as CredentialRequest).expires_in; | ||
| } | ||
| this.trustBoundary = await this.refreshTrustBoundary(data); | ||
| this.emit('tokens', tokens); | ||
| return {tokens, res: null}; | ||
| } | ||
|
|
@@ -140,13 +139,13 @@ export class Compute extends OAuth2Client { | |
| } | ||
| } | ||
|
|
||
| protected async getTrustBoundaryUrl(): Promise<string> { | ||
| public async getRegionalAccessBoundaryUrl(): Promise<string> { | ||
| const email = await this.resolveServiceAccountEmail(); | ||
| const trustBoundaryUrl = SERVICE_ACCOUNT_LOOKUP_ENDPOINT.replace( | ||
| const regionalAccessBoundaryUrl = SERVICE_ACCOUNT_LOOKUP_ENDPOINT.replace( | ||
| '{universe_domain}', | ||
| this.universeDomain, | ||
|
Comment on lines
145
to
146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SERVICE_ACCOUNT_LOOKUP_ENDPOINT no longer has a placeholder for universedomain. |
||
| ).replace('{service_account_email}', encodeURIComponent(email)); | ||
| return trustBoundaryUrl; | ||
| return regionalAccessBoundaryUrl; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -165,7 +164,7 @@ export class Compute extends OAuth2Client { | |
| return await gcpMetadata.instance('service-accounts/default/email'); | ||
| } catch (e) { | ||
| throw new Error( | ||
| 'TrustBoundary: Failed to retrieve default service account email from metadata server.', | ||
| 'RegionalAccessBoundary: Failed to retrieve default service account email from metadata server.', | ||
| { | ||
| cause: e, | ||
| }, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.