Skip to content

Commit

Permalink
[rush] New parameters for Azure Storage plugin (#4995)
Browse files Browse the repository at this point in the history
* [rush] New parameters for Azure Storage plugin

* Fixup schema

---------

Co-authored-by: David Michon <dmichon-msft@users.noreply.github.com>
  • Loading branch information
dmichon-msft and dmichon-msft authored Nov 8, 2024
1 parent d83665c commit 9193215
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Adds two new properties to the configuration for `rush-azure-storage-build-cache-plugin`: `loginFlow` selects the flow to use for interactive authentication to Entra ID, and `readRequiresAuthentication` specifies that a SAS token is required for read and therefore expired authentication is always fatal.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export abstract class AzureAuthenticationBase {
tryGetCachedCredentialAsync(options: ITryGetCachedCredentialOptionsLogWarning): Promise<ICredentialCacheEntry | undefined>;
// (undocumented)
updateCachedCredentialAsync(terminal: ITerminal, credential: string): Promise<void>;
updateCachedCredentialInteractiveAsync(terminal: ITerminal, onlyIfExistingCredentialExpiresAfter?: Date): Promise<void>;
updateCachedCredentialInteractiveAsync(terminal: ITerminal, onlyIfExistingCredentialExpiresBefore?: Date): Promise<void>;
}

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,13 @@ export abstract class AzureAuthenticationBase {
}

public constructor(options: IAzureAuthenticationBaseOptions) {
this._azureEnvironment = options.azureEnvironment || 'AzurePublicCloud';
const {
azureEnvironment = 'AzurePublicCloud',
loginFlow = process.env.CODESPACES === 'true' ? 'AdoCodespacesAuth' : 'InteractiveBrowser'
} = options;
this._azureEnvironment = azureEnvironment;
this._credentialUpdateCommandForLogging = options.credentialUpdateCommandForLogging;
this._loginFlow = options.loginFlow || 'DeviceCode';
this._loginFlow = loginFlow;
this._failoverOrder = options.loginFlowFailover || {
AdoCodespacesAuth: 'InteractiveBrowser',
InteractiveBrowser: 'DeviceCode',
Expand All @@ -174,25 +178,25 @@ export abstract class AzureAuthenticationBase {
* Launches an interactive flow to renew a cached credential.
*
* @param terminal - The terminal to log output to
* @param onlyIfExistingCredentialExpiresAfter - If specified, and a cached credential exists that is still valid
* after the date specified, no action will be taken.
* @param onlyIfExistingCredentialExpiresBefore - If specified, and a cached credential exists, action will only
* be taken if the cached credential expires before the specified date.
*/
public async updateCachedCredentialInteractiveAsync(
terminal: ITerminal,
onlyIfExistingCredentialExpiresAfter?: Date
onlyIfExistingCredentialExpiresBefore?: Date
): Promise<void> {
await CredentialCache.usingAsync(
{
supportEditing: true
},
async (credentialsCache: CredentialCache) => {
if (onlyIfExistingCredentialExpiresAfter) {
if (onlyIfExistingCredentialExpiresBefore) {
const existingCredentialExpiration: Date | undefined = credentialsCache.tryGetCacheEntry(
this._credentialCacheId
)?.expires;
if (
existingCredentialExpiration &&
existingCredentialExpiration > onlyIfExistingCredentialExpiresAfter
existingCredentialExpiration > onlyIfExistingCredentialExpiresBefore
) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {

export interface IAzureStorageBuildCacheProviderOptions extends IAzureStorageAuthenticationOptions {
blobPrefix?: string;
readRequiresAuthentication?: boolean;
}

interface IBlobError extends Error {
Expand All @@ -43,6 +44,7 @@ export class AzureStorageBuildCacheProvider
{
private readonly _blobPrefix: string | undefined;
private readonly _environmentCredential: string | undefined;
private readonly _readRequiresAuthentication: boolean;

public get isCacheWriteAllowed(): boolean {
return EnvironmentConfiguration.buildCacheWriteAllowed ?? this._isCacheWriteAllowedByConfiguration;
Expand All @@ -58,6 +60,7 @@ export class AzureStorageBuildCacheProvider

this._blobPrefix = options.blobPrefix;
this._environmentCredential = EnvironmentConfiguration.buildCacheCredential;
this._readRequiresAuthentication = !!options.readRequiresAuthentication;

if (!(this._azureEnvironment in AzureAuthorityHosts)) {
throw new Error(
Expand Down Expand Up @@ -208,8 +211,8 @@ export class AzureStorageBuildCacheProvider
if (sasString) {
const connectionString: string = this._getConnectionString(sasString);
blobServiceClient = BlobServiceClient.fromConnectionString(connectionString);
} else if (!this._isCacheWriteAllowedByConfiguration) {
// If cache write isn't allowed and we don't have a credential, assume the blob supports anonymous read
} else if (!this._readRequiresAuthentication && !this._isCacheWriteAllowedByConfiguration) {
// If we don't have a credential and read doesn't require authentication, we can still read from the cache.
blobServiceClient = new BlobServiceClient(this._storageAccountUrl);
} else {
throw new Error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface IAzureInteractiveAuthOptions {

/**
* Login flow to use for interactive authentication.
* @defaultValue 'deviceCode'
* @defaultValue 'AdoCodespacesAuth' if on GitHub Codespaces, 'InteractiveBrowser' otherwise
*/
readonly loginFlow?: LoginFlowType;

Expand Down Expand Up @@ -86,7 +86,7 @@ export default class RushAzureInteractieAuthPlugin implements IRushPlugin {
storageContainerName,
azureEnvironment = 'AzurePublicCloud',
minimumValidityInMinutes,
loginFlow = 'DeviceCode'
loginFlow = process.env.CODESPACES ? 'AdoCodespacesAuth' : 'InteractiveBrowser'
} = options;

const logger: ILogger = rushSession.getLogger(PLUGIN_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// See LICENSE in the project root for license information.

import type { IRushPlugin, RushSession, RushConfiguration } from '@rushstack/rush-sdk';
import type { AzureEnvironmentName } from './AzureAuthenticationBase';
import type { AzureEnvironmentName, LoginFlowType } from './AzureAuthenticationBase';

const PLUGIN_NAME: string = 'AzureStorageBuildCachePlugin';

Expand All @@ -25,6 +25,12 @@ interface IAzureBlobStorageConfigurationJson {
*/
azureEnvironment?: AzureEnvironmentName;

/**
* Login flow to use for interactive authentication.
* @defaultValue 'AdoCodespacesAuth' if on GitHub Codespaces, 'InteractiveBrowser' otherwise
*/
readonly loginFlow?: LoginFlowType;

/**
* An optional prefix for cache item blob names.
*/
Expand All @@ -34,6 +40,11 @@ interface IAzureBlobStorageConfigurationJson {
* If set to true, allow writing to the cache. Defaults to false.
*/
isCacheWriteAllowed?: boolean;

/**
* If set to true, reading the cache requires authentication. Defaults to false.
*/
readRequiresAuthentication?: boolean;
}

/**
Expand All @@ -55,7 +66,9 @@ export class RushAzureStorageBuildCachePlugin implements IRushPlugin {
storageContainerName: azureBlobStorageConfiguration.storageContainerName,
azureEnvironment: azureBlobStorageConfiguration.azureEnvironment,
blobPrefix: azureBlobStorageConfiguration.blobPrefix,
isCacheWriteAllowed: !!azureBlobStorageConfiguration.isCacheWriteAllowed
loginFlow: azureBlobStorageConfiguration.loginFlow,
isCacheWriteAllowed: !!azureBlobStorageConfiguration.isCacheWriteAllowed,
readRequiresAuthentication: !!azureBlobStorageConfiguration.readRequiresAuthentication
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
"enum": ["AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment"]
},

"loginFlow": {
"type": "string",
"description": "The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise.",
"enum": ["AdoCodespacesAuth", "InteractiveBrowser", "DeviceCode"]
},

"blobPrefix": {
"type": "string",
"description": "An optional prefix for cache item blob names."
Expand All @@ -33,6 +39,11 @@
"isCacheWriteAllowed": {
"type": "boolean",
"description": "If set to true, allow writing to the cache. Defaults to false."
},

"readRequiresAuthentication": {
"type": "boolean",
"description": "If set to true, reading the cache requires authentication. Defaults to false."
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
"enum": ["AzurePublicCloud", "AzureChina", "AzureGermany", "AzureGovernment"]
},

"loginFlow": {
"type": "string",
"description": "The Entra ID login flow to use. Defaults to 'AdoCodespacesAuth' on GitHub Codespaces, 'InteractiveBrowser' otherwise.",
"enum": ["AdoCodespacesAuth", "InteractiveBrowser", "DeviceCode"]
},

"minimumValidityInMinutes": {
"type": "number",
"description": "If specified and a credential exists that will be valid for at least this many minutes from the time of execution, no action will be taken."
Expand Down

0 comments on commit 9193215

Please # to comment.