Skip to content

Commit

Permalink
Bugfix: "User Profile" attributes not available for Users Attribute s…
Browse files Browse the repository at this point in the history
…earch, when admin user does not have view- or manage-realm realm-management role

Closes keycloak#27536

Signed-off-by: Daniel Fesenmeyer <[email protected]>
  • Loading branch information
danielFesenmeyer committed Jul 30, 2024
1 parent 1fe5082 commit 25059bf
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 79 deletions.
85 changes: 85 additions & 0 deletions js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
import LoginPage from "../support/pages/LoginPage";
import {keycloakBefore} from "../support/util/keycloak_hooks";

Check failure on line 3 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Replace `keycloakBefore` with `·keycloakBefore·`
import adminClient from "../support/util/AdminClient";
import {DefaultUserAttribute, UserFilterType} from "../support/pages/admin-ui/manage/users/UsersListingPage";

Check failure on line 5 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Replace `DefaultUserAttribute,·UserFilterType` with `⏎··DefaultUserAttribute,⏎··UserFilterType,⏎`
import UsersPage from "../support/pages/admin-ui/manage/users/UsersPage";

describe("Query by user attributes", () => {
const loginPage = new LoginPage();
const sidebarPage = new SidebarPage();
const usersPage = new UsersPage();
const listingPage = usersPage.listing();

const emailSuffix = "@example.org";

const user1Username = "user-attrs-1";
const user1FirstName = "John"

Check failure on line 17 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Insert `;`
const user1LastName = "Doe";
const user1Pwd = "pwd";
const user2Username = "user-attrs-2";
const user2FirstName = "Jane";
const user2LastName = user1LastName;

before(async () => {
await cleanupTestData();
const user1 = await adminClient.createUser({
username: user1Username,
credentials: [{

Check failure on line 28 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Insert `⏎········`
type: "password",

Check failure on line 29 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Insert `··`
value: user1Pwd,

Check failure on line 30 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Insert `··`
}],

Check failure on line 31 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Replace `}` with `··},⏎······`
email: user1Username + emailSuffix,
firstName: user1FirstName,
lastName: user1LastName,
enabled: true,
});
const user1Id = user1.id!;
await adminClient.addClientRoleToUser(user1Id, "master-realm", ["view-users"]);

Check failure on line 38 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Replace `"view-users"` with `⏎······"view-users",⏎····`

await adminClient.createUser({
username: user2Username,
email: user2Username + emailSuffix,
firstName: user2FirstName,
lastName: user2LastName,
enabled: true,
});
});

beforeEach(() => {
loginPage.logIn(user1Username, user1Pwd);
keycloakBefore();
sidebarPage.goToUsers();
});

after(async () => {
await cleanupTestData();
});

async function cleanupTestData() {
await adminClient.deleteUser(user1Username, true);
await adminClient.deleteUser(user2Username, true);
}

it("Query with one attribute condition", () => {
listingPage
.selectUserSearchFilter(UserFilterType.AttributeSearch)

Check failure on line 66 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Delete `··`
.openUserAttributesSearchForm()

Check failure on line 67 in js/apps/admin-ui/cypress/e2e/users_attribute_search_test.spec.ts

View workflow job for this annotation

GitHub Actions / Admin UI

Delete `··`
.addUserAttributeSearchCriteria(DefaultUserAttribute.lastName, user1LastName)
.triggerAttributesSearch()
.itemExist(user1Username, true)
.itemExist(user2Username, true);
});

it("Query with two attribute conditions", () => {
listingPage
.selectUserSearchFilter(UserFilterType.AttributeSearch)
.openUserAttributesSearchForm()
.addUserAttributeSearchCriteria(DefaultUserAttribute.lastName, user1LastName)
.addUserAttributeSearchCriteria(DefaultUserAttribute.firstName, user1FirstName)
.triggerAttributesSearch()
.itemExist(user1Username, true)
.itemExist(user2Username, false);
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export default class ListingPage extends CommonElements {
public tableRowItem = "tbody tr[data-ouia-component-type]:visible";
#table = "table[aria-label]";
#filterSessionDropdownButton = ".pf-v5-c-select button:nth-child(1)";
#searchTypeButton = "[data-testid='clientScopeSearch']";
#filterDropdownButton = "[data-testid='clientScopeSearchType']";
#protocolFilterDropdownButton = "[data-testid='clientScopeSearchProtocol']";
#kebabMenu = "[data-testid='kebab']";
Expand Down Expand Up @@ -321,8 +320,12 @@ export default class ListingPage extends CommonElements {
}

selectFilter(filter: Filter) {
cy.get(this.#searchTypeButton).click();
cy.get(this.#dropdownItem).contains(filter).click();
return this.selectFilter("clientScopeSearch", filter);
}

protected selectFilter(searchTypeButtonTestId: string, filterStr: string) {
cy.get(`[data-testid='${searchTypeButtonTestId}']`).click();
cy.get(this.#dropdownItem).contains(filterStr).click();

return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ export default class PageObject {
#selectMenuToggleBtn = ".pf-v5-c-menu-toggle";
#switchInput = ".pf-v5-c-switch__input";
#formLabel = ".pf-v5-c-form__label";
#chipGroup = ".pf-v5-c-chip-group";
#chipGroupCloseBtn = ".pf-v5-c-chip-group__close";
#chipItem = ".pf-v5-c-chip-group__list-item";
#emptyStateDiv = ".pf-v5-c-empty-state:visible";
#toolbarActionsButton = ".pf-v5-c-toolbar button[aria-label='Actions']";
#breadcrumbItem = ".pf-v5-c-breadcrumb .pf-v5-c-breadcrumb__item";

genericChipGroupSelector = ".pf-v5-c-chip-group";

protected assertExist(element: Cypress.Chainable<JQuery>, exist: boolean) {
element.should((!exist ? "not." : "") + "exist");
return this;
Expand Down Expand Up @@ -281,41 +282,38 @@ export default class PageObject {
return this;
}

#getChipGroup(groupName: string) {
return cy.get(this.#chipGroup).contains(groupName).parent().parent();
#getChipGroup(groupSelector: string, groupName: string) {
return cy.get(groupSelector).contains(groupName).parent().parent();
}

#getChipItem(itemName: string) {
return cy.get(this.#chipItem).contains(itemName).parent();
#getChipGroupWithLabel(groupSelector: string, label: string) {
cy.get(groupSelector).parent().find(".pf-v5-c-chip-group__label").contains(label);

return cy.get(groupSelector);
}

#getChipGroupItem(groupName: string, itemName: string) {
return this.#getChipGroup(groupName)
#getChipGroupItem(groupSelector: string, groupName: string, itemName: string) {
return this.#getChipGroup(groupSelector, groupName)
.find(this.#chipItem)
.contains(itemName)
.parent();
}

protected removeChipGroup(groupName: string) {
this.#getChipGroup(groupName)
protected removeChipGroup(groupSelector: string, groupName: string) {
this.#getChipGroup(groupSelector, groupName)
.find(this.#chipGroupCloseBtn)
.find("button")
.click();
return this;
}

protected removeChipItem(itemName: string) {
this.#getChipItem(itemName).find("button").click();
return this;
}

protected removeChipGroupItem(groupName: string, itemName: string) {
this.#getChipGroupItem(groupName, itemName).find("button").click();
protected removeChipGroupItem(groupSelector: string, groupName: string, itemName: string) {
this.#getChipGroupItem(groupSelector, groupName, itemName).find("button").click();
return this;
}

protected assertChipGroupExist(groupName: string, exist: boolean) {
this.assertExist(cy.contains(this.#chipGroup, groupName), exist);
protected assertChipGroupExist(groupSelector: string, groupName: string, exist: boolean) {
this.assertExist(cy.contains(groupSelector, groupName), exist);
return this;
}

Expand All @@ -325,25 +323,32 @@ export default class PageObject {
return this;
}

protected assertChipItemExist(itemName: string, exist: boolean) {
cy.get(this.#chipItem).within(() => {
cy.contains(itemName).should((exist ? "" : "not.") + "exist");
});
return this;
}

protected assertChipGroupItemExist(
groupSelector: string,
groupName: string,
itemName: string,
exist: boolean,
) {
this.assertExist(
this.#getChipGroup(groupName).contains(this.#chipItem, itemName),
this.#getChipGroup(groupSelector, groupName).contains(this.#chipItem, itemName),
exist,
);
return this;
}

protected assertLabeledChipGroupItemExist(
groupSelector: string,
labelName: string,
itemName: string,
exist: boolean,
) {
this.assertExist(
this.#getChipGroupWithLabel(groupSelector, labelName).contains(this.#chipItem, itemName),
exist,
);
return this;
}

assertEmptyStateExist(exist: boolean) {
if (exist) {
cy.get(this.#emptyStateDiv).should("exist").should("be.visible");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,13 @@ export default class AdminEventsTab extends PageObject {
}

public removeResourcePathChipGroup() {
super.removeChipGroup(AdminEventsTabSearchFormFieldsLabel.ResourcePath);
super.removeChipGroup(this.genericChipGroupSelector, AdminEventsTabSearchFormFieldsLabel.ResourcePath);
return this;
}

public assertResourceTypesChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.ResourceTypes,
exist,
);
Expand All @@ -214,6 +215,7 @@ export default class AdminEventsTab extends PageObject {

public assertOperationTypesChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.OperationTypes,
exist,
);
Expand All @@ -222,6 +224,7 @@ export default class AdminEventsTab extends PageObject {

public assertResourcePathChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.ResourcePath,
exist,
);
Expand All @@ -233,6 +236,7 @@ export default class AdminEventsTab extends PageObject {
exist: boolean,
) {
super.assertChipGroupItemExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.ResourcePath,
itemName,
exist,
Expand All @@ -242,6 +246,7 @@ export default class AdminEventsTab extends PageObject {

public assertRealmChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.Realm,
exist,
);
Expand All @@ -250,19 +255,21 @@ export default class AdminEventsTab extends PageObject {

public assertClientChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.Client,
exist,
);
return this;
}

public assertUserChipGroupExist(exist: boolean) {
super.assertChipGroupExist(AdminEventsTabSearchFormFieldsLabel.User, exist);
super.assertChipGroupExist(this.genericChipGroupSelector, AdminEventsTabSearchFormFieldsLabel.User, exist);
return this;
}

public assertIpAddressChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.IpAddress,
exist,
);
Expand All @@ -271,6 +278,7 @@ export default class AdminEventsTab extends PageObject {

public assertDateFromChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.DateFrom,
exist,
);
Expand All @@ -279,6 +287,7 @@ export default class AdminEventsTab extends PageObject {

public assertDateToChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
AdminEventsTabSearchFormFieldsLabel.DateTo,
exist,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export default class UserEventsTab extends PageObject {

public removeEventTypeChipGroupItem(itemName: string) {
super.removeChipGroupItem(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.EventType,
itemName,
);
Expand All @@ -171,6 +172,7 @@ export default class UserEventsTab extends PageObject {

public assertEventTypeChipGroupItemExist(itemName: string, exist: boolean) {
super.assertChipGroupItemExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.EventType,
itemName,
exist,
Expand All @@ -180,6 +182,7 @@ export default class UserEventsTab extends PageObject {

public assertUserIdChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.UserId,
exist,
);
Expand All @@ -188,6 +191,7 @@ export default class UserEventsTab extends PageObject {

public assertEventTypeChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.EventType,
exist,
);
Expand All @@ -196,6 +200,7 @@ export default class UserEventsTab extends PageObject {

public assertClientChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.Client,
exist,
);
Expand All @@ -204,6 +209,7 @@ export default class UserEventsTab extends PageObject {

public assertDateFromChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.DateFrom,
exist,
);
Expand All @@ -212,6 +218,7 @@ export default class UserEventsTab extends PageObject {

public assertDateToChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.DateTo,
exist,
);
Expand All @@ -220,6 +227,7 @@ export default class UserEventsTab extends PageObject {

public assertIpAddressChipGroupExist(exist: boolean) {
super.assertChipGroupExist(
this.genericChipGroupSelector,
UserEventsTabSearchFormFieldsLabel.IpAddress,
exist,
);
Expand Down
Loading

0 comments on commit 25059bf

Please sign in to comment.