Enable user profile by default
closes #25151 Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
parent
01cd645668
commit
692aeee17d
109 changed files with 1632 additions and 2281 deletions
4
.github/workflows/js-ci.yml
vendored
4
.github/workflows/js-ci.yml
vendored
|
@ -214,7 +214,7 @@ jobs:
|
||||||
- name: Start Keycloak server
|
- name: Start Keycloak server
|
||||||
run: |
|
run: |
|
||||||
tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
|
tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
|
||||||
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=declarative-user-profile,transient-users &> ~/server.log &
|
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=transient-users &> ~/server.log &
|
||||||
env:
|
env:
|
||||||
KEYCLOAK_ADMIN: admin
|
KEYCLOAK_ADMIN: admin
|
||||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||||
|
@ -297,7 +297,7 @@ jobs:
|
||||||
- name: Start Keycloak server
|
- name: Start Keycloak server
|
||||||
run: |
|
run: |
|
||||||
tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
|
tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
|
||||||
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=admin-fine-grained-authz,declarative-user-profile,transient-users &> ~/server.log &
|
keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log &
|
||||||
env:
|
env:
|
||||||
KEYCLOAK_ADMIN: admin
|
KEYCLOAK_ADMIN: admin
|
||||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||||
|
|
|
@ -73,8 +73,6 @@ public class Profile {
|
||||||
|
|
||||||
PAR("OAuth 2.0 Pushed Authorization Requests (PAR)", Type.DEFAULT),
|
PAR("OAuth 2.0 Pushed Authorization Requests (PAR)", Type.DEFAULT),
|
||||||
|
|
||||||
DECLARATIVE_USER_PROFILE("Configure user profiles using a declarative style", Type.PREVIEW),
|
|
||||||
|
|
||||||
DYNAMIC_SCOPES("Dynamic OAuth 2.0 scopes", Type.EXPERIMENTAL),
|
DYNAMIC_SCOPES("Dynamic OAuth 2.0 scopes", Type.EXPERIMENTAL),
|
||||||
|
|
||||||
CLIENT_SECRET_ROTATION("Client Secret Rotation", Type.PREVIEW),
|
CLIENT_SECRET_ROTATION("Client Secret Rotation", Type.PREVIEW),
|
||||||
|
|
|
@ -79,7 +79,6 @@ public class ProfileTest {
|
||||||
Profile.Feature.RECOVERY_CODES,
|
Profile.Feature.RECOVERY_CODES,
|
||||||
Profile.Feature.SCRIPTS,
|
Profile.Feature.SCRIPTS,
|
||||||
Profile.Feature.TOKEN_EXCHANGE,
|
Profile.Feature.TOKEN_EXCHANGE,
|
||||||
Profile.Feature.DECLARATIVE_USER_PROFILE,
|
|
||||||
Profile.Feature.CLIENT_SECRET_ROTATION,
|
Profile.Feature.CLIENT_SECRET_ROTATION,
|
||||||
Profile.Feature.UPDATE_EMAIL,
|
Profile.Feature.UPDATE_EMAIL,
|
||||||
Profile.Feature.LINKEDIN_OAUTH
|
Profile.Feature.LINKEDIN_OAUTH
|
||||||
|
@ -90,7 +89,7 @@ public class ProfileTest {
|
||||||
disabledFeatures.add(Profile.Feature.KERBEROS);
|
disabledFeatures.add(Profile.Feature.KERBEROS);
|
||||||
}
|
}
|
||||||
assertEquals(profile.getDisabledFeatures(), disabledFeatures);
|
assertEquals(profile.getDisabledFeatures(), disabledFeatures);
|
||||||
assertEquals(profile.getPreviewFeatures(), Profile.Feature.ACCOUNT3, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.MULTI_SITE, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Profile.Feature.CLIENT_SECRET_ROTATION, Profile.Feature.UPDATE_EMAIL, Profile.Feature.DPOP);
|
assertEquals(profile.getPreviewFeatures(), Profile.Feature.ACCOUNT3, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.MULTI_SITE, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.CLIENT_SECRET_ROTATION, Profile.Feature.UPDATE_EMAIL, Profile.Feature.DPOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -40,15 +40,8 @@ describe("Realm settings tabs tests", () => {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
it("shows the 'user profile' tab if enabled", () => {
|
it("shows the 'user profile' tab", () => {
|
||||||
sidebarPage.goToRealmSettings();
|
sidebarPage.goToRealmSettings();
|
||||||
cy.findByTestId(realmSettingsPage.userProfileTab).should("not.exist");
|
|
||||||
realmSettingsPage.toggleSwitch(
|
|
||||||
realmSettingsPage.profileEnabledSwitch,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
|
|
||||||
masthead.checkNotificationMessage("Realm successfully updated");
|
|
||||||
cy.findByTestId(realmSettingsPage.userProfileTab).should("exist");
|
cy.findByTestId(realmSettingsPage.userProfileTab).should("exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
import Masthead from "../support/pages/admin-ui/Masthead";
|
|
||||||
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
|
|
||||||
import LoginPage from "../support/pages/LoginPage";
|
|
||||||
import adminClient from "../support/util/AdminClient";
|
|
||||||
import { keycloakBefore } from "../support/util/keycloak_hooks";
|
|
||||||
import CreateUserPage from "../support/pages/admin-ui/manage/users/CreateUserPage";
|
|
||||||
import RealmSettingsPage from "../support/pages/admin-ui/manage/realm_settings/RealmSettingsPage";
|
|
||||||
import ListingPage from "../support/pages/admin-ui/ListingPage";
|
|
||||||
|
|
||||||
const loginPage = new LoginPage();
|
|
||||||
const sidebarPage = new SidebarPage();
|
|
||||||
const realmSettingsPage = new RealmSettingsPage();
|
|
||||||
const createUserPage = new CreateUserPage();
|
|
||||||
const masthead = new Masthead();
|
|
||||||
const listingPage = new ListingPage();
|
|
||||||
|
|
||||||
describe("User profile disabled", () => {
|
|
||||||
const realmName = "Realm_" + uuid();
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await adminClient.createRealm(realmName, {
|
|
||||||
attributes: { userProfileEnabled: "false" },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
after(async () => {
|
|
||||||
await adminClient.deleteRealm(realmName);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
loginPage.logIn();
|
|
||||||
keycloakBefore();
|
|
||||||
sidebarPage.goToRealm(realmName);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
sidebarPage.goToRealmSettings();
|
|
||||||
realmSettingsPage.goToLoginTab();
|
|
||||||
cy.wait(1000);
|
|
||||||
cy.findByTestId("email-as-username-switch").uncheck({ force: true });
|
|
||||||
cy.findByTestId("edit-username-switch").uncheck({ force: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Create user with email as username, edit username and user profile disabled", () => {
|
|
||||||
// Ensure email as username and edit username are disabled
|
|
||||||
sidebarPage.goToRealmSettings();
|
|
||||||
realmSettingsPage.goToLoginTab();
|
|
||||||
cy.findByTestId("email-as-username-switch").should("have.value", "off");
|
|
||||||
cy.findByTestId("edit-username-switch").should("have.value", "off");
|
|
||||||
|
|
||||||
// Create user
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
createUserPage.goToCreateUser();
|
|
||||||
createUserPage.createUser("testuser1");
|
|
||||||
createUserPage.save();
|
|
||||||
masthead.checkNotificationMessage("The user has been created");
|
|
||||||
|
|
||||||
// Check that username is readonly
|
|
||||||
cy.get("#kc-username").should("have.attr", "readonly");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Create user with email as username enabled, edit username disabled and user profile disabled", () => {
|
|
||||||
// Create user and check that username is not visible and that email is editable
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
createUserPage.goToCreateUser();
|
|
||||||
createUserPage.createUser("testuser2");
|
|
||||||
createUserPage.save();
|
|
||||||
masthead.checkNotificationMessage("The user has been created");
|
|
||||||
|
|
||||||
// Ensure email as username is enabled and edit username are disabled
|
|
||||||
sidebarPage.goToRealmSettings();
|
|
||||||
realmSettingsPage.goToLoginTab();
|
|
||||||
cy.findByTestId("email-as-username-switch").check({ force: true });
|
|
||||||
cy.findByTestId("email-as-username-switch").should("have.value", "on");
|
|
||||||
cy.findByTestId("edit-username-switch").should("have.value", "off");
|
|
||||||
|
|
||||||
//Find user and do some checks
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
sidebarPage.waitForPageLoad();
|
|
||||||
listingPage.goToItemDetails("testuser2");
|
|
||||||
cy.findByTestId("view-header").should("contain.text", "testuser2");
|
|
||||||
|
|
||||||
cy.get("#kc-username").should("not.exist");
|
|
||||||
cy.findByTestId("email-input").type("testuser1@gmail.com");
|
|
||||||
cy.findByTestId("save-user").click();
|
|
||||||
masthead.checkNotificationMessage("The user has been saved");
|
|
||||||
cy.findByTestId("view-header").should(
|
|
||||||
"contain.text",
|
|
||||||
"testuser1@gmail.com",
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.findByTestId("email-input").clear();
|
|
||||||
cy.findByTestId("email-input").type("testuser2@gmail.com");
|
|
||||||
cy.findByTestId("save-user").click();
|
|
||||||
masthead.checkNotificationMessage("The user has been saved");
|
|
||||||
cy.findByTestId("view-header").should(
|
|
||||||
"contain.text",
|
|
||||||
"testuser2@gmail.com",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Create user with email as username disabled, edit username enabled and user profile disabled", () => {
|
|
||||||
// Ensure email as username is disabled and edit username is enabled
|
|
||||||
sidebarPage.goToRealmSettings();
|
|
||||||
realmSettingsPage.goToLoginTab();
|
|
||||||
|
|
||||||
cy.findByTestId("email-as-username-switch").should("have.value", "off");
|
|
||||||
cy.findByTestId("edit-username-switch").check({ force: true });
|
|
||||||
cy.findByTestId("edit-username-switch").should("have.value", "on");
|
|
||||||
|
|
||||||
// Create user and check that username is visible and that email is editable
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
createUserPage.goToCreateUser();
|
|
||||||
cy.get("#kc-username").type("testuser3");
|
|
||||||
cy.findByTestId("email-input").type("testuser3@test.com");
|
|
||||||
cy.findByTestId("create-user").click();
|
|
||||||
masthead.checkNotificationMessage("The user has been created");
|
|
||||||
|
|
||||||
cy.findByTestId("email-input").clear();
|
|
||||||
cy.findByTestId("email-input").type("testuser4@test.com");
|
|
||||||
cy.findByTestId("save-user").click();
|
|
||||||
masthead.checkNotificationMessage("The user has been saved");
|
|
||||||
cy.findByTestId("view-header").should("contain.text", "testuser3");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Create user with email as username, edit username enabled and user profile disabled", () => {
|
|
||||||
// Create user and check that username is not visible and that email is editable
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
createUserPage.goToCreateUser();
|
|
||||||
createUserPage.createUser("testuser5");
|
|
||||||
cy.findByTestId("email-input").type("testuser5@gmail.com");
|
|
||||||
createUserPage.save();
|
|
||||||
masthead.checkNotificationMessage("The user has been created");
|
|
||||||
|
|
||||||
sidebarPage.goToRealmSettings();
|
|
||||||
realmSettingsPage.goToLoginTab();
|
|
||||||
cy.findByTestId("edit-username-switch").check({ force: true });
|
|
||||||
cy.findByTestId("edit-username-switch").should("have.value", "on");
|
|
||||||
cy.findByTestId("email-as-username-switch").check({ force: true });
|
|
||||||
cy.findByTestId("email-as-username-switch").should("have.value", "on");
|
|
||||||
|
|
||||||
//Find user and do some checks
|
|
||||||
sidebarPage.goToUsers();
|
|
||||||
sidebarPage.waitForPageLoad();
|
|
||||||
listingPage.goToItemDetails("testuser5");
|
|
||||||
cy.findByTestId("view-header").should("contain.text", "testuser5");
|
|
||||||
cy.get("#kc-username").should("not.exist");
|
|
||||||
|
|
||||||
cy.findByTestId("email-input").clear();
|
|
||||||
cy.findByTestId("email-input").type("testuser6@test.com");
|
|
||||||
cy.findByTestId("save-user").click();
|
|
||||||
cy.findByTestId("view-header").should("contain.text", "testuser6@test.com");
|
|
||||||
masthead.checkNotificationMessage("The user has been saved");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -31,12 +31,7 @@ describe("User profile tabs", () => {
|
||||||
const realmName = "Realm_" + uuid();
|
const realmName = "Realm_" + uuid();
|
||||||
const attributeName = "Test";
|
const attributeName = "Test";
|
||||||
|
|
||||||
before(() =>
|
before(() => adminClient.createRealm(realmName));
|
||||||
adminClient.createRealm(realmName, {
|
|
||||||
attributes: { userProfileEnabled: "true" },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
after(() => adminClient.deleteRealm(realmName));
|
after(() => adminClient.deleteRealm(realmName));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
|
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
|
||||||
import LoginPage from "../support/pages/LoginPage";
|
import LoginPage from "../support/pages/LoginPage";
|
||||||
|
import RealmSettingsPage from "../support/pages/admin-ui/manage/realm_settings/RealmSettingsPage";
|
||||||
import CreateUserPage from "../support/pages/admin-ui/manage/users/CreateUserPage";
|
import CreateUserPage from "../support/pages/admin-ui/manage/users/CreateUserPage";
|
||||||
import Masthead from "../support/pages/admin-ui/Masthead";
|
import Masthead from "../support/pages/admin-ui/Masthead";
|
||||||
import ListingPage from "../support/pages/admin-ui/ListingPage";
|
import ListingPage from "../support/pages/admin-ui/ListingPage";
|
||||||
|
@ -23,6 +24,7 @@ let groupsList: string[] = [];
|
||||||
describe("User creation", () => {
|
describe("User creation", () => {
|
||||||
const loginPage = new LoginPage();
|
const loginPage = new LoginPage();
|
||||||
const sidebarPage = new SidebarPage();
|
const sidebarPage = new SidebarPage();
|
||||||
|
const realmSettingsPage = new RealmSettingsPage();
|
||||||
const createUserPage = new CreateUserPage();
|
const createUserPage = new CreateUserPage();
|
||||||
const userGroupsPage = new UserGroupsPage();
|
const userGroupsPage = new UserGroupsPage();
|
||||||
const masthead = new Masthead();
|
const masthead = new Masthead();
|
||||||
|
@ -30,7 +32,7 @@ describe("User creation", () => {
|
||||||
const listingPage = new ListingPage();
|
const listingPage = new ListingPage();
|
||||||
const userDetailsPage = new UserDetailsPage();
|
const userDetailsPage = new UserDetailsPage();
|
||||||
const credentialsPage = new CredentialsPage();
|
const credentialsPage = new CredentialsPage();
|
||||||
const attributesTab = new AttributesTab();
|
const attributesTab = new AttributesTab(true);
|
||||||
const usersPage = new UsersPage();
|
const usersPage = new UsersPage();
|
||||||
const identityProviderLinksTab = new IdentityProviderLinksTab();
|
const identityProviderLinksTab = new IdentityProviderLinksTab();
|
||||||
|
|
||||||
|
@ -143,6 +145,14 @@ describe("User creation", () => {
|
||||||
listingPage.searchItem(itemId).itemExist(itemId);
|
listingPage.searchItem(itemId).itemExist(itemId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Select Unmanaged attributes", () => {
|
||||||
|
sidebarPage.goToRealmSettings();
|
||||||
|
sidebarPage.waitForPageLoad();
|
||||||
|
realmSettingsPage.fillUnmanagedAttributes("Enabled");
|
||||||
|
realmSettingsPage.save(realmSettingsPage.generalSaveBtn);
|
||||||
|
masthead.checkNotificationMessage("Realm successfully updated", true);
|
||||||
|
});
|
||||||
|
|
||||||
it("User attributes test", () => {
|
it("User attributes test", () => {
|
||||||
listingPage.goToItemDetails(itemId);
|
listingPage.goToItemDetails(itemId);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
export default class AttributesTab {
|
export default class AttributesTab {
|
||||||
#saveAttributeBtn = "save-attributes";
|
#saveAttributeBtn = "save-attributes";
|
||||||
#addAttributeBtn = "attributes-add-row";
|
|
||||||
#attributesTab = "attributes";
|
#attributesTab = "attributes";
|
||||||
#keyInput = "attributes-key";
|
|
||||||
#valueInput = "attributes-value";
|
|
||||||
#removeBtn = "attributes-remove";
|
|
||||||
#emptyState = "attributes-empty-state";
|
#emptyState = "attributes-empty-state";
|
||||||
|
#addAttributeBtn: string;
|
||||||
|
#keyInput: string;
|
||||||
|
#valueInput: string;
|
||||||
|
#removeBtn: string;
|
||||||
|
|
||||||
|
constructor(isForUser = false) {
|
||||||
|
if (isForUser) {
|
||||||
|
this.#addAttributeBtn = "unmanagedAttributes-add-row";
|
||||||
|
this.#keyInput = "unmanagedAttributes-key";
|
||||||
|
this.#valueInput = "unmanagedAttributes-value";
|
||||||
|
this.#removeBtn = "unmanagedAttributes-remove";
|
||||||
|
} else {
|
||||||
|
this.#addAttributeBtn = "attributes-add-row";
|
||||||
|
this.#keyInput = "attributes-key";
|
||||||
|
this.#valueInput = "attributes-value";
|
||||||
|
this.#removeBtn = "attributes-remove";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public goToAttributesTab() {
|
public goToAttributesTab() {
|
||||||
cy.findByTestId(this.#attributesTab).click();
|
cy.findByTestId(this.#attributesTab).click();
|
||||||
|
|
|
@ -67,7 +67,6 @@ export default class RealmSettingsPage extends CommonPage {
|
||||||
supportedLocalesToggle = "#kc-l-supported-locales";
|
supportedLocalesToggle = "#kc-l-supported-locales";
|
||||||
emailSaveBtn = "email-tab-save";
|
emailSaveBtn = "email-tab-save";
|
||||||
managedAccessSwitch = "user-managed-access-switch";
|
managedAccessSwitch = "user-managed-access-switch";
|
||||||
profileEnabledSwitch = "user-profile-enabled-switch";
|
|
||||||
userRegSwitch = "user-reg-switch";
|
userRegSwitch = "user-reg-switch";
|
||||||
forgotPwdSwitch = "forgot-pw-switch";
|
forgotPwdSwitch = "forgot-pw-switch";
|
||||||
rememberMeSwitch = "remember-me-switch";
|
rememberMeSwitch = "remember-me-switch";
|
||||||
|
@ -233,6 +232,7 @@ export default class RealmSettingsPage extends CommonPage {
|
||||||
#realmDisplayName = "#kc-display-name";
|
#realmDisplayName = "#kc-display-name";
|
||||||
#frontEndURL = "#kc-frontend-url";
|
#frontEndURL = "#kc-frontend-url";
|
||||||
#requireSSL = "#kc-require-ssl";
|
#requireSSL = "#kc-require-ssl";
|
||||||
|
#unmanagedAttributes = "#kc-user-profile-unmanaged-attribute-policy";
|
||||||
#fromDisplayName = "from-display-name";
|
#fromDisplayName = "from-display-name";
|
||||||
#replyToEmail = "#kc-reply-to";
|
#replyToEmail = "#kc-reply-to";
|
||||||
#port = "#kc-port";
|
#port = "#kc-port";
|
||||||
|
@ -319,6 +319,12 @@ export default class RealmSettingsPage extends CommonPage {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUnmanagedAttributes(option: string) {
|
||||||
|
cy.get(this.#unmanagedAttributes).contains(option);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
fillDisplayName(displayName: string) {
|
fillDisplayName(displayName: string) {
|
||||||
cy.get(this.#realmDisplayName).clear().type(displayName);
|
cy.get(this.#realmDisplayName).clear().type(displayName);
|
||||||
}
|
}
|
||||||
|
@ -355,6 +361,14 @@ export default class RealmSettingsPage extends CommonPage {
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fillUnmanagedAttributes(option: string) {
|
||||||
|
cy.get(this.#unmanagedAttributes)
|
||||||
|
.click()
|
||||||
|
.get(".pf-c-select__menu-item")
|
||||||
|
.contains(option)
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
|
||||||
setDefaultLocale(locale: string) {
|
setDefaultLocale(locale: string) {
|
||||||
cy.get(this.selectDefaultLocale).click();
|
cy.get(this.selectDefaultLocale).click();
|
||||||
cy.findByTestId(this.defaultLocaleList).contains(locale).click();
|
cy.findByTestId(this.defaultLocaleList).contains(locale).click();
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default class CreateUserPage {
|
||||||
cancelBtn: string;
|
cancelBtn: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.usernameInput = "#kc-username";
|
this.usernameInput = "#username";
|
||||||
|
|
||||||
this.usersEmptyState = "empty-state";
|
this.usersEmptyState = "empty-state";
|
||||||
this.emptyStateCreateUserBtn = "no-users-found-empty-action";
|
this.emptyStateCreateUserBtn = "no-users-found-empty-action";
|
||||||
|
|
|
@ -21,11 +21,11 @@ export default class UserDetailsPage extends PageObject {
|
||||||
super();
|
super();
|
||||||
this.saveBtn = "save-user";
|
this.saveBtn = "save-user";
|
||||||
this.cancelBtn = "cancel-create-user";
|
this.cancelBtn = "cancel-create-user";
|
||||||
this.emailInput = "email-input";
|
this.emailInput = "email";
|
||||||
this.emailValue = () => "example" + "_" + uuid() + "@example.com";
|
this.emailValue = () => "example" + "_" + uuid() + "@example.com";
|
||||||
this.firstNameInput = "firstName-input";
|
this.firstNameInput = "firstName";
|
||||||
this.firstNameValue = "firstname";
|
this.firstNameValue = "firstname";
|
||||||
this.lastNameInput = "lastName-input";
|
this.lastNameInput = "lastName";
|
||||||
this.lastNameValue = "lastname";
|
this.lastNameValue = "lastname";
|
||||||
this.requiredUserActions = [RequiredActionAlias.UPDATE_PASSWORD];
|
this.requiredUserActions = [RequiredActionAlias.UPDATE_PASSWORD];
|
||||||
this.identityProviderLinksTab = "identity-provider-links-tab";
|
this.identityProviderLinksTab = "identity-provider-links-tab";
|
||||||
|
|
|
@ -1461,7 +1461,6 @@ exactSearch=Exact search
|
||||||
value=Value
|
value=Value
|
||||||
filenamePlaceholder=Upload a PEM file or paste key below
|
filenamePlaceholder=Upload a PEM file or paste key below
|
||||||
deleteConfirm_one=Are you sure you want to delete this group '{{groupName}}'.
|
deleteConfirm_one=Are you sure you want to delete this group '{{groupName}}'.
|
||||||
userProfileEnabledHelp=If enabled, allows managing user profiles.
|
|
||||||
scopeDisplayNameHelp=A unique name for this scope. The name can be used to uniquely identify a scope, useful when querying for a specific scope.
|
scopeDisplayNameHelp=A unique name for this scope. The name can be used to uniquely identify a scope, useful when querying for a specific scope.
|
||||||
times.seconds=Seconds
|
times.seconds=Seconds
|
||||||
removeMappingTitle=Remove role?
|
removeMappingTitle=Remove role?
|
||||||
|
@ -1890,7 +1889,6 @@ moveToGroup=Move {{group1}} to {{group2}}
|
||||||
noRealmRoles=No realm roles
|
noRealmRoles=No realm roles
|
||||||
events-disable-confirm=If "Save events" is disabled, subsequent events will not be displayed in the "Events" menu
|
events-disable-confirm=If "Save events" is disabled, subsequent events will not be displayed in the "Events" menu
|
||||||
reqAuthnConstraints=Requested AuthnContext Constraints
|
reqAuthnConstraints=Requested AuthnContext Constraints
|
||||||
userProfileEnabled=User Profile Enabled
|
|
||||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Pushed authorization request
|
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Pushed authorization request
|
||||||
addIdpMapperNameHelp=Name of the mapper.
|
addIdpMapperNameHelp=Name of the mapper.
|
||||||
requirements.ALTERNATIVE=Alternative
|
requirements.ALTERNATIVE=Alternative
|
||||||
|
|
|
@ -1437,7 +1437,6 @@ exactSearch=Búsqueda exacta
|
||||||
value=Valor
|
value=Valor
|
||||||
filenamePlaceholder=Cargar un archivo PEM o pegar la clave a continuación
|
filenamePlaceholder=Cargar un archivo PEM o pegar la clave a continuación
|
||||||
deleteConfirm_one=¿Estás seguro de que quieres eliminar este grupo '{{groupName}}'?
|
deleteConfirm_one=¿Estás seguro de que quieres eliminar este grupo '{{groupName}}'?
|
||||||
userProfileEnabledHelp=Si está habilitado, permite gestionar perfiles de usuario.
|
|
||||||
scopeDisplayNameHelp=Un nombre único para este alcance. El nombre puede ser utilizado para identificar un alcance de manera única, útil al buscar un alcance específico.
|
scopeDisplayNameHelp=Un nombre único para este alcance. El nombre puede ser utilizado para identificar un alcance de manera única, útil al buscar un alcance específico.
|
||||||
times.seconds=Segundos
|
times.seconds=Segundos
|
||||||
removeMappingTitle=¿Eliminar asignación?
|
removeMappingTitle=¿Eliminar asignación?
|
||||||
|
@ -1865,7 +1864,6 @@ moveToGroup=Mover {{grupo1}} a {{grupo2}}
|
||||||
noRealmRoles=No hay roles de reino
|
noRealmRoles=No hay roles de reino
|
||||||
events-disable-confirm=Si se deshabilita "Guardar eventos", los eventos posteriores no se mostrarán en el menú "Eventos"
|
events-disable-confirm=Si se deshabilita "Guardar eventos", los eventos posteriores no se mostrarán en el menú "Eventos"
|
||||||
reqAuthnConstraints=Restricciones de contexto de autenticación solicitadas
|
reqAuthnConstraints=Restricciones de contexto de autenticación solicitadas
|
||||||
userProfileEnabled=Perfil de usuario habilitado
|
|
||||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Solicitud de autorización empujada
|
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Solicitud de autorización empujada
|
||||||
addIdpMapperNameHelp=Nombre del mapeador.
|
addIdpMapperNameHelp=Nombre del mapeador.
|
||||||
requirements.ALTERNATIVE=Alternativa
|
requirements.ALTERNATIVE=Alternativa
|
||||||
|
|
|
@ -1437,7 +1437,6 @@ exactSearch=Wyszukiwanie dokładne
|
||||||
value=Wartość
|
value=Wartość
|
||||||
filenamePlaceholder=Prześlij plik PEM lub wklej klucz poniżej
|
filenamePlaceholder=Prześlij plik PEM lub wklej klucz poniżej
|
||||||
deleteConfirm_one=Czy na pewno chcesz usunąć tę grupę '{{groupName}}'?
|
deleteConfirm_one=Czy na pewno chcesz usunąć tę grupę '{{groupName}}'?
|
||||||
userProfileEnabledHelp=Jeśli włączone, umożliwia zarządzanie profilami użytkowników.
|
|
||||||
scopeDisplayNameHelp=Unikalna nazwa tego zakresu. Nazwa może być używana do jednoznacznego zidentyfikowania zakresu, co jest przydatne podczas wyszukiwania konkretnego zakresu.
|
scopeDisplayNameHelp=Unikalna nazwa tego zakresu. Nazwa może być używana do jednoznacznego zidentyfikowania zakresu, co jest przydatne podczas wyszukiwania konkretnego zakresu.
|
||||||
times.seconds=Sekundy
|
times.seconds=Sekundy
|
||||||
removeMappingTitle=Usuń rolę?
|
removeMappingTitle=Usuń rolę?
|
||||||
|
@ -1865,7 +1864,6 @@ moveToGroup=Przenieś {{group1}} do {{group2}}
|
||||||
noRealmRoles=Brak ról obszaru
|
noRealmRoles=Brak ról obszaru
|
||||||
events-disable-confirm=Jeśli "Zapisz zdarzenia" jest wyłączone, kolejne zdarzenia nie będą wyświetlane w menu "Zdarzenia"
|
events-disable-confirm=Jeśli "Zapisz zdarzenia" jest wyłączone, kolejne zdarzenia nie będą wyświetlane w menu "Zdarzenia"
|
||||||
reqAuthnConstraints=Wymagane ograniczenia AuthnContext
|
reqAuthnConstraints=Wymagane ograniczenia AuthnContext
|
||||||
userProfileEnabled=Profil użytkownika włączony
|
|
||||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Żądanie autoryzacji przesuniętej
|
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Żądanie autoryzacji przesuniętej
|
||||||
addIdpMapperNameHelp=Nazwa mappera.
|
addIdpMapperNameHelp=Nazwa mappera.
|
||||||
requirements.ALTERNATIVE=Alternatywa
|
requirements.ALTERNATIVE=Alternatywa
|
||||||
|
|
|
@ -1412,7 +1412,6 @@ exactSearch=精确搜索
|
||||||
value=数值
|
value=数值
|
||||||
filenamePlaceholder=上传 PEM 文件或在下方粘贴密钥
|
filenamePlaceholder=上传 PEM 文件或在下方粘贴密钥
|
||||||
deleteConfirm_one=是否要删除此群组“{{groupName}}”。
|
deleteConfirm_one=是否要删除此群组“{{groupName}}”。
|
||||||
userProfileEnabledHelp=如果启用,允许管理用户配置文件。
|
|
||||||
times.seconds=秒
|
times.seconds=秒
|
||||||
removeMappingTitle=移除角色?
|
removeMappingTitle=移除角色?
|
||||||
executorTypeSelectAlgorithm=执行器类型选择算法
|
executorTypeSelectAlgorithm=执行器类型选择算法
|
||||||
|
@ -1831,7 +1830,6 @@ moveToGroup=将{{group1}}移动到{{group2}}
|
||||||
noRealmRoles=无领域角色
|
noRealmRoles=无领域角色
|
||||||
events-disable-confirm=如果禁用“保存事件”,后续事件将不会展示在“事件”菜单中。
|
events-disable-confirm=如果禁用“保存事件”,后续事件将不会展示在“事件”菜单中。
|
||||||
reqAuthnConstraints=请求的上下文约束
|
reqAuthnConstraints=请求的上下文约束
|
||||||
userProfileEnabled=用户资料
|
|
||||||
requirements.ALTERNATIVE=非必需
|
requirements.ALTERNATIVE=非必需
|
||||||
credentialResetConfirm=发送电子邮件
|
credentialResetConfirm=发送电子邮件
|
||||||
permissionsEnabledHelp=确定是否启用细粒度权限来管理此角色。禁用将删除所有已设置的当前权限。
|
permissionsEnabledHelp=确定是否启用细粒度权限来管理此角色。禁用将删除所有已设置的当前权限。
|
||||||
|
|
|
@ -28,7 +28,6 @@ import {
|
||||||
convertAttributeNameToForm,
|
convertAttributeNameToForm,
|
||||||
convertToFormValues,
|
convertToFormValues,
|
||||||
} from "../util";
|
} from "../util";
|
||||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
|
||||||
import {
|
import {
|
||||||
UnmanagedAttributePolicy,
|
UnmanagedAttributePolicy,
|
||||||
UserProfileConfig,
|
UserProfileConfig,
|
||||||
|
@ -57,7 +56,6 @@ export const RealmSettingsGeneralTab = ({
|
||||||
setValue,
|
setValue,
|
||||||
formState: { isDirty, errors },
|
formState: { isDirty, errors },
|
||||||
} = form;
|
} = form;
|
||||||
const isFeatureEnabled = useIsFeatureEnabled();
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const requireSslTypes = ["all", "external", "none"];
|
const requireSslTypes = ["all", "external", "none"];
|
||||||
|
@ -72,7 +70,6 @@ export const RealmSettingsGeneralTab = ({
|
||||||
];
|
];
|
||||||
const [isUnmanagedAttributeOpen, setIsUnmanagedAttributeOpen] =
|
const [isUnmanagedAttributeOpen, setIsUnmanagedAttributeOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isUserProfileEnabled, setUserProfileEnabled] = useState(false);
|
|
||||||
|
|
||||||
const setupForm = () => {
|
const setupForm = () => {
|
||||||
convertToFormValues(realm, setValue);
|
convertToFormValues(realm, setValue);
|
||||||
|
@ -86,7 +83,6 @@ export const RealmSettingsGeneralTab = ({
|
||||||
result,
|
result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
setUserProfileEnabled(realm.attributes?.["userProfileEnabled"] === "true");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useFetch(
|
useFetch(
|
||||||
|
@ -251,79 +247,40 @@ export const RealmSettingsGeneralTab = ({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{isFeatureEnabled(Feature.DeclarativeUserProfile) && (
|
<FormGroup
|
||||||
<FormGroup
|
label={t("unmanagedAttributes")}
|
||||||
hasNoPaddingTop
|
fieldId="kc-user-profile-unmanaged-attribute-policy"
|
||||||
label={t("userProfileEnabled")}
|
labelIcon={
|
||||||
labelIcon={
|
<HelpItem
|
||||||
<HelpItem
|
helpText={t("unmanagedAttributesHelpText")}
|
||||||
helpText={t("userProfileEnabledHelp")}
|
fieldLabelId="unmanagedAttributes"
|
||||||
fieldLabelId="userProfileEnabled"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
fieldId="kc-user-profile-enabled"
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name={
|
|
||||||
convertAttributeNameToForm(
|
|
||||||
"attributes.userProfileEnabled",
|
|
||||||
) as any
|
|
||||||
}
|
|
||||||
control={control}
|
|
||||||
defaultValue="false"
|
|
||||||
render={({ field }) => (
|
|
||||||
<Switch
|
|
||||||
id="kc-user-profile-enabled"
|
|
||||||
data-testid="user-profile-enabled-switch"
|
|
||||||
label={t("on")}
|
|
||||||
labelOff={t("off")}
|
|
||||||
isChecked={field.value === "true"}
|
|
||||||
onChange={(value) => {
|
|
||||||
field.onChange(value.toString());
|
|
||||||
setUserProfileEnabled(value);
|
|
||||||
}}
|
|
||||||
aria-label={t("userProfileEnabled")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
}
|
||||||
)}
|
>
|
||||||
{isUserProfileEnabled && (
|
<Select
|
||||||
<FormGroup
|
toggleId="kc-user-profile-unmanaged-attribute-policy"
|
||||||
label={t("unmanagedAttributes")}
|
onToggle={() =>
|
||||||
fieldId="kc-user-profile-unmanaged-attribute-policy"
|
setIsUnmanagedAttributeOpen(!isUnmanagedAttributeOpen)
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={t("unmanagedAttributesHelpText")}
|
|
||||||
fieldLabelId="unmanagedAttributes"
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
>
|
onSelect={(_, value) => {
|
||||||
<Select
|
if (userProfileConfig) {
|
||||||
toggleId="kc-user-profile-unmanaged-attribute-policy"
|
userProfileConfig.unmanagedAttributePolicy =
|
||||||
onToggle={() =>
|
value as UnmanagedAttributePolicy;
|
||||||
setIsUnmanagedAttributeOpen(!isUnmanagedAttributeOpen)
|
setUserProfileConfig(userProfileConfig);
|
||||||
}
|
}
|
||||||
onSelect={(_, value) => {
|
setIsUnmanagedAttributeOpen(false);
|
||||||
if (userProfileConfig) {
|
}}
|
||||||
userProfileConfig.unmanagedAttributePolicy =
|
selections={userProfileConfig?.unmanagedAttributePolicy}
|
||||||
value as UnmanagedAttributePolicy;
|
variant={SelectVariant.single}
|
||||||
setUserProfileConfig(userProfileConfig);
|
isOpen={isUnmanagedAttributeOpen}
|
||||||
}
|
>
|
||||||
setIsUnmanagedAttributeOpen(false);
|
{unmanagedAttributePolicies.map((policy) => (
|
||||||
}}
|
<SelectOption key={policy} value={policy}>
|
||||||
selections={userProfileConfig?.unmanagedAttributePolicy}
|
{t(`unmanagedAttributePolicy.${policy}`)}
|
||||||
variant={SelectVariant.single}
|
</SelectOption>
|
||||||
isOpen={isUnmanagedAttributeOpen}
|
))}
|
||||||
>
|
</Select>
|
||||||
{unmanagedAttributePolicies.map((policy) => (
|
</FormGroup>
|
||||||
<SelectOption key={policy} value={policy}>
|
|
||||||
{t(`unmanagedAttributePolicy.${policy}`)}
|
|
||||||
</SelectOption>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("endpoints")}
|
label={t("endpoints")}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
|
|
|
@ -417,16 +417,13 @@ export const RealmSettingsTabs = ({
|
||||||
</RoutableTabs>
|
</RoutableTabs>
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
{isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
<Tab
|
||||||
realm.attributes?.userProfileEnabled === "true" && (
|
title={<TabTitleText>{t("userProfile")}</TabTitleText>}
|
||||||
<Tab
|
data-testid="rs-user-profile-tab"
|
||||||
title={<TabTitleText>{t("userProfile")}</TabTitleText>}
|
{...userProfileTab}
|
||||||
data-testid="rs-user-profile-tab"
|
>
|
||||||
{...userProfileTab}
|
<UserProfileTab />
|
||||||
>
|
</Tab>
|
||||||
<UserProfileTab />
|
|
||||||
</Tab>
|
|
||||||
)}
|
|
||||||
<Tab
|
<Tab
|
||||||
title={<TabTitleText>{t("userRegistration")}</TabTitleText>}
|
title={<TabTitleText>{t("userRegistration")}</TabTitleText>}
|
||||||
data-testid="rs-userRegistration-tab"
|
data-testid="rs-userRegistration-tab"
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useFetch } from "../utils/useFetch";
|
import { useFetch } from "../utils/useFetch";
|
||||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
|
||||||
import { UserForm } from "./UserForm";
|
import { UserForm } from "./UserForm";
|
||||||
import { UserFormFields, toUserRepresentation } from "./form-state";
|
import { UserFormFields, toUserRepresentation } from "./form-state";
|
||||||
import { toUser } from "./routes/User";
|
import { toUser } from "./routes/User";
|
||||||
|
@ -27,7 +26,6 @@ export default function CreateUser() {
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { realm: realmName } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
const isFeatureEnabled = useIsFeatureEnabled();
|
|
||||||
const form = useForm<UserFormFields>({ mode: "onChange" });
|
const form = useForm<UserFormFields>({ mode: "onChange" });
|
||||||
const [addedGroups, setAddedGroups] = useState<GroupRepresentation[]>([]);
|
const [addedGroups, setAddedGroups] = useState<GroupRepresentation[]>([]);
|
||||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||||
|
@ -46,14 +44,7 @@ export default function CreateUser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setRealm(realm);
|
setRealm(realm);
|
||||||
|
setUserProfileMetadata(userProfileMetadata);
|
||||||
const isUserProfileEnabled =
|
|
||||||
isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
|
||||||
realm.attributes?.userProfileEnabled === "true";
|
|
||||||
|
|
||||||
setUserProfileMetadata(
|
|
||||||
isUserProfileEnabled ? userProfileMetadata : undefined,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,7 +34,6 @@ import { useAccess } from "../context/access/Access";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext";
|
import { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext";
|
||||||
import { useFetch } from "../utils/useFetch";
|
import { useFetch } from "../utils/useFetch";
|
||||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
|
||||||
import { useParams } from "../utils/useParams";
|
import { useParams } from "../utils/useParams";
|
||||||
import { UserAttributes } from "./UserAttributes";
|
import { UserAttributes } from "./UserAttributes";
|
||||||
import { UserConsents } from "./UserConsents";
|
import { UserConsents } from "./UserConsents";
|
||||||
|
@ -64,7 +63,6 @@ export default function EditUser() {
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const { id } = useParams<UserParams>();
|
const { id } = useParams<UserParams>();
|
||||||
const { realm: realmName } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
const isFeatureEnabled = useIsFeatureEnabled();
|
|
||||||
const form = useForm<UserFormFields>({ mode: "onChange" });
|
const form = useForm<UserFormFields>({ mode: "onChange" });
|
||||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||||
const [user, setUser] = useState<UIUserRepresentation>();
|
const [user, setUser] = useState<UIUserRepresentation>();
|
||||||
|
@ -113,27 +111,15 @@ export default function EditUser() {
|
||||||
throw new Error(t("notFound"));
|
throw new Error(t("notFound"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUserProfileEnabled =
|
|
||||||
isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
|
||||||
realm.attributes?.userProfileEnabled === "true";
|
|
||||||
|
|
||||||
const { userProfileMetadata, ...user } = userData;
|
const { userProfileMetadata, ...user } = userData;
|
||||||
setUserProfileMetadata(
|
setUserProfileMetadata(userProfileMetadata);
|
||||||
isUserProfileEnabled ? userProfileMetadata : undefined,
|
user.unmanagedAttributes = unmanagedAttributes;
|
||||||
|
user.attributes = filterManagedAttributes(
|
||||||
|
user.attributes,
|
||||||
|
unmanagedAttributes,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isUserProfileEnabled) {
|
if (upConfig.unmanagedAttributePolicy !== undefined) {
|
||||||
user.unmanagedAttributes = unmanagedAttributes;
|
|
||||||
user.attributes = filterManagedAttributes(
|
|
||||||
user.attributes,
|
|
||||||
unmanagedAttributes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
upConfig.unmanagedAttributePolicy !== undefined ||
|
|
||||||
!isUserProfileEnabled
|
|
||||||
) {
|
|
||||||
setUnmanagedAttributesEnabled(true);
|
setUnmanagedAttributesEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +132,7 @@ export default function EditUser() {
|
||||||
|
|
||||||
setBruteForced({ isBruteForceProtected, isLocked });
|
setBruteForced({ isBruteForceProtected, isLocked });
|
||||||
|
|
||||||
form.reset(toUserFormFields(user, isUserProfileEnabled));
|
form.reset(toUserFormFields(user));
|
||||||
},
|
},
|
||||||
[refreshCount],
|
[refreshCount],
|
||||||
);
|
);
|
||||||
|
@ -258,7 +244,7 @@ export default function EditUser() {
|
||||||
]}
|
]}
|
||||||
onToggle={(value) =>
|
onToggle={(value) =>
|
||||||
save({
|
save({
|
||||||
...toUserFormFields(user, !!userProfileMetadata),
|
...toUserFormFields(user),
|
||||||
enabled: value,
|
enabled: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -295,12 +281,7 @@ export default function EditUser() {
|
||||||
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
||||||
{...attributesTab}
|
{...attributesTab}
|
||||||
>
|
>
|
||||||
<UserAttributes
|
<UserAttributes user={user} save={save} upConfig={upConfig} />
|
||||||
user={user}
|
|
||||||
save={save}
|
|
||||||
upConfig={upConfig}
|
|
||||||
isUserProfileEnabled={!!userProfileMetadata}
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
<Tab
|
<Tab
|
||||||
|
|
|
@ -16,14 +16,12 @@ type UserAttributesProps = {
|
||||||
user: UserRepresentation;
|
user: UserRepresentation;
|
||||||
save: (user: UserFormFields) => void;
|
save: (user: UserFormFields) => void;
|
||||||
upConfig?: UserProfileConfig;
|
upConfig?: UserProfileConfig;
|
||||||
isUserProfileEnabled: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserAttributes = ({
|
export const UserAttributes = ({
|
||||||
user,
|
user,
|
||||||
save,
|
save,
|
||||||
upConfig,
|
upConfig,
|
||||||
isUserProfileEnabled,
|
|
||||||
}: UserAttributesProps) => {
|
}: UserAttributesProps) => {
|
||||||
const form = useFormContext<UserFormFields>();
|
const form = useFormContext<UserFormFields>();
|
||||||
|
|
||||||
|
@ -36,10 +34,10 @@ export const UserAttributes = ({
|
||||||
reset={() =>
|
reset={() =>
|
||||||
form.reset({
|
form.reset({
|
||||||
...form.getValues(),
|
...form.getValues(),
|
||||||
attributes: toUserFormFields(user, isUserProfileEnabled).attributes,
|
attributes: toUserFormFields(user).attributes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
name={isUserProfileEnabled ? "unmanagedAttributes" : "attributes"}
|
name="unmanagedAttributes"
|
||||||
isDisabled={
|
isDisabled={
|
||||||
UnmanagedAttributePolicy.AdminView ==
|
UnmanagedAttributePolicy.AdminView ==
|
||||||
upConfig?.unmanagedAttributePolicy
|
upConfig?.unmanagedAttributePolicy
|
||||||
|
|
|
@ -394,11 +394,7 @@ export const UserForm = ({
|
||||||
<Button
|
<Button
|
||||||
data-testid="cancel-create-user"
|
data-testid="cancel-create-user"
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={
|
onClick={user?.id ? () => reset(toUserFormFields(user)) : undefined}
|
||||||
user?.id
|
|
||||||
? () => reset(toUserFormFields(user, !!userProfileMetadata))
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
component={
|
component={
|
||||||
!user?.id
|
!user?.id
|
||||||
? (props) => (
|
? (props) => (
|
||||||
|
|
|
@ -18,18 +18,11 @@ export interface UIUserRepresentation extends UserRepresentation {
|
||||||
unmanagedAttributes?: Record<string, string[]>;
|
unmanagedAttributes?: Record<string, string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toUserFormFields(
|
export function toUserFormFields(data: UIUserRepresentation): UserFormFields {
|
||||||
data: UIUserRepresentation,
|
const attributes: Record<string, string | string[]> = {};
|
||||||
userProfileEnabled: boolean,
|
Object.entries(data.attributes || {}).forEach(
|
||||||
): UserFormFields {
|
([k, v]) => (attributes[beerify(k)] = v),
|
||||||
let attributes: Record<string, string | string[]> = {};
|
);
|
||||||
if (userProfileEnabled) {
|
|
||||||
Object.entries(data.attributes || {}).forEach(
|
|
||||||
([k, v]) => (attributes[beerify(k)] = v),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
attributes = arrayToKeyValue(data.attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unmanagedAttributes = arrayToKeyValue(data.unmanagedAttributes);
|
const unmanagedAttributes = arrayToKeyValue(data.unmanagedAttributes);
|
||||||
return { ...data, attributes, unmanagedAttributes };
|
return { ...data, attributes, unmanagedAttributes };
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
export enum Feature {
|
export enum Feature {
|
||||||
AdminFineGrainedAuthz = "ADMIN_FINE_GRAINED_AUTHZ",
|
AdminFineGrainedAuthz = "ADMIN_FINE_GRAINED_AUTHZ",
|
||||||
ClientPolicies = "CLIENT_POLICIES",
|
ClientPolicies = "CLIENT_POLICIES",
|
||||||
DeclarativeUserProfile = "DECLARATIVE_USER_PROFILE",
|
|
||||||
Kerberos = "KERBEROS",
|
Kerberos = "KERBEROS",
|
||||||
DynamicScopes = "DYNAMIC_SCOPES",
|
DynamicScopes = "DYNAMIC_SCOPES",
|
||||||
DPoP = "DPOP",
|
DPoP = "DPOP",
|
||||||
|
|
|
@ -40,7 +40,7 @@ async function startServer() {
|
||||||
[
|
[
|
||||||
"start-dev",
|
"start-dev",
|
||||||
"--http-port=8180",
|
"--http-port=8180",
|
||||||
"--features=account3,admin-fine-grained-authz,declarative-user-profile,transient-users",
|
"--features=account3,admin-fine-grained-authz,transient-users",
|
||||||
...keycloakArgs,
|
...keycloakArgs,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class MigrateTo24_0_0 implements Migration {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(MigrateTo24_0_0.class);
|
private static final Logger LOG = Logger.getLogger(MigrateTo24_0_0.class);
|
||||||
public static final ModelVersion VERSION = new ModelVersion("24.0.0");
|
public static final ModelVersion VERSION = new ModelVersion("24.0.0");
|
||||||
private static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled";
|
public static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void migrate(KeycloakSession session) {
|
public void migrate(KeycloakSession session) {
|
||||||
|
@ -64,15 +64,15 @@ public class MigrateTo24_0_0 implements Migration {
|
||||||
RealmModel realm = session.getContext().getRealm();
|
RealmModel realm = session.getContext().getRealm();
|
||||||
boolean isUserProfileEnabled = Boolean.parseBoolean(realm.getAttribute(REALM_USER_PROFILE_ENABLED));
|
boolean isUserProfileEnabled = Boolean.parseBoolean(realm.getAttribute(REALM_USER_PROFILE_ENABLED));
|
||||||
|
|
||||||
|
// Remove attribute as user profile is always enabled from this version
|
||||||
|
realm.removeAttribute(REALM_USER_PROFILE_ENABLED);
|
||||||
|
|
||||||
if (isUserProfileEnabled) {
|
if (isUserProfileEnabled) {
|
||||||
// existing realms with user profile enabled does not need any addition migration step
|
// existing realms with user profile enabled does not need any addition migration step
|
||||||
LOG.debugf("Skipping migration for realm %s. The declarative user profile is already enabled.", realm.getName());
|
LOG.debugf("Skipping migration for realm %s. The declarative user profile is already enabled.", realm.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// user profile is enabled by default since this version
|
|
||||||
realm.setAttribute(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
|
||||||
|
|
||||||
// for backward compatibility in terms of behavior, we enable unmanaged attributes for existing realms
|
// for backward compatibility in terms of behavior, we enable unmanaged attributes for existing realms
|
||||||
// that don't have the declarative user profile enabled
|
// that don't have the declarative user profile enabled
|
||||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||||
|
|
|
@ -28,7 +28,7 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTI
|
||||||
@LegacyStore
|
@LegacyStore
|
||||||
public class FeaturesDistTest {
|
public class FeaturesDistTest {
|
||||||
|
|
||||||
private static final String PREVIEW_FEATURES_EXPECTED_LOG = "Preview features enabled: account3:v1, admin-fine-grained-authz:v1, client-secret-rotation:v1, declarative-user-profile:v1, dpop:v1, multi-site:v1, recovery-codes:v1, scripts:v1, token-exchange:v1, update-email:v1";
|
private static final String PREVIEW_FEATURES_EXPECTED_LOG = "Preview features enabled: account3:v1, admin-fine-grained-authz:v1, client-secret-rotation:v1, dpop:v1, multi-site:v1, recovery-codes:v1, scripts:v1, token-exchange:v1, update-email:v1";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEnableOnBuild(KeycloakDistribution dist) {
|
public void testEnableOnBuild(KeycloakDistribution dist) {
|
||||||
|
@ -89,7 +89,7 @@ public class FeaturesDistTest {
|
||||||
cliResult.assertStartedDevMode();
|
cliResult.assertStartedDevMode();
|
||||||
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
||||||
containsString("Preview features enabled: admin-fine-grained-authz:v1, token-exchange:v1")));
|
containsString("Preview features enabled: admin-fine-grained-authz:v1, token-exchange:v1")));
|
||||||
assertFalse(cliResult.getOutput().contains("declarative-user-profile"));
|
assertFalse(cliResult.getOutput().contains("recovery-codes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,7 +100,7 @@ public class FeaturesDistTest {
|
||||||
cliResult.assertStartedDevMode();
|
cliResult.assertStartedDevMode();
|
||||||
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
||||||
containsString("Preview features enabled: admin-fine-grained-authz:v1, token-exchange:v1")));
|
containsString("Preview features enabled: admin-fine-grained-authz:v1, token-exchange:v1")));
|
||||||
assertFalse(cliResult.getOutput().contains("declarative-user-profile"));
|
assertFalse(cliResult.getOutput().contains("recovery-codes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertPreviewFeaturesEnabled(CLIResult result) {
|
private void assertPreviewFeaturesEnabled(CLIResult result) {
|
||||||
|
|
|
@ -48,20 +48,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
HTTP(S):
|
HTTP(S):
|
||||||
|
|
||||||
|
|
|
@ -48,20 +48,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
HTTP(S):
|
HTTP(S):
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
web-authn[:v1].
|
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation,
|
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
transient-users, update-email, web-authn.
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -42,32 +42,27 @@ public class UserResource {
|
||||||
@NoCache
|
@NoCache
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Map<String, List<String>> getUnmanagedAttributes() {
|
public Map<String, List<String>> getUnmanagedAttributes() {
|
||||||
RealmModel realm = session.getContext().getRealm();
|
|
||||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||||
|
|
||||||
if (provider.isEnabled(realm)) {
|
UserProfile profile = provider.create(USER_API, user);
|
||||||
UserProfile profile = provider.create(USER_API, user);
|
Map<String, List<String>> managedAttributes = profile.getAttributes().getReadable();
|
||||||
Map<String, List<String>> managedAttributes = profile.getAttributes().getReadable();
|
Map<String, List<String>> attributes = new HashMap<>(user.getAttributes());
|
||||||
Map<String, List<String>> attributes = new HashMap<>(user.getAttributes());
|
UPConfig upConfig = provider.getConfiguration();
|
||||||
UPConfig upConfig = provider.getConfiguration();
|
|
||||||
|
|
||||||
if (upConfig.getUnmanagedAttributePolicy() == null) {
|
if (upConfig.getUnmanagedAttributePolicy() == null) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, List<String>> unmanagedAttributes = profile.getAttributes().getUnmanagedAttributes();
|
|
||||||
managedAttributes.entrySet().removeAll(unmanagedAttributes.entrySet());
|
|
||||||
attributes.entrySet().removeAll(managedAttributes.entrySet());
|
|
||||||
|
|
||||||
attributes.remove(UserModel.USERNAME);
|
|
||||||
attributes.remove(UserModel.EMAIL);
|
|
||||||
|
|
||||||
return attributes.entrySet().stream()
|
|
||||||
.filter(entry -> ofNullable(entry.getValue()).orElse(emptyList()).stream().anyMatch(StringUtil::isNotBlank))
|
|
||||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.emptyMap();
|
Map<String, List<String>> unmanagedAttributes = profile.getAttributes().getUnmanagedAttributes();
|
||||||
|
managedAttributes.entrySet().removeAll(unmanagedAttributes.entrySet());
|
||||||
|
attributes.entrySet().removeAll(managedAttributes.entrySet());
|
||||||
|
|
||||||
|
attributes.remove(UserModel.USERNAME);
|
||||||
|
attributes.remove(UserModel.EMAIL);
|
||||||
|
|
||||||
|
return attributes.entrySet().stream()
|
||||||
|
.filter(entry -> ofNullable(entry.getValue()).orElse(emptyList()).stream().anyMatch(StringUtil::isNotBlank))
|
||||||
|
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ public final class DefaultUserProfile implements UserProfile {
|
||||||
private final Function<Attributes, UserModel> userSupplier;
|
private final Function<Attributes, UserModel> userSupplier;
|
||||||
private final Attributes attributes;
|
private final Attributes attributes;
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
private final boolean isUserProfileEnabled;
|
|
||||||
private boolean validated;
|
private boolean validated;
|
||||||
private UserModel user;
|
private UserModel user;
|
||||||
|
|
||||||
|
@ -67,8 +66,6 @@ public final class DefaultUserProfile implements UserProfile {
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
|
||||||
isUserProfileEnabled = provider.isEnabled(session.getContext().getRealm());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -226,7 +223,7 @@ public final class DefaultUserProfile implements UserProfile {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isUnmanagedAttribute = isUserProfileEnabled && metadata.getAttribute(name).isEmpty();
|
boolean isUnmanagedAttribute = metadata.getAttribute(name).isEmpty();
|
||||||
String value = isUnmanagedAttribute ? null : values.stream().findFirst().orElse(null);
|
String value = isUnmanagedAttribute ? null : values.stream().findFirst().orElse(null);
|
||||||
|
|
||||||
if (UserModel.USERNAME.equals(name)) {
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.userprofile;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
|
@ -88,12 +87,4 @@ public interface UserProfileProvider extends Provider {
|
||||||
* @see #getConfiguration()
|
* @see #getConfiguration()
|
||||||
*/
|
*/
|
||||||
void setConfiguration(UPConfig configuration);
|
void setConfiguration(UPConfig configuration);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the declarative provider is enabled to a realm
|
|
||||||
*
|
|
||||||
* @deprecated should be removed once {@link DeclarativeUserProfileProvider} becomes the default.
|
|
||||||
* @return {@code true} if the declarative provider is enabled. Otherwise, {@code false}.
|
|
||||||
*/
|
|
||||||
boolean isEnabled(RealmModel realm);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.keycloak.social.twitter.TwitterIdentityProviderFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -175,7 +176,12 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
||||||
} else if (variable.startsWith("CLAIM.")) {
|
} else if (variable.startsWith("CLAIM.")) {
|
||||||
String name = variable.substring("CLAIM.".length());
|
String name = variable.substring("CLAIM.".length());
|
||||||
Object value = AbstractClaimMapper.getClaimValue(context, name);
|
Object value = AbstractClaimMapper.getClaimValue(context, name);
|
||||||
if (value == null) value = "";
|
if (value == null) {
|
||||||
|
value = "";
|
||||||
|
} else if (value instanceof Collection && ((Collection<?>) value).size() == 1) {
|
||||||
|
// In case the value is list with single value, it might be preferred to avoid converting whole collection toString, but rather use value like "foo" instead of "[foo]"
|
||||||
|
value = ((Collection<?>) value).iterator().next();
|
||||||
|
}
|
||||||
m.appendReplacement(sb, transformer.apply(value.toString()));
|
m.appendReplacement(sb, transformer.apply(value.toString()));
|
||||||
} else {
|
} else {
|
||||||
m.appendReplacement(sb, m.group(1));
|
m.appendReplacement(sb, m.group(1));
|
||||||
|
|
|
@ -385,12 +385,6 @@ public class RealmAdminResource {
|
||||||
|
|
||||||
if (auth.users().canView()) {
|
if (auth.users().canView()) {
|
||||||
rep.setRegistrationEmailAsUsername(realm.isRegistrationEmailAsUsername());
|
rep.setRegistrationEmailAsUsername(realm.isRegistrationEmailAsUsername());
|
||||||
if (realm.getAttribute(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.FALSE)) {
|
|
||||||
// add the user profile attribute if enabled
|
|
||||||
Map<String, String> attrs = Optional.ofNullable(rep.getAttributes()).orElse(new HashMap<>());
|
|
||||||
attrs.put(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
|
||||||
rep.setAttributes(attrs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth.realm().canViewIdentityProviders()) {
|
if (auth.realm().canViewIdentityProviders()) {
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.services.messages.Messages;
|
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.userprofile.config.DeclarativeUserProfileModel;
|
import org.keycloak.userprofile.config.DeclarativeUserProfileModel;
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
@ -52,7 +51,6 @@ import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.userprofile.config.UPConfigUtils;
|
import org.keycloak.userprofile.config.UPConfigUtils;
|
||||||
import org.keycloak.representations.userprofile.config.UPGroup;
|
import org.keycloak.representations.userprofile.config.UPGroup;
|
||||||
import org.keycloak.userprofile.validator.AttributeRequiredByMetadataValidator;
|
import org.keycloak.userprofile.validator.AttributeRequiredByMetadataValidator;
|
||||||
import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
|
||||||
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.validate.AbstractSimpleValidator;
|
import org.keycloak.validate.AbstractSimpleValidator;
|
||||||
|
@ -68,7 +66,6 @@ import org.keycloak.validate.ValidatorConfig;
|
||||||
public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
|
|
||||||
public static final String UP_COMPONENT_CONFIG_KEY = "kc.user.profile.config";
|
public static final String UP_COMPONENT_CONFIG_KEY = "kc.user.profile.config";
|
||||||
public static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled";
|
|
||||||
protected static final String PARSED_CONFIG_COMPONENT_KEY = "kc.user.profile.metadata";
|
protected static final String PARSED_CONFIG_COMPONENT_KEY = "kc.user.profile.metadata";
|
||||||
protected static final String PARSED_UP_CONFIG_COMPONENT_KEY = "kc.parsed.up.config";
|
protected static final String PARSED_UP_CONFIG_COMPONENT_KEY = "kc.parsed.up.config";
|
||||||
|
|
||||||
|
@ -94,7 +91,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
private final boolean isDeclarativeConfigurationEnabled;
|
|
||||||
private final String providerId;
|
private final String providerId;
|
||||||
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry;
|
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry;
|
||||||
protected final UPConfig parsedDefaultRawConfig;
|
protected final UPConfig parsedDefaultRawConfig;
|
||||||
|
@ -102,23 +98,17 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
public DeclarativeUserProfileProvider(KeycloakSession session, DeclarativeUserProfileProviderFactory factory) {
|
public DeclarativeUserProfileProvider(KeycloakSession session, DeclarativeUserProfileProviderFactory factory) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.providerId = factory.getId();
|
this.providerId = factory.getId();
|
||||||
this.isDeclarativeConfigurationEnabled = factory.isDeclarativeConfigurationEnabled();
|
|
||||||
this.contextualMetadataRegistry = factory.getContextualMetadataRegistry();
|
this.contextualMetadataRegistry = factory.getContextualMetadataRegistry();
|
||||||
this.parsedDefaultRawConfig = factory.getParsedDefaultRawConfig();
|
this.parsedDefaultRawConfig = factory.getParsedDefaultRawConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Attributes createAttributes(UserProfileContext context, Map<String, ?> attributes,
|
protected Attributes createAttributes(UserProfileContext context, Map<String, ?> attributes,
|
||||||
UserModel user, UserProfileMetadata metadata) {
|
UserModel user, UserProfileMetadata metadata) {
|
||||||
RealmModel realm = session.getContext().getRealm();
|
|
||||||
|
|
||||||
if (isEnabled(realm)) {
|
if (user != null && user.getServiceAccountClientLink() != null) {
|
||||||
if (user != null && user.getServiceAccountClientLink() != null) {
|
return new LegacyAttributes(context, attributes, user, metadata, session);
|
||||||
return new LegacyAttributes(context, attributes, user, metadata, session);
|
|
||||||
}
|
|
||||||
return new DefaultAttributes(context, attributes, user, metadata, session);
|
|
||||||
}
|
}
|
||||||
|
return new DefaultAttributes(context, attributes, user, metadata, session);
|
||||||
return new LegacyAttributes(context, attributes, user, metadata, session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -187,16 +177,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
UserProfileMetadata decoratedMetadata = metadata.clone();
|
UserProfileMetadata decoratedMetadata = metadata.clone();
|
||||||
RealmModel realm = session.getContext().getRealm();
|
RealmModel realm = session.getContext().getRealm();
|
||||||
|
|
||||||
if (!isEnabled(realm)) {
|
|
||||||
if(!context.equals(UserProfileContext.USER_API)
|
|
||||||
&& !context.equals(UserProfileContext.UPDATE_EMAIL)) {
|
|
||||||
decoratedMetadata.addAttribute(UserModel.FIRST_NAME, 1, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(
|
|
||||||
Messages.MISSING_FIRST_NAME, metadata.getContext() == UserProfileContext.IDP_REVIEW))).setAttributeDisplayName("${firstName}");
|
|
||||||
decoratedMetadata.addAttribute(UserModel.LAST_NAME, 2, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_LAST_NAME, metadata.getContext() == UserProfileContext.IDP_REVIEW))).setAttributeDisplayName("${lastName}");
|
|
||||||
}
|
|
||||||
return decoratedMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentModel component = getComponentModel().orElse(null);
|
ComponentModel component = getComponentModel().orElse(null);
|
||||||
|
|
||||||
if (component == null) {
|
if (component == null) {
|
||||||
|
@ -218,12 +198,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UPConfig getConfiguration() {
|
public UPConfig getConfiguration() {
|
||||||
RealmModel realm = session.getContext().getRealm();
|
|
||||||
|
|
||||||
if (!isEnabled(realm)) {
|
|
||||||
return parsedDefaultRawConfig.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<ComponentModel> component = getComponentModel();
|
Optional<ComponentModel> component = getComponentModel();
|
||||||
|
|
||||||
if (component.isPresent()) {
|
if (component.isPresent()) {
|
||||||
|
@ -522,11 +496,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||||
model.getConfig().remove(UP_COMPONENT_CONFIG_KEY);
|
model.getConfig().remove(UP_COMPONENT_CONFIG_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(RealmModel realm) {
|
|
||||||
return isDeclarativeConfigurationEnabled && realm.getAttribute(REALM_USER_PROFILE_ENABLED, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,8 +85,6 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
||||||
private static final Pattern readOnlyAttributesPattern = getRegexPatternString(DEFAULT_READ_ONLY_ATTRIBUTES);
|
private static final Pattern readOnlyAttributesPattern = getRegexPatternString(DEFAULT_READ_ONLY_ATTRIBUTES);
|
||||||
private static final Pattern adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES);
|
private static final Pattern adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES);
|
||||||
|
|
||||||
private boolean isDeclarativeConfigurationEnabled;
|
|
||||||
|
|
||||||
private UPConfig parsedDefaultRawConfig;
|
private UPConfig parsedDefaultRawConfig;
|
||||||
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry = new HashMap<>();
|
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry = new HashMap<>();
|
||||||
|
|
||||||
|
@ -198,7 +196,6 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
isDeclarativeConfigurationEnabled = Profile.isFeatureEnabled(Profile.Feature.DECLARATIVE_USER_PROFILE);
|
|
||||||
parsedDefaultRawConfig = UPConfigUtils.parseDefaultConfig();
|
parsedDefaultRawConfig = UPConfigUtils.parseDefaultConfig();
|
||||||
|
|
||||||
// make sure registry is clear in case of re-deploy
|
// make sure registry is clear in case of re-deploy
|
||||||
|
@ -313,12 +310,8 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
||||||
* @return the metadata
|
* @return the metadata
|
||||||
*/
|
*/
|
||||||
protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata) {
|
protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata) {
|
||||||
if (isDeclarativeConfigurationEnabled) {
|
// default metadata for each context is based on the default realm configuration
|
||||||
// default metadata for each context is based on the default realm configuration
|
return new DeclarativeUserProfileProvider(null, this).decorateUserProfileForCache(metadata, parsedDefaultRawConfig);
|
||||||
return new DeclarativeUserProfileProvider(null, this).decorateUserProfileForCache(metadata, parsedDefaultRawConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AttributeValidatorMetadata createReadOnlyAttributeUnchangedValidator(Pattern pattern) {
|
private AttributeValidatorMetadata createReadOnlyAttributeUnchangedValidator(Pattern pattern) {
|
||||||
|
@ -461,10 +454,6 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
||||||
|
|
||||||
// GETTER METHODS FOR INTERNAL FIELDS
|
// GETTER METHODS FOR INTERNAL FIELDS
|
||||||
|
|
||||||
protected boolean isDeclarativeConfigurationEnabled() {
|
|
||||||
return isDeclarativeConfigurationEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UPConfig getParsedDefaultRawConfig() {
|
protected UPConfig getParsedDefaultRawConfig() {
|
||||||
return parsedDefaultRawConfig;
|
return parsedDefaultRawConfig;
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,7 +524,7 @@ so please make sure you rebuild all `testsuite/integration-arquillian` child mod
|
||||||
|
|
||||||
## Cluster tests
|
## Cluster tests
|
||||||
|
|
||||||
Cluster tests use 2 backend servers (Keycloak on Wildfly/EAP or Keycloak on Undertow), 1 frontend loadbalancer server node and one shared DB. Invalidation tests don't use loadbalancer.
|
Cluster tests use 2 backend servers (Keycloak on Quarkus or Keycloak on Undertow), 1 frontend loadbalancer server node and one shared DB. Invalidation tests don't use loadbalancer.
|
||||||
The browser usually communicates directly with the backend node1 and after doing some change here (eg. updating user), it verifies that the change is visible on node2 and user is updated here as well.
|
The browser usually communicates directly with the backend node1 and after doing some change here (eg. updating user), it verifies that the change is visible on node2 and user is updated here as well.
|
||||||
|
|
||||||
Failover tests use loadbalancer and they require the setup with the distributed infinispan caches switched to have 2 owners (default value is 1 owner). Otherwise failover won't reliably work.
|
Failover tests use loadbalancer and they require the setup with the distributed infinispan caches switched to have 2 owners (default value is 1 owner). Otherwise failover won't reliably work.
|
||||||
|
|
|
@ -19,20 +19,28 @@
|
||||||
package org.keycloak.testsuite.account;
|
package org.keycloak.testsuite.account;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import jakarta.ws.rs.BadRequestException;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.representations.account.UserRepresentation;
|
import org.keycloak.representations.account.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
import org.keycloak.userprofile.UserProfileConstants;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
@ -47,6 +55,46 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AccountRestServiceReadOnlyAttributesTest.class);
|
private static final Logger logger = Logger.getLogger(AccountRestServiceReadOnlyAttributesTest.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void configureUserProfile() {
|
||||||
|
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||||
|
UPConfig cfg = userProfileRes.getConfiguration();
|
||||||
|
//cfg.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("someOtherAttr"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("usercertificate"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("uSErCertificate"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("KERBEROS_PRINCIPAL"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("noKerberos_Principal"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("KERBEROS_PRINCIPALno"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("enabled"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("CREATED_TIMESTAMP"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("saml.something"));
|
||||||
|
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedfoo"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedFOo"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedFoot"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedbar"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedBAr"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedBArr"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedbarrier"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("nodeniedbar"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("nodeniedBARrier"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("saml.persistent.name.id.for.foo"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("saml.persistent.name.id.for._foo_"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("saml.persistent.name.idafor.foo"));
|
||||||
|
// TODO: Doublecheck this. We should either document that attributes with custom characters are not allowed or we should enable to configure them
|
||||||
|
// cfg.addOrReplaceAttribute(createUpAttribute("deniedsome/thing"));
|
||||||
|
// cfg.addOrReplaceAttribute(createUpAttribute("deniedsome*thing"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedsomeithing"));
|
||||||
|
cfg.addOrReplaceAttribute(createUpAttribute("deniedSomeAdmin"));
|
||||||
|
userProfileRes.update(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UPAttribute createUpAttribute(String name) {
|
||||||
|
return new UPAttribute(name, new UPAttributePermissions(Collections.emptySet(), Set.of(UserProfileConstants.ROLE_USER, UserProfileConstants.ROLE_ADMIN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test read-only attributes from provider configuration have precedence over the user-profile realm configuration settings (Read-only attributes from provider config are always read-only)
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateProfileCannotUpdateReadOnlyAttributes() throws IOException {
|
public void testUpdateProfileCannotUpdateReadOnlyAttributes() throws IOException {
|
||||||
// Denied by default
|
// Denied by default
|
||||||
|
@ -85,9 +133,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
||||||
testAccountUpdateAttributeExpectFailure("saml.persistent.name.id.for._foo_");
|
testAccountUpdateAttributeExpectFailure("saml.persistent.name.id.for._foo_");
|
||||||
testAccountUpdateAttributeExpectSuccess("saml.persistent.name.idafor.foo");
|
testAccountUpdateAttributeExpectSuccess("saml.persistent.name.idafor.foo");
|
||||||
|
|
||||||
|
// TODO: Uncomment similarly like above
|
||||||
// Special characters inside should be quoted
|
// Special characters inside should be quoted
|
||||||
testAccountUpdateAttributeExpectFailure("deniedsome/thing");
|
//testAccountUpdateAttributeExpectFailure("deniedsome/thing");
|
||||||
testAccountUpdateAttributeExpectFailure("deniedsome*thing");
|
//testAccountUpdateAttributeExpectFailure("deniedsome*thing");
|
||||||
testAccountUpdateAttributeExpectSuccess("deniedsomeithing");
|
testAccountUpdateAttributeExpectSuccess("deniedsomeithing");
|
||||||
|
|
||||||
// Denied only for admin, but allowed for normal user
|
// Denied only for admin, but allowed for normal user
|
||||||
|
@ -135,9 +184,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
||||||
user.singleAttribute(attrName, "foo-updated");
|
user.singleAttribute(attrName, "foo-updated");
|
||||||
updateError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
updateError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
// Ignore removal of read-only attributes
|
// Removal of read-only attribute not allowed
|
||||||
user.getAttributes().remove(attrName);
|
user.getAttributes().remove(attrName);
|
||||||
user = updateAndGet(user);
|
updateError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
user = get();
|
||||||
assertTrue(user.getAttributes().containsKey(attrName));
|
assertTrue(user.getAttributes().containsKey(attrName));
|
||||||
|
|
||||||
// Revert with admin REST
|
// Revert with admin REST
|
||||||
|
@ -178,6 +228,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
||||||
private UserRepresentation updateAndGet(UserRepresentation user) throws IOException {
|
private UserRepresentation updateAndGet(UserRepresentation user) throws IOException {
|
||||||
int status = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asStatus();
|
int status = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||||
assertEquals(204, status);
|
assertEquals(204, status);
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserRepresentation get() throws IOException {
|
||||||
return SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
return SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
package org.keycloak.testsuite.account;
|
package org.keycloak.testsuite.account;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
@ -66,11 +68,11 @@ import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.TokenUtil;
|
import org.keycloak.testsuite.util.TokenUtil;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
import org.keycloak.userprofile.UserProfileContext;
|
import org.keycloak.userprofile.UserProfileContext;
|
||||||
import org.keycloak.validate.validators.EmailValidator;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.ClientErrorException;
|
import jakarta.ws.rs.ClientErrorException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
|
@ -91,6 +93,7 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -100,6 +103,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
@Rule
|
@Rule
|
||||||
public AssertEvents events = new AssertEvents(this);
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
setUserProfileConfiguration(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditUsernameAllowed() throws IOException {
|
public void testEditUsernameAllowed() throws IOException {
|
||||||
UserRepresentation user = getUser();
|
UserRepresentation user = getUser();
|
||||||
|
@ -115,14 +125,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
realmRep.setEditUsernameAllowed(true);
|
realmRep.setEditUsernameAllowed(true);
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
user = getUser();
|
user = getUser();
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(user.getUserProfileMetadata());
|
assertNotNull(user.getUserProfileMetadata());
|
||||||
// can write both username and email
|
// can write both username and email
|
||||||
assertUserProfileAttributeMetadata(user, "username", "${username}", true, false);
|
assertUserProfileAttributeMetadata(user, "username", "${username}", true, false);
|
||||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||||
assertUserProfileAttributeMetadata(user, "firstName", "${firstName}", true, false);
|
assertUserProfileAttributeMetadata(user, "firstName", "${firstName}", true, false);
|
||||||
assertUserProfileAttributeMetadata(user, "lastName", "${lastName}", true, false);
|
assertUserProfileAttributeMetadata(user, "lastName", "${lastName}", true, false);
|
||||||
}
|
|
||||||
user.setUsername("changed-username");
|
user.setUsername("changed-username");
|
||||||
user.setEmail("changed-email@keycloak.org");
|
user.setEmail("changed-email@keycloak.org");
|
||||||
user = updateAndGet(user);
|
user = updateAndGet(user);
|
||||||
|
@ -133,12 +143,12 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
realmRep.setEditUsernameAllowed(false);
|
realmRep.setEditUsernameAllowed(false);
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
user = getUser();
|
user = getUser();
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(user.getUserProfileMetadata());
|
assertNotNull(user.getUserProfileMetadata());
|
||||||
// username is readonly but email is writable
|
// username is readonly but email is writable
|
||||||
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true);
|
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true);
|
||||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||||
}
|
|
||||||
user.setUsername("should-not-change");
|
user.setUsername("should-not-change");
|
||||||
user.setEmail("changed-email@keycloak.org");
|
user.setEmail("changed-email@keycloak.org");
|
||||||
updateError(user, 400, Messages.READ_ONLY_USERNAME);
|
updateError(user, 400, Messages.READ_ONLY_USERNAME);
|
||||||
|
@ -147,13 +157,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
realmRep.setEditUsernameAllowed(true);
|
realmRep.setEditUsernameAllowed(true);
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
user = getUser();
|
user = getUser();
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(user.getUserProfileMetadata());
|
assertNotNull(user.getUserProfileMetadata());
|
||||||
// username is read-only, not required, and is the same as email
|
// username is read-only, not required, and is the same as email
|
||||||
// but email is writable
|
// but email is writable
|
||||||
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
||||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||||
}
|
|
||||||
user.setUsername("should-be-the-email");
|
user.setUsername("should-be-the-email");
|
||||||
user.setEmail("user@keycloak.org");
|
user.setEmail("user@keycloak.org");
|
||||||
user = updateAndGet(user);
|
user = updateAndGet(user);
|
||||||
|
@ -164,12 +174,12 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
realmRep.setEditUsernameAllowed(false);
|
realmRep.setEditUsernameAllowed(false);
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
user = getUser();
|
user = getUser();
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(user.getUserProfileMetadata());
|
assertNotNull(user.getUserProfileMetadata());
|
||||||
// username is read-only and is the same as email, but email is read-only
|
// username is read-only and is the same as email, but email is read-only
|
||||||
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
||||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
|
assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
|
||||||
}
|
|
||||||
user.setUsername("should-be-the-email");
|
user.setUsername("should-be-the-email");
|
||||||
user.setEmail("should-not-change@keycloak.org");
|
user.setEmail("should-not-change@keycloak.org");
|
||||||
user = updateAndGet(user);
|
user = updateAndGet(user);
|
||||||
|
@ -210,45 +220,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
assertNull(user.getUserProfileMetadata());
|
assertNull(user.getUserProfileMetadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
protected static UserProfileAttributeMetadata getUserProfileAttributeMetadata(UserRepresentation user, String attName) {
|
||||||
public void testEditUsernameDisallowed() throws IOException {
|
|
||||||
try {
|
|
||||||
RealmResource realm = adminClient.realm("test");
|
|
||||||
RealmRepresentation realmRep = realm.toRepresentation();
|
|
||||||
realmRep.setEditUsernameAllowed(false);
|
|
||||||
realm.update(realmRep);
|
|
||||||
|
|
||||||
UserRepresentation user = getUser();
|
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(user.getUserProfileMetadata());
|
|
||||||
UserProfileAttributeMetadata upm = assertUserProfileAttributeMetadata(user, "username", "${username}", true, true);
|
|
||||||
//makes sure internal validators are not exposed
|
|
||||||
Assert.assertEquals(0, upm.getValidators().size());
|
|
||||||
|
|
||||||
upm = assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
|
||||||
Assert.assertEquals(1, upm.getValidators().size());
|
|
||||||
Assert.assertTrue(upm.getValidators().containsKey(EmailValidator.ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
realmRep.setRegistrationEmailAsUsername(true);
|
|
||||||
realm.update(realmRep);
|
|
||||||
user = getUser();
|
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
UserProfileAttributeMetadata upm = assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
|
|
||||||
Assert.assertEquals(1, upm.getValidators().size());
|
|
||||||
Assert.assertTrue(upm.getValidators().containsKey(EmailValidator.ID));
|
|
||||||
|
|
||||||
assertUserProfileAttributeMetadata(user, "firstName", "${firstName}", true, false);
|
|
||||||
assertUserProfileAttributeMetadata(user, "lastName", "${lastName}", true, false);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
RealmRepresentation realmRep = testRealm().toRepresentation();
|
|
||||||
realmRep.setEditUsernameAllowed(true);
|
|
||||||
testRealm().update(realmRep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UserProfileAttributeMetadata getUserProfileAttributeMetadata(UserRepresentation user, String attName) {
|
|
||||||
if(user.getUserProfileMetadata() == null)
|
if(user.getUserProfileMetadata() == null)
|
||||||
return null;
|
return null;
|
||||||
for(UserProfileAttributeMetadata uam : user.getUserProfileMetadata().getAttributes()) {
|
for(UserProfileAttributeMetadata uam : user.getUserProfileMetadata().getAttributes()) {
|
||||||
|
@ -259,14 +231,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserProfileAttributeMetadata assertUserProfileAttributeMetadata(UserRepresentation user, String attName, String displayName, boolean required, boolean readOnly) {
|
protected static UserProfileAttributeMetadata assertUserProfileAttributeMetadata(UserRepresentation user, String attName, String displayName, boolean required, boolean readOnly) {
|
||||||
UserProfileAttributeMetadata uam = getUserProfileAttributeMetadata(user, attName);
|
UserProfileAttributeMetadata uam = getUserProfileAttributeMetadata(user, attName);
|
||||||
if (isDeclarativeUserProfile()) {
|
|
||||||
assertNotNull(uam);
|
assertNotNull(uam);
|
||||||
assertEquals("Unexpected display name for attribute " + uam.getName(), displayName, uam.getDisplayName());
|
assertEquals("Unexpected display name for attribute " + uam.getName(), displayName, uam.getDisplayName());
|
||||||
assertEquals("Unexpected required flag for attribute " + uam.getName(), required, uam.isRequired());
|
assertEquals("Unexpected required flag for attribute " + uam.getName(), required, uam.isRequired());
|
||||||
assertEquals("Unexpected readonly flag for attribute " + uam.getName(), readOnly, uam.isReadOnly());
|
assertEquals("Unexpected readonly flag for attribute " + uam.getName(), readOnly, uam.isReadOnly());
|
||||||
}
|
|
||||||
return uam;
|
return uam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,6 +256,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateSingleField() throws IOException {
|
public void testUpdateSingleField() throws IOException {
|
||||||
|
String userProfileConfig = "{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
||||||
|
+ "]}";
|
||||||
|
setUserProfileConfiguration(userProfileConfig);
|
||||||
|
|
||||||
UserRepresentation user = getUser();
|
UserRepresentation user = getUser();
|
||||||
String originalUsername = user.getUsername();
|
String originalUsername = user.getUsername();
|
||||||
String originalFirstName = user.getFirstName();
|
String originalFirstName = user.getFirstName();
|
||||||
|
@ -374,6 +353,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateProfileEvent() throws IOException {
|
public void testUpdateProfileEvent() throws IOException {
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"attr1\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"attr2\"," + PERMISSIONS_ALL + "}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
UserRepresentation user = getUser();
|
UserRepresentation user = getUser();
|
||||||
String originalUsername = user.getUsername();
|
String originalUsername = user.getUsername();
|
||||||
String originalFirstName = user.getFirstName();
|
String originalFirstName = user.getFirstName();
|
||||||
|
@ -426,6 +412,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateProfile() throws IOException {
|
public void testUpdateProfile() throws IOException {
|
||||||
|
String userProfileCfg = "{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"attr1\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"attr2\"," + PERMISSIONS_ALL + "}"
|
||||||
|
+ "]}";
|
||||||
|
setUserProfileConfiguration(userProfileCfg);
|
||||||
|
|
||||||
UserRepresentation user = getUser();
|
UserRepresentation user = getUser();
|
||||||
String originalUsername = user.getUsername();
|
String originalUsername = user.getUsername();
|
||||||
String originalFirstName = user.getFirstName();
|
String originalFirstName = user.getFirstName();
|
||||||
|
@ -602,6 +596,10 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
|
|
||||||
protected UserRepresentation getUser(boolean fetchMetadata) throws IOException {
|
protected UserRepresentation getUser(boolean fetchMetadata) throws IOException {
|
||||||
String accountUrl = getAccountUrl(null) + "?userProfileMetadata=" + fetchMetadata;
|
String accountUrl = getAccountUrl(null) + "?userProfileMetadata=" + fetchMetadata;
|
||||||
|
return getUser(accountUrl, httpClient, tokenUtil);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static UserRepresentation getUser(String accountUrl, CloseableHttpClient httpClient, TokenUtil tokenUtil) throws IOException {
|
||||||
SimpleHttp a = SimpleHttp.doGet(accountUrl, httpClient).auth(tokenUtil.getToken());
|
SimpleHttp a = SimpleHttp.doGet(accountUrl, httpClient).auth(tokenUtil.getToken());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1719,7 +1717,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDeclarativeUserProfile() {
|
protected void setUserProfileConfiguration(String configuration) {
|
||||||
return false;
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.keycloak.testsuite.account.AccountRestServiceTest.assertUserProfileAttributeMetadata;
|
||||||
|
import static org.keycloak.testsuite.account.AccountRestServiceTest.getUserProfileAttributeMetadata;
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_ONLY;
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_ONLY;
|
||||||
|
@ -34,7 +36,6 @@ import java.util.Optional;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -42,31 +43,24 @@ import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
||||||
import org.keycloak.representations.idm.UserProfileMetadata;
|
import org.keycloak.representations.idm.UserProfileMetadata;
|
||||||
import org.keycloak.representations.account.UserRepresentation;
|
import org.keycloak.representations.account.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.userprofile.UserProfileContext;
|
import org.keycloak.userprofile.UserProfileContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test account rest service with custom user profile configurations
|
||||||
*
|
*
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
* @author Vlastimil Elias <velias@redhat.com>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
public class AccountRestServiceWithUserProfileTest extends AbstractRestServiceTest {
|
||||||
public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTest {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
super.before();
|
super.before();
|
||||||
enableDynamicUserProfile();
|
|
||||||
setUserProfileConfiguration(null);
|
setUserProfileConfiguration(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isDeclarativeUserProfile() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final static String UP_CONFIG_FOR_METADATA = "{\"attributes\": ["
|
private final static String UP_CONFIG_FOR_METADATA = "{\"attributes\": ["
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {\"scopes\":[\"profile\"]}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {\"scopes\":[\"profile\"]}, \"displayName\": \"${profile.firstName}\", \"validations\": {\"length\": { \"max\": 255 }}},"
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}, \"displayName\": \"Last name\", \"annotations\": {\"formHintKey\" : \"userEmailFormFieldHint\", \"anotherKey\" : 10, \"yetAnotherKey\" : \"some value\"}},"
|
||||||
|
@ -101,9 +95,7 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
|
||||||
public void testEditUsernameAllowed() throws IOException {
|
public void testEditUsernameAllowed() throws IOException {
|
||||||
super.testEditUsernameAllowed();
|
|
||||||
setUserProfileConfiguration(UP_CONFIG_FOR_METADATA);
|
setUserProfileConfiguration(UP_CONFIG_FOR_METADATA);
|
||||||
|
|
||||||
UserRepresentation user = getUser();
|
UserRepresentation user = getUser();
|
||||||
|
@ -221,7 +213,6 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
|
||||||
public void testEditUsernameDisallowed() throws IOException {
|
public void testEditUsernameDisallowed() throws IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -340,40 +331,6 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateProfileEvent() throws IOException {
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"attr1\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"attr2\"," + PERMISSIONS_ALL + "}"
|
|
||||||
+ "]}");
|
|
||||||
super.testUpdateProfileEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Override
|
|
||||||
public void testUpdateProfile() throws IOException {
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"attr1\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"attr2\"," + PERMISSIONS_ALL + "}"
|
|
||||||
+ "]}");
|
|
||||||
super.testUpdateProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Override
|
|
||||||
public void testUpdateSingleField() throws IOException {
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}}"
|
|
||||||
+ "]}");
|
|
||||||
super.testUpdateSingleField();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testManageUserLocaleAttribute() throws IOException {
|
public void testManageUserLocaleAttribute() throws IOException {
|
||||||
RealmRepresentation realmRep = testRealm().toRepresentation();
|
RealmRepresentation realmRep = testRealm().toRepresentation();
|
||||||
|
@ -417,12 +374,24 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void enableDynamicUserProfile() {
|
protected UserRepresentation getUser() throws IOException {
|
||||||
RealmRepresentation testRealm = testRealm().toRepresentation();
|
return getUser(true);
|
||||||
|
}
|
||||||
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
protected UserRepresentation getUser(boolean fetchMetadata) throws IOException {
|
||||||
|
String accountUrl = getAccountUrl(null) + "?userProfileMetadata=" + fetchMetadata;
|
||||||
|
return AccountRestServiceTest.getUser(accountUrl, httpClient, tokenUtil);
|
||||||
|
}
|
||||||
|
|
||||||
testRealm().update(testRealm);
|
protected UserRepresentation updateAndGet(UserRepresentation user) throws IOException {
|
||||||
|
SimpleHttp a = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user);
|
||||||
|
try {
|
||||||
|
assertEquals(204, a.asStatus());
|
||||||
|
} catch (AssertionError e) {
|
||||||
|
System.err.println("Error during user update: " + a.asString());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return getUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package org.keycloak.testsuite.account.custom;
|
package org.keycloak.testsuite.account.custom;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel.Requirement;
|
import org.keycloak.models.AuthenticationExecutionModel.Requirement;
|
||||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
import org.keycloak.models.utils.TimeBasedOTP;
|
||||||
|
@ -29,6 +31,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.admin.Users;
|
import org.keycloak.testsuite.admin.Users;
|
||||||
import org.keycloak.testsuite.auth.page.login.OneTimeCode;
|
import org.keycloak.testsuite.auth.page.login.OneTimeCode;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.pages.LoginConfigTotpPage;
|
import org.keycloak.testsuite.pages.LoginConfigTotpPage;
|
||||||
import org.keycloak.testsuite.pages.LoginTotpPage;
|
import org.keycloak.testsuite.pages.LoginTotpPage;
|
||||||
import org.keycloak.testsuite.pages.PageUtils;
|
import org.keycloak.testsuite.pages.PageUtils;
|
||||||
|
@ -86,6 +89,12 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
|
||||||
testLoginOneTimeCodePage.setAuthRealm(testRealmPage);
|
testLoginOneTimeCodePage.setAuthRealm(testRealmPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void configureUserProfile() {
|
||||||
|
UserProfileResource userProfileRes = testRealmResource().users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
|
}
|
||||||
|
|
||||||
private void configureRequiredActions() {
|
private void configureRequiredActions() {
|
||||||
//set configure TOTP as required action to test user
|
//set configure TOTP as required action to test user
|
||||||
List<String> requiredActions = new ArrayList<>();
|
List<String> requiredActions = new ArrayList<>();
|
||||||
|
|
|
@ -16,13 +16,25 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.actions;
|
package org.keycloak.testsuite.actions;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.userprofile.UserProfileConstants.ROLE_USER;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributeRequired;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
|
import org.keycloak.validate.validators.LengthValidator;
|
||||||
|
|
||||||
public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActionUpdateEmailTest {
|
public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActionUpdateEmailTest {
|
||||||
|
|
||||||
|
@ -41,6 +53,46 @@ public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActio
|
||||||
Assert.assertEquals("Brady", user.getLastName());
|
Assert.assertEquals("Brady", user.getLastName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomEmailValidator() throws Exception {
|
||||||
|
UserProfileResource userProfile = testRealm().users().userProfile();
|
||||||
|
UPConfig upConfig = userProfile.getConfiguration();
|
||||||
|
UPAttribute emailConfig = upConfig.getAttribute(UserModel.EMAIL);
|
||||||
|
emailConfig.addValidation(LengthValidator.ID, Map.of("min", "1", "max", "1"));
|
||||||
|
getCleanup().addCleanup(() -> {
|
||||||
|
emailConfig.getValidations().remove(LengthValidator.ID);
|
||||||
|
userProfile.update(upConfig);
|
||||||
|
});
|
||||||
|
userProfile.update(upConfig);
|
||||||
|
|
||||||
|
changeEmailUsingAIA("new@email.com");
|
||||||
|
assertTrue(emailUpdatePage.getEmailError().contains("Length must be between 1 and 1."));
|
||||||
|
|
||||||
|
emailConfig.getValidations().remove(LengthValidator.ID);
|
||||||
|
userProfile.update(upConfig);
|
||||||
|
changeEmailUsingAIA("new@email.com");
|
||||||
|
events.expect(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost")
|
||||||
|
.detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnlyEmailSupportedInContext() throws Exception {
|
||||||
|
UserProfileResource userProfile = testRealm().users().userProfile();
|
||||||
|
UPConfig upConfig = userProfile.getConfiguration();
|
||||||
|
String unexpectedAttributeName = "unexpectedAttribute";
|
||||||
|
upConfig.addOrReplaceAttribute(new UPAttribute(unexpectedAttributeName, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of())));
|
||||||
|
getCleanup().addCleanup(() -> {
|
||||||
|
upConfig.removeAttribute(unexpectedAttributeName);
|
||||||
|
userProfile.update(upConfig);
|
||||||
|
});
|
||||||
|
userProfile.update(upConfig);
|
||||||
|
|
||||||
|
assertFalse(driver.getPageSource().contains(unexpectedAttributeName));
|
||||||
|
changeEmailUsingAIA("new@email.com");
|
||||||
|
events.expect(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost")
|
||||||
|
.detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void changeEmailUsingAIA(String newEmail) throws Exception {
|
protected void changeEmailUsingAIA(String newEmail) throws Exception {
|
||||||
doAIA();
|
doAIA();
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.actions;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.keycloak.userprofile.UserProfileConstants.ROLE_USER;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.events.Details;
|
|
||||||
import org.keycloak.events.EventType;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributeRequired;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.validate.validators.LengthValidator;
|
|
||||||
|
|
||||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class AppInitiatedActionUpdateEmailUserProfileTest extends AppInitiatedActionUpdateEmailTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
super.configureTestRealm(testRealm);
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCustomEmailValidator() throws Exception {
|
|
||||||
UserProfileResource userProfile = testRealm().users().userProfile();
|
|
||||||
UPConfig upConfig = userProfile.getConfiguration();
|
|
||||||
UPAttribute emailConfig = upConfig.getAttribute(UserModel.EMAIL);
|
|
||||||
emailConfig.addValidation(LengthValidator.ID, Map.of("min", "1", "max", "1"));
|
|
||||||
getCleanup().addCleanup(() -> {
|
|
||||||
emailConfig.getValidations().remove(LengthValidator.ID);
|
|
||||||
userProfile.update(upConfig);
|
|
||||||
});
|
|
||||||
userProfile.update(upConfig);
|
|
||||||
|
|
||||||
changeEmailUsingAIA("new@email.com");
|
|
||||||
assertTrue(emailUpdatePage.getEmailError().contains("Length must be between 1 and 1."));
|
|
||||||
|
|
||||||
emailConfig.getValidations().remove(LengthValidator.ID);
|
|
||||||
userProfile.update(upConfig);
|
|
||||||
changeEmailUsingAIA("new@email.com");
|
|
||||||
events.expect(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost")
|
|
||||||
.detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnlyEmailSupportedInContext() throws Exception {
|
|
||||||
UserProfileResource userProfile = testRealm().users().userProfile();
|
|
||||||
UPConfig upConfig = userProfile.getConfiguration();
|
|
||||||
String unexpectedAttributeName = "unexpectedAttribute";
|
|
||||||
upConfig.addOrReplaceAttribute(new UPAttribute(unexpectedAttributeName, new UPAttributePermissions(Set.of(), Set.of(ROLE_USER)), new UPAttributeRequired(Set.of(ROLE_USER), Set.of())));
|
|
||||||
getCleanup().addCleanup(() -> {
|
|
||||||
upConfig.removeAttribute(unexpectedAttributeName);
|
|
||||||
userProfile.update(upConfig);
|
|
||||||
});
|
|
||||||
userProfile.update(upConfig);
|
|
||||||
|
|
||||||
assertFalse(driver.getPageSource().contains(unexpectedAttributeName));
|
|
||||||
changeEmailUsingAIA("new@email.com");
|
|
||||||
events.expect(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost")
|
|
||||||
.detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,6 +37,8 @@ import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Only covers basic use cases for App Initialized actions. Complete dynamic user profile behavior is tested in {@link RequiredActionUpdateProfileWithUserProfileTest} as it shares same code as the App initialized action.
|
||||||
|
*
|
||||||
* @author Stan Silvert
|
* @author Stan Silvert
|
||||||
*/
|
*/
|
||||||
public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedActionTest {
|
public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedActionTest {
|
||||||
|
@ -52,10 +54,6 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
||||||
@Page
|
@Page
|
||||||
protected ErrorPage errorPage;
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
protected boolean isDynamicForm() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
}
|
}
|
||||||
|
@ -212,10 +210,7 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
||||||
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
||||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||||
|
|
||||||
if(isDynamicForm())
|
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
|
||||||
else
|
|
||||||
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
|
|
||||||
|
|
||||||
events.assertEmpty();
|
events.assertEmpty();
|
||||||
}
|
}
|
||||||
|
@ -237,10 +232,7 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
||||||
Assert.assertEquals("", updateProfilePage.getLastName());
|
Assert.assertEquals("", updateProfilePage.getLastName());
|
||||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||||
|
|
||||||
if(isDynamicForm())
|
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
||||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
|
||||||
else
|
|
||||||
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
|
|
||||||
|
|
||||||
events.assertEmpty();
|
events.assertEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.actions;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only covers basic use cases for App Initialized actions. Complete dynamic user profile behavior is tested in {@link RequiredActionUpdateProfileWithUserProfileTest} as it shares same code as the App initialized action.
|
|
||||||
*
|
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class AppInitiatedActionUpdateProfileWithUserProfileTest extends AppInitiatedActionUpdateProfileTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isDynamicForm() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
super.configureTestRealm(testRealm);
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void beforeTest() {
|
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
|
|
||||||
super.beforeTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.actions;
|
|
||||||
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
|
|
||||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class RequiredActionUpdateEmailUserProfileTest extends RequiredActionUpdateEmailTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
|
||||||
super.configureTestRealm(testRealm);
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,6 +29,7 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -36,6 +37,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
@ -67,10 +69,6 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
@Page
|
@Page
|
||||||
protected ErrorPage errorPage;
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
protected boolean isDynamicForm() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
ActionUtil.addRequiredActionForUser(testRealm, "test-user@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
|
ActionUtil.addRequiredActionForUser(testRealm, "test-user@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
|
||||||
|
@ -177,11 +175,7 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
Assert.assertEquals("", updateProfilePage.getFirstName());
|
Assert.assertEquals("", updateProfilePage.getFirstName());
|
||||||
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
||||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||||
|
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||||
if(isDynamicForm())
|
|
||||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
|
||||||
else
|
|
||||||
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
|
|
||||||
|
|
||||||
events.assertEmpty();
|
events.assertEmpty();
|
||||||
}
|
}
|
||||||
|
@ -203,10 +197,7 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
Assert.assertEquals("", updateProfilePage.getLastName());
|
Assert.assertEquals("", updateProfilePage.getLastName());
|
||||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||||
|
|
||||||
if(isDynamicForm())
|
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
||||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
|
||||||
else
|
|
||||||
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
|
|
||||||
|
|
||||||
events.assertEmpty();
|
events.assertEmpty();
|
||||||
}
|
}
|
||||||
|
@ -350,37 +341,47 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateProfileWithoutRemoveCustomAttributes() {
|
public void updateProfileWithoutRemoveCustomAttributes() {
|
||||||
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
|
||||||
UserResource user = adminClient.realm("test").users().get(userRep.getId());
|
UPConfig upConfig = upResource.getConfiguration();
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
|
||||||
|
upResource.update(upConfig);
|
||||||
|
|
||||||
userRep.setAttributes(new HashMap<>());
|
try {
|
||||||
userRep.getAttributes().put("custom", Arrays.asList("custom"));
|
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
||||||
|
UserResource user = adminClient.realm("test").users().get(userRep.getId());
|
||||||
|
|
||||||
user.update(userRep);
|
userRep.setAttributes(new HashMap<>());
|
||||||
|
userRep.getAttributes().put("custom", Arrays.asList("custom"));
|
||||||
|
|
||||||
loginPage.open();
|
user.update(userRep);
|
||||||
|
|
||||||
loginPage.login("test-user@localhost", "password");
|
loginPage.open();
|
||||||
|
|
||||||
updateProfilePage.assertCurrent();
|
loginPage.login("test-user@localhost", "password");
|
||||||
assertFalse(updateProfilePage.isCancelDisplayed());
|
|
||||||
|
|
||||||
updateProfilePage.prepareUpdate().username("test-user@localhost").firstName("New first").lastName("New last").email("new@email.com").submit();
|
updateProfilePage.assertCurrent();
|
||||||
|
assertFalse(updateProfilePage.isCancelDisplayed());
|
||||||
|
|
||||||
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.CONTEXT, UserProfileContext.UPDATE_PROFILE.name()).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
updateProfilePage.prepareUpdate().username("test-user@localhost").firstName("New first").lastName("New last").email("new@email.com").submit();
|
||||||
|
|
||||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.CONTEXT, UserProfileContext.UPDATE_PROFILE.name()).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
|
||||||
|
|
||||||
events.expectLogin().assertEvent();
|
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||||
|
|
||||||
// assert user is really updated in persistent store
|
events.expectLogin().assertEvent();
|
||||||
userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
|
||||||
Assert.assertEquals("New first", userRep.getFirstName());
|
// assert user is really updated in persistent store
|
||||||
Assert.assertEquals("New last", userRep.getLastName());
|
userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
||||||
Assert.assertEquals("new@email.com", userRep.getEmail());
|
Assert.assertEquals("New first", userRep.getFirstName());
|
||||||
Assert.assertEquals("test-user@localhost", userRep.getUsername());
|
Assert.assertEquals("New last", userRep.getLastName());
|
||||||
Assert.assertNotNull(userRep.getAttributes());
|
Assert.assertEquals("new@email.com", userRep.getEmail());
|
||||||
Assert.assertTrue(userRep.getAttributes().containsKey("custom"));
|
Assert.assertEquals("test-user@localhost", userRep.getUsername());
|
||||||
|
Assert.assertNotNull(userRep.getAttributes());
|
||||||
|
Assert.assertTrue(userRep.getAttributes().containsKey("custom"));
|
||||||
|
} finally {
|
||||||
|
upConfig.setUnmanagedAttributePolicy(null);
|
||||||
|
upResource.update(upConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,31 +35,39 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.forms.RegisterWithUserProfileTest;
|
import org.keycloak.testsuite.forms.RegisterWithUserProfileTest;
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
|
||||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||||
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||||
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test update-profile required action with custom user profile configurations
|
||||||
*
|
*
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
* @author Vlastimil Elias <velias@redhat.com>
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActionUpdateProfileTest {
|
|
||||||
|
|
||||||
protected static final String PASSWORD = "password";
|
protected static final String PASSWORD = "password";
|
||||||
protected static final String USERNAME1 = "test-user@localhost";
|
protected static final String USERNAME1 = "test-user@localhost";
|
||||||
|
@ -67,17 +75,23 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActi
|
||||||
private static ClientRepresentation client_scope_default;
|
private static ClientRepresentation client_scope_default;
|
||||||
private static ClientRepresentation client_scope_optional;
|
private static ClientRepresentation client_scope_optional;
|
||||||
|
|
||||||
@Override
|
@Rule
|
||||||
protected boolean isDynamicForm() {
|
public AssertEvents events = new AssertEvents(this);
|
||||||
return true;
|
|
||||||
}
|
@Page
|
||||||
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
super.configureTestRealm(testRealm);
|
|
||||||
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
|
|
||||||
testRealm.setClientScopes(new ArrayList<>());
|
testRealm.setClientScopes(new ArrayList<>());
|
||||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
|
||||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("profile").protocol("openid-connect").build());
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("profile").protocol("openid-connect").build());
|
||||||
|
@ -94,7 +108,26 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActi
|
||||||
@Before
|
@Before
|
||||||
public void beforeTest() {
|
public void beforeTest() {
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(),null);
|
||||||
super.beforeTest();
|
|
||||||
|
ApiUtil.removeUserByUsername(testRealm(), "test-user@localhost");
|
||||||
|
UserRepresentation user = UserBuilder.create().enabled(true)
|
||||||
|
.username("test-user@localhost")
|
||||||
|
.email("test-user@localhost")
|
||||||
|
.firstName("Tom")
|
||||||
|
.lastName("Brady")
|
||||||
|
.emailVerified(true)
|
||||||
|
.requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.name()).build();
|
||||||
|
ApiUtil.createUserAndResetPasswordWithAdminClient(testRealm(), user, "password");
|
||||||
|
|
||||||
|
ApiUtil.removeUserByUsername(testRealm(), "john-doh@localhost");
|
||||||
|
user = UserBuilder.create().enabled(true)
|
||||||
|
.username("john-doh@localhost")
|
||||||
|
.email("john-doh@localhost")
|
||||||
|
.firstName("John")
|
||||||
|
.lastName("Doh")
|
||||||
|
.emailVerified(true)
|
||||||
|
.requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.name()).build();
|
||||||
|
ApiUtil.createUserAndResetPasswordWithAdminClient(testRealm(), user, "password");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -619,16 +652,6 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActi
|
||||||
assertEquals("LastCC", user.getLastName());
|
assertEquals("LastCC", user.getLastName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateProfileWithoutRemoveCustomAttributes() {
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"custom\"," + PERMISSIONS_ALL + "}"
|
|
||||||
+ "]}");
|
|
||||||
super.updateProfileWithoutRemoveCustomAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setUserProfileConfiguration(String configuration) {
|
protected void setUserProfileConfiguration(String configuration) {
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,14 @@ import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractAuthTest;
|
import org.keycloak.testsuite.AbstractAuthTest;
|
||||||
import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
|
import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
|
||||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.util.ServerURLs;
|
import org.keycloak.testsuite.util.ServerURLs;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -120,6 +122,14 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void enableUnmanagedAttributes() {
|
||||||
|
for (RealmRepresentation realm : adminClient.realms().findAll()) {
|
||||||
|
UserProfileResource upResource = adminClient.realm(realm.getRealm()).users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(upResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Fix to not require re-import
|
// TODO: Fix to not require re-import
|
||||||
@Override
|
@Override
|
||||||
protected boolean isImportAfterEachMethod() {
|
protected boolean isImportAfterEachMethod() {
|
||||||
|
|
|
@ -9,10 +9,8 @@ import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.enableDynamicUserProfile;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.setUserProfileConfiguration;
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.setUserProfileConfiguration;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -21,7 +19,6 @@ import org.junit.Test;
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
@ -29,7 +26,6 @@ import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||||
|
@ -50,7 +46,6 @@ import jakarta.ws.rs.core.Response;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class DeclarativeUserTest extends AbstractAdminTest {
|
public class DeclarativeUserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
private static final String TEST_REALM_USER_MANAGER_NAME = "test-realm-user-manager";
|
private static final String TEST_REALM_USER_MANAGER_NAME = "test-realm-user-manager";
|
||||||
|
@ -63,7 +58,6 @@ public class DeclarativeUserTest extends AbstractAdminTest {
|
||||||
RealmRepresentation realmRep = realm.toRepresentation();
|
RealmRepresentation realmRep = realm.toRepresentation();
|
||||||
realmRep.setInternationalizationEnabled(true);
|
realmRep.setInternationalizationEnabled(true);
|
||||||
realmRep.setSupportedLocales(new HashSet<>(Arrays.asList("en", "de")));
|
realmRep.setSupportedLocales(new HashSet<>(Arrays.asList("en", "de")));
|
||||||
enableDynamicUserProfile(realmRep);
|
|
||||||
realm.update(realmRep);
|
realm.update(realmRep);
|
||||||
setUserProfileConfiguration(realm, "{\"attributes\": ["
|
setUserProfileConfiguration(realm, "{\"attributes\": ["
|
||||||
+ "{\"name\": \"username\", " + PERMISSIONS_ALL + "},"
|
+ "{\"name\": \"username\", " + PERMISSIONS_ALL + "},"
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
|
import org.keycloak.models.CibaConfig;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.credential.OTPCredentialModel;
|
import org.keycloak.models.credential.OTPCredentialModel;
|
||||||
|
@ -302,33 +303,25 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
}, Resource.REALM, false, true);
|
}, Resource.REALM, false, true);
|
||||||
|
|
||||||
try (RealmAttributeUpdater updater = new RealmAttributeUpdater(adminClient.realm(REALM_NAME))
|
RealmRepresentation realm = clients.get(AdminRoles.QUERY_REALMS).realm(REALM_NAME).toRepresentation();
|
||||||
.setAttribute(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString())
|
assertGettersEmpty(realm);
|
||||||
.update()) {
|
assertNull(realm.isRegistrationEmailAsUsername());
|
||||||
RealmRepresentation realm = clients.get(AdminRoles.QUERY_REALMS).realm(REALM_NAME).toRepresentation();
|
assertNull(realm.getAttributes());
|
||||||
assertGettersEmpty(realm);
|
|
||||||
assertNull(realm.isRegistrationEmailAsUsername());
|
|
||||||
assertNull(realm.getAttributes());
|
|
||||||
|
|
||||||
realm = clients.get(AdminRoles.VIEW_USERS).realm(REALM_NAME).toRepresentation();
|
realm = clients.get(AdminRoles.VIEW_USERS).realm(REALM_NAME).toRepresentation();
|
||||||
assertNotNull(realm.isRegistrationEmailAsUsername());
|
assertNotNull(realm.isRegistrationEmailAsUsername());
|
||||||
assertNotNull(realm.getAttributes());
|
|
||||||
assertNotNull(realm.getAttributes().get(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED));
|
|
||||||
|
|
||||||
realm = clients.get(AdminRoles.MANAGE_USERS).realm(REALM_NAME).toRepresentation();
|
realm = clients.get(AdminRoles.MANAGE_USERS).realm(REALM_NAME).toRepresentation();
|
||||||
assertNotNull(realm.isRegistrationEmailAsUsername());
|
assertNotNull(realm.isRegistrationEmailAsUsername());
|
||||||
assertNotNull(realm.getAttributes());
|
|
||||||
assertNotNull(realm.getAttributes().get(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED));
|
|
||||||
|
|
||||||
// query users only if granted through fine-grained admin
|
// query users only if granted through fine-grained admin
|
||||||
realm = clients.get(AdminRoles.QUERY_USERS).realm(REALM_NAME).toRepresentation();
|
realm = clients.get(AdminRoles.QUERY_USERS).realm(REALM_NAME).toRepresentation();
|
||||||
assertNull(realm.isRegistrationEmailAsUsername());
|
assertNull(realm.isRegistrationEmailAsUsername());
|
||||||
assertNull(realm.getAttributes());
|
assertNull(realm.getAttributes());
|
||||||
}
|
|
||||||
|
|
||||||
// this should pass given that users granted with "query" roles are allowed to access the realm with limited access
|
// this should pass given that users granted with "query" roles are allowed to access the realm with limited access
|
||||||
for (String role : AdminRoles.ALL_QUERY_ROLES) {
|
for (String role : AdminRoles.ALL_QUERY_ROLES) {
|
||||||
invoke(realm -> clients.get(role).realms().realm(REALM_NAME).toRepresentation(), clients.get(role), true);
|
invoke(realmm -> clients.get(role).realms().realm(REALM_NAME).toRepresentation(), clients.get(role), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(new Invocation() {
|
invoke(new Invocation() {
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.admin;
|
package org.keycloak.testsuite.admin;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.jboss.arquillian.drone.api.annotation.Drone;
|
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assume;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -32,9 +32,9 @@ import org.keycloak.admin.client.resource.GroupResource;
|
||||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.admin.client.resource.RoleMappingResource;
|
import org.keycloak.admin.client.resource.RoleMappingResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.common.util.Base64;
|
import org.keycloak.common.util.Base64;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
@ -63,12 +63,17 @@ import org.keycloak.representations.idm.MappingsRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
||||||
|
import org.keycloak.representations.idm.UserProfileMetadata;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.testsuite.ProfileAssume;
|
|
||||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
||||||
import org.keycloak.testsuite.pages.ErrorPage;
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
import org.keycloak.testsuite.pages.InfoPage;
|
import org.keycloak.testsuite.pages.InfoPage;
|
||||||
|
@ -87,6 +92,7 @@ import org.keycloak.testsuite.util.MailUtils;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.RoleBuilder;
|
import org.keycloak.testsuite.util.RoleBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
import org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
@ -180,8 +186,18 @@ public class UserTest extends AbstractAdminTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeUserTest() {
|
public void beforeUserTest() throws IOException {
|
||||||
createAppClientInRealm(REALM_NAME);
|
createAppClientInRealm(REALM_NAME);
|
||||||
|
|
||||||
|
VerifyProfileTest.setUserProfileConfiguration(realm, null);
|
||||||
|
UPConfig upConfig = realm.users().userProfile().getConfiguration();
|
||||||
|
|
||||||
|
for (String name : managedAttributes) {
|
||||||
|
upConfig.addOrReplaceAttribute(createAttributeMetadata(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifyProfileTest.setUserProfileConfiguration(realm, JsonSerialization.writeValueAsString(upConfig));
|
||||||
|
|
||||||
assertAdminEvents.clear();
|
assertAdminEvents.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1241,16 +1257,27 @@ public class UserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void wildcardSearch() {
|
public void wildcardSearch() {
|
||||||
Assume.assumeFalse("Default validators do not allow special chars", isDeclarativeUserProfile());
|
UserProfileResource upResource = realm.users().userProfile();
|
||||||
createUser("0user\\\\0", "email0@emal");
|
UPConfig upConfig = upResource.getConfiguration();
|
||||||
createUser("1user\\\\", "email1@emal");
|
Map<String, Object> prohibitedCharsOrigCfg = upConfig.getAttribute(UserModel.USERNAME).getValidations().get(UsernameProhibitedCharactersValidator.ID);
|
||||||
createUser("2user\\\\%", "email2@emal");
|
upConfig.getAttribute(UserModel.USERNAME).getValidations().remove(UsernameProhibitedCharactersValidator.ID);
|
||||||
createUser("3user\\\\*", "email3@emal");
|
upResource.update(upConfig);
|
||||||
createUser("4user\\\\_", "email4@emal");
|
assertAdminEvents.clear();
|
||||||
|
|
||||||
assertThat(realm.users().search("*", null, null), hasSize(5));
|
try {
|
||||||
assertThat(realm.users().search("*user\\", null, null), hasSize(5));
|
createUser("0user\\\\0", "email0@emal");
|
||||||
assertThat(realm.users().search("\"2user\\\\%\"", null, null), hasSize(1));
|
createUser("1user\\\\", "email1@emal");
|
||||||
|
createUser("2user\\\\%", "email2@emal");
|
||||||
|
createUser("3user\\\\*", "email3@emal");
|
||||||
|
createUser("4user\\\\_", "email4@emal");
|
||||||
|
|
||||||
|
assertThat(realm.users().search("*", null, null), hasSize(5));
|
||||||
|
assertThat(realm.users().search("*user\\", null, null), hasSize(5));
|
||||||
|
assertThat(realm.users().search("\"2user\\\\%\"", null, null), hasSize(1));
|
||||||
|
} finally {
|
||||||
|
upConfig.getAttribute(UserModel.USERNAME).addValidation(UsernameProhibitedCharactersValidator.ID, prohibitedCharsOrigCfg);
|
||||||
|
upResource.update(upConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2465,20 +2492,16 @@ public class UserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
UserRepresentation update = new UserRepresentation();
|
UserRepresentation update = new UserRepresentation();
|
||||||
update.setId(userId);
|
update.setId(userId);
|
||||||
if (isDeclarativeUserProfile()) {
|
// user profile requires sending all attributes otherwise they are removed
|
||||||
// user profile requires sending all attributes otherwise they are removed
|
update.setEmail(email);
|
||||||
update.setEmail(email);
|
|
||||||
}
|
|
||||||
update.setAttributes(Map.of("phoneNumber", List.of("123")));
|
update.setAttributes(Map.of("phoneNumber", List.of("123")));
|
||||||
updateUser(realm.users().get(userId), update);
|
updateUser(realm.users().get(userId), update);
|
||||||
|
|
||||||
UserRepresentation updated = realm.users().get(userId).toRepresentation();
|
UserRepresentation updated = realm.users().get(userId).toRepresentation();
|
||||||
assertThat(updated.getUsername(), equalTo(userName));
|
assertThat(updated.getUsername(), equalTo(userName));
|
||||||
if (isDeclarativeUserProfile()) {
|
assertThat(updated.getAttributes().get("phoneNumber"), equalTo(List.of("123")));
|
||||||
assertThat(updated.getAttributes().get("phoneNumber"), equalTo(List.of("123")));
|
|
||||||
} else {
|
|
||||||
assertThat(updated.getAttributes(), equalTo(Map.of("phoneNumber", List.of("123"))));
|
|
||||||
}
|
|
||||||
assertThat(updated.getEmail(), equalTo(email));
|
assertThat(updated.getEmail(), equalTo(email));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2494,9 +2517,7 @@ public class UserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateUser(user, userRep);
|
updateUser(user, userRep);
|
||||||
if (isDeclarativeUserProfile()) {
|
fail("Should fail because realm does not allow edit username");
|
||||||
fail("Should fail because realm does not allow edit username");
|
|
||||||
}
|
|
||||||
} catch (BadRequestException expected) {
|
} catch (BadRequestException expected) {
|
||||||
ErrorRepresentation error = expected.getResponse().readEntity(ErrorRepresentation.class);
|
ErrorRepresentation error = expected.getResponse().readEntity(ErrorRepresentation.class);
|
||||||
assertEquals("error-user-attribute-read-only", error.getErrorMessage());
|
assertEquals("error-user-attribute-read-only", error.getErrorMessage());
|
||||||
|
@ -2869,26 +2890,42 @@ public class UserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultMaxResults() {
|
public void defaultMaxResults() {
|
||||||
UsersResource users = adminClient.realms().realm("test").users();
|
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
|
||||||
|
UPConfig upConfig = upResource.getConfiguration();
|
||||||
|
upConfig.addOrReplaceAttribute(createAttributeMetadata("aName"));
|
||||||
|
upResource.update(upConfig);
|
||||||
|
|
||||||
for (int i = 0; i < 110; i++) {
|
try {
|
||||||
users.create(UserBuilder.create().username("test-" + i).addAttribute("aName", "aValue").build()).close();
|
UsersResource users = adminClient.realms().realm("test").users();
|
||||||
|
|
||||||
|
for (int i = 0; i < 110; i++) {
|
||||||
|
users.create(UserBuilder.create().username("test-" + i).addAttribute("aName", "aValue").build()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UserRepresentation> result = users.search("test", null, null);
|
||||||
|
assertEquals(100, result.size());
|
||||||
|
for (UserRepresentation user : result) {
|
||||||
|
assertThat(user.getAttributes(), Matchers.notNullValue());
|
||||||
|
assertThat(user.getAttributes().keySet(), Matchers.hasSize(1));
|
||||||
|
assertThat(user.getAttributes(), Matchers.hasEntry(is("aName"), Matchers.contains("aValue")));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(105, users.search("test", 0, 105).size());
|
||||||
|
assertEquals(111, users.search("test", 0, 1000).size());
|
||||||
|
} finally {
|
||||||
|
upConfig.removeAttribute("aName");
|
||||||
|
upResource.update(upConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UserRepresentation> result = users.search("test", null, null);
|
|
||||||
assertEquals(100, result.size());
|
|
||||||
for (UserRepresentation user : result) {
|
|
||||||
assertThat(user.getAttributes(), Matchers.notNullValue());
|
|
||||||
assertThat(user.getAttributes().keySet(), Matchers.hasSize(1));
|
|
||||||
assertThat(user.getAttributes(), Matchers.hasEntry(is("aName"), Matchers.contains("aValue")));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(105, users.search("test", 0, 105).size());
|
|
||||||
assertEquals(111, users.search("test", 0, 1000).size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultMaxResultsBrief() {
|
public void defaultMaxResultsBrief() {
|
||||||
|
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
|
||||||
|
UPConfig upConfig = upResource.getConfiguration();
|
||||||
|
upConfig.addOrReplaceAttribute(createAttributeMetadata("aName"));
|
||||||
|
upResource.update(upConfig);
|
||||||
|
|
||||||
|
try {
|
||||||
UsersResource users = adminClient.realms().realm("test").users();
|
UsersResource users = adminClient.realms().realm("test").users();
|
||||||
|
|
||||||
for (int i = 0; i < 110; i++) {
|
for (int i = 0; i < 110; i++) {
|
||||||
|
@ -2900,6 +2937,10 @@ public class UserTest extends AbstractAdminTest {
|
||||||
for (UserRepresentation user : result) {
|
for (UserRepresentation user : result) {
|
||||||
assertThat(user.getAttributes(), Matchers.nullValue());
|
assertThat(user.getAttributes(), Matchers.nullValue());
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
upConfig.removeAttribute("aName");
|
||||||
|
upResource.update(upConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3461,7 +3502,75 @@ public class UserTest extends AbstractAdminTest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDeclarativeUserProfile() {
|
@Test
|
||||||
return false;
|
public void testUserProfileMetadata() {
|
||||||
|
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||||
|
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
||||||
|
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||||
|
assertNotNull(metadata);
|
||||||
|
|
||||||
|
for (String name : managedAttributes) {
|
||||||
|
assertNotNull(metadata.getAttributeMetadata(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUsernameReadOnlyIfEmailAsUsernameEnabled() {
|
||||||
|
switchRegistrationEmailAsUsername(true);
|
||||||
|
getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false));
|
||||||
|
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||||
|
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
||||||
|
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||||
|
assertNotNull(metadata);
|
||||||
|
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
||||||
|
assertNotNull(username);
|
||||||
|
assertTrue(username.isReadOnly());
|
||||||
|
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
||||||
|
assertNotNull(email);
|
||||||
|
assertFalse(email.isReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmailNotReadOnlyIfEmailAsUsernameEnabledAndEditUsernameDisabled() {
|
||||||
|
switchRegistrationEmailAsUsername(true);
|
||||||
|
getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false));
|
||||||
|
RealmRepresentation rep = realm.toRepresentation();
|
||||||
|
assertFalse(rep.isEditUsernameAllowed());
|
||||||
|
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
||||||
|
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
||||||
|
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
||||||
|
assertNotNull(metadata);
|
||||||
|
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
||||||
|
assertNotNull(username);
|
||||||
|
assertTrue(username.isReadOnly());
|
||||||
|
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
||||||
|
assertNotNull(email);
|
||||||
|
assertFalse(email.isReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultCharacterValidationOnUsername() {
|
||||||
|
List<String> invalidNames = List.of("1user\\\\", "2user\\\\%", "3user\\\\*", "4user\\\\_");
|
||||||
|
|
||||||
|
for (String invalidName : invalidNames) {
|
||||||
|
try {
|
||||||
|
createUser(invalidName, "test@invalid.org");
|
||||||
|
fail("Should fail because the username contains invalid characters");
|
||||||
|
} catch (WebApplicationException bre) {
|
||||||
|
assertEquals(400, bre.getResponse().getStatus());
|
||||||
|
ErrorRepresentation error = bre.getResponse().readEntity(ErrorRepresentation.class);
|
||||||
|
assertEquals("error-username-invalid-character", error.getErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UPAttribute createAttributeMetadata(String name) {
|
||||||
|
UPAttribute attribute = new UPAttribute();
|
||||||
|
attribute.setName(name);
|
||||||
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
|
permissions.setEdit(Set.of("user", "admin"));
|
||||||
|
attribute.setPermissions(permissions);
|
||||||
|
this.managedAttributes.add(name);
|
||||||
|
return attribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.testsuite.admin;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.WebApplicationException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
|
||||||
import org.keycloak.representations.idm.UserProfileMetadata;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class UserTestWithUserProfile extends UserTest {
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void onBefore() throws IOException {
|
|
||||||
RealmRepresentation realmRep = realm.toRepresentation();
|
|
||||||
VerifyProfileTest.disableDynamicUserProfile(realm);
|
|
||||||
assertAdminEvents.poll(); // update realm
|
|
||||||
assertAdminEvents.poll(); // set UP configuration
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(realmRep);
|
|
||||||
realm.update(realmRep);
|
|
||||||
assertAdminEvents.poll();
|
|
||||||
VerifyProfileTest.setUserProfileConfiguration(realm, null);
|
|
||||||
assertAdminEvents.poll();
|
|
||||||
UPConfig upConfig = realm.users().userProfile().getConfiguration();
|
|
||||||
|
|
||||||
for (String name : managedAttributes) {
|
|
||||||
upConfig.addOrReplaceAttribute(createAttributeMetadata(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
VerifyProfileTest.setUserProfileConfiguration(realm, JsonSerialization.writeValueAsString(upConfig));
|
|
||||||
assertAdminEvents.poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUserProfileMetadata() {
|
|
||||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
|
||||||
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
|
||||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
|
||||||
assertNotNull(metadata);
|
|
||||||
|
|
||||||
for (String name : managedAttributes) {
|
|
||||||
assertNotNull(metadata.getAttributeMetadata(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUsernameReadOnlyIfEmailAsUsernameEnabled() {
|
|
||||||
switchRegistrationEmailAsUsername(true);
|
|
||||||
getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false));
|
|
||||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
|
||||||
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
|
||||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
|
||||||
assertNotNull(metadata);
|
|
||||||
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
|
||||||
assertNotNull(username);
|
|
||||||
assertTrue(username.isReadOnly());
|
|
||||||
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
|
||||||
assertNotNull(email);
|
|
||||||
assertFalse(email.isReadOnly());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEmailNotReadOnlyIfEmailAsUsernameEnabledAndEditUsernameDisabled() {
|
|
||||||
switchRegistrationEmailAsUsername(true);
|
|
||||||
getCleanup().addCleanup(() -> switchRegistrationEmailAsUsername(false));
|
|
||||||
RealmRepresentation rep = realm.toRepresentation();
|
|
||||||
assertFalse(rep.isEditUsernameAllowed());
|
|
||||||
String userId = createUser("user-metadata", "user-metadata@keycloak.org");
|
|
||||||
UserRepresentation user = realm.users().get(userId).toRepresentation(true);
|
|
||||||
UserProfileMetadata metadata = user.getUserProfileMetadata();
|
|
||||||
assertNotNull(metadata);
|
|
||||||
UserProfileAttributeMetadata username = metadata.getAttributeMetadata(UserModel.USERNAME);
|
|
||||||
assertNotNull(username);
|
|
||||||
assertTrue(username.isReadOnly());
|
|
||||||
UserProfileAttributeMetadata email = metadata.getAttributeMetadata(UserModel.EMAIL);
|
|
||||||
assertNotNull(email);
|
|
||||||
assertFalse(email.isReadOnly());
|
|
||||||
}
|
|
||||||
|
|
||||||
private UPAttribute createAttributeMetadata(String name) {
|
|
||||||
UPAttribute attribute = new UPAttribute();
|
|
||||||
attribute.setName(name);
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
|
||||||
permissions.setEdit(Set.of("user", "admin"));
|
|
||||||
attribute.setPermissions(permissions);
|
|
||||||
this.managedAttributes.add(name);
|
|
||||||
return attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDefaultCharacterValidationOnUsername() {
|
|
||||||
List<String> invalidNames = List.of("1user\\\\", "2user\\\\%", "3user\\\\*", "4user\\\\_");
|
|
||||||
|
|
||||||
for (String invalidName : invalidNames) {
|
|
||||||
try {
|
|
||||||
createUser(invalidName, "test@invalid.org");
|
|
||||||
fail("Should fail because the username contains invalid characters");
|
|
||||||
} catch (WebApplicationException bre) {
|
|
||||||
assertEquals(400, bre.getResponse().getStatus());
|
|
||||||
ErrorRepresentation error = bre.getResponse().readEntity(ErrorRepresentation.class);
|
|
||||||
assertEquals("error-username-invalid-character", error.getErrorMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isDeclarativeUserProfile() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.admin.client.resource.GroupResource;
|
||||||
import org.keycloak.admin.client.resource.GroupsResource;
|
import org.keycloak.admin.client.resource.GroupsResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.admin.client.resource.RoleMappingResource;
|
import org.keycloak.admin.client.resource.RoleMappingResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
@ -39,7 +40,9 @@ import org.keycloak.representations.idm.MappingsRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.updaters.Creator;
|
import org.keycloak.testsuite.updaters.Creator;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
|
@ -1281,6 +1284,10 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
String groupName = "brief-grouptest-group";
|
String groupName = "brief-grouptest-group";
|
||||||
String userName = "brief-grouptest-user";
|
String userName = "brief-grouptest-user";
|
||||||
|
|
||||||
|
// enable user profile unmanaged attributes
|
||||||
|
UserProfileResource upResource = realm.users().userProfile();
|
||||||
|
UPConfig cfg = VerifyProfileTest.enableUnmanagedAttributes(upResource);
|
||||||
|
|
||||||
GroupsResource groups = realm.groups();
|
GroupsResource groups = realm.groups();
|
||||||
try (Response response = groups.add(GroupBuilder.create().name(groupName).build())) {
|
try (Response response = groups.add(GroupBuilder.create().name(groupName).build())) {
|
||||||
String groupId = ApiUtil.getCreatedId(response);
|
String groupId = ApiUtil.getCreatedId(response);
|
||||||
|
@ -1308,6 +1315,9 @@ public class GroupTest extends AbstractGroupTest {
|
||||||
|
|
||||||
group.remove();
|
group.remove();
|
||||||
user.remove();
|
user.remove();
|
||||||
|
} finally {
|
||||||
|
cfg.setUnmanagedAttributePolicy(null);
|
||||||
|
upResource.update(cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,23 +23,19 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.readDefaultConfig;
|
import static org.keycloak.userprofile.config.UPConfigUtils.readDefaultConfig;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserProfileAttributeGroupMetadata;
|
import org.keycloak.representations.idm.UserProfileAttributeGroupMetadata;
|
||||||
import org.keycloak.representations.idm.UserProfileMetadata;
|
import org.keycloak.representations.idm.UserProfileMetadata;
|
||||||
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.representations.userprofile.config.UPGroup;
|
import org.keycloak.representations.userprofile.config.UPGroup;
|
||||||
|
@ -49,15 +45,10 @@ import org.keycloak.userprofile.config.UPConfigUtils;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class UserProfileAdminTest extends AbstractAdminTest {
|
public class UserProfileAdminTest extends AbstractAdminTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
if (testRealm.getAttributes() == null) {
|
|
||||||
testRealm.setAttributes(new HashMap<>());
|
|
||||||
}
|
|
||||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.pages.ErrorPage;
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
||||||
import org.keycloak.testsuite.pages.IdpLinkEmailPage;
|
import org.keycloak.testsuite.pages.IdpLinkEmailPage;
|
||||||
|
@ -179,8 +180,13 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeBrokerTest() {
|
public void beforeBrokerTest() {
|
||||||
importRealm(bc.createConsumerRealm());
|
RealmRepresentation consumerRealm = bc.createConsumerRealm();
|
||||||
importRealm(bc.createProviderRealm());
|
RealmRepresentation providerRealm = bc.createProviderRealm();
|
||||||
|
importRealm(consumerRealm);
|
||||||
|
importRealm(providerRealm);
|
||||||
|
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(adminClient.realm(consumerRealm.getRealm()).users().userProfile());
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(adminClient.realm(providerRealm.getRealm()).users().userProfile());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
|
@ -65,17 +65,6 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa
|
||||||
@Rule
|
@Rule
|
||||||
public AssertEvents events = new AssertEvents(this);
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|
||||||
protected void enableDynamicUserProfile() {
|
|
||||||
|
|
||||||
RealmResource rr = adminClient.realm(bc.consumerRealmName());
|
|
||||||
|
|
||||||
RealmRepresentation testRealm = rr.toRepresentation();
|
|
||||||
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
|
|
||||||
rr.update(testRealm);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOn
|
* Refers to in old test suite: org.keycloak.testsuite.broker.AbstractFirstBrokerLoginTest#testErrorPageWhenDuplicationNotAllowed_updateProfileOn
|
||||||
|
|
|
@ -114,8 +114,8 @@ public class KcOidcFirstBrokerLoginDetectExistingUserTest extends AbstractInitia
|
||||||
public void loginWhenUserExistsOnConsumer() {
|
public void loginWhenUserExistsOnConsumer() {
|
||||||
updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin);
|
updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
final String firstname = "Firstname(loginWhenUserExistsOnConsumer)";
|
final String firstname = "Firstname_loginWhenUserExistsOnConsumer";
|
||||||
final String lastname = "Lastname(loginWhenUserExistsOnConsumer)";
|
final String lastname = "Lastname_loginWhenUserExistsOnConsumer";
|
||||||
final String username = "firstandlastname";
|
final String username = "firstandlastname";
|
||||||
final String email = "firstnamelastname@example.org";
|
final String email = "firstnamelastname@example.org";
|
||||||
createUser(bc.providerRealmName(), username, BrokerTestConstants.USER_PASSWORD, firstname, lastname, email);
|
createUser(bc.providerRealmName(), username, BrokerTestConstants.USER_PASSWORD, firstname, lastname, email);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.testsuite.broker;
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
@ -7,16 +8,25 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.forms.RegisterWithUserProfileTest;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||||
import org.keycloak.testsuite.pages.RegisterPage;
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.util.AccountHelper;
|
import org.keycloak.testsuite.util.AccountHelper;
|
||||||
|
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.NoSuchElementException;
|
import org.openqa.selenium.NoSuchElementException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.ATTRIBUTE_DEPARTMENT;
|
||||||
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
|
||||||
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -340,4 +350,384 @@ public class KcOidcFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
|
||||||
assertTrue("We should be on the login page.", driver.getPageSource().contains("Sign in to your account"));
|
assertTrue("We should be on the login page.", driver.getPageSource().contains("Sign in to your account"));
|
||||||
final var socialButton = this.loginPage.findSocialButton(bc.getIDPAlias());
|
final var socialButton = this.loginPage.findSocialButton(bc.getIDPAlias());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisplayName() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + PERMISSIONS_ALL + ", \"required\":{}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
//assert field names
|
||||||
|
// i18n replaced
|
||||||
|
org.junit.Assert.assertEquals("First name", updateAccountInformationPage.getLabelForField("firstName"));
|
||||||
|
// attribute name used if no display name set
|
||||||
|
org.junit.Assert.assertEquals("lastName", updateAccountInformationPage.getLabelForField("lastName"));
|
||||||
|
// direct value in display name
|
||||||
|
org.junit.Assert.assertEquals("Department", updateAccountInformationPage.getLabelForField("department"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAttributeGrouping() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}, \"group\": \"company\"},"
|
||||||
|
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"group\": \"contact\"}"
|
||||||
|
+ "], \"groups\": ["
|
||||||
|
+ "{\"name\": \"company\", \"displayDescription\": \"Company field desc\" },"
|
||||||
|
+ "{\"name\": \"contact\" }"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
//assert fields location in form
|
||||||
|
String htmlFormId = "kc-idp-review-profile-form";
|
||||||
|
|
||||||
|
//assert fields and groups location in form
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAttributeGuiOrder() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}},"
|
||||||
|
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + "}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
//assert fields location in form
|
||||||
|
String htmlFormId = "kc-idp-review-profile-form";
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#username")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#firstName")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
org.junit.Assert.assertTrue(
|
||||||
|
driver.findElement(
|
||||||
|
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#email")
|
||||||
|
).isDisplayed()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAttributeInputTypes() {
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ RegisterWithUserProfileTest.UP_CONFIG_PART_INPUT_TYPES
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
RegisterWithUserProfileTest.assertFieldTypes(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReviewWhenMissing_requiredReadOnlyAttributeDoesnotForceUpdate() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", " + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReviewWhenMissing_requiredButNotSelectedByScopeAttributeDoesnotForceUpdate() {
|
||||||
|
|
||||||
|
addDepartmentScopeIntoRealm();
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", " + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"department\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReviewWhenMissing_requiredAndSelectedByScopeAttributeForcesUpdate() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
//we use 'profile' scope which is requested by default
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", " + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"profile\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReview_requiredReadOnlyAttributeNotRenderedAndNotBlockingProcess() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\", " + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
org.junit.Assert.assertFalse(updateAccountInformationPage.isDepartmentPresent());
|
||||||
|
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "requiredReadOnlyAttributeNotRenderedAndNotBlockingRegistration", "requiredReadOnlyAttributeNotRenderedAndNotBlockingRegistration@email", "FirstAA", "LastAA");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReview_attributeRequiredAndSelectedByScopeMustBeSet() {
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
//we use 'profile' scope which is requested by default
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"profile\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
//check required validation works
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "attributeRequiredAndSelectedByScopeMustBeSet", "attributeRequiredAndSelectedByScopeMustBeSet@email", "FirstAA", "LastAA", "");
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "attributeRequiredAndSelectedByScopeMustBeSet", "attributeRequiredAndSelectedByScopeMustBeSet@email", "FirstAA", "LastAA", "DepartmentAA");
|
||||||
|
|
||||||
|
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeRequiredAndSelectedByScopeMustBeSet");
|
||||||
|
assertEquals("FirstAA", user.getFirstName());
|
||||||
|
assertEquals("LastAA", user.getLastName());
|
||||||
|
assertEquals("DepartmentAA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReview_attributeNotRequiredAndSelectedByScopeCanBeIgnored() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
//we use 'profile' scope which is requested by default
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\"profile\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
org.junit.Assert.assertTrue(updateAccountInformationPage.isDepartmentPresent());
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "attributeNotRequiredAndSelectedByScopeCanBeIgnored", "attributeNotRequiredAndSelectedByScopeCanBeIgnored@email", "FirstAA", "LastAA");
|
||||||
|
|
||||||
|
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeNotRequiredAndSelectedByScopeCanBeIgnored");
|
||||||
|
assertEquals("FirstAA", user.getFirstName());
|
||||||
|
assertEquals("LastAA", user.getLastName());
|
||||||
|
assertThat(StringUtils.isEmpty(user.firstAttribute(ATTRIBUTE_DEPARTMENT)), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReview_attributeNotRequiredAndSelectedByScopeCanBeSet() {
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
//we use 'profile' scope which is requested by default
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\"profile\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
org.junit.Assert.assertTrue(updateAccountInformationPage.isDepartmentPresent());
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "attributeNotRequiredAndSelectedByScopeCanBeSet", "attributeNotRequiredAndSelectedByScopeCanBeSet@email", "FirstAA", "LastAA","Department AA");
|
||||||
|
|
||||||
|
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeNotRequiredAndSelectedByScopeCanBeSet");
|
||||||
|
assertEquals("FirstAA", user.getFirstName());
|
||||||
|
assertEquals("LastAA", user.getLastName());
|
||||||
|
assertEquals("Department AA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicUserProfileReview_attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingProcess() {
|
||||||
|
|
||||||
|
addDepartmentScopeIntoRealm();
|
||||||
|
|
||||||
|
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
||||||
|
|
||||||
|
setUserProfileConfiguration("{\"attributes\": ["
|
||||||
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
|
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"department\"]}}"
|
||||||
|
+ "]}");
|
||||||
|
|
||||||
|
oauth.clientId("broker-app");
|
||||||
|
loginPage.open(bc.consumerRealmName());
|
||||||
|
|
||||||
|
logInWithBroker(bc);
|
||||||
|
|
||||||
|
waitForPage(driver, "update account information", false);
|
||||||
|
updateAccountInformationPage.assertCurrent();
|
||||||
|
|
||||||
|
org.junit.Assert.assertFalse(updateAccountInformationPage.isDepartmentPresent());
|
||||||
|
updateAccountInformationPage.updateAccountInformation( "attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration", "attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration@email", "FirstAA", "LastAA");
|
||||||
|
|
||||||
|
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration");
|
||||||
|
assertEquals("FirstAA", user.getFirstName());
|
||||||
|
assertEquals("LastAA", user.getLastName());
|
||||||
|
assertEquals(null, user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDepartmentScopeIntoRealm() {
|
||||||
|
testRealm().clientScopes().create(ClientScopeBuilder.create().name("department").protocol("openid-connect").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setUserProfileConfiguration(String configuration) {
|
||||||
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RealmResource testRealm() {
|
||||||
|
return adminClient.realm(bc.consumerRealmName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,434 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.broker;
|
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.ATTRIBUTE_DEPARTMENT;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ADMIN_EDITABLE;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.PERMISSIONS_ALL;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.RegisterWithUserProfileTest;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
|
||||||
import org.openqa.selenium.By;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class KcOidcFirstBrokerLoginWithUserProfileTest extends KcOidcFirstBrokerLoginTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Before
|
|
||||||
public void beforeBrokerTest() {
|
|
||||||
super.beforeBrokerTest();
|
|
||||||
enableDynamicUserProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDisplayName() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\",\"displayName\":\"${firstName}\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", \"displayName\" : \"Department\", " + PERMISSIONS_ALL + ", \"required\":{}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
//assert field names
|
|
||||||
// i18n replaced
|
|
||||||
Assert.assertEquals("First name", updateAccountInformationPage.getLabelForField("firstName"));
|
|
||||||
// attribute name used if no display name set
|
|
||||||
Assert.assertEquals("lastName", updateAccountInformationPage.getLabelForField("lastName"));
|
|
||||||
// direct value in display name
|
|
||||||
Assert.assertEquals("Department", updateAccountInformationPage.getLabelForField("department"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAttributeGrouping() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}, \"group\": \"company\"},"
|
|
||||||
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"group\": \"contact\"}"
|
|
||||||
+ "], \"groups\": ["
|
|
||||||
+ "{\"name\": \"company\", \"displayDescription\": \"Company field desc\" },"
|
|
||||||
+ "{\"name\": \"contact\" }"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
//assert fields location in form
|
|
||||||
String htmlFormId = "kc-idp-review-profile-form";
|
|
||||||
|
|
||||||
//assert fields and groups location in form
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAttributeGuiOrder() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"lastName\"," + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", " + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\":{}},"
|
|
||||||
+ "{\"name\": \"username\", " + VerifyProfileTest.PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"email\", " + VerifyProfileTest.PERMISSIONS_ALL + "}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
//assert fields location in form
|
|
||||||
String htmlFormId = "kc-idp-review-profile-form";
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#username")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#firstName")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
Assert.assertTrue(
|
|
||||||
driver.findElement(
|
|
||||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#email")
|
|
||||||
).isDisplayed()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAttributeInputTypes() {
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ RegisterWithUserProfileTest.UP_CONFIG_PART_INPUT_TYPES
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
RegisterWithUserProfileTest.assertFieldTypes(driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReviewWhenMissing_requiredReadOnlyAttributeDoesnotForceUpdate() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", " + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReviewWhenMissing_requiredButNotSelectedByScopeAttributeDoesnotForceUpdate() {
|
|
||||||
|
|
||||||
addDepartmentScopeIntoRealm();
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", " + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"department\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReviewWhenMissing_requiredAndSelectedByScopeAttributeForcesUpdate() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::setUpMissingUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
//we use 'profile' scope which is requested by default
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", " + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"profile\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReview_requiredReadOnlyAttributeNotRenderedAndNotBlockingProcess() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\", " + PERMISSIONS_ADMIN_EDITABLE + ", \"required\":{}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
Assert.assertFalse(updateAccountInformationPage.isDepartmentPresent());
|
|
||||||
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "requiredReadOnlyAttributeNotRenderedAndNotBlockingRegistration", "requiredReadOnlyAttributeNotRenderedAndNotBlockingRegistration@email", "FirstAA", "LastAA");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReview_attributeRequiredAndSelectedByScopeMustBeSet() {
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
//we use 'profile' scope which is requested by default
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
|
||||||
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"profile\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
//check required validation works
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "attributeRequiredAndSelectedByScopeMustBeSet", "attributeRequiredAndSelectedByScopeMustBeSet@email", "FirstAA", "LastAA", "");
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "attributeRequiredAndSelectedByScopeMustBeSet", "attributeRequiredAndSelectedByScopeMustBeSet@email", "FirstAA", "LastAA", "DepartmentAA");
|
|
||||||
|
|
||||||
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeRequiredAndSelectedByScopeMustBeSet");
|
|
||||||
assertEquals("FirstAA", user.getFirstName());
|
|
||||||
assertEquals("LastAA", user.getLastName());
|
|
||||||
assertEquals("DepartmentAA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReview_attributeNotRequiredAndSelectedByScopeCanBeIgnored() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
//we use 'profile' scope which is requested by default
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\"profile\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
Assert.assertTrue(updateAccountInformationPage.isDepartmentPresent());
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "attributeNotRequiredAndSelectedByScopeCanBeIgnored", "attributeNotRequiredAndSelectedByScopeCanBeIgnored@email", "FirstAA", "LastAA");
|
|
||||||
|
|
||||||
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeNotRequiredAndSelectedByScopeCanBeIgnored");
|
|
||||||
assertEquals("FirstAA", user.getFirstName());
|
|
||||||
assertEquals("LastAA", user.getLastName());
|
|
||||||
assertThat(StringUtils.isEmpty(user.firstAttribute(ATTRIBUTE_DEPARTMENT)), is(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReview_attributeNotRequiredAndSelectedByScopeCanBeSet() {
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
//we use 'profile' scope which is requested by default
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"selector\":{\"scopes\":[\"profile\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
Assert.assertTrue(updateAccountInformationPage.isDepartmentPresent());
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "attributeNotRequiredAndSelectedByScopeCanBeSet", "attributeNotRequiredAndSelectedByScopeCanBeSet@email", "FirstAA", "LastAA","Department AA");
|
|
||||||
|
|
||||||
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeNotRequiredAndSelectedByScopeCanBeSet");
|
|
||||||
assertEquals("FirstAA", user.getFirstName());
|
|
||||||
assertEquals("LastAA", user.getLastName());
|
|
||||||
assertEquals("Department AA", user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDynamicUserProfileReview_attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingProcess() {
|
|
||||||
|
|
||||||
addDepartmentScopeIntoRealm();
|
|
||||||
|
|
||||||
updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin);
|
|
||||||
|
|
||||||
setUserProfileConfiguration("{\"attributes\": ["
|
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
|
||||||
+ "{\"name\": \"department\"," + PERMISSIONS_ALL + ", \"required\":{}, \"selector\":{\"scopes\":[\"department\"]}}"
|
|
||||||
+ "]}");
|
|
||||||
|
|
||||||
oauth.clientId("broker-app");
|
|
||||||
loginPage.open(bc.consumerRealmName());
|
|
||||||
|
|
||||||
logInWithBroker(bc);
|
|
||||||
|
|
||||||
waitForPage(driver, "update account information", false);
|
|
||||||
updateAccountInformationPage.assertCurrent();
|
|
||||||
|
|
||||||
Assert.assertFalse(updateAccountInformationPage.isDepartmentPresent());
|
|
||||||
updateAccountInformationPage.updateAccountInformation( "attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration", "attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration@email", "FirstAA", "LastAA");
|
|
||||||
|
|
||||||
UserRepresentation user = VerifyProfileTest.getUserByUsername(testRealm(),"attributeRequiredButNotSelectedByScopeIsNotRenderedAndNotBlockingRegistration");
|
|
||||||
assertEquals("FirstAA", user.getFirstName());
|
|
||||||
assertEquals("LastAA", user.getLastName());
|
|
||||||
assertEquals(null, user.firstAttribute(ATTRIBUTE_DEPARTMENT));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDepartmentScopeIntoRealm() {
|
|
||||||
testRealm().clientScopes().create(ClientScopeBuilder.create().name("department").protocol("openid-connect").build());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setUserProfileConfiguration(String configuration) {
|
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RealmResource testRealm() {
|
|
||||||
return adminClient.realm(bc.consumerRealmName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -30,7 +30,7 @@ public class KcOidcUsernameTemplateMapperTest extends AbstractUsernameTemplateMa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getMapperTemplate() {
|
protected String getMapperTemplate() {
|
||||||
return "kc-oidc-idp-[%s]";
|
return "kc-oidc-idp-%s";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.broker;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class KcSamlFirstBrokerLoginWithUserProfileTest extends KcSamlFirstBrokerLoginTest {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Before
|
|
||||||
public void beforeBrokerTest() {
|
|
||||||
super.beforeBrokerTest();
|
|
||||||
enableDynamicUserProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -56,7 +56,7 @@ public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest {
|
||||||
userTemplateImporterMapper.setName("custom-username-import-mapper");
|
userTemplateImporterMapper.setName("custom-username-import-mapper");
|
||||||
userTemplateImporterMapper.setIdentityProviderMapper(UsernameTemplateMapper.PROVIDER_ID);
|
userTemplateImporterMapper.setIdentityProviderMapper(UsernameTemplateMapper.PROVIDER_ID);
|
||||||
userTemplateImporterMapper.setConfig(ImmutableMap.<String, String>builder()
|
userTemplateImporterMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||||
.put(UsernameTemplateMapper.TEMPLATE, "${ALIAS}:${CLAIM.sub}")
|
.put(UsernameTemplateMapper.TEMPLATE, "${ALIAS}_${CLAIM.sub}")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
IdentityProviderMapperRepresentation jwtClaimsAttrMapper = new IdentityProviderMapperRepresentation();
|
IdentityProviderMapperRepresentation jwtClaimsAttrMapper = new IdentityProviderMapperRepresentation();
|
||||||
|
@ -71,22 +71,6 @@ public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest {
|
||||||
return Lists.newArrayList(userTemplateImporterMapper, jwtClaimsAttrMapper);
|
return Lists.newArrayList(userTemplateImporterMapper, jwtClaimsAttrMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* See: KEYCLOAK-8100
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void usernameShouldBeDerivedFromAliasAndIdpSubClaim() {
|
|
||||||
|
|
||||||
logInAsUserInIDP();
|
|
||||||
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
|
|
||||||
|
|
||||||
UserRepresentation user = adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserEmail(), 0, 1).get(0);
|
|
||||||
|
|
||||||
String username = user.getUsername();
|
|
||||||
|
|
||||||
assertEquals("Should render alias:sub as Username", bc.getIDPAlias() + ":" + idpUserId, username);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See: KEYCLOAK-8100
|
* See: KEYCLOAK-8100
|
||||||
*/
|
*/
|
||||||
|
@ -99,5 +83,8 @@ public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest {
|
||||||
UserRepresentation user = adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserEmail(), 0, 1).get(0);
|
UserRepresentation user = adminClient.realm(bc.consumerRealmName()).users().search(bc.getUserEmail(), 0, 1).get(0);
|
||||||
|
|
||||||
assertEquals("Should render idpSub as mappedSub attribute", idpUserId, user.getAttributes().get("mappedSub").get(0));
|
assertEquals("Should render idpSub as mappedSub attribute", idpUserId, user.getAttributes().get("mappedSub").get(0));
|
||||||
|
|
||||||
|
String username = user.getUsername();
|
||||||
|
assertEquals("Should render alias:sub as Username", bc.getIDPAlias() + "_" + idpUserId, username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ public abstract class AbstractInvalidationClusterTest<T, TR> extends AbstractClu
|
||||||
RealmRepresentation testRealm = new RealmRepresentation();
|
RealmRepresentation testRealm = new RealmRepresentation();
|
||||||
testRealm.setRealm("test_" + RandomStringUtils.randomAlphabetic(5));
|
testRealm.setRealm("test_" + RandomStringUtils.randomAlphabetic(5));
|
||||||
testRealm.setEnabled(true);
|
testRealm.setEnabled(true);
|
||||||
|
testRealm.setEditUsernameAllowed(true);
|
||||||
return testRealm;
|
return testRealm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,13 +265,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExportUserProfileConfig() throws IOException {
|
public void testExportUserProfileConfig() throws IOException {
|
||||||
//Enable user profile on realm
|
|
||||||
RealmResource realmRes = adminClient.realm(TEST_REALM);
|
RealmResource realmRes = adminClient.realm(TEST_REALM);
|
||||||
RealmRepresentation realmRep = realmRes.toRepresentation();
|
|
||||||
Map<String, String> realmAttr = realmRep.getAttributesOrEmpty();
|
|
||||||
realmAttr.put(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
|
||||||
realmRep.setAttributes(realmAttr);
|
|
||||||
realmRes.update(realmRep);
|
|
||||||
|
|
||||||
//add some non-default config
|
//add some non-default config
|
||||||
UPConfig persistedConfig = VerifyProfileTest.setUserProfileConfiguration(realmRes, VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT);
|
UPConfig persistedConfig = VerifyProfileTest.setUserProfileConfiguration(realmRes, VerifyProfileTest.CONFIGURATION_FOR_USER_EDIT);
|
||||||
|
|
|
@ -52,7 +52,6 @@ import static org.keycloak.userprofile.UserProfileUtil.USER_METADATA_GROUP;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
||||||
|
|
||||||
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
||||||
|
@ -188,39 +187,31 @@ public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserProfile() throws Exception {
|
public void testUserProfile() throws Exception {
|
||||||
RealmRepresentation realm = testRealmResource().toRepresentation();
|
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||||
VerifyProfileTest.enableDynamicUserProfile(realm);
|
|
||||||
testRealmResource().update(realm);
|
|
||||||
|
|
||||||
try {
|
// User-profile data should be present (including KERBEROS_PRINCIPAL attribute)
|
||||||
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
UserResource johnResource = ApiUtil.findUserByUsernameId(testRealmResource(), "hnelson");
|
||||||
|
UserRepresentation john = johnResource.toRepresentation(true);
|
||||||
|
Assert.assertNames(john.getUserProfileMetadata().getAttributes(), UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, UserModel.USERNAME, KerberosConstants.KERBEROS_PRINCIPAL);
|
||||||
|
|
||||||
// User-profile data should be present (including KERBEROS_PRINCIPAL attribute)
|
// KERBEROS_PRINCIPAL attribute should be read-only and should be in "User metadata" group
|
||||||
UserResource johnResource = ApiUtil.findUserByUsernameId(testRealmResource(), "hnelson");
|
UserProfileAttributeMetadata krbPrincipalAttribute = john.getUserProfileMetadata().getAttributeMetadata(KerberosConstants.KERBEROS_PRINCIPAL);
|
||||||
UserRepresentation john = johnResource.toRepresentation(true);
|
Assert.assertTrue(krbPrincipalAttribute.isReadOnly());
|
||||||
Assert.assertNames(john.getUserProfileMetadata().getAttributes(), UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, UserModel.USERNAME, KerberosConstants.KERBEROS_PRINCIPAL);
|
Assert.assertEquals(USER_METADATA_GROUP, krbPrincipalAttribute.getGroup());
|
||||||
|
|
||||||
// KERBEROS_PRINCIPAL attribute should be read-only and should be in "User metadata" group
|
// Test Update profile
|
||||||
UserProfileAttributeMetadata krbPrincipalAttribute = john.getUserProfileMetadata().getAttributeMetadata(KerberosConstants.KERBEROS_PRINCIPAL);
|
john.getRequiredActions().add(UserModel.RequiredAction.UPDATE_PROFILE.toString());
|
||||||
Assert.assertTrue(krbPrincipalAttribute.isReadOnly());
|
johnResource.update(john);
|
||||||
Assert.assertEquals(USER_METADATA_GROUP, krbPrincipalAttribute.getGroup());
|
|
||||||
|
|
||||||
// Test Update profile
|
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
||||||
john.getRequiredActions().add(UserModel.RequiredAction.UPDATE_PROFILE.toString());
|
Assert.assertEquals(200, spnegoResponse.getStatus());
|
||||||
johnResource.update(john);
|
String responseText = spnegoResponse.readEntity(String.class);
|
||||||
|
Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account."));
|
||||||
|
Assert.assertFalse(responseText.contains("KERBEROS_PRINCIPAL"));
|
||||||
|
spnegoResponse.close();
|
||||||
|
|
||||||
Response spnegoResponse = spnegoLogin("hnelson", "secret");
|
john.getRequiredActions().remove(UserModel.RequiredAction.UPDATE_PROFILE.toString());
|
||||||
Assert.assertEquals(200, spnegoResponse.getStatus());
|
johnResource.update(john);
|
||||||
String responseText = spnegoResponse.readEntity(String.class);
|
|
||||||
Assert.assertTrue(responseText.contains("You need to update your user profile to activate your account."));
|
|
||||||
Assert.assertFalse(responseText.contains("KERBEROS_PRINCIPAL"));
|
|
||||||
spnegoResponse.close();
|
|
||||||
|
|
||||||
john.getRequiredActions().remove(UserModel.RequiredAction.UPDATE_PROFILE.toString());
|
|
||||||
johnResource.update(john);
|
|
||||||
} finally {
|
|
||||||
VerifyProfileTest.disableDynamicUserProfile(testRealmResource());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
package org.keycloak.testsuite.federation.ldap;
|
package org.keycloak.testsuite.federation.ldap;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
@ -33,6 +32,7 @@ import org.junit.FixMethodOrder;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.federation.kerberos.KerberosFederationProvider;
|
import org.keycloak.federation.kerberos.KerberosFederationProvider;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
@ -41,18 +41,17 @@ import org.keycloak.models.credential.PasswordCredentialModel;
|
||||||
import org.keycloak.representations.account.UserRepresentation;
|
import org.keycloak.representations.account.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.services.messages.Messages;
|
import org.keycloak.services.messages.Messages;
|
||||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.util.LDAPRule;
|
import org.keycloak.testsuite.util.LDAPRule;
|
||||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||||
import org.keycloak.testsuite.util.TokenUtil;
|
import org.keycloak.testsuite.util.TokenUtil;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,56 +114,48 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
||||||
public void testUpdateProfile() throws IOException {
|
public void testUpdateProfile() throws IOException {
|
||||||
UserRepresentation user = getProfile();
|
UserRepresentation user = getProfile();
|
||||||
|
|
||||||
List<String> origLdapId = new ArrayList<>(user.getAttributes().get(LDAPConstants.LDAP_ID));
|
// Metadata attributes like LDAP_ID are not present
|
||||||
List<String> origLdapEntryDn = new ArrayList<>(user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN));
|
Assert.assertNull(user.getAttributes());
|
||||||
Assert.assertEquals(1, origLdapId.size());
|
|
||||||
Assert.assertEquals(1, origLdapEntryDn.size());
|
|
||||||
assertThat(user.getAttributes().keySet(), not(contains(KerberosFederationProvider.KERBEROS_PRINCIPAL)));
|
|
||||||
|
|
||||||
// Trying to add KERBEROS_PRINCIPAL should fail (Adding attribute, which was not yet present)
|
org.keycloak.representations.idm.UserRepresentation adminRestUserRep = testRealm().users()
|
||||||
|
.search(user.getUsername()).get(0);
|
||||||
|
List<String> origLdapId = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ID);
|
||||||
|
List<String> origLdapEntryDn = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN);
|
||||||
|
Assert.assertNotNull(origLdapId.get(0));
|
||||||
|
Assert.assertNotNull(origLdapEntryDn.get(0));
|
||||||
|
|
||||||
|
// Trying to add KERBEROS_PRINCIPAL (Adding attribute, which was not yet present). Request does not fail, but attribute is not updated
|
||||||
user.setFirstName("JohnUpdated");
|
user.setFirstName("JohnUpdated");
|
||||||
user.setLastName("DoeUpdated");
|
user.setLastName("DoeUpdated");
|
||||||
user.singleAttribute(KerberosFederationProvider.KERBEROS_PRINCIPAL, "foo");
|
user.singleAttribute(KerberosFederationProvider.KERBEROS_PRINCIPAL, "foo");
|
||||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
updateProfileExpectSuccess(user);
|
||||||
|
user = getProfile();
|
||||||
|
Assert.assertEquals("JohnUpdated", user.getFirstName());
|
||||||
|
Assert.assertEquals("DoeUpdated", user.getLastName());
|
||||||
|
Assert.assertNull(user.getAttributes());
|
||||||
|
|
||||||
// The same test, but consider case sensitivity
|
// Trying to update LDAP_ID should fail (Updating existing attribute, which is present on the user even if not visible to the user)
|
||||||
user.getAttributes().remove(KerberosFederationProvider.KERBEROS_PRINCIPAL);
|
user.singleAttribute(LDAPConstants.LDAP_ID, "123");
|
||||||
user.singleAttribute("KERberos_principal", "foo");
|
|
||||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
|
||||||
|
|
||||||
// Trying to update LDAP_ID should fail (Updating existing attribute, which was present)
|
|
||||||
user.getAttributes().remove("KERberos_principal");
|
|
||||||
user.setFirstName("JohnUpdated");
|
|
||||||
user.setLastName("DoeUpdated");
|
|
||||||
user.getAttributes().get(LDAPConstants.LDAP_ID).remove(0);
|
|
||||||
user.getAttributes().get(LDAPConstants.LDAP_ID).add("123");
|
|
||||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
|
||||||
|
|
||||||
// Trying to delete LDAP_ID should fail (Removing attribute, which was present here already)
|
|
||||||
user.getAttributes().get(LDAPConstants.LDAP_ID).remove(0);
|
|
||||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
// ignore removal for read-only attributes
|
// ignore removal for read-only attributes
|
||||||
user.getAttributes().remove(LDAPConstants.LDAP_ID);
|
user.getAttributes().remove(LDAPConstants.LDAP_ID);
|
||||||
updateProfileExpectSuccess(user);
|
updateProfileExpectSuccess(user);
|
||||||
user = getProfile();
|
user = getProfile();
|
||||||
assertFalse(user.getAttributes().get(LDAPConstants.LDAP_ID).isEmpty());
|
Assert.assertNull(user.getAttributes());
|
||||||
|
|
||||||
// Trying to update LDAP_ENTRY_DN should fail
|
// Trying to update LDAP_ENTRY_DN should fail
|
||||||
user.getAttributes().put(LDAPConstants.LDAP_ID, origLdapId);
|
user.singleAttribute(LDAPConstants.LDAP_ENTRY_DN, "ou=foo,dc=bar");
|
||||||
user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN).remove(0);
|
|
||||||
user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN).add("ou=foo,dc=bar");
|
|
||||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
// Update firstName and lastName should be fine
|
// Update firstName and lastName should be fine
|
||||||
user.getAttributes().put(LDAPConstants.LDAP_ENTRY_DN, origLdapEntryDn);
|
user.getAttributes().remove(LDAPConstants.LDAP_ENTRY_DN);
|
||||||
updateProfileExpectSuccess(user);
|
updateProfileExpectSuccess(user);
|
||||||
|
|
||||||
user = getProfile();
|
user = getProfile();
|
||||||
assertEquals("JohnUpdated", user.getFirstName());
|
assertEquals("JohnUpdated", user.getFirstName());
|
||||||
assertEquals("DoeUpdated", user.getLastName());
|
assertEquals("DoeUpdated", user.getLastName());
|
||||||
assertEquals(origLdapId, user.getAttributes().get(LDAPConstants.LDAP_ID));
|
Assert.assertNull(user.getAttributes());
|
||||||
assertEquals(origLdapEntryDn, user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN));
|
|
||||||
|
|
||||||
// Revert
|
// Revert
|
||||||
user.setFirstName("John");
|
user.setFirstName("John");
|
||||||
|
@ -172,6 +163,67 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
||||||
updateProfileExpectSuccess(user);
|
updateProfileExpectSuccess(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateProfileUnmanagedAttributes() throws IOException {
|
||||||
|
// User profile unmanaged attributes supported
|
||||||
|
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||||
|
UPConfig origConfig = VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
|
|
||||||
|
try {
|
||||||
|
UserRepresentation user = getProfile();
|
||||||
|
|
||||||
|
// Metadata attributes like LDAP_ID are not present
|
||||||
|
Assert.assertNull(user.getAttributes());
|
||||||
|
|
||||||
|
org.keycloak.representations.idm.UserRepresentation adminRestUserRep = testRealm().users()
|
||||||
|
.search(user.getUsername()).get(0);
|
||||||
|
List<String> origLdapId = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ID);
|
||||||
|
List<String> origLdapEntryDn = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN);
|
||||||
|
Assert.assertNotNull(origLdapId.get(0));
|
||||||
|
Assert.assertNotNull(origLdapEntryDn.get(0));
|
||||||
|
|
||||||
|
// Trying to add KERBEROS_PRINCIPAL should fail (Adding attribute, which was not yet present)
|
||||||
|
user.setFirstName("JohnUpdated");
|
||||||
|
user.setLastName("DoeUpdated");
|
||||||
|
user.singleAttribute(KerberosFederationProvider.KERBEROS_PRINCIPAL, "foo");
|
||||||
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
|
// The same test, but consider case sensitivity
|
||||||
|
user.getAttributes().remove(KerberosFederationProvider.KERBEROS_PRINCIPAL);
|
||||||
|
user.singleAttribute("KERberos_principal", "foo");
|
||||||
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
|
// Trying to update LDAP_ID should fail (Updating existing attribute, which was present)
|
||||||
|
user.getAttributes().remove("KERberos_principal");
|
||||||
|
user.setFirstName("JohnUpdated");
|
||||||
|
user.setLastName("DoeUpdated");
|
||||||
|
user.singleAttribute(LDAPConstants.LDAP_ID, "123");
|
||||||
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
|
// Trying to delete LDAP_ID (by set to null) should fail (Removing attribute, which was present here already)
|
||||||
|
user.singleAttribute(LDAPConstants.LDAP_ID, null);
|
||||||
|
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||||
|
|
||||||
|
// ignore removal for read-only attributes
|
||||||
|
user.getAttributes().remove(LDAPConstants.LDAP_ID);
|
||||||
|
updateProfileExpectSuccess(user);
|
||||||
|
user = getProfile();
|
||||||
|
Assert.assertNull(user.getAttributes());
|
||||||
|
|
||||||
|
user = getProfile();
|
||||||
|
assertEquals("JohnUpdated", user.getFirstName());
|
||||||
|
assertEquals("DoeUpdated", user.getLastName());
|
||||||
|
Assert.assertNull(user.getAttributes());
|
||||||
|
|
||||||
|
// Revert
|
||||||
|
user.setFirstName("John");
|
||||||
|
user.setLastName("Doe");
|
||||||
|
updateProfileExpectSuccess(user);
|
||||||
|
} finally {
|
||||||
|
userProfileRes.update(origConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCredentials() throws IOException {
|
public void testGetCredentials() throws IOException {
|
||||||
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
List<AccountCredentialResource.CredentialContainer> credentials = getCredentials();
|
||||||
|
@ -231,7 +283,8 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
||||||
assertEquals("john-alias@email.org", usernew.getEmail());
|
assertEquals("john-alias@email.org", usernew.getEmail());
|
||||||
assertFalse(usernew.isEmailVerified());
|
assertFalse(usernew.isEmailVerified());
|
||||||
|
|
||||||
usernew.getAttributes().clear();
|
// No metadata attributes like LDAP_ID or LDAP_ENTRY_DN present in account REST API
|
||||||
|
Assert.assertNull(usernew.getAttributes());
|
||||||
|
|
||||||
//clean up
|
//clean up
|
||||||
usernew.setEmail("john@email.org");
|
usernew.setEmail("john@email.org");
|
||||||
|
@ -240,13 +293,22 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
||||||
org.keycloak.representations.idm.UserRepresentation userRep = testRealm().users()
|
org.keycloak.representations.idm.UserRepresentation userRep = testRealm().users()
|
||||||
.search(usernew.getUsername()).get(0);
|
.search(usernew.getUsername()).get(0);
|
||||||
|
|
||||||
|
// Metadata attributes present in admin REST API
|
||||||
|
assertTrue(userRep.getAttributes().containsKey(LDAPConstants.LDAP_ID));
|
||||||
|
assertTrue(userRep.getAttributes().containsKey(LDAPConstants.LDAP_ENTRY_DN));
|
||||||
|
|
||||||
userRep.setAttributes(null);
|
userRep.setAttributes(null);
|
||||||
|
|
||||||
testRealm().users().get(userRep.getId()).update(userRep);
|
testRealm().users().get(userRep.getId()).update(userRep);
|
||||||
usernew = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
usernew = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||||
|
|
||||||
assertTrue(usernew.getAttributes().containsKey(LDAPConstants.LDAP_ID));
|
// Metadata attributes still not present in account REST
|
||||||
assertTrue(usernew.getAttributes().containsKey(LDAPConstants.LDAP_ENTRY_DN));
|
Assert.assertNull(usernew.getAttributes());
|
||||||
|
|
||||||
|
// Metadata attributes still present in admin REST API
|
||||||
|
userRep = testRealm().users().search(usernew.getUsername()).get(0);
|
||||||
|
assertTrue(userRep.getAttributes().containsKey(LDAPConstants.LDAP_ID));
|
||||||
|
assertTrue(userRep.getAttributes().containsKey(LDAPConstants.LDAP_ENTRY_DN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.federation.ldap;
|
package org.keycloak.testsuite.federation.ldap;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import jakarta.ws.rs.BadRequestException;
|
||||||
|
@ -36,7 +38,11 @@ import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
@ -48,6 +54,10 @@ import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.keycloak.testsuite.forms.VerifyProfileTest.setUserProfileConfiguration;
|
||||||
|
import static org.keycloak.util.JsonSerialization.writeValueAsString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -80,6 +90,10 @@ public class LDAPAdminRestApiTest extends AbstractLDAPTest {
|
||||||
LDAPObject john = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
LDAPObject john = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
|
||||||
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john, "Password1");
|
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john, "Password1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
UPConfig cfg = testRealm().users().userProfile().getConfiguration();
|
||||||
|
cfg.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||||
|
testRealm().users().userProfile().update(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -245,4 +259,75 @@ public class LDAPAdminRestApiTest extends AbstractLDAPTest {
|
||||||
assertEquals("unknown_error", error.getError());
|
assertEquals("unknown_error", error.getError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateReadOnlyAttributeWhenNotSetToUser() throws Exception {
|
||||||
|
RealmRepresentation realmRep = testRealm().toRepresentation();
|
||||||
|
enableSyncRegistration(realmRep, Boolean.FALSE);
|
||||||
|
|
||||||
|
UserRepresentation newUser = UserBuilder.create()
|
||||||
|
.username("admintestuser1")
|
||||||
|
.password("userpass")
|
||||||
|
.addAttribute("foo", "foo-value")
|
||||||
|
.enabled(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
UPConfig origUpConfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
|
||||||
|
try (Response response = testRealm().users().create(newUser)) {
|
||||||
|
enableDynamicUserProfileConfig();
|
||||||
|
String newUserId = ApiUtil.getCreatedId(response);
|
||||||
|
|
||||||
|
getCleanup().addUserId(newUserId);
|
||||||
|
|
||||||
|
UserResource user = testRealm().users().get(newUserId);
|
||||||
|
UserRepresentation userRep = user.toRepresentation();
|
||||||
|
assertNull(userRep.getAttributes());
|
||||||
|
|
||||||
|
userRep.singleAttribute(LDAPConstants.LDAP_ID, "");
|
||||||
|
user.update(userRep);
|
||||||
|
userRep = testRealm().users().get(newUserId).toRepresentation();
|
||||||
|
assertNull(userRep.getAttributes());
|
||||||
|
userRep.singleAttribute(LDAPConstants.LDAP_ID, null);
|
||||||
|
user.update(userRep);
|
||||||
|
userRep = testRealm().users().get(newUserId).toRepresentation();
|
||||||
|
assertNull(userRep.getAttributes());
|
||||||
|
|
||||||
|
try {
|
||||||
|
userRep.singleAttribute(LDAPConstants.LDAP_ID, "should-fail");
|
||||||
|
user.update(userRep);
|
||||||
|
fail("Should fail, attribute is read-only");
|
||||||
|
} catch (BadRequestException ignore) {
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
enableSyncRegistration(realmRep, Boolean.TRUE);
|
||||||
|
testRealm().users().userProfile().update(origUpConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableDynamicUserProfileConfig() throws IOException {
|
||||||
|
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
upConfig.setUnmanagedAttributePolicy(null);
|
||||||
|
|
||||||
|
UPAttribute attribute = new UPAttribute();
|
||||||
|
|
||||||
|
attribute.setName(LDAPConstants.LDAP_ID);
|
||||||
|
|
||||||
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
|
|
||||||
|
permissions.setView(Collections.singleton("admin"));
|
||||||
|
|
||||||
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
|
upConfig.addOrReplaceAttribute(attribute);
|
||||||
|
|
||||||
|
setUserProfileConfiguration(testRealm(), writeValueAsString(upConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableSyncRegistration(RealmRepresentation realmRep, Boolean aFalse) {
|
||||||
|
ComponentRepresentation ldapStorage = testRealm().components()
|
||||||
|
.query(realmRep.getId(), UserStorageProvider.class.getName()).get(0);
|
||||||
|
ldapStorage.getConfig().put(LDAPConstants.SYNC_REGISTRATIONS, Collections.singletonList(aFalse.toString()));
|
||||||
|
testRealm().components().component(ldapStorage.getId()).update(ldapStorage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.testsuite.federation.ldap;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.disableDynamicUserProfile;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.setUserProfileConfiguration;
|
|
||||||
import static org.keycloak.util.JsonSerialization.writeValueAsString;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.BadRequestException;
|
|
||||||
import jakarta.ws.rs.core.Response;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.junit.FixMethodOrder;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runners.MethodSorters;
|
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.models.LDAPConstants;
|
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
|
||||||
|
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
|
||||||
public class LDAPAdminRestApiWithUserProfileTest extends LDAPAdminRestApiTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateReadOnlyAttributeWhenNotSetToUser() throws Exception {
|
|
||||||
RealmRepresentation realmRep = testRealm().toRepresentation();
|
|
||||||
enableSyncRegistration(realmRep, Boolean.FALSE);
|
|
||||||
|
|
||||||
UserRepresentation newUser = UserBuilder.create()
|
|
||||||
.username("admintestuser1")
|
|
||||||
.password("userpass")
|
|
||||||
.addAttribute("foo", "foo-value")
|
|
||||||
.enabled(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try (Response response = testRealm().users().create(newUser)) {
|
|
||||||
enableDynamicUserProfile(realmRep);
|
|
||||||
String newUserId = ApiUtil.getCreatedId(response);
|
|
||||||
|
|
||||||
getCleanup().addUserId(newUserId);
|
|
||||||
|
|
||||||
UserResource user = testRealm().users().get(newUserId);
|
|
||||||
UserRepresentation userRep = user.toRepresentation();
|
|
||||||
assertNull(userRep.getAttributes());
|
|
||||||
|
|
||||||
userRep.singleAttribute(LDAPConstants.LDAP_ID, "");
|
|
||||||
user.update(userRep);
|
|
||||||
userRep = testRealm().users().get(newUserId).toRepresentation();
|
|
||||||
assertNull(userRep.getAttributes());
|
|
||||||
userRep.singleAttribute(LDAPConstants.LDAP_ID, null);
|
|
||||||
user.update(userRep);
|
|
||||||
userRep = testRealm().users().get(newUserId).toRepresentation();
|
|
||||||
assertNull(userRep.getAttributes());
|
|
||||||
|
|
||||||
try {
|
|
||||||
userRep.singleAttribute(LDAPConstants.LDAP_ID, "should-fail");
|
|
||||||
user.update(userRep);
|
|
||||||
fail("Should fail, attribute is read-only");
|
|
||||||
} catch (BadRequestException ignore) {
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
disableDynamicUserProfile(testRealm());
|
|
||||||
enableSyncRegistration(realmRep, Boolean.TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableDynamicUserProfile(RealmRepresentation realmRep) throws IOException {
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(realmRep);
|
|
||||||
|
|
||||||
testRealm().update(realmRep);
|
|
||||||
|
|
||||||
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
|
|
||||||
UPAttribute attribute = new UPAttribute();
|
|
||||||
|
|
||||||
attribute.setName(LDAPConstants.LDAP_ID);
|
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
|
||||||
|
|
||||||
permissions.setView(Collections.singleton("admin"));
|
|
||||||
|
|
||||||
attribute.setPermissions(permissions);
|
|
||||||
|
|
||||||
upConfig.addOrReplaceAttribute(attribute);
|
|
||||||
|
|
||||||
setUserProfileConfiguration(testRealm(), writeValueAsString(upConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableSyncRegistration(RealmRepresentation realmRep, Boolean aFalse) {
|
|
||||||
ComponentRepresentation ldapStorage = testRealm().components()
|
|
||||||
.query(realmRep.getId(), UserStorageProvider.class.getName()).get(0);
|
|
||||||
ldapStorage.getConfig().put(LDAPConstants.SYNC_REGISTRATIONS, Collections.singletonList(aFalse.toString()));
|
|
||||||
testRealm().components().component(ldapStorage.getId()).update(ldapStorage);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,6 +23,7 @@ import org.junit.ClassRule;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.ComponentValidationException;
|
import org.keycloak.component.ComponentValidationException;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
@ -38,6 +39,7 @@ import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
|
||||||
import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
|
import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
|
||||||
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.util.LDAPRule;
|
import org.keycloak.testsuite.util.LDAPRule;
|
||||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||||
|
|
||||||
|
@ -85,6 +87,10 @@ public class LDAPBinaryAttributesTest extends AbstractLDAPTest {
|
||||||
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// User profile unmanaged attributes supported
|
||||||
|
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,9 @@ import org.junit.FixMethodOrder;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
@ -42,8 +40,6 @@ import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||||
import org.keycloak.testsuite.util.LDAPRule;
|
import org.keycloak.testsuite.util.LDAPRule;
|
||||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||||
|
@ -56,7 +52,6 @@ import static org.keycloak.userprofile.UserProfileUtil.USER_METADATA_GROUP;
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class LDAPUserProfileTest extends AbstractLDAPTest {
|
public class LDAPUserProfileTest extends AbstractLDAPTest {
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
|
@ -91,10 +86,6 @@ public class LDAPUserProfileTest extends AbstractLDAPTest {
|
||||||
LDAPObject john2 = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "johnkeycloak2", "John", "Doe", "john2@email.org", null, "1234");
|
LDAPObject john2 = LDAPTestUtils.addLDAPUser(ctx.getLdapProvider(), appRealm, "johnkeycloak2", "John", "Doe", "john2@email.org", null, "1234");
|
||||||
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john2, "Password1");
|
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john2, "Password1");
|
||||||
});
|
});
|
||||||
|
|
||||||
RealmRepresentation realm = testRealm().toRepresentation();
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(realm);
|
|
||||||
testRealm().update(realm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -26,11 +26,13 @@ import jakarta.ws.rs.core.Response;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.FixMethodOrder;
|
import org.junit.FixMethodOrder;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
import org.keycloak.admin.client.resource.ComponentResource;
|
import org.keycloak.admin.client.resource.ComponentResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.models.LDAPConstants;
|
import org.keycloak.models.LDAPConstants;
|
||||||
|
@ -51,6 +53,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.federation.ldap.LDAPProvidersIntegrationTest;
|
import org.keycloak.testsuite.federation.ldap.LDAPProvidersIntegrationTest;
|
||||||
import org.keycloak.testsuite.federation.ldap.LDAPTestAsserts;
|
import org.keycloak.testsuite.federation.ldap.LDAPTestAsserts;
|
||||||
import org.keycloak.testsuite.federation.ldap.LDAPTestContext;
|
import org.keycloak.testsuite.federation.ldap.LDAPTestContext;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,6 +69,12 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void enableUserProfileUnmanagedAttributes() {
|
||||||
|
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void assertFederatedUserLink(UserRepresentation user) {
|
protected void assertFederatedUserLink(UserRepresentation user) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.common.util.ObjectUtil;
|
import org.keycloak.common.util.ObjectUtil;
|
||||||
|
@ -44,6 +45,7 @@ import org.keycloak.testsuite.arquillian.annotation.ModelTest;
|
||||||
import org.keycloak.testsuite.federation.UserMapStorage;
|
import org.keycloak.testsuite.federation.UserMapStorage;
|
||||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||||
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
|
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.RegisterPage;
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
|
@ -160,6 +162,9 @@ public class UserStorageTest extends AbstractAuthTest {
|
||||||
propProviderRWId = addComponent(newPropProviderRW());
|
propProviderRWId = addComponent(newPropProviderRW());
|
||||||
|
|
||||||
createAppClientInRealm(testRealmResource().toRepresentation().getRealm());
|
createAppClientInRealm(testRealmResource().toRepresentation().getRealm());
|
||||||
|
|
||||||
|
UserProfileResource userProfileRes = testRealmResource().users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package org.keycloak.testsuite.forms;
|
package org.keycloak.testsuite.forms;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory;
|
import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory;
|
||||||
import org.keycloak.authentication.authenticators.access.DenyAccessAuthenticatorFactory;
|
import org.keycloak.authentication.authenticators.access.DenyAccessAuthenticatorFactory;
|
||||||
import org.keycloak.authentication.authenticators.browser.PasswordFormFactory;
|
import org.keycloak.authentication.authenticators.browser.PasswordFormFactory;
|
||||||
|
@ -63,6 +65,12 @@ public class ConditionalUserAttributeAuthenticatorTest extends AbstractTestRealm
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {}
|
public void configureTestRealm(RealmRepresentation testRealm) {}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void configureUserProfile() {
|
||||||
|
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||||
|
}
|
||||||
|
|
||||||
private void createUsers() {
|
private void createUsers() {
|
||||||
GroupRepresentation subGroup = GroupBuilder.create().name(SUBGROUP).build();
|
GroupRepresentation subGroup = GroupBuilder.create().name(SUBGROUP).build();
|
||||||
testRealm().groups().add(subGroup);
|
testRealm().groups().add(subGroup);
|
||||||
|
|
|
@ -56,6 +56,8 @@ import org.keycloak.testsuite.util.AccountHelper;
|
||||||
|
|
||||||
import jakarta.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -611,7 +613,7 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserRepresentation getUser(String userId) {
|
private UserRepresentation getUser(String userId) {
|
||||||
return testRealm().users().get(userId).toRepresentation();
|
return testRealm().users().get(userId).toRepresentation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,26 +34,34 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
|
import org.keycloak.testsuite.pages.ErrorPage;
|
||||||
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
import org.keycloak.testsuite.pages.RegisterPage;
|
||||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||||
|
import org.keycloak.testsuite.util.GreenMailRule;
|
||||||
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test user registration with customized user-profile configurations
|
||||||
|
*
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
* @author Vlastimil Elias <velias@redhat.com>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
public class RegisterWithUserProfileTest extends RegisterTest {
|
|
||||||
|
|
||||||
private static final String SCOPE_LAST_NAME = "lastName";
|
private static final String SCOPE_LAST_NAME = "lastName";
|
||||||
|
|
||||||
|
@ -63,13 +71,26 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
||||||
public static String UP_CONFIG_BASIC_ATTRIBUTES = "{\"name\": \"username\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
public static String UP_CONFIG_BASIC_ATTRIBUTES = "{\"name\": \"username\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {\"roles\" : [\"user\"]}},";
|
+ "{\"name\": \"email\"," + PERMISSIONS_ALL + ", \"required\": {\"roles\" : [\"user\"]}},";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public AssertEvents events = new AssertEvents(this);
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected LoginPage loginPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected ErrorPage errorPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
protected RegisterPage registerPage;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public GreenMailRule greenMail = new GreenMailRule();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
|
|
||||||
super.configureTestRealm(testRealm);
|
|
||||||
|
|
||||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
|
||||||
|
|
||||||
testRealm.setClientScopes(new ArrayList<>());
|
testRealm.setClientScopes(new ArrayList<>());
|
||||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_LAST_NAME).protocol("openid-connect").build());
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_LAST_NAME).protocol("openid-connect").build());
|
||||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
|
||||||
|
@ -683,7 +704,11 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUserProfileConfiguration(String configuration) {
|
private void setUserProfileConfiguration(String configuration) {
|
||||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserRepresentation getUser(String userId) {
|
||||||
|
return testRealm().users().get(userId).toRepresentation();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,14 +21,12 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -41,6 +39,7 @@ import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -79,7 +78,6 @@ import org.openqa.selenium.By;
|
||||||
/**
|
/**
|
||||||
* @author Vlastimil Elias <velias@redhat.com>
|
* @author Vlastimil Elias <velias@redhat.com>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
public static final String SCOPE_DEPARTMENT = "department";
|
public static final String SCOPE_DEPARTMENT = "department";
|
||||||
|
@ -117,9 +115,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
|
|
||||||
enableDynamicUserProfile(testRealm);
|
|
||||||
|
|
||||||
UserRepresentation user = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test").email("login@test.com").enabled(true).password("password").build();
|
UserRepresentation user = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test").email("login@test.com").enabled(true).password("password").build();
|
||||||
UserRepresentation user2 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test2").email("login2@test.com").enabled(true).password("password").build();
|
UserRepresentation user2 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test2").email("login2@test.com").enabled(true).password("password").build();
|
||||||
UserRepresentation user3 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test3").email("login3@test.com").enabled(true).password("password").lastName("ExistingLast").build();
|
UserRepresentation user3 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test3").email("login3@test.com").enabled(true).password("password").lastName("ExistingLast").build();
|
||||||
|
@ -404,15 +399,8 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIgnoreCustomAttributeWhenUserProfileIsDisabled() {
|
public void testIgnoreCustomAttributeWhenUserProfileIsDisabled() {
|
||||||
try {
|
testingClient.server(TEST_REALM_NAME).run(setEmptyFirstNameAndCustomAttribute());
|
||||||
disableDynamicUserProfile(testRealm());
|
testDefaultProfile();
|
||||||
testingClient.server(TEST_REALM_NAME).run(setEmptyFirstNameAndCustomAttribute());
|
|
||||||
testDefaultProfile();
|
|
||||||
} finally {
|
|
||||||
RealmRepresentation realm = testRealm().toRepresentation();
|
|
||||||
enableDynamicUserProfile(realm);
|
|
||||||
testRealm().update(realm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RunOnServer setEmptyFirstNameAndCustomAttribute() {
|
private static RunOnServer setEmptyFirstNameAndCustomAttribute() {
|
||||||
|
@ -1166,7 +1154,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigurationRemainsAfterReset() throws IOException {
|
public void testConfigurationPersisted() throws IOException {
|
||||||
String customConfig = "{\"attributes\": ["
|
String customConfig = "{\"attributes\": ["
|
||||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||||
|
@ -1175,13 +1163,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
UPConfig persistedConfig = setUserProfileConfiguration(customConfig);
|
UPConfig persistedConfig = setUserProfileConfiguration(customConfig);
|
||||||
|
|
||||||
RealmResource realmRes = testRealm();
|
JsonTestUtils.assertJsonEquals(JsonSerialization.writeValueAsString(persistedConfig), testRealm().users().userProfile().getConfiguration());
|
||||||
disableDynamicUserProfile(realmRes, false);
|
|
||||||
RealmRepresentation realm = realmRes.toRepresentation();
|
|
||||||
enableDynamicUserProfile(realm);
|
|
||||||
testRealm().update(realm);
|
|
||||||
|
|
||||||
JsonTestUtils.assertJsonEquals(JsonSerialization.writeValueAsString(persistedConfig), realmRes.users().userProfile().getConfiguration());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserRepresentation getUser(String userId) {
|
protected UserRepresentation getUser(String userId) {
|
||||||
|
@ -1211,30 +1193,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enableDynamicUserProfile(RealmRepresentation testRealm) {
|
|
||||||
if (testRealm.getAttributes() == null) {
|
|
||||||
testRealm.setAttributes(new HashMap<>());
|
|
||||||
}
|
|
||||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void disableDynamicUserProfile(RealmResource realm) {
|
|
||||||
disableDynamicUserProfile(realm, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void disableDynamicUserProfile(RealmResource realm, boolean reset) {
|
|
||||||
RealmRepresentation realmRep = realm.toRepresentation();
|
|
||||||
if (realmRep.getAttributes() == null) {
|
|
||||||
realmRep.setAttributes(new HashMap<>());
|
|
||||||
}
|
|
||||||
realmRep.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.FALSE.toString());
|
|
||||||
realm.update(realmRep);
|
|
||||||
if (reset) {
|
|
||||||
setUserProfileConfiguration(realm, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static UPConfig setUserProfileConfiguration(RealmResource testRealm, String configuration) {
|
public static UPConfig setUserProfileConfiguration(RealmResource testRealm, String configuration) {
|
||||||
try {
|
try {
|
||||||
UPConfig config = configuration == null ? null : JsonSerialization.readValue(configuration, UPConfig.class);
|
UPConfig config = configuration == null ? null : JsonSerialization.readValue(configuration, UPConfig.class);
|
||||||
|
@ -1261,6 +1219,13 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UPConfig enableUnmanagedAttributes(UserProfileResource upResource) {
|
||||||
|
UPConfig cfg = upResource.getConfiguration();
|
||||||
|
cfg.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||||
|
upResource.update(cfg);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
public static UserRepresentation getUser(RealmResource testRealm, String userId) {
|
public static UserRepresentation getUser(RealmResource testRealm, String userId) {
|
||||||
return testRealm.users().get(userId).toRepresentation();
|
return testRealm.users().get(userId).toRepresentation();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.testsuite.i18n;
|
|
||||||
|
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
|
||||||
*/
|
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class LoginPageWithUserProfileTest extends LoginPageTest {
|
|
||||||
}
|
|
|
@ -106,13 +106,13 @@ import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.keycloak.migration.migrators.MigrateTo24_0_0.REALM_USER_PROFILE_ENABLED;
|
||||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
|
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
|
||||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
|
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
|
||||||
import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
|
import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
|
||||||
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
||||||
import static org.keycloak.testsuite.Assert.assertNames;
|
import static org.keycloak.testsuite.Assert.assertNames;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
|
||||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY;
|
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.UP_COMPONENT_CONFIG_KEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1204,7 +1204,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
RealmRepresentation rep = realm.toRepresentation();
|
RealmRepresentation rep = realm.toRepresentation();
|
||||||
Map<String, String> attributes = rep.getAttributes();
|
Map<String, String> attributes = rep.getAttributes();
|
||||||
String userProfileEnabled = attributes.get(REALM_USER_PROFILE_ENABLED);
|
String userProfileEnabled = attributes.get(REALM_USER_PROFILE_ENABLED);
|
||||||
assertTrue(Boolean.parseBoolean(userProfileEnabled));
|
assertNull(userProfileEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testUnmanagedAttributePolicySet(RealmResource realm, UnmanagedAttributePolicy policy) {
|
private void testUnmanagedAttributePolicySet(RealmResource realm, UnmanagedAttributePolicy policy) {
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
package org.keycloak.testsuite.migration;
|
package org.keycloak.testsuite.migration;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.exportimport.util.ImportUtils;
|
import org.keycloak.exportimport.util.ImportUtils;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.utils.io.IOUtil;
|
import org.keycloak.testsuite.utils.io.IOUtil;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -31,7 +29,6 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Tests that we can import json file from previous version. MigrationTest only tests DB.
|
* Tests that we can import json file from previous version. MigrationTest only tests DB.
|
||||||
*/
|
*/
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigrationTest {
|
public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigrationTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,9 +19,7 @@ package org.keycloak.testsuite.migration;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.arquillian.migration.Migration;
|
import org.keycloak.testsuite.arquillian.migration.Migration;
|
||||||
|
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
@ -34,7 +32,6 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
|
* @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class MigrationTest extends AbstractMigrationTest {
|
public class MigrationTest extends AbstractMigrationTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -139,7 +139,6 @@ public class ImportTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public void importUserProfile() throws Exception {
|
public void importUserProfile() throws Exception {
|
||||||
final String realmString = IOUtils.toString(getClass().getResourceAsStream("/model/import-userprofile.json"), StandardCharsets.UTF_8);
|
final String realmString = IOUtils.toString(getClass().getResourceAsStream("/model/import-userprofile.json"), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.ClientScopeResource;
|
import org.keycloak.admin.client.resource.ClientScopeResource;
|
||||||
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.UriUtils;
|
import org.keycloak.common.util.UriUtils;
|
||||||
|
@ -47,11 +48,13 @@ import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||||
import org.keycloak.testsuite.updaters.ProtocolMappersUpdater;
|
import org.keycloak.testsuite.updaters.ProtocolMappersUpdater;
|
||||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
|
@ -120,6 +123,10 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
* @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored()
|
* @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored()
|
||||||
*/
|
*/
|
||||||
oauth.clientId("test-app");
|
oauth.clientId("test-app");
|
||||||
|
|
||||||
|
// enable user profile unmanaged attributes
|
||||||
|
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
|
||||||
|
VerifyProfileTest.enableUnmanagedAttributes(upResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.common.Profile;
|
|
||||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
|
@ -38,8 +37,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
import org.keycloak.testsuite.util.RealmBuilder;
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
@ -47,7 +44,6 @@ import org.keycloak.testsuite.util.UserBuilder;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class ServiceAccountUserProfileTest extends AbstractKeycloakTest {
|
public class ServiceAccountUserProfileTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
private static String userId;
|
private static String userId;
|
||||||
|
@ -116,7 +112,6 @@ public class ServiceAccountUserProfileTest extends AbstractKeycloakTest {
|
||||||
realm.user(serviceAccountUser);
|
realm.user(serviceAccountUser);
|
||||||
|
|
||||||
RealmRepresentation realmRep = realm.build();
|
RealmRepresentation realmRep = realm.build();
|
||||||
VerifyProfileTest.enableDynamicUserProfile(realmRep);
|
|
||||||
testRealms.add(realmRep);
|
testRealms.add(realmRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ public class LogoutTest extends AbstractSamlTest {
|
||||||
.targetAttributeSamlResponse()
|
.targetAttributeSamlResponse()
|
||||||
.targetUri(getSamlBrokerUrl(REALM_NAME))
|
.targetUri(getSamlBrokerUrl(REALM_NAME))
|
||||||
.build()
|
.build()
|
||||||
.updateProfile().username("a").email("a@b.c").firstName("A").lastName("B").build()
|
.updateProfile().username("aaa").email("a@b.c").firstName("A").lastName("B").build()
|
||||||
.followOneRedirect()
|
.followOneRedirect()
|
||||||
|
|
||||||
// Now returning back to the app
|
// Now returning back to the app
|
||||||
|
|
|
@ -22,15 +22,23 @@ package org.keycloak.testsuite.theme;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
||||||
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
@ -52,12 +60,19 @@ public class CustomRegistrationTemplateTest extends AbstractTestRealmKeycloakTes
|
||||||
@Page
|
@Page
|
||||||
protected AppPage appPage;
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
private UPConfig upConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
testRealm.setRegistrationAllowed(true);
|
testRealm.setRegistrationAllowed(true);
|
||||||
testRealm.setLoginTheme("address");
|
testRealm.setLoginTheme("address");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void onBefore() {
|
||||||
|
upConfig = updateUserProfileConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegistration() {
|
public void testRegistration() {
|
||||||
//contains few special characters we want to be sure they are allowed in username
|
//contains few special characters we want to be sure they are allowed in username
|
||||||
|
@ -67,6 +82,50 @@ public class CustomRegistrationTemplateTest extends AbstractTestRealmKeycloakTes
|
||||||
assertCustomAttributes(attributes);
|
assertCustomAttributes(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeEnabled() {
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
UserRepresentation user = register();
|
||||||
|
assertCustomAttributes(user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeAdminEdit() {
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
UserRepresentation user = register();
|
||||||
|
assertNull(user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeDisabled() {
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
UserRepresentation user = register();
|
||||||
|
assertNull(user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UPConfig updateUserProfileConfiguration() {
|
||||||
|
UPConfig upCOnfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
upCOnfig.setUnmanagedAttributePolicy(null);
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("street", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("locality", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("region", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("postal_code", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("country", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
testRealm().users().userProfile().update(upCOnfig);
|
||||||
|
return upCOnfig;
|
||||||
|
}
|
||||||
|
|
||||||
protected static void assertCustomAttributes(Map<String, List<String>> attributes) {
|
protected static void assertCustomAttributes(Map<String, List<String>> attributes) {
|
||||||
for (Entry<String, String> attribute : CUSTOM_ATTRIBUTES.entrySet()) {
|
for (Entry<String, String> attribute : CUSTOM_ATTRIBUTES.entrySet()) {
|
||||||
String name = attribute.getKey();
|
String name = attribute.getKey();
|
||||||
|
@ -93,9 +152,6 @@ public class CustomRegistrationTemplateTest extends AbstractTestRealmKeycloakTes
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToRegistrationPage() {
|
private void navigateToRegistrationPage() {
|
||||||
RealmRepresentation realm = testRealm().toRepresentation();
|
|
||||||
realm.setRegistrationAllowed(true);
|
|
||||||
testRealm().update(realm);
|
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
loginPage.clickRegister();
|
loginPage.clickRegister();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.testsuite.theme;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.disableDynamicUserProfile;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.enableDynamicUserProfile;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
|
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class CustomRegistrationTemplateUserProfileTest extends CustomRegistrationTemplateTest {
|
|
||||||
|
|
||||||
private UPConfig upConfig;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void onBefore() {
|
|
||||||
upConfig = updateUserProfileConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void onAfter() {
|
|
||||||
disableDynamicUserProfile(testRealm());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Test
|
|
||||||
public void testRegistration() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
super.testRegistration();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeEnabled() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
UserRepresentation user = register();
|
|
||||||
assertCustomAttributes(user.getAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeAdminEdit() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
UserRepresentation user = register();
|
|
||||||
assertNull(user.getAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeDisabled() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
UserRepresentation user = register();
|
|
||||||
assertNull(user.getAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private UPConfig updateUserProfileConfiguration() {
|
|
||||||
RealmRepresentation realm = testRealm().toRepresentation();
|
|
||||||
enableDynamicUserProfile(realm);
|
|
||||||
testRealm().update(realm);
|
|
||||||
UPConfig upCOnfig = testRealm().users().userProfile().getConfiguration();
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("street", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("locality", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("region", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("postal_code", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("country", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
testRealm().users().userProfile().update(upCOnfig);
|
|
||||||
return upCOnfig;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,15 +23,18 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
||||||
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -39,7 +42,11 @@ import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
||||||
|
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||||
|
@ -63,26 +70,31 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
||||||
@Page
|
@Page
|
||||||
protected AppPage appPage;
|
protected AppPage appPage;
|
||||||
|
|
||||||
|
private UPConfig upConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
testRealm.setLoginTheme("address");
|
testRealm.setLoginTheme("address");
|
||||||
// the custom theme expects email as username and the username field is not rendered at all
|
// the custom theme expects email as username and the username field is not rendered at all
|
||||||
testRealm.setRegistrationEmailAsUsername(true);
|
testRealm.setRegistrationEmailAsUsername(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void onBefore() {
|
||||||
UserRepresentation user = UserBuilder.create().enabled(true)
|
UserRepresentation user = UserBuilder.create().enabled(true)
|
||||||
.username("tom")
|
.username("tom")
|
||||||
.email("tom@keycloak.org")
|
.email("tom@keycloak.org")
|
||||||
.password("password")
|
.password("password")
|
||||||
.firstName("Tom")
|
.firstName("Tom")
|
||||||
.lastName("Brady").build();
|
.lastName("Brady")
|
||||||
testRealm.getUsers().add(user);
|
.requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.name())
|
||||||
}
|
.build();
|
||||||
|
Response resp = testRealm().users().create(user);
|
||||||
|
String userId = ApiUtil.getCreatedId(resp);
|
||||||
|
resp.close();
|
||||||
|
getCleanup().addUserId(userId);
|
||||||
|
|
||||||
@Before
|
upConfig = updateUserProfileConfiguration();
|
||||||
public void onBefore() {
|
|
||||||
UserRepresentation user = getUser("tom");
|
|
||||||
user.setAttributes(Map.of());
|
|
||||||
user.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
|
|
||||||
testRealm().users().get(user.getId()).update(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -94,6 +106,37 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
||||||
assertCustomAttributes(user.getAttributes());
|
assertCustomAttributes(user.getAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeEnabled() {
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
testUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeAdminEdit() {
|
||||||
|
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
UserRepresentation user = updateProfile();
|
||||||
|
assertNull(user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnmanagedAttributeDisabled() {
|
||||||
|
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
||||||
|
upConfig.removeAttribute(name);
|
||||||
|
}
|
||||||
|
testRealm().users().userProfile().update(upConfig);
|
||||||
|
UserRepresentation user = updateProfile();
|
||||||
|
assertNull(user.getAttributes());
|
||||||
|
}
|
||||||
|
|
||||||
protected UserRepresentation updateProfile() {
|
protected UserRepresentation updateProfile() {
|
||||||
navigateToUpdateProfilePage();
|
navigateToUpdateProfilePage();
|
||||||
updateProfilePage.update(CUSTOM_ATTRIBUTES.entrySet().stream()
|
updateProfilePage.update(CUSTOM_ATTRIBUTES.entrySet().stream()
|
||||||
|
@ -124,4 +167,16 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
||||||
loginPage.login("tom@keycloak.org", "password");
|
loginPage.login("tom@keycloak.org", "password");
|
||||||
updateProfilePage.assertCurrent();
|
updateProfilePage.assertCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UPConfig updateUserProfileConfiguration() {
|
||||||
|
UPConfig upCOnfig = testRealm().users().userProfile().getConfiguration();
|
||||||
|
upCOnfig.setUnmanagedAttributePolicy(null);
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("street", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("locality", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("region", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("postal_code", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
upCOnfig.addOrReplaceAttribute(new UPAttribute("country", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
||||||
|
testRealm().users().userProfile().update(upCOnfig);
|
||||||
|
return upCOnfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.testsuite.theme;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.disableDynamicUserProfile;
|
|
||||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.enableDynamicUserProfile;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.common.Profile.Feature;
|
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttribute;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
|
||||||
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
|
||||||
|
|
||||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
|
||||||
public class CustomUpdateProfileTemplateUserProfileTest extends CustomUpdateProfileTemplateTest {
|
|
||||||
|
|
||||||
private UPConfig upConfig;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void onBefore() {
|
|
||||||
super.onBefore();
|
|
||||||
upConfig = updateUserProfileConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void onAfter() {
|
|
||||||
disableDynamicUserProfile(testRealm());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Test
|
|
||||||
public void testUpdateProfile() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
super.testUpdateProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeEnabled() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(UnmanagedAttributePolicy.ENABLED);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
super.testUpdateProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeAdminEdit() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
UserRepresentation user = updateProfile();
|
|
||||||
assertNull(user.getAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnmanagedAttributeDisabled() {
|
|
||||||
upConfig.setUnmanagedAttributePolicy(null);
|
|
||||||
for (String name : CUSTOM_ATTRIBUTES.keySet()) {
|
|
||||||
upConfig.removeAttribute(name);
|
|
||||||
}
|
|
||||||
testRealm().users().userProfile().update(upConfig);
|
|
||||||
UserRepresentation user = updateProfile();
|
|
||||||
assertNull(user.getAttributes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private UPConfig updateUserProfileConfiguration() {
|
|
||||||
RealmRepresentation realm = testRealm().toRepresentation();
|
|
||||||
enableDynamicUserProfile(realm);
|
|
||||||
testRealm().update(realm);
|
|
||||||
UPConfig upCOnfig = testRealm().users().userProfile().getConfiguration();
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("street", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("locality", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("region", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("postal_code", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
upCOnfig.addOrReplaceAttribute(new UPAttribute("country", new UPAttributePermissions(Set.of(ROLE_ADMIN), Set.of(ROLE_USER))));
|
|
||||||
testRealm().users().userProfile().update(upCOnfig);
|
|
||||||
return upCOnfig;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue