mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -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 {
|
protected toBase64UrlEncoding(base64string: string): string {
|
||||||
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteAllCacheMsal(): Promise<void> {
|
public async deleteAllCacheMsal(): Promise<void> {
|
||||||
this.clientApplication.clearCache();
|
this.clientApplication.clearCache();
|
||||||
await this.msalCacheProvider.clearLocalCache();
|
|
||||||
|
// unlink both cache files
|
||||||
|
await this.msalCacheProvider.unlinkMsalCache();
|
||||||
|
await this.msalCacheProvider.unlinkLocalCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteAllCacheAdal(): Promise<void> {
|
public async deleteAllCacheAdal(): Promise<void> {
|
||||||
const results = await this.tokenCache.findCredentials('');
|
const results = await this.tokenCache.findCredentials('');
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,9 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
|||||||
try {
|
try {
|
||||||
// Fetch cached token from local cache if token is available and valid.
|
// Fetch cached token from local cache if token is available and valid.
|
||||||
let accessToken = await this.msalCacheProvider.getTokenFromLocalCache(account.key.accountId, tenantId, resource);
|
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;
|
return accessToken;
|
||||||
} // else fallback to fetching a new token.
|
} // else fallback to fetching a new token.
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class MsalCachePluginProvider {
|
|||||||
public getCachePlugin(): ICachePlugin {
|
public getCachePlugin(): ICachePlugin {
|
||||||
const beforeCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
|
const beforeCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const decryptedData = await this.readCache(this._msalCacheConfiguration);
|
const decryptedData = await this.readCache(this._msalCacheConfiguration, this._localCacheConfiguration);
|
||||||
cacheContext.tokenCache.deserialize(decryptedData);
|
cacheContext.tokenCache.deserialize(decryptedData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle deserialization error in cache file in case file gets corrupted.
|
// Handle deserialization error in cache file in case file gets corrupted.
|
||||||
@@ -101,7 +101,7 @@ export class MsalCachePluginProvider {
|
|||||||
* @returns Access Token.
|
* @returns Access Token.
|
||||||
*/
|
*/
|
||||||
public async getTokenFromLocalCache(accountId: string, tenantId: string, resource: azdata.AzureResource): Promise<Token | undefined> {
|
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 => (
|
let token = cache?.tokens?.find(token => (
|
||||||
token.key === accountId &&
|
token.key === accountId &&
|
||||||
token.tenantId === tenantId &&
|
token.tenantId === tenantId &&
|
||||||
@@ -118,7 +118,7 @@ export class MsalCachePluginProvider {
|
|||||||
let updateCount = 0;
|
let updateCount = 0;
|
||||||
let indexToUpdate = -1;
|
let indexToUpdate = -1;
|
||||||
let cache: LocalAccountCache;
|
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) {
|
if (cache?.tokens) {
|
||||||
cache.tokens.forEach((t, i) => {
|
cache.tokens.forEach((t, i) => {
|
||||||
if (t.key === token.key && t.tenantId === token.tenantId && t.resource === token.resource
|
if (t.key === token.key && t.tenantId === token.tenantId && t.resource === token.resource
|
||||||
@@ -157,7 +157,7 @@ export class MsalCachePluginProvider {
|
|||||||
* @param accountId Account ID
|
* @param accountId Account ID
|
||||||
*/
|
*/
|
||||||
public async clearAccountFromLocalCache(accountId: string): Promise<void> {
|
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[] = [];
|
let tokenIndices: number[] = [];
|
||||||
if (cache?.tokens) {
|
if (cache?.tokens) {
|
||||||
cache.tokens.forEach((t, i) => {
|
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> {
|
public async unlinkMsalCache(): Promise<void> {
|
||||||
await this.writeCache(JSON.stringify({ tokens: [] }), this._localCacheConfiguration);
|
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
|
//#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 {
|
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);
|
const decryptedData = await this._fileEncryptionHelper.fileOpener(cache!, true);
|
||||||
return decryptedData;
|
return decryptedData;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 'ENOENT') {
|
if (e.code === 'ENOENT') {
|
||||||
// File doesn't exist, log and continue
|
// 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 {
|
else {
|
||||||
Logger.error(`MsalCachePlugin: Failed to read from cache file: ${e}`);
|
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}`);
|
Logger.verbose(`MsalCachePlugin: Error occurred when trying to read cache file ${currentConfig.name}, file will be deleted: ${e.message}`);
|
||||||
await fsPromises.unlink(config.cacheFilePath);
|
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.
|
return '{}'; // Return empty json string if cache not read.
|
||||||
} finally {
|
} finally {
|
||||||
lockFile.unlockSync(config.lockFilePath);
|
lockFile.unlockSync(currentConfig.lockFilePath);
|
||||||
config.lockTaken = false;
|
currentConfig.lockTaken = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user