keycloak-scim/js/libs/keycloak-admin-client/test/users.spec.ts
Tobi a71d07d9d8
Fix search by attribute tests (#27670)
Closes #16948

Signed-off-by: twobiers <22715034+twobiers@users.noreply.github.com>
2024-03-11 17:16:22 +01:00

700 lines
19 KiB
TypeScript

// tslint:disable:no-unused-expression
import { faker } from "@faker-js/faker";
import { fail } from "assert";
import * as chai from "chai";
import { omit } from "lodash-es";
import { KeycloakAdminClient } from "../src/client.js";
import type ClientRepresentation from "../src/defs/clientRepresentation.js";
import type FederatedIdentityRepresentation from "../src/defs/federatedIdentityRepresentation.js";
import type GroupRepresentation from "../src/defs/groupRepresentation.js";
import { RequiredActionAlias } from "../src/defs/requiredActionProviderRepresentation.js";
import type RoleRepresentation from "../src/defs/roleRepresentation.js";
import type UserRepresentation from "../src/defs/userRepresentation.js";
import { UnmanagedAttributePolicy } from "../src/defs/userProfileMetadata.js";
import { credentials } from "./constants.js";
const expect = chai.expect;
describe("Users", () => {
let kcAdminClient: KeycloakAdminClient;
let currentClient: ClientRepresentation;
let currentUser: UserRepresentation;
let currentRole: RoleRepresentation;
let federatedIdentity: FederatedIdentityRepresentation;
before(async () => {
kcAdminClient = new KeycloakAdminClient();
await kcAdminClient.auth(credentials);
// Enable unmanaged attributes
const currentProfileConfig = await kcAdminClient.users.getProfile();
await kcAdminClient.users.updateProfile({
...currentProfileConfig,
unmanagedAttributePolicy: UnmanagedAttributePolicy.Enabled,
});
// initialize user
const username = faker.internet.userName();
const user = await kcAdminClient.users.create({
username,
email: "test@keycloak.org",
// enabled required to be true in order to send actions email
emailVerified: true,
enabled: true,
attributes: {
key: "value",
},
});
expect(user.id).to.be.ok;
currentUser = (await kcAdminClient.users.findOne({ id: user.id }))!;
// add smtp to realm
await kcAdminClient.realms.update(
{ realm: "master" },
{
smtpServer: {
auth: true,
from: "0830021730-07fb21@inbox.mailtrap.io",
host: "smtp.mailtrap.io",
user: process.env.SMTP_USER,
password: process.env.SMTP_PWD,
},
},
);
});
after(async () => {
const userId = currentUser.id;
await kcAdminClient.users.del({
id: userId!,
});
const user = await kcAdminClient.users.findOne({
id: userId!,
});
expect(user).to.be.null;
});
it("list users", async () => {
const users = await kcAdminClient.users.find();
expect(users).to.be.ok;
});
it("count users", async () => {
const numUsers = await kcAdminClient.users.count();
// admin user + created user in before hook
expect(numUsers).to.equal(2);
});
it("count users with filter", async () => {
const numUsers = await kcAdminClient.users.count({
email: "test@keycloak.org",
});
expect(numUsers).to.equal(1);
});
it.skip("gets the profile", async () => {
const profile = await kcAdminClient.users.getProfile();
expect(profile).to.be.ok;
});
it.skip("updates the profile", async () => {
const profile = await kcAdminClient.users.updateProfile({});
expect(profile).to.be.ok;
});
it("find users by custom attributes", async () => {
// Searching by attributes is only available from Keycloak > 15
const users = await kcAdminClient.users.find({ q: "key:value" });
expect(users.length).to.be.equal(1);
expect(users[0]).to.be.deep.include(currentUser);
});
it("find users by builtin attributes", async () => {
// Searching by attributes is only available from Keycloak > 15
const users = await kcAdminClient.users.find({
q: `email:${currentUser.email}`,
});
expect(users.length).to.be.equal(1);
expect(users[0]).to.be.deep.include(currentUser);
});
it("get single users", async () => {
const userId = currentUser.id;
const user = await kcAdminClient.users.findOne({
id: userId!,
});
expect(user).to.be.deep.include(currentUser);
});
it("update single users", async () => {
const userId = currentUser.id;
await kcAdminClient.users.update(
{ id: userId! },
{
firstName: "william",
lastName: "chang",
requiredActions: [RequiredActionAlias.UPDATE_PASSWORD],
emailVerified: true,
},
);
const user = await kcAdminClient.users.findOne({
id: userId!,
});
expect(user).to.deep.include({
firstName: "william",
lastName: "chang",
requiredActions: [RequiredActionAlias.UPDATE_PASSWORD],
emailVerified: true,
});
});
/**
* reset password
*/
it("should reset user password", async () => {
const userId = currentUser.id;
await kcAdminClient.users.resetPassword({
id: userId!,
credential: {
temporary: false,
type: "password",
value: "test",
},
});
});
/**
* get user credentials
*/
it("get user credentials", async () => {
const userId = currentUser.id;
const result = await kcAdminClient.users.getCredentials({
id: userId!,
});
expect(result.map((c) => c.type)).to.include("password");
});
it("get configured user storage credential types", async () => {
const userId = currentUser.id;
const result = await kcAdminClient.users.getUserStorageCredentialTypes({
id: userId!,
});
expect(result).to.be.deep.eq([]);
});
/**
* delete user credentials
*/
it("delete user credentials", async () => {
const userId = currentUser.id;
const result = await kcAdminClient.users.getCredentials({
id: userId!,
});
expect(result.map((c) => c.type)).to.include("password");
const credential = result[0];
await kcAdminClient.users.deleteCredential({
id: userId!,
credentialId: credential.id!,
});
const credentialsAfterDelete = await kcAdminClient.users.getCredentials({
id: userId!,
});
expect(credentialsAfterDelete).to.not.deep.include(credential);
// Add deleted password back
await kcAdminClient.users.resetPassword({
id: userId!,
credential: {
temporary: false,
type: "password",
value: "test",
},
});
});
/**
* update a credential label for a user
*/
it("update a credential label for a user", async () => {
const userId = currentUser.id;
const result = await kcAdminClient.users.getCredentials({
id: userId!,
});
expect(result.map((c) => c.type)).to.include("password");
const credential = result[0];
await kcAdminClient.users.updateCredentialLabel(
{ id: userId!, credentialId: credential.id! },
"New user label",
);
const credentialsAfterLabelUpdate =
await kcAdminClient.users.getCredentials({
id: userId!,
});
expect(credentialsAfterLabelUpdate.map((c) => c.userLabel)).to.include(
"New user label",
);
});
/**
* Groups
*/
describe("user groups", () => {
let currentGroup: GroupRepresentation;
before(async () => {
const group = await kcAdminClient.groups.create({
name: "cool-group",
});
expect(group.id).to.be.ok;
currentGroup = (await kcAdminClient.groups.findOne({ id: group.id }))!;
});
after(async () => {
const groupId = currentGroup.id;
const groups = await kcAdminClient.groups.find({ max: 100 });
await Promise.all(
groups.map((_group: GroupRepresentation) => {
return kcAdminClient.groups.del({ id: _group.id! });
}),
);
const group = await kcAdminClient.groups.findOne({
id: groupId!,
});
expect(group).to.be.null;
});
it("add group", async () => {
let count = (
await kcAdminClient.users.countGroups({ id: currentUser.id! })
).count;
expect(count).to.eq(0);
await kcAdminClient.users.addToGroup({
groupId: currentGroup.id!,
id: currentUser.id!,
});
count = (await kcAdminClient.users.countGroups({ id: currentUser.id! }))
.count;
expect(count).to.eq(1);
});
it("count groups", async () => {
let { count } = await kcAdminClient.users.countGroups({
id: currentUser.id!,
});
expect(count).to.eq(1);
count = (
await kcAdminClient.users.countGroups({
id: currentUser.id!,
search: "cool-group",
})
).count;
expect(count).to.eq(1);
count = (
await kcAdminClient.users.countGroups({
id: currentUser.id!,
search: "fake-group",
})
).count;
expect(count).to.eq(0);
});
it("list groups", async () => {
const groups = await kcAdminClient.users.listGroups({
id: currentUser.id!,
});
expect(groups).to.be.ok;
expect(groups.length).to.be.eq(1);
expect(groups[0].name).to.eq("cool-group");
});
it("remove group", async () => {
const newGroup = await kcAdminClient.groups.create({
name: "test-group",
});
await kcAdminClient.users.addToGroup({
id: currentUser.id!,
groupId: newGroup.id,
});
let count = (
await kcAdminClient.users.countGroups({ id: currentUser.id! })
).count;
expect(count).to.eq(2);
try {
await kcAdminClient.users.delFromGroup({
id: currentUser.id!,
groupId: newGroup.id,
});
} catch (e) {
fail("Didn't expect an error when deleting a valid group id");
}
count = (await kcAdminClient.users.countGroups({ id: currentUser.id! }))
.count;
expect(count).to.equal(1);
await kcAdminClient.groups.del({ id: newGroup.id });
// delete a non-existing group should throw an error
try {
await kcAdminClient.users.delFromGroup({
id: currentUser.id!,
groupId: "fake-group-id",
});
fail(
"Expected an error when deleting a fake id not assigned to the user",
);
} catch (e) {
expect(e).to.be.ok;
}
});
});
/**
* Role mappings
*/
describe("role-mappings", () => {
before(async () => {
// create new role
const roleName = faker.internet.userName();
await kcAdminClient.roles.create({
name: roleName,
});
const role = await kcAdminClient.roles.findOneByName({
name: roleName,
});
currentRole = role!;
});
after(async () => {
await kcAdminClient.roles.delByName({ name: currentRole.name! });
});
it("add a role to user", async () => {
// add role-mappings with role id
await kcAdminClient.users.addRealmRoleMappings({
id: currentUser.id!,
// at least id and name should appear
roles: [
{
id: currentRole.id!,
name: currentRole.name!,
},
],
});
});
it("list available role-mappings for user", async () => {
const roles = await kcAdminClient.users.listAvailableRealmRoleMappings({
id: currentUser.id!,
});
// admin, create-realm
// not sure why others like offline_access, uma_authorization not included
expect(roles.length).to.be.least(2);
});
it("list role-mappings of user", async () => {
const res = await kcAdminClient.users.listRoleMappings({
id: currentUser.id!,
});
expect(res).have.all.keys("realmMappings");
});
it("list realm role-mappings of user", async () => {
const roles = await kcAdminClient.users.listRealmRoleMappings({
id: currentUser.id!,
});
// currentRole will have an empty `attributes`, but role-mappings do not
expect(roles).to.deep.include(omit(currentRole, "attributes"));
});
it("list realm composite role-mappings of user", async () => {
const roles = await kcAdminClient.users.listCompositeRealmRoleMappings({
id: currentUser.id!,
});
// todo: add data integrity check later
expect(roles).to.be.ok;
});
it("del realm role-mappings from user", async () => {
await kcAdminClient.users.delRealmRoleMappings({
id: currentUser.id!,
roles: [
{
id: currentRole.id!,
name: currentRole.name!,
},
],
});
const roles = await kcAdminClient.users.listRealmRoleMappings({
id: currentUser.id!,
});
expect(roles).to.not.deep.include(currentRole);
});
});
/**
* client Role mappings
*/
describe("client role-mappings", () => {
before(async () => {
// create new client
const clientId = faker.internet.userName();
await kcAdminClient.clients.create({
clientId,
});
const clients = await kcAdminClient.clients.find({ clientId });
expect(clients[0]).to.be.ok;
currentClient = clients[0];
// create new client role
const roleName = faker.internet.userName();
await kcAdminClient.clients.createRole({
id: currentClient.id,
name: roleName,
});
// assign to currentRole
currentRole = await kcAdminClient.clients.findRole({
id: currentClient.id!,
roleName,
});
});
after(async () => {
await kcAdminClient.clients.delRole({
id: currentClient.id!,
roleName: currentRole.name!,
});
await kcAdminClient.clients.del({ id: currentClient.id! });
});
it("add a client role to user", async () => {
// add role-mappings with role id
await kcAdminClient.users.addClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
// at least id and name should appear
roles: [
{
id: currentRole.id!,
name: currentRole.name!,
},
],
});
});
it("list available client role-mappings for user", async () => {
const roles = await kcAdminClient.users.listAvailableClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
});
expect(roles).to.be.empty;
});
it("list composite client role-mappings for user", async () => {
const roles = await kcAdminClient.users.listCompositeClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
});
expect(roles).to.be.ok;
});
it("list client role-mappings of user", async () => {
const roles = await kcAdminClient.users.listClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
});
// currentRole will have an empty `attributes`, but role-mappings do not
expect(currentRole).to.deep.include(roles[0]);
});
it("del client role-mappings from user", async () => {
const roleName = faker.internet.userName();
await kcAdminClient.clients.createRole({
id: currentClient.id,
name: roleName,
});
const role = await kcAdminClient.clients.findRole({
id: currentClient.id!,
roleName,
});
// delete the created role
await kcAdminClient.users.delClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
roles: [
{
id: role.id!,
name: role.name!,
},
],
});
// check if mapping is successfully deleted
const roles = await kcAdminClient.users.listClientRoleMappings({
id: currentUser.id!,
clientUniqueId: currentClient.id!,
});
// should only left the one we added in the previous test
expect(roles.length).to.be.eql(1);
});
});
describe("User sessions", () => {
before(async () => {
kcAdminClient = new KeycloakAdminClient();
await kcAdminClient.auth(credentials);
// create client
const clientId = faker.internet.userName();
await kcAdminClient.clients.create({
clientId,
consentRequired: true,
});
const clients = await kcAdminClient.clients.find({ clientId });
expect(clients[0]).to.be.ok;
currentClient = clients[0];
});
after(async () => {
await kcAdminClient.clients.del({
id: currentClient.id!,
});
});
it("list user sessions", async () => {
// @TODO: In order to test it, currentUser has to be logged in
const userSessions = await kcAdminClient.users.listSessions({
id: currentUser.id!,
});
expect(userSessions).to.be.ok;
});
it("list users off-line sessions", async () => {
// @TODO: In order to test it, currentUser has to be logged in
const userOfflineSessions = await kcAdminClient.users.listOfflineSessions(
{ id: currentUser.id!, clientId: currentClient.id! },
);
expect(userOfflineSessions).to.be.ok;
});
it("logout user from all sessions", async () => {
// @TODO: In order to test it, currentUser has to be logged in
await kcAdminClient.users.logout({ id: currentUser.id! });
});
it("list consents granted by the user", async () => {
const consents = await kcAdminClient.users.listConsents({
id: currentUser.id!,
});
expect(consents).to.be.ok;
});
it("revoke consent and offline tokens for particular client", async () => {
// @TODO: In order to test it, currentUser has to granted consent to client
const consents = await kcAdminClient.users.listConsents({
id: currentUser.id!,
});
if (consents.length) {
const consent = consents[0];
await kcAdminClient.users.revokeConsent({
id: currentUser.id!,
clientId: consent.clientId!,
});
}
});
it("impersonate user", async () => {
const result = await kcAdminClient.users.impersonation(
{ id: currentUser.id! },
{ user: currentUser.id!, realm: kcAdminClient.realmName },
);
expect(result).to.be.ok;
await kcAdminClient.auth(credentials);
});
});
describe("Federated Identity user integration", () => {
before(async () => {
kcAdminClient = new KeycloakAdminClient();
await kcAdminClient.auth(credentials);
federatedIdentity = {
identityProvider: "foobar",
userId: "userid1",
userName: "username1",
};
});
it("should list user federated identities and expect empty", async () => {
const federatedIdentities =
await kcAdminClient.users.listFederatedIdentities({
id: currentUser.id!,
});
expect(federatedIdentities).to.be.eql([]);
});
it("should add federated identity to user", async () => {
await kcAdminClient.users.addToFederatedIdentity({
id: currentUser.id!,
federatedIdentityId: "foobar",
federatedIdentity,
});
// @TODO: In order to test the integration with federated identities, the User Federation
// would need to be created first, this is not implemented yet.
// const federatedIdentities = await kcAdminClient.users.listFederatedIdentities({
// id: currentUser.id,
// });
// expect(federatedIdentities[0]).to.be.eql(federatedIdentity);
});
it("should remove federated identity from user", async () => {
await kcAdminClient.users.delFromFederatedIdentity({
id: currentUser.id!,
federatedIdentityId: "foobar",
});
const federatedIdentities =
await kcAdminClient.users.listFederatedIdentities({
id: currentUser.id!,
});
expect(federatedIdentities).to.be.eql([]);
});
});
});