Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix credentials not refreshed in ds tree and misc profile related fixes #3111

Merged
merged 23 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
480e2ec
WIP Fix credential desyncs across tree nodes
t1m0thyj Sep 12, 2024
b6313c0
Prompt for credentials if SSO profile fails validation
t1m0thyj Sep 13, 2024
5aa24a8
Merge branch 'fix/multi-apiml-logout' into fix/update-credentials-desync
t1m0thyj Sep 16, 2024
18dd054
Update unit tests for FSProvider profile metadata
t1m0thyj Sep 17, 2024
021eb08
WIP Add integration tests for UpdateCredentials
t1m0thyj Sep 19, 2024
a38d249
Fix unit tests and debug integration test
t1m0thyj Sep 23, 2024
b4141ff
Fix integration test and show inactive icon for global profiles
t1m0thyj Sep 24, 2024
ef0142b
Merge branch 'main' into fix/update-credentials-desync
t1m0thyj Sep 24, 2024
739d3e1
Disable default global zosmf profile in test
t1m0thyj Sep 24, 2024
151f66d
Add test coverage and remove todo comment
t1m0thyj Sep 24, 2024
911029f
Add test coverage for checkCurrentProfile
t1m0thyj Sep 24, 2024
02b1fd4
Update changelog for misc profile fixes
t1m0thyj Sep 24, 2024
cf6a9f1
Don't reassign profile to keep object reference
t1m0thyj Sep 24, 2024
3fbdd25
Fix syncSessionNode unit test
t1m0thyj Sep 24, 2024
2654a1f
Add test coverage to ZE API
t1m0thyj Sep 24, 2024
a6610cc
Merge branch 'main' into fix/update-credentials-desync
t1m0thyj Sep 24, 2024
2b958ab
Merge branch 'main' into fix/update-credentials-desync
t1m0thyj Sep 24, 2024
ab9b609
Update lockfile for rollup vuln
t1m0thyj Sep 24, 2024
60eb150
Fix error updating credentials when autoStore is false
t1m0thyj Sep 24, 2024
223e1c0
Fix circular dep and update l10n bundle
t1m0thyj Sep 24, 2024
c8ceebf
Update ZE API changelog to mention deprecated method
t1m0thyj Sep 24, 2024
82fa3f7
Merge branch 'main' into fix/update-credentials-desync
t1m0thyj Sep 25, 2024
409bcd4
Update CHANGELOG.md
t1m0thyj Sep 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/zowe-explorer-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t

