diff --git a/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts b/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts index 4272a59edd..25b4f8458b 100644 --- a/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts +++ b/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts @@ -207,7 +207,7 @@ export class ProfilesCache { this.allTypes.push(type); } // check for proper merging of apiml tokens - this.checkMergingConfigAllProfiles(); + await this.checkMergingConfigAllProfiles(); this.profilesForValidation = []; } catch (error) { this.log.error(error as string); @@ -295,7 +295,7 @@ export class ProfilesCache { for (const prof of profilesForType) { const profAttr = this.getMergedAttrs(mProfileInfo, prof); let profile = this.getProfileLoaded(prof.profName, prof.profType, profAttr); - profile = this.checkMergingConfigSingleProfile(profile); + profile = await this.checkMergingConfigSingleProfile(profile); profByType.push(profile); } } @@ -388,9 +388,14 @@ export class ProfilesCache { } // This will retrieve the base profile from imperative - public async fetchBaseProfile(): Promise { + public async fetchBaseProfile(profileName?: string): Promise { const mProfileInfo = await this.getProfileInfo(); - const baseProfileAttrs = mProfileInfo.getDefaultProfile("base"); + let baseProfileAttrs: zowe.imperative.IProfAttrs; + if (profileName == null) { + baseProfileAttrs = mProfileInfo.getDefaultProfile("base"); + } else { + baseProfileAttrs = mProfileInfo.getAllProfiles("base").find((p) => p.profName === profileName); + } if (baseProfileAttrs == null) { return undefined; } @@ -464,14 +469,14 @@ export class ProfilesCache { } // used by refresh to check correct merging of allProfiles - protected checkMergingConfigAllProfiles(): void { - const baseProfile = this.defaultProfileByType.get("base"); + protected async checkMergingConfigAllProfiles(): Promise { const allProfiles: zowe.imperative.IProfileLoaded[] = []; - this.allTypes.forEach((type) => { + for (const type of this.allTypes) { try { const allProfilesByType: zowe.imperative.IProfileLoaded[] = []; const profByType = this.profilesByType.get(type); - profByType.forEach((profile) => { + for (const profile of profByType) { + const baseProfile = await this.findApimlProfile(profile); if (this.shouldRemoveTokenFromProfile(profile, baseProfile)) { profile.profile.tokenType = undefined; profile.profile.tokenValue = undefined; @@ -482,20 +487,20 @@ export class ProfilesCache { } allProfiles.push(profile); allProfilesByType.push(profile); - }); + } this.profilesByType.set(type, allProfilesByType); } catch (error) { // do nothing, skip if profile type is not included in config file this.log.debug(error as string); } - }); + } this.allProfiles = []; this.allProfiles.push(...allProfiles); } // check correct merging of a single profile - protected checkMergingConfigSingleProfile(profile: zowe.imperative.IProfileLoaded): zowe.imperative.IProfileLoaded { - const baseProfile = this.defaultProfileByType.get("base"); + protected async checkMergingConfigSingleProfile(profile: zowe.imperative.IProfileLoaded): Promise { + const baseProfile = await this.findApimlProfile(profile); if (this.shouldRemoveTokenFromProfile(profile, baseProfile)) { profile.profile.tokenType = undefined; profile.profile.tokenValue = undefined; @@ -510,10 +515,38 @@ export class ProfilesCache { for (const arg of mergedArgs.knownArgs) { profile[arg.argName] = arg.argValue; } + // if (profile.apimlProfile != null) { + // const apimlProfAttrs = mProfileInfo.getAllProfiles("base").find((p) => p.profName === profile.apimlProfile); + // if (apimlProfAttrs != null) { + // const apimlMergedArgs = mProfileInfo.mergeArgsForProfile(apimlProfAttrs, { getSecureVals: true }); + // for (const arg of apimlMergedArgs.knownArgs) { + // profile[arg.argName] = arg.argValue; + // } + // } + // } } return profile; } + /** + * Look for a base profile where APIML token can be stored for SSO login. + * @param profile Imperative loaded profile object + * @returns Base profile to store APIML token + */ + public async findApimlProfile(profile: zowe.imperative.IProfileLoaded): Promise { + // if (profile.profile?.apimlProfile != null) { + // return profile.profile.apimlProfile as string; + // } + if ((await this.getProfileInfo()).usingTeamConfig && profile.name.includes(".")) { + for (const baseProfile of await this.fetchAllProfilesByType("base")) { + if (profile.name.startsWith(baseProfile.name + ".")) { + return baseProfile; + } + } + } + return this.fetchBaseProfile(); + } + // create an array that includes registered types from apiRegister.registeredApiTypes() // and allExternalTypes private getAllProfileTypes(registeredTypes: string[]): string[] { @@ -529,7 +562,8 @@ export class ProfilesCache { profile?.profile?.host && profile?.profile?.port && (baseProfile?.profile.host !== profile?.profile.host || baseProfile?.profile.port !== profile?.profile.port) && - profile?.profile.tokenType === zowe.imperative.SessConstants.TOKEN_TYPE_APIML + profile?.type !== zowe.ProfileConstants.BaseProfile.type && + (profile?.profile.tokenType as string)?.startsWith(zowe.imperative.SessConstants.TOKEN_TYPE_APIML) ); } } diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index 5930969b05..e788086fbf 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -2,7 +2,7 @@ "displayName": "Zowe Explorer", "description": "VS Code extension, powered by Zowe CLI, that streamlines interaction with mainframe data sets, USS files, and jobs", "viewsContainers.activitybar": "Zowe Explorer", - "zowe.promptCredentials": "Update Credentials", + "zowe.promptCredentials": "Manage Credentials", "zowe.extRefresh": "Refresh Zowe Explorer", "zowe.ds.explorer": "Data Sets", "zowe.uss.explorer": "Unix System Services (USS)", diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index cb32c1b381..62119393d3 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -939,30 +939,20 @@ "group": "099_zowe_ussModification:@4" }, { - "when": "view == zowe.uss.explorer && viewItem =~ /_validate/ && !listMultiSelection", - "command": "zowe.uss.disableValidation", + "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", + "command": "zowe.promptCredentials", "group": "098_zowe_ussProfileAuthentication@1" }, { - "when": "view == zowe.uss.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", - "command": "zowe.uss.enableValidation", + "when": "view == zowe.uss.explorer && viewItem =~ /_validate/ && !listMultiSelection", + "command": "zowe.uss.disableValidation", "group": "098_zowe_ussProfileAuthentication@2" }, { - "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", - "command": "zowe.promptCredentials", + "when": "view == zowe.uss.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", + "command": "zowe.uss.enableValidation", "group": "098_zowe_ussProfileAuthentication@3" }, - { - "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", - "command": "zowe.uss.ssoLogin", - "group": "098_zowe_ussProfileAuthentication@4" - }, - { - "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", - "command": "zowe.uss.ssoLogout", - "group": "098_zowe_ussProfileAuthentication@5" - }, { "when": "viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", "command": "zowe.uss.editSession", @@ -1149,30 +1139,20 @@ "group": "099_zowe_dsModification@5" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /_validate/ && !listMultiSelection", - "command": "zowe.ds.disableValidation", + "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", + "command": "zowe.promptCredentials", "group": "098_zowe_dsProfileAuthentication@6" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", - "command": "zowe.ds.enableValidation", + "when": "view == zowe.ds.explorer && viewItem =~ /_validate/ && !listMultiSelection", + "command": "zowe.ds.disableValidation", "group": "098_zowe_dsProfileAuthentication@7" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", - "command": "zowe.promptCredentials", + "when": "view == zowe.ds.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", + "command": "zowe.ds.enableValidation", "group": "098_zowe_dsProfileAuthentication@8" }, - { - "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", - "command": "zowe.ds.ssoLogin", - "group": "098_zowe_dsProfileAuthentication@9" - }, - { - "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", - "command": "zowe.ds.ssoLogout", - "group": "098_zowe_dsProfileAuthentication@10" - }, { "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", "command": "zowe.ds.editSession", @@ -1314,30 +1294,20 @@ "group": "099_zowe_jobsModification" }, { - "when": "view == zowe.jobs.explorer && viewItem =~ /_validate/ && !listMultiSelection", - "command": "zowe.jobs.disableValidation", + "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", + "command": "zowe.promptCredentials", "group": "098_zowe_jobsProfileAuthentication@3" }, { - "when": "view == zowe.jobs.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", - "command": "zowe.jobs.enableValidation", + "when": "view == zowe.jobs.explorer && viewItem =~ /_validate/ && !listMultiSelection", + "command": "zowe.jobs.disableValidation", "group": "098_zowe_jobsProfileAuthentication@4" }, { - "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.promptCredentials", + "when": "view == zowe.jobs.explorer && viewItem =~ /_noValidate/ && !listMultiSelection", + "command": "zowe.jobs.enableValidation", "group": "098_zowe_jobsProfileAuthentication@5" }, - { - "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.jobs.ssoLogin", - "group": "098_zowe_jobsProfileAuthentication@6" - }, - { - "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.jobs.ssoLogout", - "group": "098_zowe_jobsProfileAuthentication@7" - }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", "command": "zowe.jobs.editSession", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index 5930969b05..e788086fbf 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -2,7 +2,7 @@ "displayName": "Zowe Explorer", "description": "VS Code extension, powered by Zowe CLI, that streamlines interaction with mainframe data sets, USS files, and jobs", "viewsContainers.activitybar": "Zowe Explorer", - "zowe.promptCredentials": "Update Credentials", + "zowe.promptCredentials": "Manage Credentials", "zowe.extRefresh": "Refresh Zowe Explorer", "zowe.ds.explorer": "Data Sets", "zowe.uss.explorer": "Unix System Services (USS)", diff --git a/packages/zowe-explorer/src/ApimlAuthProvider.ts b/packages/zowe-explorer/src/ApimlAuthProvider.ts new file mode 100644 index 0000000000..0c65955517 --- /dev/null +++ b/packages/zowe-explorer/src/ApimlAuthProvider.ts @@ -0,0 +1,180 @@ +/** + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ + +import { + authentication, + AuthenticationProvider, + AuthenticationProviderAuthenticationSessionsChangeEvent, + AuthenticationSession, + Disposable, + Event, + EventEmitter, +} from "vscode"; +import * as zowe from "@zowe/cli"; +import { Profiles } from "./Profiles"; + +export class ApimlAuthenticationProvider implements AuthenticationProvider, Disposable { + private _disposable: Disposable; + private _sessionChangeEmitter = new EventEmitter(); + private _sessionCache: AuthenticationSession[]; + private static mInstance: ApimlAuthenticationProvider; + + public static readonly authId = "zowe.apiml"; + public static readonly authName = "Zowe API ML"; + + private constructor() { + this._disposable = Disposable.from( + authentication.registerAuthenticationProvider(ApimlAuthenticationProvider.authId, ApimlAuthenticationProvider.authName, this, { + supportsMultipleAccounts: false, + }) + ); + } + + public static get instance(): ApimlAuthenticationProvider { + if (this.mInstance == null) { + this.mInstance = new ApimlAuthenticationProvider(); + } + return this.mInstance; + } + + public async checkForUpdates(): Promise { + const oldSessions: AuthenticationSession[] = JSON.parse(JSON.stringify(this._sessionCache)); + const newSessions = await this.getSessions(); + const added: AuthenticationSession[] = []; + const removed: AuthenticationSession[] = []; + const changed: AuthenticationSession[] = []; + for (const sessionId of new Set([...oldSessions.map((s) => s.id), ...newSessions.map((s) => s.id)])) { + const oldSession = oldSessions.find((s) => s.id === sessionId); + const newSession = newSessions.find((s) => s.id === sessionId); + if (oldSession == null && newSession != null) { + added.push(newSession); + } else if (oldSession != null && newSession == null) { + removed.push(newSession); + } else if (oldSession.accessToken !== newSession.accessToken) { + changed.push(newSession); + } + } + if (added || removed || changed) { + this._sessionChangeEmitter.fire({ added, removed, changed }); + } + } + + public async login(profile: zowe.imperative.IProfileLoaded): Promise<[string, string]> { + const apimlProfile = await Profiles.getInstance().findApimlProfile(profile); + if (apimlProfile != null) { + const session = await this.createSession([apimlProfile.name], profile.profile); + const loginTokenType = session.account.id.slice(session.account.id.indexOf(zowe.imperative.SessConstants.TOKEN_TYPE_APIML)); + return [loginTokenType, session.accessToken]; + } + } + + public async logout(profile: zowe.imperative.IProfileLoaded): Promise { + const apimlProfile = await Profiles.getInstance().findApimlProfile(profile); + const apimlSession = this._sessionCache.find((session) => session.scopes[0] === apimlProfile.name); + if (apimlSession != null) { + await this.removeSession(apimlSession.id, profile.profile); + } + } + + public get onDidChangeSessions(): Event { + return this._sessionChangeEmitter.event; + } + + public async getSessions(_scopes?: string[]): Promise { + await this.updateSessionCache(); + return this._sessionCache; + } + + public async createSession(scopes: string[], loginProps?: zowe.imperative.IProfile): Promise { + const profileName = scopes[0]; + const profiles = Profiles.getInstance(); + const baseProfile = await profiles.fetchBaseProfile(profileName); + const creds = await profiles.loginCredentialPrompt(); + if (!creds) { + return Promise.reject(); + } + const loginSession = new zowe.imperative.Session({ + hostname: (loginProps ?? baseProfile.profile).host, + port: (loginProps ?? baseProfile.profile).port, + user: creds[0], + password: creds[1], + rejectUnauthorized: (loginProps ?? baseProfile.profile).rejectUnauthorized, + tokenType: zowe.imperative.SessConstants.TOKEN_TYPE_APIML, + type: zowe.imperative.SessConstants.AUTH_TYPE_TOKEN, + }); + const profileProps: zowe.imperative.IProfile = { + tokenType: loginSession.ISession.tokenType, + tokenValue: await zowe.Login.apimlLogin(loginSession), + }; + await profiles.updateBaseProfileFileLogin(baseProfile, profileProps); + const baseIndex = profiles.allProfiles.findIndex((profile) => profile.name === baseProfile.name); + profiles.allProfiles[baseIndex] = { ...baseProfile, profile: { ...baseProfile.profile, ...profileProps } }; + const session = this.buildSession(profiles.allProfiles[baseIndex]); + if (this._sessionCache.find((s) => s.id === session.id) == null) { + this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] }); + } else { + this._sessionChangeEmitter.fire({ added: [], removed: [], changed: [session] }); + } + return session; + } + + public async removeSession(sessionId: string, logoutProps?: zowe.imperative.IProfile): Promise { + const session = this._sessionCache.find((s) => s.id === sessionId); + const profileName = session.scopes[0]; + const profiles = Profiles.getInstance(); + const baseProfile = await profiles.fetchBaseProfile(profileName); + const logoutSession = new zowe.imperative.Session({ + hostname: (logoutProps ?? baseProfile.profile).host, + port: (logoutProps ?? baseProfile.profile).port, + rejectUnauthorized: (logoutProps ?? baseProfile.profile).rejectUnauthorized, + tokenType: (logoutProps ?? baseProfile.profile).tokenType, + tokenValue: (logoutProps ?? baseProfile.profile).tokenValue, + type: zowe.imperative.SessConstants.AUTH_TYPE_TOKEN, + }); + try { + await zowe.Logout.apimlLogout(logoutSession); + } catch (err) { + if ( + !(err instanceof zowe.imperative.ImperativeError) || + (err.errorCode as unknown as number) !== zowe.imperative.RestConstants.HTTP_STATUS_401 + ) { + throw err; + } + } + await profiles.updateBaseProfileFileLogout(baseProfile); + this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] }); + } + + public dispose(): void { + this._disposable.dispose(); + } + + private buildSession(profile: zowe.imperative.IProfileLoaded): AuthenticationSession { + return { + id: `${profile.name}_${profile.type}`, + accessToken: profile.profile.tokenValue, + account: { + label: profile.name, + id: `${profile.name}_${profile.profile.tokenType as string}`, + }, + scopes: [profile.name], + }; + } + + private async updateSessionCache(): Promise { + this._sessionCache = []; + for (const baseProfile of await Profiles.getInstance().fetchAllProfilesByType("base")) { + if (baseProfile.profile.tokenType?.startsWith(zowe.imperative.SessConstants.TOKEN_TYPE_APIML) && baseProfile.profile.tokenValue != null) { + this._sessionCache.push(this.buildSession(baseProfile)); + } + } + } +} diff --git a/packages/zowe-explorer/src/Profiles.ts b/packages/zowe-explorer/src/Profiles.ts index fbda781b9e..ada3b9403c 100644 --- a/packages/zowe-explorer/src/Profiles.ts +++ b/packages/zowe-explorer/src/Profiles.ts @@ -46,10 +46,15 @@ nls.config({ const localize: nls.LocalizeFunc = nls.loadMessageBundle(); let InputBoxOptions: vscode.InputBoxOptions; +interface IAuthProvider { + login: (profile: zowe.imperative.IProfileLoaded) => Promise<[string, string]>; + logout: (profile: zowe.imperative.IProfileLoaded) => Promise; +} + export class Profiles extends ProfilesCache { // Processing stops if there are no profiles detected - public static async createInstance(log: zowe.imperative.Logger): Promise { - Profiles.loader = new Profiles(log, vscode.workspace.workspaceFolders?.[0]?.uri.fsPath); + public static async createInstance(log: zowe.imperative.Logger, authProvider: IAuthProvider): Promise { + Profiles.loader = new Profiles(log, vscode.workspace.workspaceFolders?.[0]?.uri.fsPath, authProvider); await Profiles.loader.refresh(ZoweExplorerApiRegister.getInstance()); return Profiles.loader; } @@ -68,7 +73,7 @@ export class Profiles extends ProfilesCache { private jobsSchema: string = globals.SETTINGS_JOBS_HISTORY; private mProfileInfo: zowe.imperative.ProfileInfo; private profilesOpCancelled = localize("profiles.operation.cancelled", "Operation Cancelled"); - public constructor(log: zowe.imperative.Logger, cwd?: string) { + public constructor(log: zowe.imperative.Logger, cwd?: string, private apimlAuthProvider?: IAuthProvider) { super(log, cwd); } @@ -1140,7 +1145,6 @@ export class Profiles extends ProfilesCache { public async ssoLogin(node?: IZoweNodeType, label?: string): Promise { ZoweLogger.trace("Profiles.ssoLogin called."); - let loginToken: string; let loginTokenType: string; let creds: string[]; let serviceProfile: zowe.imperative.IProfileLoaded; @@ -1163,7 +1167,7 @@ export class Profiles extends ProfilesCache { Gui.showMessage(localize("ssoAuth.noBase", "This profile does not support token authentication.")); return; } - if (loginTokenType && loginTokenType !== zowe.imperative.SessConstants.TOKEN_TYPE_APIML) { + if (loginTokenType && !loginTokenType.startsWith(zowe.imperative.SessConstants.TOKEN_TYPE_APIML)) { // this will handle extenders if (node) { session = node.getSession(); @@ -1177,7 +1181,7 @@ export class Profiles extends ProfilesCache { session.ISession.user = creds[0]; session.ISession.password = creds[1]; try { - loginToken = await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).login(session); + await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).login(session); const profIndex = this.allProfiles.findIndex((profile) => profile.name === serviceProfile.name); this.allProfiles[profIndex] = { ...serviceProfile, profile: { ...serviceProfile, ...session } }; node.setProfileToChoice({ @@ -1191,40 +1195,19 @@ export class Profiles extends ProfilesCache { return; } } else { - const baseProfile = await this.fetchBaseProfile(); - if (baseProfile) { - creds = await this.loginCredentialPrompt(); - if (!creds) { - return; - } - try { - const updSession = new zowe.imperative.Session({ - hostname: serviceProfile.profile.host, - port: serviceProfile.profile.port, - user: creds[0], - password: creds[1], - rejectUnauthorized: serviceProfile.profile.rejectUnauthorized, - tokenType: loginTokenType, - type: zowe.imperative.SessConstants.AUTH_TYPE_TOKEN, - }); - loginToken = await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).login(updSession); - const updBaseProfile: zowe.imperative.IProfile = { - tokenType: loginTokenType, - tokenValue: loginToken, - }; - await this.updateBaseProfileFileLogin(baseProfile, updBaseProfile); - const baseIndex = this.allProfiles.findIndex((profile) => profile.name === baseProfile.name); - this.allProfiles[baseIndex] = { ...baseProfile, profile: { ...baseProfile.profile, ...updBaseProfile } }; + try { + creds = await this.apimlAuthProvider.login(serviceProfile); + if (creds != null) { node.setProfileToChoice({ ...node.getProfile(), - profile: { ...node.getProfile().profile, ...updBaseProfile }, + profile: { ...node.getProfile().profile, tokenType: creds[0], tokenValue: creds[1] }, }); - } catch (error) { - const errMsg = localize("ssoLogin.unableToLogin", "Unable to log in with {0}. {1}", serviceProfile.name, error?.message); - ZoweLogger.error(errMsg); - Gui.errorMessage(errMsg); - return; } + } catch (error) { + const errMsg = localize("ssoLogin.unableToLogin", "Unable to log in with {0}. {1}", serviceProfile.name, error?.message); + ZoweLogger.error(errMsg); + Gui.errorMessage(errMsg); + return; } } Gui.showMessage(localize("ssoLogin.successful", "Login to authentication service was successful.")); @@ -1240,25 +1223,13 @@ export class Profiles extends ProfilesCache { } try { // this will handle extenders - if (serviceProfile.type !== "zosmf" && serviceProfile.profile?.tokenType !== zowe.imperative.SessConstants.TOKEN_TYPE_APIML) { + if (serviceProfile.type !== "zosmf" && !serviceProfile.profile?.tokenType.startsWith(zowe.imperative.SessConstants.TOKEN_TYPE_APIML)) { await ZoweExplorerApiRegister.getInstance() .getCommonApi(serviceProfile) .logout(await node.getSession()); } else { // this will handle base profile apiml tokens - const baseProfile = await this.fetchBaseProfile(); - const loginTokenType = ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).getTokenTypeName(); - const updSession = new zowe.imperative.Session({ - hostname: serviceProfile.profile.host, - port: serviceProfile.profile.port, - rejectUnauthorized: serviceProfile.profile.rejectUnauthorized, - tokenType: loginTokenType, - tokenValue: serviceProfile.profile.tokenValue, - type: zowe.imperative.SessConstants.AUTH_TYPE_TOKEN, - }); - await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).logout(updSession); - - await this.updateBaseProfileFileLogout(baseProfile); + await this.apimlAuthProvider.logout(serviceProfile); } Gui.showMessage(localize("ssoLogout.successful", "Logout from authentication service was successful for {0}.", serviceProfile.name)); } catch (error) { @@ -1275,6 +1246,25 @@ export class Profiles extends ProfilesCache { await Gui.showTextDocument(document); } + /** + * Look for a base profile where APIML token can be stored for SSO login. + * @param profile Imperative loaded profile object + * @returns Name of base profile to store APIML token + */ + public async getApimlProfileName(profile: zowe.imperative.IProfileLoaded): Promise { + // if (profile.profile?.apimlProfile != null) { + // return profile.profile.apimlProfile as string; + // } + if ((await this.getProfileInfo()).usingTeamConfig && profile.name.includes(".")) { + for (const baseProfile of await this.fetchAllProfilesByType("base")) { + if (profile.name.startsWith(baseProfile.name + ".")) { + return baseProfile.name; + } + } + } + return (await this.fetchBaseProfile())?.name; + } + private async getConfigLocationPrompt(action: string): Promise { ZoweLogger.trace("Profiles.getConfigLocationPrompt called."); let placeHolderText: string; @@ -1361,7 +1351,7 @@ export class Profiles extends ProfilesCache { return ret; } - private async updateBaseProfileFileLogin(profile: zowe.imperative.IProfileLoaded, updProfile: zowe.imperative.IProfile): Promise { + public async updateBaseProfileFileLogin(profile: zowe.imperative.IProfileLoaded, updProfile: zowe.imperative.IProfile): Promise { ZoweLogger.trace("Profiles.updateBaseProfileFileLogin called."); const upd = { profileName: profile.name, profileType: profile.type }; const mProfileInfo = await this.getProfileInfo(); @@ -1370,7 +1360,7 @@ export class Profiles extends ProfilesCache { await mProfileInfo.updateProperty({ ...upd, property: "tokenValue", value: updProfile.tokenValue, setSecure }); } - private async updateBaseProfileFileLogout(profile: zowe.imperative.IProfileLoaded): Promise { + public async updateBaseProfileFileLogout(profile: zowe.imperative.IProfileLoaded): Promise { ZoweLogger.trace("Profiles.updateBaseProfileFileLogout called."); const mProfileInfo = await this.getProfileInfo(); const setSecure = mProfileInfo.isSecured(); @@ -1380,7 +1370,7 @@ export class Profiles extends ProfilesCache { await mProfileInfo.updateKnownProperty({ mergedArgs, property: "tokenType", value: undefined }); } - private async loginCredentialPrompt(): Promise { + public async loginCredentialPrompt(): Promise { ZoweLogger.trace("Profiles.loginCredentialPrompt called."); let newPass: string; const newUser = await this.userInfo(); diff --git a/packages/zowe-explorer/src/extension.ts b/packages/zowe-explorer/src/extension.ts index e68d3f999c..a84620a601 100644 --- a/packages/zowe-explorer/src/extension.ts +++ b/packages/zowe-explorer/src/extension.ts @@ -26,6 +26,7 @@ import { IZoweProviders, registerCommonCommands, registerRefreshCommand, watchCo import { ZoweLogger } from "./utils/LoggerUtils"; import { ZoweSaveQueue } from "./abstract/ZoweSaveQueue"; import { PollDecorator } from "./utils/DecorationProviders"; +import { ApimlAuthenticationProvider } from "./ApimlAuthProvider"; /** * The function that runs when the extension is loaded @@ -46,7 +47,7 @@ export async function activate(context: vscode.ExtensionContext): Promise