diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts index d0cd5c884..4ea8cda1d 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts @@ -389,7 +389,6 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem const metadata = dsEntry?.metadata ?? this._getInfoFromUri(uri); const profileEncoding = dsEntry?.encoding ? null : dsEntry?.metadata.profile.profile?.encoding; try { - await AuthHandler.waitIfLocked(metadata.profile); await AuthHandler.lockProfile(metadata.profile); const resp = await ZoweExplorerApiRegister.getMvsApi(metadata.profile).getContents(metadata.dsName, { binary: dsEntry?.encoding?.kind === "binary", @@ -437,7 +436,7 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem } catch (error) { //Response will error if the file is not found //Callers of fetchDatasetAtUri() do not expect it to throw an error - await AuthUtils.lockProfileOnAuthError(error, metadata.profile); + await AuthUtils.handleProfileAuthOnError(error, metadata.profile); return null; } } diff --git a/packages/zowe-explorer/src/trees/job/JobFSProvider.ts b/packages/zowe-explorer/src/trees/job/JobFSProvider.ts index 8dbf564a1..0b0c47613 100644 --- a/packages/zowe-explorer/src/trees/job/JobFSProvider.ts +++ b/packages/zowe-explorer/src/trees/job/JobFSProvider.ts @@ -27,6 +27,7 @@ import { FsAbstractUtils, ZoweExplorerApiType, ZosEncoding, + AuthHandler, } from "@zowe/zowe-explorer-api"; import { IDownloadSpoolContentParms, IJob, IJobFile } from "@zowe/zos-jobs-for-zowe-sdk"; import { Profiles } from "../../configuration/Profiles"; @@ -207,6 +208,7 @@ export class JobFSProvider extends BaseProvider implements vscode.FileSystemProv const bufBuilder = new BufferBuilder(); const jesApi = ZoweExplorerApiRegister.getJesApi(spoolEntry.metadata.profile); + await AuthHandler.lockProfile(spoolEntry.metadata.profile); try { if (jesApi.downloadSingleSpool) { const spoolDownloadObject: IDownloadSpoolContentParms = { @@ -227,9 +229,10 @@ export class JobFSProvider extends BaseProvider implements vscode.FileSystemProv bufBuilder.write(await jesApi.getSpoolContentById(jobEntry.job.jobname, jobEntry.job.jobid, spoolEntry.spool.id)); } } catch (err) { - await AuthUtils.lockProfileOnAuthError(err, spoolEntry.metadata.profile); + await AuthUtils.handleProfileAuthOnError(err, spoolEntry.metadata.profile); throw err; } + AuthHandler.unlockProfile(spoolEntry.metadata.profile); this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); spoolEntry.data = bufBuilder.read() ?? new Uint8Array(); diff --git a/packages/zowe-explorer/src/trees/uss/UssFSProvider.ts b/packages/zowe-explorer/src/trees/uss/UssFSProvider.ts index 9367b2af8..795d8110a 100644 --- a/packages/zowe-explorer/src/trees/uss/UssFSProvider.ts +++ b/packages/zowe-explorer/src/trees/uss/UssFSProvider.ts @@ -280,7 +280,6 @@ export class UssFSProvider extends BaseProvider implements vscode.FileSystemProv try { await this.autoDetectEncoding(file as UssFile); const profileEncoding = file.encoding ? null : file.metadata.profile.profile?.encoding; - await AuthHandler.waitIfLocked(metadata.profile); await AuthHandler.lockProfile(metadata.profile); resp = await ZoweExplorerApiRegister.getUssApi(metadata.profile).getContents(filePath, { binary: file.encoding?.kind === "binary", @@ -294,7 +293,7 @@ export class UssFSProvider extends BaseProvider implements vscode.FileSystemProv if (err instanceof Error) { ZoweLogger.error(err.message); } - await AuthUtils.lockProfileOnAuthError(err, metadata.profile); + await AuthUtils.handleProfileAuthOnError(err, metadata.profile); return; } @@ -325,8 +324,6 @@ export class UssFSProvider extends BaseProvider implements vscode.FileSystemProv if (entry.encoding !== undefined) { return; } - - await AuthHandler.waitIfLocked(entry.metadata.profile); await AuthHandler.lockProfile(entry.metadata.profile); const ussApi = ZoweExplorerApiRegister.getUssApi(entry.metadata.profile); if (ussApi.getTag != null) { diff --git a/packages/zowe-explorer/src/utils/AuthUtils.ts b/packages/zowe-explorer/src/utils/AuthUtils.ts index 5d36daa89..4683978eb 100644 --- a/packages/zowe-explorer/src/utils/AuthUtils.ts +++ b/packages/zowe-explorer/src/utils/AuthUtils.ts @@ -24,18 +24,27 @@ interface ErrorContext { } export class AuthUtils { - public static async lockProfileOnAuthError(err: Error, profile: imperative.IProfileLoaded): Promise { + /** + * Locks the profile if an authentication error has occurred (prevents further requests in filesystem until unlocked). + * If the error is not an authentication error, the profile is unlocked for further use. + * + * @param err {Error} The error that occurred + * @param profile {imperative.IProfileLoaded} The profile used when the error occurred + */ + public static async handleProfileAuthOnError(err: Error, profile: imperative.IProfileLoaded): Promise { if ( err instanceof imperative.ImperativeError && profile != null && (Number(err.errorCode) === imperative.RestConstants.HTTP_STATUS_401 || err.message.includes("All configured authentication methods failed")) ) { + // In the case of an authentication error, find a more user-friendly error message if available. const errorCorrelation = ErrorCorrelator.getInstance().correlateError(ZoweExplorerApiType.All, err, { templateArgs: { profileName: profile.name, }, }); + // If the profile is already locked, prompt the user to re-authenticate. if (AuthHandler.isLocked(profile)) { await AuthHandler.promptForAuthentication(err, profile, { ssoLogin: Constants.PROFILES_CACHE.ssoLogin.bind(Constants.PROFILES_CACHE), @@ -44,6 +53,7 @@ export class AuthUtils { errorCorrelation, }); } else { + // Lock the profile and prompt the user to authenticate by providing login/credential prompt callbacks. await AuthHandler.lockProfile(profile, err, { ssoLogin: Constants.PROFILES_CACHE.ssoLogin.bind(Constants.PROFILES_CACHE), promptCredentials: Constants.PROFILES_CACHE.promptCredentials.bind(Constants.PROFILES_CACHE), @@ -51,7 +61,8 @@ export class AuthUtils { errorCorrelation, }); } - } else { + } else if (AuthHandler.isLocked(profile)) { + // Error doesn't mean criteria to continue holding the lock. Unlock the profile to allow further use AuthHandler.unlockProfile(profile); } } @@ -97,7 +108,7 @@ export class AuthUtils { (httpErrorCode === imperative.RestConstants.HTTP_STATUS_401 || imperativeError.message.includes("All configured authentication methods failed")) ) { - return AuthHandler.promptForAuthentication(imperativeError, profile, { + await AuthHandler.lockProfile(profile, imperativeError, { ssoLogin: Constants.PROFILES_CACHE.ssoLogin.bind(Constants.PROFILES_CACHE), promptCredentials: Constants.PROFILES_CACHE.promptCredentials.bind(Constants.PROFILES_CACHE), isUsingTokenAuth: await AuthUtils.isUsingTokenAuth(profile.name),