keycloak-scim/cypress/integration/clients_test.spec.ts
2022-03-14 09:51:43 -04:00

777 lines
24 KiB
TypeScript

import LoginPage from "../support/pages/LoginPage";
import Masthead from "../support/pages/admin_console/Masthead";
import ListingPage, {
Filter,
FilterAssignedType,
} from "../support/pages/admin_console/ListingPage";
import SidebarPage from "../support/pages/admin_console/SidebarPage";
import CreateClientPage from "../support/pages/admin_console/manage/clients/CreateClientPage";
import ModalUtils from "../support/util/ModalUtils";
import AdvancedTab from "../support/pages/admin_console/manage/clients/AdvancedTab";
import adminClient from "../support/util/AdminClient";
import InitialAccessTokenTab from "../support/pages/admin_console/manage/clients/InitialAccessTokenTab";
import { keycloakBefore } from "../support/util/keycloak_hooks";
import RoleMappingTab from "../support/pages/admin_console/manage/RoleMappingTab";
import KeysTab from "../support/pages/admin_console/manage/clients/KeysTab";
import ClientScopesTab from "../support/pages/admin_console/manage/clients/ClientScopesTab";
import CreateRealmRolePage from "../support/pages/admin_console/manage/realm_roles/CreateRealmRolePage";
import AssociatedRolesPage from "../support/pages/admin_console/manage/realm_roles/AssociatedRolesPage";
import ClientRolesTab from "../support/pages/admin_console/manage/clients/ClientRolesTab";
let itemId = "client_crud";
const loginPage = new LoginPage();
const associatedRolesPage = new AssociatedRolesPage();
const masthead = new Masthead();
const sidebarPage = new SidebarPage();
const listingPage = new ListingPage();
const createClientPage = new CreateClientPage();
const modalUtils = new ModalUtils();
const createRealmRolePage = new CreateRealmRolePage();
describe("Clients test", () => {
describe("Client details - Client scopes subtab", () => {
const clientScopesTab = new ClientScopesTab();
const clientId = "client-scopes-subtab-test";
const clientScopeName = "client-scope-test";
const clientScopeNameDefaultType = "client-scope-test-default-type";
const clientScopeNameOptionalType = "client-scope-test-optional-type";
const clientScope = {
name: clientScopeName,
description: "",
protocol: "openid-connect",
attributes: {
"include.in.token.scope": "true",
"display.on.consent.screen": "true",
"gui.order": "1",
"consent.screen.text": "",
},
};
const msgScopeMappingRemoved = "Scope mapping successfully removed";
before(async () => {
adminClient.createClient({
clientId,
protocol: "openid-connect",
publicClient: false,
});
for (let i = 0; i < 5; i++) {
clientScope.name = clientScopeName + i;
await adminClient.createClientScope(clientScope);
await adminClient.addDefaultClientScopeInClient(
clientScopeName + i,
clientId
);
}
clientScope.name = clientScopeNameDefaultType;
await adminClient.createClientScope(clientScope);
clientScope.name = clientScopeNameOptionalType;
await adminClient.createClientScope(clientScope);
});
beforeEach(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToClients();
cy.intercept("/admin/realms/master/clients/*").as("fetchClient");
listingPage.searchItem(clientId).goToItemDetails(clientId);
cy.wait("@fetchClient");
clientScopesTab.goToClientScopesTab();
});
after(async () => {
adminClient.deleteClient(clientId);
for (let i = 0; i < 5; i++) {
await adminClient.deleteClientScope(clientScopeName + i);
}
await adminClient.deleteClientScope(clientScopeNameDefaultType);
await adminClient.deleteClientScope(clientScopeNameOptionalType);
});
it("Should list client scopes", () => {
listingPage.itemsGreaterThan(1).itemExist(clientScopeName + 0);
});
it("Should search existing client scope by name", () => {
listingPage
.searchItem(clientScopeName + 0, false)
.itemExist(clientScopeName + 0)
.itemsEqualTo(2);
});
it("Should search non-existent client scope by name", () => {
const itemName = "non-existent-item";
listingPage.searchItem(itemName, false).checkTableExists(false);
});
it("Should search existing client scope by assigned type", () => {
listingPage
.selectFilter(Filter.AssignedType)
.selectSecondaryFilterAssignedType(FilterAssignedType.Default)
.itemExist(FilterAssignedType.Default)
.itemExist(FilterAssignedType.Optional, false)
.selectSecondaryFilterAssignedType(FilterAssignedType.Optional)
.itemExist(FilterAssignedType.Default, false)
.itemExist(FilterAssignedType.Optional)
.selectSecondaryFilterAssignedType(FilterAssignedType.AllTypes)
.itemExist(FilterAssignedType.Default)
.itemExist(FilterAssignedType.Optional);
});
/*it("Should empty search", () => {
});*/
const newItemsWithExpectedAssignedTypes = [
[clientScopeNameOptionalType, FilterAssignedType.Optional],
[clientScopeNameDefaultType, FilterAssignedType.Default],
];
newItemsWithExpectedAssignedTypes.forEach(($type) => {
const [itemName, assignedType] = $type;
it(`Should add client scope ${itemName} with ${assignedType} assigned type`, () => {
listingPage.clickPrimaryButton();
modalUtils.checkModalTitle("Add client scopes to " + clientId);
listingPage.clickItemCheckbox(itemName);
modalUtils.confirmModalWithItem(assignedType);
masthead.checkNotificationMessage("Scope mapping successfully updated");
listingPage
.searchItem(itemName, false)
.itemExist(itemName)
.itemExist(assignedType);
});
});
const expectedItemAssignedTypes = [
FilterAssignedType.Optional,
FilterAssignedType.Default,
];
expectedItemAssignedTypes.forEach(($assignedType) => {
const itemName = clientScopeName + 0;
it(`Should change item ${itemName} AssignedType to ${$assignedType} from search bar`, () => {
listingPage
.searchItem(itemName, false)
.clickItemCheckbox(itemName)
.changeTypeToOfSelectedItems($assignedType);
masthead.checkNotificationMessage("Scope mapping updated");
listingPage.searchItem(itemName, false).itemExist($assignedType);
});
});
it("Should show items on next page are more than 11", () => {
listingPage.showNextPageTableItems().itemsGreaterThan(1);
});
it("Should remove client scope from item bar", () => {
const itemName = clientScopeName + 0;
listingPage.searchItem(itemName, false).removeItem(itemName);
masthead.checkNotificationMessage(msgScopeMappingRemoved);
listingPage.searchItem(itemName, false).checkTableExists(false);
});
/*it("Should remove client scope from search bar", () => {
//covered by next test
});*/
// TODO: https://github.com/keycloak/keycloak-admin-ui/issues/1854
it("Should remove multiple client scopes from search bar", () => {
const itemName1 = clientScopeName + 1;
const itemName2 = clientScopeName + 2;
listingPage
.clickSearchBarActionButton()
.checkDropdownItemIsDisabled("Remove")
.searchItem(clientScopeName, false)
.clickItemCheckbox(itemName1)
.clickItemCheckbox(itemName2)
.clickSearchBarActionButton()
.clickSearchBarActionItem("Remove");
masthead.checkNotificationMessage(msgScopeMappingRemoved);
listingPage
.searchItem(clientScopeName, false)
.itemExist(itemName1, false)
.itemExist(itemName2, false)
.clickSearchBarActionButton();
//.checkDropdownItemIsDisabled("Remove");
});
//TODO: https://github.com/keycloak/keycloak-admin-ui/issues/1874
/* it("Should show initial items after filtering", () => {
listingPage
.selectFilter(Filter.AssignedType)
.selectFilterAssignedType(FilterAssignedType.Optional)
.selectFilter(Filter.Name)
.itemExist(FilterAssignedType.Default)
.itemExist(FilterAssignedType.Optional);
});*/
});
describe("Client creation", () => {
before(() => {
keycloakBefore();
loginPage.logIn();
});
beforeEach(() => {
sidebarPage.goToClients();
});
it("Should cancel creating client", () => {
listingPage.goToCreateItem();
createClientPage.continue().checkClientIdRequiredMessage();
createClientPage
.fillClientData("")
.selectClientType("openid-connect")
.cancel();
cy.url().should("not.include", "/add-client");
});
it("Should navigate to previous using 'back' button", () => {
listingPage.goToCreateItem();
createClientPage.continue().checkClientIdRequiredMessage();
createClientPage
.fillClientData("test_client")
.selectClientType("openid-connect")
.continue()
.back()
.checkGeneralSettingsStepActive();
});
it("Should fail creating client", () => {
listingPage.goToCreateItem();
createClientPage.continue().checkClientIdRequiredMessage();
createClientPage
.fillClientData("")
.selectClientType("openid-connect")
.continue()
.checkClientIdRequiredMessage();
createClientPage.fillClientData("account").continue().save();
// The error should inform about duplicated name/id
masthead.checkNotificationMessage(
"Could not create client: 'Client account already exists'"
);
});
it("Client CRUD test", () => {
itemId += "_" + (Math.random() + 1).toString(36).substring(7);
// Create
listingPage.itemExist(itemId, false).goToCreateItem();
createClientPage
.selectClientType("openid-connect")
.fillClientData(itemId)
.continue()
.switchClientAuthentication()
.clickDirectAccess()
.clickImplicitFlow()
.clickOAuthDeviceAuthorizationGrant()
.clickOidcCibaGrant()
.clickServiceAccountRoles()
.clickStandardFlow()
.save();
masthead.checkNotificationMessage("Client created successfully");
sidebarPage.goToClients();
listingPage.searchItem("John Doe", false).checkEmptyList();
listingPage.searchItem("").itemExist("account");
listingPage.searchItem(itemId).itemExist(itemId);
// Delete
listingPage.deleteItem(itemId);
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle(`Delete ${itemId} ?`).confirmModal();
masthead.checkNotificationMessage("The client has been deleted");
listingPage.itemExist(itemId, false);
});
it.skip("Initial access token", () => {
const initialAccessTokenTab = new InitialAccessTokenTab();
initialAccessTokenTab
.goToInitialAccessTokenTab()
.shouldBeEmpty()
.goToCreateFromEmptyList()
.fillNewTokenData(1, 3)
.save();
modalUtils.checkModalTitle("Initial access token details").closeModal();
masthead.checkNotificationMessage(
"New initial access token has been created"
);
initialAccessTokenTab.shouldNotBeEmpty();
listingPage
.searchItem("John Doe", false)
.checkEmptyList()
.searchItem("", false);
initialAccessTokenTab.getFirstId((id) => {
listingPage
.checkRowColumnValue(id, 4, "3")
.checkRowColumnValue(id, 5, "3")
.itemExist(id);
});
listingPage.goToCreateItem();
initialAccessTokenTab.fillNewTokenData(1, 3).save();
modalUtils.closeModal();
initialAccessTokenTab.getFirstId((id) => {
listingPage.deleteItem(id);
sidebarPage.waitForPageLoad();
modalUtils
.checkModalTitle("Delete initial access token?")
.confirmModal();
});
masthead.checkNotificationMessage(
"Initial access token deleted successfully"
);
initialAccessTokenTab.shouldNotBeEmpty();
initialAccessTokenTab.getFirstId((id) => {
listingPage.deleteItem(id);
sidebarPage.waitForPageLoad();
modalUtils.confirmModal();
});
initialAccessTokenTab.shouldBeEmpty();
});
});
describe("Roles tab test", () => {
const rolesTab = new ClientRolesTab();
let client: string;
before(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToClients();
client = "client_" + (Math.random() + 1).toString(36).substring(7);
listingPage.goToCreateItem();
createClientPage
.selectClientType("openid-connect")
.fillClientData(client)
.continue()
.save();
masthead.checkNotificationMessage("Client created successfully", true);
});
beforeEach(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToClients();
listingPage.searchItem(client).goToItemDetails(client);
rolesTab.goToRolesTab();
});
after(() => {
adminClient.deleteClient(client);
});
it("Should fail to create client role with empty name", () => {
rolesTab.goToCreateRoleFromEmptyState();
createRealmRolePage.fillRealmRoleData("").save();
createRealmRolePage.checkRealmRoleNameRequiredMessage();
});
it("Should create client role", () => {
rolesTab.goToCreateRoleFromEmptyState();
createRealmRolePage.fillRealmRoleData(itemId).save();
masthead.checkNotificationMessage("Role created", true);
});
it("Should update client role description", () => {
listingPage.searchItem(itemId, false).goToItemDetails(itemId);
const updateDescription = "updated description";
createRealmRolePage.updateDescription(updateDescription).save();
masthead.checkNotificationMessage("The role has been saved", true);
createRealmRolePage.checkDescription(updateDescription);
});
it("Should add attribute to client role", () => {
cy.intercept("/admin/realms/master/roles-by-id/*").as("load");
listingPage.goToItemDetails(itemId);
rolesTab.goToAttributesTab();
cy.wait(["@load", "@load"]);
rolesTab.addAttribute();
masthead.checkNotificationMessage("The role has been saved", true);
});
it("Should delete attribute from client role", () => {
cy.intercept("/admin/realms/master/roles-by-id/*").as("load");
listingPage.goToItemDetails(itemId);
rolesTab.goToAttributesTab();
cy.wait(["@load", "@load"]);
rolesTab.deleteAttribute();
masthead.checkNotificationMessage("The role has been saved", true);
});
it("Should create client role to be deleted", () => {
rolesTab.goToCreateRoleFromToolbar();
createRealmRolePage.fillRealmRoleData("client_role_to_be_deleted").save();
masthead.checkNotificationMessage("Role created", true);
});
it("Should fail to create duplicate client role", () => {
rolesTab.goToCreateRoleFromToolbar();
createRealmRolePage.fillRealmRoleData(itemId).save();
masthead.checkNotificationMessage(
`Could not create role: Role with name ${itemId} already exists`,
true
);
});
it("Should search existing client role", () => {
listingPage.searchItem(itemId, false).itemExist(itemId);
});
it("Should search non-existing role test", () => {
listingPage.searchItem("role_DNE", false);
cy.findByTestId(listingPage.emptyState).should("exist");
});
it("roles empty search test", () => {
listingPage.searchItem("", false);
cy.get("table:visible");
});
it("Add associated roles test", () => {
listingPage.searchItem(itemId, false).goToItemDetails(itemId);
// Add associated realm role
associatedRolesPage.addAssociatedRealmRole("create-realm");
masthead.checkNotificationMessage(
"Associated roles have been added",
true
);
// Add associated client role
associatedRolesPage.addAssociatedClientRole("create-client");
masthead.checkNotificationMessage(
"Associated roles have been added",
true
);
rolesTab.goToAssociatedRolesTab();
// Add associated client role
associatedRolesPage.addAssociatedClientRole("manage-consent");
masthead.checkNotificationMessage(
"Associated roles have been added",
true
);
});
it("Should hide inherited roles test", () => {
listingPage.searchItem(itemId, false).goToItemDetails(itemId);
rolesTab.goToAssociatedRolesTab();
rolesTab.hideInheritedRoles();
});
it("Should delete associated roles test", () => {
listingPage.searchItem(itemId, false).goToItemDetails(itemId);
rolesTab.goToAssociatedRolesTab();
listingPage.removeItem("create-realm");
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle("Remove associated role?").confirmModal();
sidebarPage.waitForPageLoad();
masthead.checkNotificationMessage(
"Associated roles have been removed",
true
);
listingPage.removeItem("manage-consent");
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle("Remove associated role?").confirmModal();
});
it("Should delete associated role from search bar test", () => {
listingPage.searchItem(itemId, false).goToItemDetails(itemId);
sidebarPage.waitForPageLoad();
rolesTab.goToAssociatedRolesTab();
cy.get('td[data-label="Role name"]')
.contains("create-client")
.parent()
.within(() => {
cy.get("input").click();
});
associatedRolesPage.removeAssociatedRoles();
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle("Remove associated roles?").confirmModal();
sidebarPage.waitForPageLoad();
masthead.checkNotificationMessage(
"Associated roles have been removed",
true
);
});
it("Should delete client role test", () => {
listingPage.deleteItem(itemId);
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle("Delete role?").confirmModal();
});
it("Should delete client role from role details test", () => {
listingPage
.searchItem("client_role_to_be_deleted", false)
.goToItemDetails("client_role_to_be_deleted");
createRealmRolePage.clickActionMenu("Delete this role");
modalUtils.confirmModal();
masthead.checkNotificationMessage("The role has been deleted", true);
});
});
describe("Advanced tab test", () => {
const advancedTab = new AdvancedTab();
let client: string;
before(() => {
keycloakBefore();
loginPage.logIn();
});
beforeEach(() => {
sidebarPage.goToClients();
client = "client_" + (Math.random() + 1).toString(36).substring(7);
listingPage.goToCreateItem();
createClientPage
.selectClientType("openid-connect")
.fillClientData(client)
.continue()
.save();
advancedTab.goToAdvancedTab();
});
afterEach(() => {
adminClient.deleteClient(client);
});
it("Clustering", () => {
advancedTab.expandClusterNode();
advancedTab.registerNodeManually().fillHost("localhost").saveHost();
advancedTab.checkTestClusterAvailability(true);
});
it("Fine grain OpenID connect configuration", () => {
const algorithm = "ES384";
advancedTab
.selectAccessTokenSignatureAlgorithm(algorithm)
.saveFineGrain();
advancedTab
.selectAccessTokenSignatureAlgorithm("HS384")
.revertFineGrain();
advancedTab.checkAccessTokenSignatureAlgorithm(algorithm);
});
});
describe("Service account tab test", () => {
const serviceAccountTab = new RoleMappingTab();
const serviceAccountName = "service-account-client";
before(() => {
keycloakBefore();
loginPage.logIn();
adminClient.createClient({
protocol: "openid-connect",
clientId: serviceAccountName,
publicClient: false,
authorizationServicesEnabled: true,
serviceAccountsEnabled: true,
standardFlowEnabled: true,
});
});
beforeEach(() => {
sidebarPage.goToClients();
});
after(() => {
adminClient.deleteClient(serviceAccountName);
});
it("List", () => {
listingPage
.searchItem(serviceAccountName)
.goToItemDetails(serviceAccountName);
serviceAccountTab
.goToServiceAccountTab()
.checkRoles(["manage-account", "offline_access", "uma_authorization"]);
});
it("Assign", () => {
listingPage.goToItemDetails(serviceAccountName);
serviceAccountTab
.goToServiceAccountTab()
.assignRole(false)
.selectRow("create-realm")
.assign();
masthead.checkNotificationMessage("Role mapping updated");
serviceAccountTab.selectRow("create-realm").unAssign();
sidebarPage.waitForPageLoad();
modalUtils.checkModalTitle("Remove mapping?").confirmModal();
masthead.checkNotificationMessage("Scope mapping successfully removed");
});
});
describe("Mapping tab", () => {
const clientScopeTab = new ClientScopesTab();
const mappingClient = "mapping-client";
beforeEach(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToClients();
listingPage.searchItem(mappingClient).goToItemDetails(mappingClient);
});
before(() => {
adminClient.createClient({
protocol: "openid-connect",
clientId: mappingClient,
publicClient: false,
});
});
after(() => {
adminClient.deleteClient(mappingClient);
});
it("Add mapping to openid client", () => {
clientScopeTab.goToClientScopesTab().clickDedicatedScope(mappingClient);
cy.findByTestId("mappersTab").click();
cy.findByText("Add predefined mapper").click();
cy.get("table input").first().click();
cy.findByTestId("confirm").click();
masthead.checkNotificationMessage("Mapping successfully created");
});
});
describe("Keys tab test", () => {
const keysName = "keys-client";
before(() => {
keycloakBefore();
loginPage.logIn();
adminClient.createClient({
protocol: "openid-connect",
clientId: keysName,
publicClient: false,
});
});
beforeEach(() => {
sidebarPage.goToClients();
listingPage.searchItem(keysName).goToItemDetails(keysName);
});
after(() => {
adminClient.deleteClient(keysName);
});
it("Change use JWKS Url", () => {
const keysTab = new KeysTab();
keysTab.goToTab().checkSaveDisabled();
keysTab.toggleUseJwksUrl().checkSaveDisabled(false);
});
it("Generate new keys", () => {
const keysTab = new KeysTab();
keysTab.goToTab().clickGenerate();
keysTab.fillGenerateModal("keyname", "123", "1234").clickConfirm();
masthead.checkNotificationMessage(
"New key pair and certificate generated successfully"
);
});
});
describe("Realm client", () => {
const clientName = "master-realm";
before(() => {
keycloakBefore();
loginPage.logIn();
sidebarPage.goToClients();
listingPage.searchItem(clientName).goToItemDetails(clientName);
});
it("Displays the correct tabs", () => {
cy.findByTestId("client-tabs")
.findByTestId("clientSettingsTab")
.should("exist");
cy.findByTestId("client-tabs").findByTestId("rolesTab").should("exist");
cy.findByTestId("client-tabs")
.findByTestId("advancedTab")
.should("exist");
cy.findByTestId("client-tabs").find("li").should("have.length", 3);
});
it("Hides the delete action", () => {
cy.findByTestId("action-dropdown").click();
cy.findByTestId("delete-client").should("not.exist");
});
});
describe("Bearer only", () => {
const clientId = "bearer-only";
before(() => {
keycloakBefore();
loginPage.logIn();
adminClient.createClient({
clientId,
protocol: "openid-connect",
publicClient: false,
bearerOnly: true,
});
sidebarPage.goToClients();
cy.intercept("/admin/realms/master/clients/*").as("fetchClient");
listingPage.searchItem(clientId).goToItemDetails(clientId);
cy.wait("@fetchClient");
});
after(() => {
adminClient.deleteClient(clientId);
});
it("Shows an explainer text for bearer only clients", () => {
cy.findByTestId("bearer-only-explainer-label").trigger("mouseenter");
cy.findByTestId("bearer-only-explainer-tooltip").should("exist");
});
it("Hides the capability config section", () => {
cy.findByTestId("capability-config-form").should("not.exist");
cy.findByTestId("jump-link-capability-config").should("not.exist");
});
});
});