mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Delete both cache files on decryption failure (#23291)
This commit is contained in:
@@ -870,10 +870,15 @@ export abstract class AzureAuth implements vscode.Disposable {
|
||||
protected toBase64UrlEncoding(base64string: string): string {
|
||||
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
||||
}
|
||||
|
||||
public async deleteAllCacheMsal(): Promise<void> {
|
||||
this.clientApplication.clearCache();
|
||||
await this.msalCacheProvider.clearLocalCache();
|
||||
|
||||
// unlink both cache files
|
||||
await this.msalCacheProvider.unlinkMsalCache();
|
||||
await this.msalCacheProvider.unlinkLocalCache();
|
||||
}
|
||||
|
||||
public async deleteAllCacheAdal(): Promise<void> {
|
||||
const results = await this.tokenCache.findCredentials('');
|
||||
|
||||
|
||||
@@ -152,7 +152,9 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
try {
|
||||
// Fetch cached token from local cache if token is available and valid.
|
||||
let accessToken = await this.msalCacheProvider.getTokenFromLocalCache(account.key.accountId, tenantId, resource);
|
||||
if (this.isValidToken(accessToken)) {
|
||||
if (this.isValidToken(accessToken) &&
|
||||
// Ensure MSAL Cache contains user account
|
||||
(await this.clientApplication.getAllAccounts()).find((accountInfo) => accountInfo.homeAccountId === account.key.accountId)) {
|
||||
return accessToken;
|
||||
} // else fallback to fetching a new token.
|
||||
} catch (e) {
|
||||
|
||||
@@ -65,7 +65,7 @@ export class MsalCachePluginProvider {
|
||||
public getCachePlugin(): ICachePlugin {
|
||||
const beforeCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
|
||||
try {
|
||||
const decryptedData = await this.readCache(this._msalCacheConfiguration);
|
||||
const decryptedData = await this.readCache(this._msalCacheConfiguration, this._localCacheConfiguration);
|
||||
cacheContext.tokenCache.deserialize(decryptedData);
|
||||
} catch (e) {
|
||||
// Handle deserialization error in cache file in case file gets corrupted.
|
||||
@@ -101,7 +101,7 @@ export class MsalCachePluginProvider {
|
||||
* @returns Access Token.
|
||||
*/
|
||||
public async getTokenFromLocalCache(accountId: string, tenantId: string, resource: azdata.AzureResource): Promise<Token | undefined> {
|
||||
let cache = JSON.parse(await this.readCache(this._localCacheConfiguration)) as LocalAccountCache;
|
||||
let cache = JSON.parse(await this.readCache(this._localCacheConfiguration, this._msalCacheConfiguration)) as LocalAccountCache;
|
||||
let token = cache?.tokens?.find(token => (
|
||||
token.key === accountId &&
|
||||
token.tenantId === tenantId &&
|
||||
@@ -118,7 +118,7 @@ export class MsalCachePluginProvider {
|
||||
let updateCount = 0;
|
||||
let indexToUpdate = -1;
|
||||
let cache: LocalAccountCache;
|
||||
cache = JSON.parse(await this.readCache(this._localCacheConfiguration)) as LocalAccountCache;
|
||||
cache = JSON.parse(await this.readCache(this._localCacheConfiguration, this._msalCacheConfiguration)) as LocalAccountCache;
|
||||
if (cache?.tokens) {
|
||||
cache.tokens.forEach((t, i) => {
|
||||
if (t.key === token.key && t.tenantId === token.tenantId && t.resource === token.resource
|
||||
@@ -157,7 +157,7 @@ export class MsalCachePluginProvider {
|
||||
* @param accountId Account ID
|
||||
*/
|
||||
public async clearAccountFromLocalCache(accountId: string): Promise<void> {
|
||||
let cache = JSON.parse(await this.readCache(this._localCacheConfiguration)) as LocalAccountCache;
|
||||
let cache = JSON.parse(await this.readCache(this._localCacheConfiguration, this._msalCacheConfiguration)) as LocalAccountCache;
|
||||
let tokenIndices: number[] = [];
|
||||
if (cache?.tokens) {
|
||||
cache.tokens.forEach((t, i) => {
|
||||
@@ -173,10 +173,17 @@ export class MsalCachePluginProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears local access token cache.
|
||||
* Deletes Msal access token cache file
|
||||
*/
|
||||
public async clearLocalCache(): Promise<void> {
|
||||
await this.writeCache(JSON.stringify({ tokens: [] }), this._localCacheConfiguration);
|
||||
public async unlinkMsalCache(): Promise<void> {
|
||||
await fsPromises.unlink(this._msalCacheConfiguration.cacheFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes local access token cache file.
|
||||
*/
|
||||
public async unlinkLocalCache(): Promise<void> {
|
||||
await fsPromises.unlink(this._localCacheConfiguration.cacheFilePath);
|
||||
}
|
||||
|
||||
//#region Private helper methods
|
||||
@@ -194,26 +201,42 @@ export class MsalCachePluginProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async readCache(config: CacheConfiguration): Promise<string> {
|
||||
config.lockTaken = await this.waitAndLock(config.lockFilePath, config.lockTaken);
|
||||
/**
|
||||
* Reads from an encrypted cache file based on currentConfig provided.
|
||||
* @param currentConfig Currently used cache configuration.
|
||||
* @param alternateConfig Alternate cache configuration for resetting needs.
|
||||
* @returns Decrypted data.
|
||||
*/
|
||||
private async readCache(currentConfig: CacheConfiguration, alternateConfig: CacheConfiguration): Promise<string> {
|
||||
currentConfig.lockTaken = await this.waitAndLock(currentConfig.lockFilePath, currentConfig.lockTaken);
|
||||
try {
|
||||
const cache = await fsPromises.readFile(config.cacheFilePath, { encoding: 'utf8' });
|
||||
const cache = await fsPromises.readFile(currentConfig.cacheFilePath, { encoding: 'utf8' });
|
||||
const decryptedData = await this._fileEncryptionHelper.fileOpener(cache!, true);
|
||||
return decryptedData;
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
// File doesn't exist, log and continue
|
||||
Logger.verbose(`MsalCachePlugin: Cache file for '${config.name}' cache not found on disk: ${e.code}`);
|
||||
Logger.verbose(`MsalCachePlugin: Cache file for '${currentConfig.name}' cache not found on disk: ${e.code}`);
|
||||
}
|
||||
else {
|
||||
Logger.error(`MsalCachePlugin: Failed to read from cache file: ${e}`);
|
||||
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file, file will be deleted: ${e.message}`);
|
||||
await fsPromises.unlink(config.cacheFilePath);
|
||||
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file ${currentConfig.name}, file will be deleted: ${e.message}`);
|
||||
await fsPromises.unlink(currentConfig.cacheFilePath);
|
||||
|
||||
// Ensure both configurations are not same.
|
||||
if (currentConfig.name !== alternateConfig.name) {
|
||||
// Delete alternate cache file as well.
|
||||
alternateConfig.lockTaken = await this.waitAndLock(alternateConfig.lockFilePath, alternateConfig.lockTaken);
|
||||
await fsPromises.unlink(alternateConfig.cacheFilePath);
|
||||
lockFile.unlockSync(alternateConfig.lockFilePath);
|
||||
alternateConfig.lockTaken = false;
|
||||
Logger.verbose(`MsalCachePlugin: Cache file for ${alternateConfig.name} cache also deleted.`);
|
||||
}
|
||||
}
|
||||
return '{}'; // Return empty json string if cache not read.
|
||||
} finally {
|
||||
lockFile.unlockSync(config.lockFilePath);
|
||||
config.lockTaken = false;
|
||||
lockFile.unlockSync(currentConfig.lockFilePath);
|
||||
currentConfig.lockTaken = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user