- Updated the `TableViewProvider.setTableView` function to show the Zowe Resources panel if a table is provided. If `null` is passed, the Zowe Resources panel will be hidden. [#3113](https://github.com/zowe/zowe-explorer-vscode/issues/3113)
- Fixed behavior of logout action when token is defined in both base profile and parent profile. [#3076](https://github.com/zowe/zowe-explorer-vscode/issues/3076)
- Fixed profiles cache not refreshing on password change. [#3120](https://github.com/zowe/zowe-explorer-vscode/issues/3120)

## `3.0.0-next.202409132122`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,12 @@ function createProfInfoMock(profiles: Partial<imperative.IProfileLoaded>[]): imp
const teamConfigApi: Partial<imperative.Config> = {
api: {
profiles: {
get: jest.fn().mockReturnValue({}),
get: jest.fn(),
getProfilePathFromName: jest.fn().mockImplementation((x) => x),
},
secure: {
secureFields: jest.fn().mockReturnValue([]),
securePropsForProfile: jest.fn().mockReturnValue([]),
},
} as any,
exists: true,
Expand Down Expand Up @@ -223,6 +224,7 @@ describe("ProfilesCache", () => {
profCache.allProfiles = [lpar1Profile as imperative.IProfileLoaded];
(profCache as any).defaultProfileByType = new Map([["zosmf", { ...profCache.allProfiles[0] }]]);
expect(profCache.allProfiles[0].profile).toMatchObject(lpar1Profile.profile);
// eslint-disable-next-line deprecation/deprecation
profCache.updateProfilesArrays({
...lpar1Profile,
profile: lpar2Profile.profile,
Expand Down Expand Up @@ -566,18 +568,18 @@ describe("ProfilesCache", () => {
expect(profile).toMatchObject({ name: "lpar1", type: "base" });
});

it("fetchBaseProfile should return typeless profile if base profile does not contain token type", async () => {
it("fetchBaseProfile should return typeless profile if base profile does not contain token value", async () => {
const profCache = new ProfilesCache(fakeLogger as unknown as imperative.Logger);
jest.spyOn(profCache, "getProfileInfo").mockResolvedValue(createProfInfoMock([baseProfile]));
const profile = await profCache.fetchBaseProfile("lpar1.zosmf");
expect(profile).toMatchObject({ name: "lpar1", type: "base" });
});

it("fetchBaseProfile should return base profile if it contains token type", async () => {
it("fetchBaseProfile should return base profile if it contains token value", async () => {
const profCache = new ProfilesCache(fakeLogger as unknown as imperative.Logger);
const profInfoMock = createProfInfoMock([baseProfile]);
jest.spyOn(profCache, "getProfileInfo").mockResolvedValue(profInfoMock);
mocked(profInfoMock.getTeamConfig().api.profiles.get).mockReturnValueOnce({ tokenType: imperative.SessConstants.TOKEN_TYPE_JWT });
mocked(profInfoMock.getTeamConfig().api.secure.securePropsForProfile).mockReturnValue(["tokenValue"]);
const profile = await profCache.fetchBaseProfile("lpar1.zosmf");
expect(profile).toMatchObject(baseProfile);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as vscode from "vscode";
import { ZoweTreeNode } from "../../../src/tree/ZoweTreeNode";
import { IZoweTreeNode } from "../../../src/tree/IZoweTreeNode";
import * as imperative from "@zowe/imperative";
import { BaseProvider } from "../../../src";

describe("ZoweTreeNode", () => {
const makeNode = (
Expand Down Expand Up @@ -79,4 +80,21 @@ describe("ZoweTreeNode", () => {
expect(node.getProfile()).toBeUndefined();
expect(node.getProfileName()).toBeUndefined();
});

it("setProfileToChoice should update profile for associated FSProvider entry", () => {
const node = makeNode("test", vscode.TreeItemCollapsibleState.None, undefined);
const fsEntry = {
metadata: {
profile: { name: "oldProfile" },
},
};
node.setProfileToChoice(
{ name: "newProfile" } as unknown as imperative.IProfileLoaded,
{
lookup: jest.fn().mockReturnValue(fsEntry),
} as unknown as BaseProvider
);
expect(node.getProfileName()).toBe("newProfile");
expect(fsEntry.metadata.profile.name).toBe("newProfile");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ describe("ZoweVsCodeExtension", () => {
mProfileSchemaCache: new Map(),
readProfilesFromDisk: jest.fn(),
});
testCache.allProfiles = [serviceProfile, baseProfile];
jest.spyOn(testCache, "getProfileInfo").mockResolvedValue(testProfInfo);

return {
Expand Down Expand Up @@ -338,6 +339,9 @@ describe("ZoweVsCodeExtension", () => {
};

const quickPickMock = jest.spyOn(Gui, "showQuickPick").mockImplementation((items) => items[0]);
await ZoweVsCodeExtension.profilesCache.refresh({
registeredApiTypes: jest.fn().mockReturnValue(["service"]),
} as unknown as Types.IApiRegisterClient);
await ZoweVsCodeExtension.ssoLogin({ serviceProfile: "lpar.service" });

const testSession = new imperative.Session(JSON.parse(JSON.stringify(blockMocks.expectedSession.ISession)));
Expand Down Expand Up @@ -541,9 +545,6 @@ describe("ZoweVsCodeExtension", () => {

jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue(["user", "pass"]);
const quickPickMock = jest.spyOn(Gui, "showQuickPick").mockImplementation((items) => items[0]);
await ZoweVsCodeExtension.profilesCache.refresh({
registeredApiTypes: jest.fn().mockReturnValue(["service"]),
} as unknown as Types.IApiRegisterClient);
const didLogin = await ZoweVsCodeExtension.ssoLogin({
serviceProfile: serviceProfileLoaded,
defaultTokenType: "apimlAuthenticationToken",
Expand Down Expand Up @@ -633,6 +634,9 @@ describe("ZoweVsCodeExtension", () => {

const testSpy = jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue(["abc", "def"]);
const quickPickMock = jest.spyOn(Gui, "showQuickPick").mockImplementation((items) => items[0]);
await ZoweVsCodeExtension.profilesCache.refresh({
registeredApiTypes: jest.fn().mockReturnValue(["service"]),
} as unknown as Types.IApiRegisterClient);
const didLogin = await ZoweVsCodeExtension.ssoLogin({
serviceProfile: serviceProfileLoaded,
defaultTokenType: "apimlAuthenticationToken",
Expand Down Expand Up @@ -686,6 +690,7 @@ describe("ZoweVsCodeExtension", () => {
updateProperty: mockUpdateProperty,
}),
refresh: jest.fn(),
updateCachedProfile: jest.fn(),
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce("fakeUser").mockResolvedValueOnce("fakePassword");
const saveCredentialsSpy = jest.spyOn(ZoweVsCodeExtension as any, "saveCredentials");
Expand All @@ -711,6 +716,7 @@ describe("ZoweVsCodeExtension", () => {
updateProperty: mockUpdateProperty,
}),
refresh: jest.fn(),
updateCachedProfile: jest.fn(),
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce("fakeUser").mockResolvedValueOnce("fakePassword");
const saveCredentialsSpy = jest.spyOn(ZoweVsCodeExtension as any, "saveCredentials");
Expand Down Expand Up @@ -739,6 +745,7 @@ describe("ZoweVsCodeExtension", () => {
updateProperty: mockUpdateProperty,
}),
refresh: jest.fn(),
updateCachedProfile: jest.fn(),
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce("fakeUser").mockResolvedValueOnce("fakePassword");
jest.spyOn(Gui, "showMessage").mockResolvedValueOnce("yes");
Expand All @@ -765,6 +772,7 @@ describe("ZoweVsCodeExtension", () => {
updateProperty: mockUpdateProperty,
}),
refresh: jest.fn(),
updateCachedProfile: jest.fn(),
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce("fakeUser").mockResolvedValueOnce("fakePassword");
jest.spyOn(Gui, "showMessage").mockResolvedValueOnce(undefined);
Expand Down
35 changes: 31 additions & 4 deletions packages/zowe-explorer-api/src/profiles/ProfilesCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import { ZosmfProfile } from "@zowe/zosmf-for-zowe-sdk";
import { ZosTsoProfile } from "@zowe/zos-tso-for-zowe-sdk";
import { ZosUssProfile } from "@zowe/zos-uss-for-zowe-sdk";
import { Types } from "../Types";

export class ProfilesCache {
public profilesForValidation: Validation.IValidationProfile[] = [];
Expand Down Expand Up @@ -79,9 +80,8 @@

/**
* Updates profile in allProfiles array and if default updates defaultProfileByType
*
* @deprecated Use `updateCachedProfile` instead
zFernand0 marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} profileLoaded
*
* @returns {void}
*/
public updateProfilesArrays(profileLoaded: imperative.IProfileLoaded): void {
Expand All @@ -97,6 +97,30 @@
}
}

public async updateCachedProfile(
profileLoaded: imperative.IProfileLoaded,
profileNode?: Types.IZoweNodeType,
zeRegister?: Types.IApiRegisterClient
): Promise<void> {
if ((await this.getProfileInfo()).getTeamConfig().properties.autoStore) {
await this.refresh(zeRegister);

Check warning on line 106 in packages/zowe-explorer-api/src/profiles/ProfilesCache.ts

View check run for this annotation

Codecov / codecov/patch

packages/zowe-explorer-api/src/profiles/ProfilesCache.ts#L106

Added line #L106 was not covered by tests
} else {
// Note: When autoStore is disabled, nested profiles within this service profile may not have their credentials updated.
const profIndex = this.allProfiles.findIndex((profile) => profile.type === profileLoaded.type && profile.name === profileLoaded.name);
this.allProfiles[profIndex].profile = profileLoaded.profile;
const defaultProf = this.defaultProfileByType.get(profileLoaded.type);
if (defaultProf != null && defaultProf.name === profileLoaded.name) {
this.defaultProfileByType.set(profileLoaded.type, profileLoaded);

Check warning on line 113 in packages/zowe-explorer-api/src/profiles/ProfilesCache.ts

View check run for this annotation

Codecov / codecov/patch

packages/zowe-explorer-api/src/profiles/ProfilesCache.ts#L113

Added line #L113 was not covered by tests
}
}
if (profileNode) {
profileNode.setProfileToChoice({
...profileNode.getProfile(),
profile: { ...profileNode.getProfile().profile, ...profileLoaded.profile },
});
}
}

/**
* This returns default profile by type from defaultProfileByType
*
Expand Down Expand Up @@ -319,11 +343,14 @@
const mProfileInfo = await this.getProfileInfo();
const baseProfileAttrs = mProfileInfo.getDefaultProfile("base");
const config = mProfileInfo.getTeamConfig();
if (profileName?.includes(".") && (baseProfileAttrs == null || config.api.profiles.get(baseProfileAttrs.profName).tokenType == null)) {
if (
profileName?.includes(".") &&
(baseProfileAttrs == null || !config.api.secure.securePropsForProfile(baseProfileAttrs.profName).includes("tokenValue"))
) {
// Retrieve parent typeless profile as base profile if:
// (1) The active profile name is nested (contains a period) AND
// (2) No default base profile was found OR
// Default base profile does not have tokenType defined
// Default base profile does not have tokenValue in secure array
const parentProfile = this.getParentProfileForToken(profileName, config);
return this.getProfileLoaded(parentProfile, "base", config.api.profiles.get(parentProfile));
} else if (baseProfileAttrs == null) {
Expand Down
7 changes: 6 additions & 1 deletion packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import * as vscode from "vscode";
import * as imperative from "@zowe/imperative";
import { IZoweTreeNode } from "./IZoweTreeNode";
import type { BaseProvider } from "../fs/BaseProvider";

/**
* Common implementation of functions and methods associated with the
Expand Down Expand Up @@ -99,8 +100,12 @@ export class ZoweTreeNode extends vscode.TreeItem {
*
* @param {imperative.IProfileLoaded} The profile you will set the node to use
*/
public setProfileToChoice(aProfile: imperative.IProfileLoaded): void {
public setProfileToChoice(aProfile: imperative.IProfileLoaded, fsProvider?: BaseProvider): void {
this.profile = aProfile;
const fsEntry = fsProvider?.lookup(this.resourceUri, true);
if (fsEntry != null) {
fsEntry.metadata.profile = aProfile;
}
}
/**
* Sets the session for this node to the one chosen in parameters.
Expand Down
23 changes: 3 additions & 20 deletions packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class ZoweVsCodeExtension {
await profInfo.updateProperty({ ...upd, property: "user", value: creds[0], setSecure });
await profInfo.updateProperty({ ...upd, property: "password", value: creds[1], setSecure });
}
await cache.refresh(apiRegister);
await cache.updateCachedProfile(loadProfile, undefined, apiRegister);

return loadProfile;
}
Expand Down Expand Up @@ -215,7 +215,7 @@ export class ZoweVsCodeExtension {

await cache.updateBaseProfileFileLogin(profileToUpdate, updBaseProfile, !connOk);
serviceProfile.profile = { ...serviceProfile.profile, ...updBaseProfile };
await this.updateProfileInCache({ ...opts, serviceProfile });
await cache.updateCachedProfile(serviceProfile, opts.profileNode);
return true;
}

Expand Down Expand Up @@ -278,27 +278,10 @@ export class ZoweVsCodeExtension {
!serviceProfile.name.startsWith(baseProfile.name + ".");
await cache.updateBaseProfileFileLogout(connOk ? baseProfile : serviceProfile);
serviceProfile.profile = { ...serviceProfile.profile, tokenType: undefined, tokenValue: undefined };
await this.updateProfileInCache({ ...opts, serviceProfile });
await cache.updateCachedProfile(serviceProfile, opts.profileNode);
return true;
}

private static async updateProfileInCache(opts: BaseProfileAuthOptions & { serviceProfile: imperative.IProfileLoaded }): Promise<void> {
const cache: ProfilesCache = opts.zeProfiles ?? ZoweVsCodeExtension.profilesCache;
if ((await cache.getProfileInfo()).getTeamConfig().properties.autoStore !== false) {
await cache.refresh();
} else {
// Note: It should be expected that nested profiles within this service profile will have their credentials updated.
const profIndex = cache.allProfiles.findIndex((profile) => profile.name === opts.serviceProfile.name);
cache.allProfiles[profIndex] = { ...cache.allProfiles[profIndex], profile: opts.serviceProfile.profile };
}
if (opts.profileNode) {
opts.profileNode.setProfileToChoice({
...opts.profileNode.getProfile(),
profile: { ...opts.profileNode.getProfile().profile, ...opts.serviceProfile.profile },
});
}
}

/**
* This method is intended to be used for authentication (login, logout) purposes
*
Expand Down
3 changes: 3 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- Fix issue with extender profiles not being included in fresh team configuration file. [#3122](https://github.com/zowe/zowe-explorer-vscode/issues/3122)
- Fixed issue where file extensions were removed from data sets, causing language detection to sometimes fail for Zowe Explorer extenders. [#3121](https://github.com/zowe/zowe-explorer-vscode/issues/3121)
- Fixed an issue where copying and pasting a file/folder in the USS tree would fail abruptly, displaying an error. [#3128](https://github.com/zowe/zowe-explorer-vscode/issues/3128)
- Fixed issue where obsolete credentials persisted for PDS member nodes in Data Sets tree. [#3112](https://github.com/zowe/zowe-explorer-vscode/issues/3112)
- Fixed issue where Search operation did not prompt for credentials if profile contains expired token. [#2259](https://github.com/zowe/zowe-explorer-vscode/issues/2259)
- Fixed issue where inactive status was not displayed for profiles loaded from Global Config. [#3134](https://github.com/zowe/zowe-explorer-vscode/issues/3134)

## `3.0.0-next.202409132122`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Feature: Update credentials for z/OSMF profile

Scenario: Prompt for missing <authType> credentials
Given a user who has profile with <authType> auth in team config
And a user who is looking at the Zowe Explorer tree views
And the user has a profile in their Data Sets tree
When a user clicks search button for the profile
Then the user will be prompted for <authType> credentials
And the profile node icon will be marked as inactive

Examples:
| authType |
| basic |
| token |
Loading
Loading