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
|
||||
run: |
|
||||
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:
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
|
@ -297,7 +297,7 @@ jobs:
|
|||
- name: Start Keycloak server
|
||||
run: |
|
||||
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:
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
|
|
|
@ -73,8 +73,6 @@ public class Profile {
|
|||
|
||||
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),
|
||||
|
||||
CLIENT_SECRET_ROTATION("Client Secret Rotation", Type.PREVIEW),
|
||||
|
|
|
@ -79,7 +79,6 @@ public class ProfileTest {
|
|||
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.LINKEDIN_OAUTH
|
||||
|
@ -90,7 +89,7 @@ public class ProfileTest {
|
|||
disabledFeatures.add(Profile.Feature.KERBEROS);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -40,15 +40,8 @@ describe("Realm settings tabs tests", () => {
|
|||
return this;
|
||||
};
|
||||
|
||||
it("shows the 'user profile' tab if enabled", () => {
|
||||
it("shows the 'user profile' tab", () => {
|
||||
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");
|
||||
});
|
||||
|
||||
|
|
|
@ -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 attributeName = "Test";
|
||||
|
||||
before(() =>
|
||||
adminClient.createRealm(realmName, {
|
||||
attributes: { userProfileEnabled: "true" },
|
||||
}),
|
||||
);
|
||||
|
||||
before(() => adminClient.createRealm(realmName));
|
||||
after(() => adminClient.deleteRealm(realmName));
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { v4 as uuid } from "uuid";
|
|||
|
||||
import SidebarPage from "../support/pages/admin-ui/SidebarPage";
|
||||
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 Masthead from "../support/pages/admin-ui/Masthead";
|
||||
import ListingPage from "../support/pages/admin-ui/ListingPage";
|
||||
|
@ -23,6 +24,7 @@ let groupsList: string[] = [];
|
|||
describe("User creation", () => {
|
||||
const loginPage = new LoginPage();
|
||||
const sidebarPage = new SidebarPage();
|
||||
const realmSettingsPage = new RealmSettingsPage();
|
||||
const createUserPage = new CreateUserPage();
|
||||
const userGroupsPage = new UserGroupsPage();
|
||||
const masthead = new Masthead();
|
||||
|
@ -30,7 +32,7 @@ describe("User creation", () => {
|
|||
const listingPage = new ListingPage();
|
||||
const userDetailsPage = new UserDetailsPage();
|
||||
const credentialsPage = new CredentialsPage();
|
||||
const attributesTab = new AttributesTab();
|
||||
const attributesTab = new AttributesTab(true);
|
||||
const usersPage = new UsersPage();
|
||||
const identityProviderLinksTab = new IdentityProviderLinksTab();
|
||||
|
||||
|
@ -143,6 +145,14 @@ describe("User creation", () => {
|
|||
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", () => {
|
||||
listingPage.goToItemDetails(itemId);
|
||||
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
export default class AttributesTab {
|
||||
#saveAttributeBtn = "save-attributes";
|
||||
#addAttributeBtn = "attributes-add-row";
|
||||
#attributesTab = "attributes";
|
||||
#keyInput = "attributes-key";
|
||||
#valueInput = "attributes-value";
|
||||
#removeBtn = "attributes-remove";
|
||||
#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() {
|
||||
cy.findByTestId(this.#attributesTab).click();
|
||||
|
|
|
@ -67,7 +67,6 @@ export default class RealmSettingsPage extends CommonPage {
|
|||
supportedLocalesToggle = "#kc-l-supported-locales";
|
||||
emailSaveBtn = "email-tab-save";
|
||||
managedAccessSwitch = "user-managed-access-switch";
|
||||
profileEnabledSwitch = "user-profile-enabled-switch";
|
||||
userRegSwitch = "user-reg-switch";
|
||||
forgotPwdSwitch = "forgot-pw-switch";
|
||||
rememberMeSwitch = "remember-me-switch";
|
||||
|
@ -233,6 +232,7 @@ export default class RealmSettingsPage extends CommonPage {
|
|||
#realmDisplayName = "#kc-display-name";
|
||||
#frontEndURL = "#kc-frontend-url";
|
||||
#requireSSL = "#kc-require-ssl";
|
||||
#unmanagedAttributes = "#kc-user-profile-unmanaged-attribute-policy";
|
||||
#fromDisplayName = "from-display-name";
|
||||
#replyToEmail = "#kc-reply-to";
|
||||
#port = "#kc-port";
|
||||
|
@ -319,6 +319,12 @@ export default class RealmSettingsPage extends CommonPage {
|
|||
return this;
|
||||
}
|
||||
|
||||
getUnmanagedAttributes(option: string) {
|
||||
cy.get(this.#unmanagedAttributes).contains(option);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fillDisplayName(displayName: string) {
|
||||
cy.get(this.#realmDisplayName).clear().type(displayName);
|
||||
}
|
||||
|
@ -355,6 +361,14 @@ export default class RealmSettingsPage extends CommonPage {
|
|||
.click();
|
||||
}
|
||||
|
||||
fillUnmanagedAttributes(option: string) {
|
||||
cy.get(this.#unmanagedAttributes)
|
||||
.click()
|
||||
.get(".pf-c-select__menu-item")
|
||||
.contains(option)
|
||||
.click();
|
||||
}
|
||||
|
||||
setDefaultLocale(locale: string) {
|
||||
cy.get(this.selectDefaultLocale).click();
|
||||
cy.findByTestId(this.defaultLocaleList).contains(locale).click();
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class CreateUserPage {
|
|||
cancelBtn: string;
|
||||
|
||||
constructor() {
|
||||
this.usernameInput = "#kc-username";
|
||||
this.usernameInput = "#username";
|
||||
|
||||
this.usersEmptyState = "empty-state";
|
||||
this.emptyStateCreateUserBtn = "no-users-found-empty-action";
|
||||
|
|
|
@ -21,11 +21,11 @@ export default class UserDetailsPage extends PageObject {
|
|||
super();
|
||||
this.saveBtn = "save-user";
|
||||
this.cancelBtn = "cancel-create-user";
|
||||
this.emailInput = "email-input";
|
||||
this.emailInput = "email";
|
||||
this.emailValue = () => "example" + "_" + uuid() + "@example.com";
|
||||
this.firstNameInput = "firstName-input";
|
||||
this.firstNameInput = "firstName";
|
||||
this.firstNameValue = "firstname";
|
||||
this.lastNameInput = "lastName-input";
|
||||
this.lastNameInput = "lastName";
|
||||
this.lastNameValue = "lastname";
|
||||
this.requiredUserActions = [RequiredActionAlias.UPDATE_PASSWORD];
|
||||
this.identityProviderLinksTab = "identity-provider-links-tab";
|
||||
|
|
|
@ -1461,7 +1461,6 @@ exactSearch=Exact search
|
|||
value=Value
|
||||
filenamePlaceholder=Upload a PEM file or paste key below
|
||||
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.
|
||||
times.seconds=Seconds
|
||||
removeMappingTitle=Remove role?
|
||||
|
@ -1890,7 +1889,6 @@ moveToGroup=Move {{group1}} to {{group2}}
|
|||
noRealmRoles=No realm roles
|
||||
events-disable-confirm=If "Save events" is disabled, subsequent events will not be displayed in the "Events" menu
|
||||
reqAuthnConstraints=Requested AuthnContext Constraints
|
||||
userProfileEnabled=User Profile Enabled
|
||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Pushed authorization request
|
||||
addIdpMapperNameHelp=Name of the mapper.
|
||||
requirements.ALTERNATIVE=Alternative
|
||||
|
|
|
@ -1437,7 +1437,6 @@ exactSearch=Búsqueda exacta
|
|||
value=Valor
|
||||
filenamePlaceholder=Cargar un archivo PEM o pegar la clave a continuación
|
||||
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.
|
||||
times.seconds=Segundos
|
||||
removeMappingTitle=¿Eliminar asignación?
|
||||
|
@ -1865,7 +1864,6 @@ moveToGroup=Mover {{grupo1}} a {{grupo2}}
|
|||
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"
|
||||
reqAuthnConstraints=Restricciones de contexto de autenticación solicitadas
|
||||
userProfileEnabled=Perfil de usuario habilitado
|
||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Solicitud de autorización empujada
|
||||
addIdpMapperNameHelp=Nombre del mapeador.
|
||||
requirements.ALTERNATIVE=Alternativa
|
||||
|
|
|
@ -1437,7 +1437,6 @@ exactSearch=Wyszukiwanie dokładne
|
|||
value=Wartość
|
||||
filenamePlaceholder=Prześlij plik PEM lub wklej klucz poniżej
|
||||
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.
|
||||
times.seconds=Sekundy
|
||||
removeMappingTitle=Usuń rolę?
|
||||
|
@ -1865,7 +1864,6 @@ moveToGroup=Przenieś {{group1}} do {{group2}}
|
|||
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"
|
||||
reqAuthnConstraints=Wymagane ograniczenia AuthnContext
|
||||
userProfileEnabled=Profil użytkownika włączony
|
||||
eventTypes.PUSHED_AUTHORIZATION_REQUEST.description=Żądanie autoryzacji przesuniętej
|
||||
addIdpMapperNameHelp=Nazwa mappera.
|
||||
requirements.ALTERNATIVE=Alternatywa
|
||||
|
|
|
@ -1412,7 +1412,6 @@ exactSearch=精确搜索
|
|||
value=数值
|
||||
filenamePlaceholder=上传 PEM 文件或在下方粘贴密钥
|
||||
deleteConfirm_one=是否要删除此群组“{{groupName}}”。
|
||||
userProfileEnabledHelp=如果启用,允许管理用户配置文件。
|
||||
times.seconds=秒
|
||||
removeMappingTitle=移除角色?
|
||||
executorTypeSelectAlgorithm=执行器类型选择算法
|
||||
|
@ -1831,7 +1830,6 @@ moveToGroup=将{{group1}}移动到{{group2}}
|
|||
noRealmRoles=无领域角色
|
||||
events-disable-confirm=如果禁用“保存事件”,后续事件将不会展示在“事件”菜单中。
|
||||
reqAuthnConstraints=请求的上下文约束
|
||||
userProfileEnabled=用户资料
|
||||
requirements.ALTERNATIVE=非必需
|
||||
credentialResetConfirm=发送电子邮件
|
||||
permissionsEnabledHelp=确定是否启用细粒度权限来管理此角色。禁用将删除所有已设置的当前权限。
|
||||
|
|
|
@ -28,7 +28,6 @@ import {
|
|||
convertAttributeNameToForm,
|
||||
convertToFormValues,
|
||||
} from "../util";
|
||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
||||
import {
|
||||
UnmanagedAttributePolicy,
|
||||
UserProfileConfig,
|
||||
|
@ -57,7 +56,6 @@ export const RealmSettingsGeneralTab = ({
|
|||
setValue,
|
||||
formState: { isDirty, errors },
|
||||
} = form;
|
||||
const isFeatureEnabled = useIsFeatureEnabled();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const requireSslTypes = ["all", "external", "none"];
|
||||
|
@ -72,7 +70,6 @@ export const RealmSettingsGeneralTab = ({
|
|||
];
|
||||
const [isUnmanagedAttributeOpen, setIsUnmanagedAttributeOpen] =
|
||||
useState(false);
|
||||
const [isUserProfileEnabled, setUserProfileEnabled] = useState(false);
|
||||
|
||||
const setupForm = () => {
|
||||
convertToFormValues(realm, setValue);
|
||||
|
@ -86,7 +83,6 @@ export const RealmSettingsGeneralTab = ({
|
|||
result,
|
||||
);
|
||||
}
|
||||
setUserProfileEnabled(realm.attributes?.["userProfileEnabled"] === "true");
|
||||
};
|
||||
|
||||
useFetch(
|
||||
|
@ -251,44 +247,6 @@ export const RealmSettingsGeneralTab = ({
|
|||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{isFeatureEnabled(Feature.DeclarativeUserProfile) && (
|
||||
<FormGroup
|
||||
hasNoPaddingTop
|
||||
label={t("userProfileEnabled")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={t("userProfileEnabledHelp")}
|
||||
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 && (
|
||||
<FormGroup
|
||||
label={t("unmanagedAttributes")}
|
||||
fieldId="kc-user-profile-unmanaged-attribute-policy"
|
||||
|
@ -323,7 +281,6 @@ export const RealmSettingsGeneralTab = ({
|
|||
))}
|
||||
</Select>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup
|
||||
label={t("endpoints")}
|
||||
labelIcon={
|
||||
|
|
|
@ -417,8 +417,6 @@ export const RealmSettingsTabs = ({
|
|||
</RoutableTabs>
|
||||
</Tab>
|
||||
)}
|
||||
{isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
||||
realm.attributes?.userProfileEnabled === "true" && (
|
||||
<Tab
|
||||
title={<TabTitleText>{t("userProfile")}</TabTitleText>}
|
||||
data-testid="rs-user-profile-tab"
|
||||
|
@ -426,7 +424,6 @@ export const RealmSettingsTabs = ({
|
|||
>
|
||||
<UserProfileTab />
|
||||
</Tab>
|
||||
)}
|
||||
<Tab
|
||||
title={<TabTitleText>{t("userRegistration")}</TabTitleText>}
|
||||
data-testid="rs-userRegistration-tab"
|
||||
|
|
|
@ -15,7 +15,6 @@ import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"
|
|||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { useFetch } from "../utils/useFetch";
|
||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
||||
import { UserForm } from "./UserForm";
|
||||
import { UserFormFields, toUserRepresentation } from "./form-state";
|
||||
import { toUser } from "./routes/User";
|
||||
|
@ -27,7 +26,6 @@ export default function CreateUser() {
|
|||
const { addAlert, addError } = useAlerts();
|
||||
const navigate = useNavigate();
|
||||
const { realm: realmName } = useRealm();
|
||||
const isFeatureEnabled = useIsFeatureEnabled();
|
||||
const form = useForm<UserFormFields>({ mode: "onChange" });
|
||||
const [addedGroups, setAddedGroups] = useState<GroupRepresentation[]>([]);
|
||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||
|
@ -46,14 +44,7 @@ export default function CreateUser() {
|
|||
}
|
||||
|
||||
setRealm(realm);
|
||||
|
||||
const isUserProfileEnabled =
|
||||
isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
||||
realm.attributes?.userProfileEnabled === "true";
|
||||
|
||||
setUserProfileMetadata(
|
||||
isUserProfileEnabled ? userProfileMetadata : undefined,
|
||||
);
|
||||
setUserProfileMetadata(userProfileMetadata);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
|
|
@ -34,7 +34,6 @@ import { useAccess } from "../context/access/Access";
|
|||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { UserProfileProvider } from "../realm-settings/user-profile/UserProfileContext";
|
||||
import { useFetch } from "../utils/useFetch";
|
||||
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
|
||||
import { useParams } from "../utils/useParams";
|
||||
import { UserAttributes } from "./UserAttributes";
|
||||
import { UserConsents } from "./UserConsents";
|
||||
|
@ -64,7 +63,6 @@ export default function EditUser() {
|
|||
const { hasAccess } = useAccess();
|
||||
const { id } = useParams<UserParams>();
|
||||
const { realm: realmName } = useRealm();
|
||||
const isFeatureEnabled = useIsFeatureEnabled();
|
||||
const form = useForm<UserFormFields>({ mode: "onChange" });
|
||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||
const [user, setUser] = useState<UIUserRepresentation>();
|
||||
|
@ -113,27 +111,15 @@ export default function EditUser() {
|
|||
throw new Error(t("notFound"));
|
||||
}
|
||||
|
||||
const isUserProfileEnabled =
|
||||
isFeatureEnabled(Feature.DeclarativeUserProfile) &&
|
||||
realm.attributes?.userProfileEnabled === "true";
|
||||
|
||||
const { userProfileMetadata, ...user } = userData;
|
||||
setUserProfileMetadata(
|
||||
isUserProfileEnabled ? userProfileMetadata : undefined,
|
||||
);
|
||||
|
||||
if (isUserProfileEnabled) {
|
||||
setUserProfileMetadata(userProfileMetadata);
|
||||
user.unmanagedAttributes = unmanagedAttributes;
|
||||
user.attributes = filterManagedAttributes(
|
||||
user.attributes,
|
||||
unmanagedAttributes,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
upConfig.unmanagedAttributePolicy !== undefined ||
|
||||
!isUserProfileEnabled
|
||||
) {
|
||||
if (upConfig.unmanagedAttributePolicy !== undefined) {
|
||||
setUnmanagedAttributesEnabled(true);
|
||||
}
|
||||
|
||||
|
@ -146,7 +132,7 @@ export default function EditUser() {
|
|||
|
||||
setBruteForced({ isBruteForceProtected, isLocked });
|
||||
|
||||
form.reset(toUserFormFields(user, isUserProfileEnabled));
|
||||
form.reset(toUserFormFields(user));
|
||||
},
|
||||
[refreshCount],
|
||||
);
|
||||
|
@ -258,7 +244,7 @@ export default function EditUser() {
|
|||
]}
|
||||
onToggle={(value) =>
|
||||
save({
|
||||
...toUserFormFields(user, !!userProfileMetadata),
|
||||
...toUserFormFields(user),
|
||||
enabled: value,
|
||||
})
|
||||
}
|
||||
|
@ -295,12 +281,7 @@ export default function EditUser() {
|
|||
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
||||
{...attributesTab}
|
||||
>
|
||||
<UserAttributes
|
||||
user={user}
|
||||
save={save}
|
||||
upConfig={upConfig}
|
||||
isUserProfileEnabled={!!userProfileMetadata}
|
||||
/>
|
||||
<UserAttributes user={user} save={save} upConfig={upConfig} />
|
||||
</Tab>
|
||||
)}
|
||||
<Tab
|
||||
|
|
|
@ -16,14 +16,12 @@ type UserAttributesProps = {
|
|||
user: UserRepresentation;
|
||||
save: (user: UserFormFields) => void;
|
||||
upConfig?: UserProfileConfig;
|
||||
isUserProfileEnabled: boolean;
|
||||
};
|
||||
|
||||
export const UserAttributes = ({
|
||||
user,
|
||||
save,
|
||||
upConfig,
|
||||
isUserProfileEnabled,
|
||||
}: UserAttributesProps) => {
|
||||
const form = useFormContext<UserFormFields>();
|
||||
|
||||
|
@ -36,10 +34,10 @@ export const UserAttributes = ({
|
|||
reset={() =>
|
||||
form.reset({
|
||||
...form.getValues(),
|
||||
attributes: toUserFormFields(user, isUserProfileEnabled).attributes,
|
||||
attributes: toUserFormFields(user).attributes,
|
||||
})
|
||||
}
|
||||
name={isUserProfileEnabled ? "unmanagedAttributes" : "attributes"}
|
||||
name="unmanagedAttributes"
|
||||
isDisabled={
|
||||
UnmanagedAttributePolicy.AdminView ==
|
||||
upConfig?.unmanagedAttributePolicy
|
||||
|
|
|
@ -394,11 +394,7 @@ export const UserForm = ({
|
|||
<Button
|
||||
data-testid="cancel-create-user"
|
||||
variant="link"
|
||||
onClick={
|
||||
user?.id
|
||||
? () => reset(toUserFormFields(user, !!userProfileMetadata))
|
||||
: undefined
|
||||
}
|
||||
onClick={user?.id ? () => reset(toUserFormFields(user)) : undefined}
|
||||
component={
|
||||
!user?.id
|
||||
? (props) => (
|
||||
|
|
|
@ -18,18 +18,11 @@ export interface UIUserRepresentation extends UserRepresentation {
|
|||
unmanagedAttributes?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function toUserFormFields(
|
||||
data: UIUserRepresentation,
|
||||
userProfileEnabled: boolean,
|
||||
): UserFormFields {
|
||||
let attributes: Record<string, string | string[]> = {};
|
||||
if (userProfileEnabled) {
|
||||
export function toUserFormFields(data: UIUserRepresentation): UserFormFields {
|
||||
const attributes: Record<string, string | string[]> = {};
|
||||
Object.entries(data.attributes || {}).forEach(
|
||||
([k, v]) => (attributes[beerify(k)] = v),
|
||||
);
|
||||
} else {
|
||||
attributes = arrayToKeyValue(data.attributes);
|
||||
}
|
||||
|
||||
const unmanagedAttributes = arrayToKeyValue(data.unmanagedAttributes);
|
||||
return { ...data, attributes, unmanagedAttributes };
|
||||
|
|
|
@ -3,7 +3,6 @@ import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
|||
export enum Feature {
|
||||
AdminFineGrainedAuthz = "ADMIN_FINE_GRAINED_AUTHZ",
|
||||
ClientPolicies = "CLIENT_POLICIES",
|
||||
DeclarativeUserProfile = "DECLARATIVE_USER_PROFILE",
|
||||
Kerberos = "KERBEROS",
|
||||
DynamicScopes = "DYNAMIC_SCOPES",
|
||||
DPoP = "DPOP",
|
||||
|
|
|
@ -40,7 +40,7 @@ async function startServer() {
|
|||
[
|
||||
"start-dev",
|
||||
"--http-port=8180",
|
||||
"--features=account3,admin-fine-grained-authz,declarative-user-profile,transient-users",
|
||||
"--features=account3,admin-fine-grained-authz,transient-users",
|
||||
...keycloakArgs,
|
||||
],
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ public class MigrateTo24_0_0 implements Migration {
|
|||
|
||||
private static final Logger LOG = Logger.getLogger(MigrateTo24_0_0.class);
|
||||
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
|
||||
public void migrate(KeycloakSession session) {
|
||||
|
@ -64,15 +64,15 @@ public class MigrateTo24_0_0 implements Migration {
|
|||
RealmModel realm = session.getContext().getRealm();
|
||||
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) {
|
||||
// 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());
|
||||
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
|
||||
// that don't have the declarative user profile enabled
|
||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
|
|
|
@ -28,7 +28,7 @@ import static org.keycloak.quarkus.runtime.cli.command.AbstractStartCommand.OPTI
|
|||
@LegacyStore
|
||||
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
|
||||
public void testEnableOnBuild(KeycloakDistribution dist) {
|
||||
|
@ -89,7 +89,7 @@ public class FeaturesDistTest {
|
|||
cliResult.assertStartedDevMode();
|
||||
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
||||
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
|
||||
|
@ -100,7 +100,7 @@ public class FeaturesDistTest {
|
|||
cliResult.assertStartedDevMode();
|
||||
assertThat(cliResult.getOutput(), CoreMatchers.allOf(
|
||||
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) {
|
||||
|
|
|
@ -48,20 +48,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
HTTP(S):
|
||||
|
||||
|
|
|
@ -48,20 +48,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
HTTP(S):
|
||||
|
||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -75,20 +75,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -76,20 +76,19 @@ Feature:
|
|||
--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],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], declarative-user-profile[:v1], device-flow[:
|
||||
v1], docker[:v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], impersonation[:
|
||||
v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
par[:v1], preview, recovery-codes[:v1], scripts[:v1], step-up-authentication
|
||||
[:v1], token-exchange[:v1], transient-users[:v1], update-email[:v1],
|
||||
web-authn[:v1].
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], par[:v1], preview,
|
||||
recovery-codes[:v1], scripts[:v1], step-up-authentication[:v1],
|
||||
token-exchange[:v1], transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-user-profile, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site, par,
|
||||
preview, recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -42,10 +42,8 @@ public class UserResource {
|
|||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Map<String, List<String>> getUnmanagedAttributes() {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
|
||||
if (provider.isEnabled(realm)) {
|
||||
UserProfile profile = provider.create(USER_API, user);
|
||||
Map<String, List<String>> managedAttributes = profile.getAttributes().getReadable();
|
||||
Map<String, List<String>> attributes = new HashMap<>(user.getAttributes());
|
||||
|
@ -67,7 +65,4 @@ public class UserResource {
|
|||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ public final class DefaultUserProfile implements UserProfile {
|
|||
private final Function<Attributes, UserModel> userSupplier;
|
||||
private final Attributes attributes;
|
||||
private final KeycloakSession session;
|
||||
private final boolean isUserProfileEnabled;
|
||||
private boolean validated;
|
||||
private UserModel user;
|
||||
|
||||
|
@ -67,8 +66,6 @@ public final class DefaultUserProfile implements UserProfile {
|
|||
this.attributes = attributes;
|
||||
this.user = user;
|
||||
this.session = session;
|
||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
isUserProfileEnabled = provider.isEnabled(session.getContext().getRealm());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -226,7 +223,7 @@ public final class DefaultUserProfile implements UserProfile {
|
|||
continue;
|
||||
}
|
||||
|
||||
boolean isUnmanagedAttribute = isUserProfileEnabled && metadata.getAttribute(name).isEmpty();
|
||||
boolean isUnmanagedAttribute = metadata.getAttribute(name).isEmpty();
|
||||
String value = isUnmanagedAttribute ? null : values.stream().findFirst().orElse(null);
|
||||
|
||||
if (UserModel.USERNAME.equals(name)) {
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.userprofile;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
|
@ -88,12 +87,4 @@ public interface UserProfileProvider extends Provider {
|
|||
* @see #getConfiguration()
|
||||
*/
|
||||
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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -175,7 +176,12 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
|
|||
} else if (variable.startsWith("CLAIM.")) {
|
||||
String name = variable.substring("CLAIM.".length());
|
||||
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()));
|
||||
} else {
|
||||
m.appendReplacement(sb, m.group(1));
|
||||
|
|
|
@ -385,12 +385,6 @@ public class RealmAdminResource {
|
|||
|
||||
if (auth.users().canView()) {
|
||||
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()) {
|
||||
|
|
|
@ -41,7 +41,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileModel;
|
||||
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.representations.userprofile.config.UPGroup;
|
||||
import org.keycloak.userprofile.validator.AttributeRequiredByMetadataValidator;
|
||||
import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
||||
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.validate.AbstractSimpleValidator;
|
||||
|
@ -68,7 +66,6 @@ import org.keycloak.validate.ValidatorConfig;
|
|||
public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
||||
|
||||
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_UP_CONFIG_COMPONENT_KEY = "kc.parsed.up.config";
|
||||
|
||||
|
@ -94,7 +91,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
|||
}
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final boolean isDeclarativeConfigurationEnabled;
|
||||
private final String providerId;
|
||||
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry;
|
||||
protected final UPConfig parsedDefaultRawConfig;
|
||||
|
@ -102,25 +98,19 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
|||
public DeclarativeUserProfileProvider(KeycloakSession session, DeclarativeUserProfileProviderFactory factory) {
|
||||
this.session = session;
|
||||
this.providerId = factory.getId();
|
||||
this.isDeclarativeConfigurationEnabled = factory.isDeclarativeConfigurationEnabled();
|
||||
this.contextualMetadataRegistry = factory.getContextualMetadataRegistry();
|
||||
this.parsedDefaultRawConfig = factory.getParsedDefaultRawConfig();
|
||||
}
|
||||
|
||||
protected Attributes createAttributes(UserProfileContext context, Map<String, ?> attributes,
|
||||
UserModel user, UserProfileMetadata metadata) {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
|
||||
if (isEnabled(realm)) {
|
||||
if (user != null && user.getServiceAccountClientLink() != null) {
|
||||
return new LegacyAttributes(context, attributes, user, metadata, session);
|
||||
}
|
||||
return new DefaultAttributes(context, attributes, user, metadata, session);
|
||||
}
|
||||
|
||||
return new LegacyAttributes(context, attributes, user, metadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserProfile create(UserProfileContext context, UserModel user) {
|
||||
return createUserProfile(context, user.getAttributes(), user);
|
||||
|
@ -187,16 +177,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
|||
UserProfileMetadata decoratedMetadata = metadata.clone();
|
||||
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);
|
||||
|
||||
if (component == null) {
|
||||
|
@ -218,12 +198,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
|||
|
||||
@Override
|
||||
public UPConfig getConfiguration() {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
|
||||
if (!isEnabled(realm)) {
|
||||
return parsedDefaultRawConfig.clone();
|
||||
}
|
||||
|
||||
Optional<ComponentModel> component = getComponentModel();
|
||||
|
||||
if (component.isPresent()) {
|
||||
|
@ -522,11 +496,6 @@ public class DeclarativeUserProfileProvider implements UserProfileProvider {
|
|||
model.getConfig().remove(UP_COMPONENT_CONFIG_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(RealmModel realm) {
|
||||
return isDeclarativeConfigurationEnabled && realm.getAttribute(REALM_USER_PROFILE_ENABLED, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 adminReadOnlyAttributesPattern = getRegexPatternString(DEFAULT_ADMIN_READ_ONLY_ATTRIBUTES);
|
||||
|
||||
private boolean isDeclarativeConfigurationEnabled;
|
||||
|
||||
private UPConfig parsedDefaultRawConfig;
|
||||
private final Map<UserProfileContext, UserProfileMetadata> contextualMetadataRegistry = new HashMap<>();
|
||||
|
||||
|
@ -198,7 +196,6 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
|||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
isDeclarativeConfigurationEnabled = Profile.isFeatureEnabled(Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
parsedDefaultRawConfig = UPConfigUtils.parseDefaultConfig();
|
||||
|
||||
// make sure registry is clear in case of re-deploy
|
||||
|
@ -313,14 +310,10 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
|||
* @return the metadata
|
||||
*/
|
||||
protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata) {
|
||||
if (isDeclarativeConfigurationEnabled) {
|
||||
// default metadata for each context is based on the default realm configuration
|
||||
return new DeclarativeUserProfileProvider(null, this).decorateUserProfileForCache(metadata, parsedDefaultRawConfig);
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private AttributeValidatorMetadata createReadOnlyAttributeUnchangedValidator(Pattern pattern) {
|
||||
return new AttributeValidatorMetadata(ReadOnlyAttributeUnchangedValidator.ID,
|
||||
ValidatorConfig.builder().config(ReadOnlyAttributeUnchangedValidator.CFG_PATTERN, pattern)
|
||||
|
@ -461,10 +454,6 @@ public class DeclarativeUserProfileProviderFactory implements UserProfileProvide
|
|||
|
||||
// GETTER METHODS FOR INTERNAL FIELDS
|
||||
|
||||
protected boolean isDeclarativeConfigurationEnabled() {
|
||||
return isDeclarativeConfigurationEnabled;
|
||||
}
|
||||
|
||||
protected UPConfig getParsedDefaultRawConfig() {
|
||||
return parsedDefaultRawConfig;
|
||||
}
|
||||
|
|
|
@ -524,7 +524,7 @@ so please make sure you rebuild all `testsuite/integration-arquillian` child mod
|
|||
|
||||
## 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.
|
||||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.representations.account.UserRepresentation;
|
||||
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.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.userprofile.UserProfileConstants;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
@ -47,6 +55,46 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
|||
|
||||
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
|
||||
public void testUpdateProfileCannotUpdateReadOnlyAttributes() throws IOException {
|
||||
// Denied by default
|
||||
|
@ -85,9 +133,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
|||
testAccountUpdateAttributeExpectFailure("saml.persistent.name.id.for._foo_");
|
||||
testAccountUpdateAttributeExpectSuccess("saml.persistent.name.idafor.foo");
|
||||
|
||||
// TODO: Uncomment similarly like above
|
||||
// Special characters inside should be quoted
|
||||
testAccountUpdateAttributeExpectFailure("deniedsome/thing");
|
||||
testAccountUpdateAttributeExpectFailure("deniedsome*thing");
|
||||
//testAccountUpdateAttributeExpectFailure("deniedsome/thing");
|
||||
//testAccountUpdateAttributeExpectFailure("deniedsome*thing");
|
||||
testAccountUpdateAttributeExpectSuccess("deniedsomeithing");
|
||||
|
||||
// Denied only for admin, but allowed for normal user
|
||||
|
@ -135,9 +184,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
|||
user.singleAttribute(attrName, "foo-updated");
|
||||
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 = updateAndGet(user);
|
||||
updateError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||
user = get();
|
||||
assertTrue(user.getAttributes().containsKey(attrName));
|
||||
|
||||
// Revert with admin REST
|
||||
|
@ -178,6 +228,10 @@ public class AccountRestServiceReadOnlyAttributesTest extends AbstractRestServic
|
|||
private UserRepresentation updateAndGet(UserRepresentation user) throws IOException {
|
||||
int status = SimpleHttp.doPost(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).json(user).asStatus();
|
||||
assertEquals(204, status);
|
||||
return get();
|
||||
}
|
||||
|
||||
private UserRepresentation get() throws IOException {
|
||||
return SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
package org.keycloak.testsuite.account;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
|
@ -66,11 +68,11 @@ import org.keycloak.testsuite.AssertEvents;
|
|||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.TokenUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.validate.validators.EmailValidator;
|
||||
|
||||
import jakarta.ws.rs.ClientErrorException;
|
||||
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.assertNull;
|
||||
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>
|
||||
|
@ -100,6 +103,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void before() {
|
||||
super.before();
|
||||
setUserProfileConfiguration(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditUsernameAllowed() throws IOException {
|
||||
UserRepresentation user = getUser();
|
||||
|
@ -115,14 +125,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
realmRep.setEditUsernameAllowed(true);
|
||||
realm.update(realmRep);
|
||||
user = getUser();
|
||||
if (isDeclarativeUserProfile()) {
|
||||
|
||||
assertNotNull(user.getUserProfileMetadata());
|
||||
// can write both username and email
|
||||
assertUserProfileAttributeMetadata(user, "username", "${username}", true, false);
|
||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||
assertUserProfileAttributeMetadata(user, "firstName", "${firstName}", true, false);
|
||||
assertUserProfileAttributeMetadata(user, "lastName", "${lastName}", true, false);
|
||||
}
|
||||
|
||||
user.setUsername("changed-username");
|
||||
user.setEmail("changed-email@keycloak.org");
|
||||
user = updateAndGet(user);
|
||||
|
@ -133,12 +143,12 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
realmRep.setEditUsernameAllowed(false);
|
||||
realm.update(realmRep);
|
||||
user = getUser();
|
||||
if (isDeclarativeUserProfile()) {
|
||||
|
||||
assertNotNull(user.getUserProfileMetadata());
|
||||
// username is readonly but email is writable
|
||||
assertUserProfileAttributeMetadata(user, "username", "${username}", true, true);
|
||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||
}
|
||||
|
||||
user.setUsername("should-not-change");
|
||||
user.setEmail("changed-email@keycloak.org");
|
||||
updateError(user, 400, Messages.READ_ONLY_USERNAME);
|
||||
|
@ -147,13 +157,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
realmRep.setEditUsernameAllowed(true);
|
||||
realm.update(realmRep);
|
||||
user = getUser();
|
||||
if (isDeclarativeUserProfile()) {
|
||||
|
||||
assertNotNull(user.getUserProfileMetadata());
|
||||
// username is read-only, not required, and is the same as email
|
||||
// but email is writable
|
||||
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, false);
|
||||
}
|
||||
|
||||
user.setUsername("should-be-the-email");
|
||||
user.setEmail("user@keycloak.org");
|
||||
user = updateAndGet(user);
|
||||
|
@ -164,12 +174,12 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
realmRep.setEditUsernameAllowed(false);
|
||||
realm.update(realmRep);
|
||||
user = getUser();
|
||||
if (isDeclarativeUserProfile()) {
|
||||
|
||||
assertNotNull(user.getUserProfileMetadata());
|
||||
// username is read-only and is the same as email, but email is read-only
|
||||
assertUserProfileAttributeMetadata(user, "username", "${username}", false, true);
|
||||
assertUserProfileAttributeMetadata(user, "email", "${email}", true, true);
|
||||
}
|
||||
|
||||
user.setUsername("should-be-the-email");
|
||||
user.setEmail("should-not-change@keycloak.org");
|
||||
user = updateAndGet(user);
|
||||
|
@ -210,45 +220,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
assertNull(user.getUserProfileMetadata());
|
||||
}
|
||||
|
||||
@Test
|
||||
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) {
|
||||
protected static UserProfileAttributeMetadata getUserProfileAttributeMetadata(UserRepresentation user, String attName) {
|
||||
if(user.getUserProfileMetadata() == null)
|
||||
return null;
|
||||
for(UserProfileAttributeMetadata uam : user.getUserProfileMetadata().getAttributes()) {
|
||||
|
@ -259,14 +231,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
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);
|
||||
if (isDeclarativeUserProfile()) {
|
||||
|
||||
assertNotNull(uam);
|
||||
assertEquals("Unexpected display name for attribute " + uam.getName(), displayName, uam.getDisplayName());
|
||||
assertEquals("Unexpected required flag for attribute " + uam.getName(), required, uam.isRequired());
|
||||
assertEquals("Unexpected readonly flag for attribute " + uam.getName(), readOnly, uam.isReadOnly());
|
||||
}
|
||||
|
||||
return uam;
|
||||
}
|
||||
|
||||
|
@ -284,6 +256,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
|
||||
@Test
|
||||
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();
|
||||
String originalUsername = user.getUsername();
|
||||
String originalFirstName = user.getFirstName();
|
||||
|
@ -374,6 +353,13 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
|
||||
@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 + "}"
|
||||
+ "]}");
|
||||
|
||||
UserRepresentation user = getUser();
|
||||
String originalUsername = user.getUsername();
|
||||
String originalFirstName = user.getFirstName();
|
||||
|
@ -426,6 +412,14 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
|
||||
@Test
|
||||
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();
|
||||
String originalUsername = user.getUsername();
|
||||
String originalFirstName = user.getFirstName();
|
||||
|
@ -602,6 +596,10 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
|
||||
protected UserRepresentation getUser(boolean fetchMetadata) throws IOException {
|
||||
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());
|
||||
|
||||
try {
|
||||
|
@ -1719,7 +1717,7 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean isDeclarativeUserProfile() {
|
||||
return false;
|
||||
protected void setUserProfileConfiguration(String configuration) {
|
||||
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.assertNotNull;
|
||||
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_ADMIN_EDITABLE;
|
||||
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.Test;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
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.account.UserRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
|
||||
/**
|
||||
* Test account rest service with custom user profile configurations
|
||||
*
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTest {
|
||||
public class AccountRestServiceWithUserProfileTest extends AbstractRestServiceTest {
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void before() {
|
||||
super.before();
|
||||
enableDynamicUserProfile();
|
||||
setUserProfileConfiguration(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeclarativeUserProfile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final static String UP_CONFIG_FOR_METADATA = "{\"attributes\": ["
|
||||
+ "{\"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\"}},"
|
||||
|
@ -101,9 +95,7 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
|||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testEditUsernameAllowed() throws IOException {
|
||||
super.testEditUsernameAllowed();
|
||||
setUserProfileConfiguration(UP_CONFIG_FOR_METADATA);
|
||||
|
||||
UserRepresentation user = getUser();
|
||||
|
@ -221,7 +213,6 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
|||
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testEditUsernameDisallowed() throws IOException {
|
||||
|
||||
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
|
||||
public void testManageUserLocaleAttribute() throws IOException {
|
||||
RealmRepresentation realmRep = testRealm().toRepresentation();
|
||||
|
@ -417,12 +374,24 @@ public class AccountRestServiceWithUserProfileTest extends AccountRestServiceTes
|
|||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||
}
|
||||
|
||||
protected void enableDynamicUserProfile() {
|
||||
RealmRepresentation testRealm = testRealm().toRepresentation();
|
||||
protected UserRepresentation getUser() throws IOException {
|
||||
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;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.models.AuthenticationExecutionModel.Requirement;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
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.Users;
|
||||
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.LoginTotpPage;
|
||||
import org.keycloak.testsuite.pages.PageUtils;
|
||||
|
@ -86,6 +89,12 @@ public class CustomAuthFlowOTPTest extends AbstractCustomAccountManagementTest {
|
|||
testLoginOneTimeCodePage.setAuthRealm(testRealmPage);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void configureUserProfile() {
|
||||
UserProfileResource userProfileRes = testRealmResource().users().userProfile();
|
||||
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||
}
|
||||
|
||||
private void configureRequiredActions() {
|
||||
//set configure TOTP as required action to test user
|
||||
List<String> requiredActions = new ArrayList<>();
|
||||
|
|
|
@ -16,13 +16,25 @@
|
|||
*/
|
||||
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.keycloak.userprofile.UserProfileConstants.ROLE_USER;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.UserModel;
|
||||
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 {
|
||||
|
||||
|
@ -41,6 +53,46 @@ public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActio
|
|||
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
|
||||
protected void changeEmailUsingAIA(String newEmail) throws Exception {
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedActionTest {
|
||||
|
@ -52,10 +54,6 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
|||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
protected boolean isDynamicForm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
}
|
||||
|
@ -212,10 +210,7 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
|||
Assert.assertEquals("New last", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||
|
||||
if(isDynamicForm())
|
||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||
else
|
||||
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||
|
||||
events.assertEmpty();
|
||||
}
|
||||
|
@ -237,10 +232,7 @@ public class AppInitiatedActionUpdateProfileTest extends AbstractAppInitiatedAct
|
|||
Assert.assertEquals("", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||
|
||||
if(isDynamicForm())
|
||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
||||
else
|
||||
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
|
||||
|
||||
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.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.events.Details;
|
||||
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.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
|
@ -67,10 +69,6 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
protected boolean isDynamicForm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
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("New last", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||
|
||||
if(isDynamicForm())
|
||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||
else
|
||||
Assert.assertEquals("Please specify first name.", updateProfilePage.getInputErrors().getFirstNameError());
|
||||
|
||||
events.assertEmpty();
|
||||
}
|
||||
|
@ -203,10 +197,7 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
Assert.assertEquals("", updateProfilePage.getLastName());
|
||||
Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
|
||||
|
||||
if(isDynamicForm())
|
||||
Assert.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
|
||||
else
|
||||
Assert.assertEquals("Please specify last name.", updateProfilePage.getInputErrors().getLastNameError());
|
||||
|
||||
events.assertEmpty();
|
||||
}
|
||||
|
@ -350,6 +341,12 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
|
||||
@Test
|
||||
public void updateProfileWithoutRemoveCustomAttributes() {
|
||||
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
|
||||
UPConfig upConfig = upResource.getConfiguration();
|
||||
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
|
||||
upResource.update(upConfig);
|
||||
|
||||
try {
|
||||
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
|
||||
UserResource user = adminClient.realm("test").users().get(userRep.getId());
|
||||
|
||||
|
@ -381,6 +378,10 @@ public class RequiredActionUpdateProfileTest extends AbstractTestRealmKeycloakTe
|
|||
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 org.apache.commons.lang3.StringUtils;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
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.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
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.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
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.KeycloakModelUtils;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.openqa.selenium.By;
|
||||
|
||||
/**
|
||||
* Test update-profile required action with custom user profile configurations
|
||||
*
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActionUpdateProfileTest {
|
||||
public class RequiredActionUpdateProfileWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
protected static final String PASSWORD = "password";
|
||||
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_optional;
|
||||
|
||||
@Override
|
||||
protected boolean isDynamicForm() {
|
||||
return true;
|
||||
}
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@Page
|
||||
protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
|
||||
|
||||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
super.configureTestRealm(testRealm);
|
||||
|
||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
||||
|
||||
testRealm.setClientScopes(new ArrayList<>());
|
||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).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
|
||||
public void beforeTest() {
|
||||
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
|
||||
|
@ -619,16 +652,6 @@ public class RequiredActionUpdateProfileWithUserProfileTest extends RequiredActi
|
|||
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) {
|
||||
VerifyProfileTest.setUserProfileConfiguration(testRealm(), configuration);
|
||||
}
|
||||
|
|
|
@ -24,12 +24,14 @@ import org.jboss.shrinkwrap.api.asset.StringAsset;
|
|||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractAuthTest;
|
||||
import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.util.ServerURLs;
|
||||
|
||||
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
|
||||
@Override
|
||||
protected boolean isImportAfterEachMethod() {
|
||||
|
|
|
@ -9,10 +9,8 @@ import static org.hamcrest.Matchers.hasSize;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
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.PERMISSIONS_ALL;
|
||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.enableDynamicUserProfile;
|
||||
import static org.keycloak.testsuite.forms.VerifyProfileTest.setUserProfileConfiguration;
|
||||
|
||||
import org.junit.After;
|
||||
|
@ -21,7 +19,6 @@ import org.junit.Test;
|
|||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
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.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
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>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class DeclarativeUserTest extends AbstractAdminTest {
|
||||
|
||||
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();
|
||||
realmRep.setInternationalizationEnabled(true);
|
||||
realmRep.setSupportedLocales(new HashSet<>(Arrays.asList("en", "de")));
|
||||
enableDynamicUserProfile(realmRep);
|
||||
realm.update(realmRep);
|
||||
setUserProfileConfiguration(realm, "{\"attributes\": ["
|
||||
+ "{\"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.common.Profile;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.CibaConfig;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.credential.OTPCredentialModel;
|
||||
|
@ -302,9 +303,6 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
|||
}
|
||||
}, Resource.REALM, false, true);
|
||||
|
||||
try (RealmAttributeUpdater updater = new RealmAttributeUpdater(adminClient.realm(REALM_NAME))
|
||||
.setAttribute(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString())
|
||||
.update()) {
|
||||
RealmRepresentation realm = clients.get(AdminRoles.QUERY_REALMS).realm(REALM_NAME).toRepresentation();
|
||||
assertGettersEmpty(realm);
|
||||
assertNull(realm.isRegistrationEmailAsUsername());
|
||||
|
@ -312,23 +310,18 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
|||
|
||||
realm = clients.get(AdminRoles.VIEW_USERS).realm(REALM_NAME).toRepresentation();
|
||||
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();
|
||||
assertNotNull(realm.isRegistrationEmailAsUsername());
|
||||
assertNotNull(realm.getAttributes());
|
||||
assertNotNull(realm.getAttributes().get(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED));
|
||||
|
||||
// query users only if granted through fine-grained admin
|
||||
realm = clients.get(AdminRoles.QUERY_USERS).realm(REALM_NAME).toRepresentation();
|
||||
assertNull(realm.isRegistrationEmailAsUsername());
|
||||
assertNull(realm.getAttributes());
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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() {
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
|
||||
package org.keycloak.testsuite.admin;
|
||||
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.drone.api.annotation.Drone;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Assert;
|
||||
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.RealmResource;
|
||||
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.UsersResource;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.Base64;
|
||||
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.RequiredActionProviderRepresentation;
|
||||
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.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.UserStorageProvider;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||
import org.keycloak.testsuite.federation.UserMapStorageFactory;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.page.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
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.RoleBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.userprofile.validator.UsernameProhibitedCharactersValidator;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
@ -180,8 +186,18 @@ public class UserTest extends AbstractAdminTest {
|
|||
}
|
||||
|
||||
@Before
|
||||
public void beforeUserTest() {
|
||||
public void beforeUserTest() throws IOException {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1241,7 +1257,14 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
@Test
|
||||
public void wildcardSearch() {
|
||||
Assume.assumeFalse("Default validators do not allow special chars", isDeclarativeUserProfile());
|
||||
UserProfileResource upResource = realm.users().userProfile();
|
||||
UPConfig upConfig = upResource.getConfiguration();
|
||||
Map<String, Object> prohibitedCharsOrigCfg = upConfig.getAttribute(UserModel.USERNAME).getValidations().get(UsernameProhibitedCharactersValidator.ID);
|
||||
upConfig.getAttribute(UserModel.USERNAME).getValidations().remove(UsernameProhibitedCharactersValidator.ID);
|
||||
upResource.update(upConfig);
|
||||
assertAdminEvents.clear();
|
||||
|
||||
try {
|
||||
createUser("0user\\\\0", "email0@emal");
|
||||
createUser("1user\\\\", "email1@emal");
|
||||
createUser("2user\\\\%", "email2@emal");
|
||||
|
@ -1251,6 +1274,10 @@ public class UserTest extends AbstractAdminTest {
|
|||
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
|
||||
|
@ -2465,20 +2492,16 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
UserRepresentation update = new UserRepresentation();
|
||||
update.setId(userId);
|
||||
if (isDeclarativeUserProfile()) {
|
||||
// user profile requires sending all attributes otherwise they are removed
|
||||
update.setEmail(email);
|
||||
}
|
||||
|
||||
update.setAttributes(Map.of("phoneNumber", List.of("123")));
|
||||
updateUser(realm.users().get(userId), update);
|
||||
|
||||
UserRepresentation updated = realm.users().get(userId).toRepresentation();
|
||||
assertThat(updated.getUsername(), equalTo(userName));
|
||||
if (isDeclarativeUserProfile()) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -2494,9 +2517,7 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
try {
|
||||
updateUser(user, userRep);
|
||||
if (isDeclarativeUserProfile()) {
|
||||
fail("Should fail because realm does not allow edit username");
|
||||
}
|
||||
} catch (BadRequestException expected) {
|
||||
ErrorRepresentation error = expected.getResponse().readEntity(ErrorRepresentation.class);
|
||||
assertEquals("error-user-attribute-read-only", error.getErrorMessage());
|
||||
|
@ -2869,6 +2890,12 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
@Test
|
||||
public void defaultMaxResults() {
|
||||
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();
|
||||
|
||||
for (int i = 0; i < 110; i++) {
|
||||
|
@ -2885,10 +2912,20 @@ public class UserTest extends AbstractAdminTest {
|
|||
|
||||
assertEquals(105, users.search("test", 0, 105).size());
|
||||
assertEquals(111, users.search("test", 0, 1000).size());
|
||||
} finally {
|
||||
upConfig.removeAttribute("aName");
|
||||
upResource.update(upConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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();
|
||||
|
||||
for (int i = 0; i < 110; i++) {
|
||||
|
@ -2900,6 +2937,10 @@ public class UserTest extends AbstractAdminTest {
|
|||
for (UserRepresentation user : result) {
|
||||
assertThat(user.getAttributes(), Matchers.nullValue());
|
||||
}
|
||||
} finally {
|
||||
upConfig.removeAttribute("aName");
|
||||
upResource.update(upConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -3461,7 +3502,75 @@ public class UserTest extends AbstractAdminTest {
|
|||
);
|
||||
}
|
||||
|
||||
protected boolean isDeclarativeUserProfile() {
|
||||
return false;
|
||||
@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());
|
||||
}
|
||||
|
||||
@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.RealmResource;
|
||||
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.UsersResource;
|
||||
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.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.updaters.Creator;
|
||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
|
@ -1281,6 +1284,10 @@ public class GroupTest extends AbstractGroupTest {
|
|||
String groupName = "brief-grouptest-group";
|
||||
String userName = "brief-grouptest-user";
|
||||
|
||||
// enable user profile unmanaged attributes
|
||||
UserProfileResource upResource = realm.users().userProfile();
|
||||
UPConfig cfg = VerifyProfileTest.enableUnmanagedAttributes(upResource);
|
||||
|
||||
GroupsResource groups = realm.groups();
|
||||
try (Response response = groups.add(GroupBuilder.create().name(groupName).build())) {
|
||||
String groupId = ApiUtil.getCreatedId(response);
|
||||
|
@ -1308,6 +1315,9 @@ public class GroupTest extends AbstractGroupTest {
|
|||
|
||||
group.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.assertNotNull;
|
||||
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 java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserProfileAttributeGroupMetadata;
|
||||
import org.keycloak.representations.idm.UserProfileMetadata;
|
||||
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.UPConfig;
|
||||
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>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class UserProfileAdminTest extends AbstractAdminTest {
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
||||
import org.keycloak.testsuite.pages.IdpLinkEmailPage;
|
||||
|
@ -179,8 +180,13 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
|||
|
||||
@Before
|
||||
public void beforeBrokerTest() {
|
||||
importRealm(bc.createConsumerRealm());
|
||||
importRealm(bc.createProviderRealm());
|
||||
RealmRepresentation consumerRealm = bc.createConsumerRealm();
|
||||
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
|
||||
|
|
|
@ -65,17 +65,6 @@ public abstract class AbstractFirstBrokerLoginTest extends AbstractInitializedBa
|
|||
@Rule
|
||||
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
|
||||
|
|
|
@ -114,8 +114,8 @@ public class KcOidcFirstBrokerLoginDetectExistingUserTest extends AbstractInitia
|
|||
public void loginWhenUserExistsOnConsumer() {
|
||||
updateExecutions(AbstractBrokerTest::disableUpdateProfileOnFirstLogin);
|
||||
|
||||
final String firstname = "Firstname(loginWhenUserExistsOnConsumer)";
|
||||
final String lastname = "Lastname(loginWhenUserExistsOnConsumer)";
|
||||
final String firstname = "Firstname_loginWhenUserExistsOnConsumer";
|
||||
final String lastname = "Lastname_loginWhenUserExistsOnConsumer";
|
||||
final String username = "firstandlastname";
|
||||
final String email = "firstnamelastname@example.org";
|
||||
createUser(bc.providerRealmName(), username, BrokerTestConstants.USER_PASSWORD, firstname, lastname, email);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Test;
|
||||
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.UserRepresentation;
|
||||
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.RegisterPage;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||
import org.openqa.selenium.By;
|
||||
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.assertTrue;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
|
||||
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>
|
||||
|
@ -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"));
|
||||
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
|
||||
protected String getMapperTemplate() {
|
||||
return "kc-oidc-idp-[%s]";
|
||||
return "kc-oidc-idp-%s";
|
||||
}
|
||||
|
||||
@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.setIdentityProviderMapper(UsernameTemplateMapper.PROVIDER_ID);
|
||||
userTemplateImporterMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||
.put(UsernameTemplateMapper.TEMPLATE, "${ALIAS}:${CLAIM.sub}")
|
||||
.put(UsernameTemplateMapper.TEMPLATE, "${ALIAS}_${CLAIM.sub}")
|
||||
.build());
|
||||
|
||||
IdentityProviderMapperRepresentation jwtClaimsAttrMapper = new IdentityProviderMapperRepresentation();
|
||||
|
@ -71,22 +71,6 @@ public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest {
|
|||
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
|
||||
*/
|
||||
|
@ -99,5 +83,8 @@ public class UsernameTemplateMapperTest extends AbstractBaseBrokerTest {
|
|||
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));
|
||||
|
||||
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();
|
||||
testRealm.setRealm("test_" + RandomStringUtils.randomAlphabetic(5));
|
||||
testRealm.setEnabled(true);
|
||||
testRealm.setEditUsernameAllowed(true);
|
||||
return testRealm;
|
||||
}
|
||||
|
||||
|
|
|
@ -265,13 +265,7 @@ public class ExportImportTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testExportUserProfileConfig() throws IOException {
|
||||
//Enable user profile on 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
|
||||
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>
|
||||
*/
|
||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
||||
|
||||
private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-standalone-connection.properties";
|
||||
|
@ -188,11 +187,6 @@ public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
|||
|
||||
@Test
|
||||
public void testUserProfile() throws Exception {
|
||||
RealmRepresentation realm = testRealmResource().toRepresentation();
|
||||
VerifyProfileTest.enableDynamicUserProfile(realm);
|
||||
testRealmResource().update(realm);
|
||||
|
||||
try {
|
||||
assertSuccessfulSpnegoLogin("hnelson", "hnelson", "secret");
|
||||
|
||||
// User-profile data should be present (including KERBEROS_PRINCIPAL attribute)
|
||||
|
@ -218,9 +212,6 @@ public class KerberosStandaloneTest extends AbstractKerberosSingleRealmTest {
|
|||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
@ -33,6 +32,7 @@ import org.junit.FixMethodOrder;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.federation.kerberos.KerberosFederationProvider;
|
||||
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.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||
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.LDAPTestUtils;
|
||||
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.assertFalse;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
|
@ -115,11 +114,73 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
|||
public void testUpdateProfile() throws IOException {
|
||||
UserRepresentation user = getProfile();
|
||||
|
||||
List<String> origLdapId = new ArrayList<>(user.getAttributes().get(LDAPConstants.LDAP_ID));
|
||||
List<String> origLdapEntryDn = new ArrayList<>(user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN));
|
||||
Assert.assertEquals(1, origLdapId.size());
|
||||
Assert.assertEquals(1, origLdapEntryDn.size());
|
||||
assertThat(user.getAttributes().keySet(), not(contains(KerberosFederationProvider.KERBEROS_PRINCIPAL)));
|
||||
// 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 (Adding attribute, which was not yet present). Request does not fail, but attribute is not updated
|
||||
user.setFirstName("JohnUpdated");
|
||||
user.setLastName("DoeUpdated");
|
||||
user.singleAttribute(KerberosFederationProvider.KERBEROS_PRINCIPAL, "foo");
|
||||
updateProfileExpectSuccess(user);
|
||||
user = getProfile();
|
||||
Assert.assertEquals("JohnUpdated", user.getFirstName());
|
||||
Assert.assertEquals("DoeUpdated", user.getLastName());
|
||||
Assert.assertNull(user.getAttributes());
|
||||
|
||||
// Trying to update LDAP_ID should fail (Updating existing attribute, which is present on the user even if not visible to the user)
|
||||
user.singleAttribute(LDAPConstants.LDAP_ID, "123");
|
||||
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());
|
||||
|
||||
// Trying to update LDAP_ENTRY_DN should fail
|
||||
user.singleAttribute(LDAPConstants.LDAP_ENTRY_DN, "ou=foo,dc=bar");
|
||||
updateProfileExpectError(user, 400, Messages.UPDATE_READ_ONLY_ATTRIBUTES_REJECTED);
|
||||
|
||||
// Update firstName and lastName should be fine
|
||||
user.getAttributes().remove(LDAPConstants.LDAP_ENTRY_DN);
|
||||
updateProfileExpectSuccess(user);
|
||||
|
||||
user = getProfile();
|
||||
assertEquals("JohnUpdated", user.getFirstName());
|
||||
assertEquals("DoeUpdated", user.getLastName());
|
||||
Assert.assertNull(user.getAttributes());
|
||||
|
||||
// Revert
|
||||
user.setFirstName("John");
|
||||
user.setLastName("Doe");
|
||||
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");
|
||||
|
@ -136,40 +197,31 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
|||
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");
|
||||
user.singleAttribute(LDAPConstants.LDAP_ID, "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);
|
||||
// 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();
|
||||
assertFalse(user.getAttributes().get(LDAPConstants.LDAP_ID).isEmpty());
|
||||
|
||||
// Trying to update LDAP_ENTRY_DN should fail
|
||||
user.getAttributes().put(LDAPConstants.LDAP_ID, origLdapId);
|
||||
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);
|
||||
|
||||
// Update firstName and lastName should be fine
|
||||
user.getAttributes().put(LDAPConstants.LDAP_ENTRY_DN, origLdapEntryDn);
|
||||
updateProfileExpectSuccess(user);
|
||||
Assert.assertNull(user.getAttributes());
|
||||
|
||||
user = getProfile();
|
||||
assertEquals("JohnUpdated", user.getFirstName());
|
||||
assertEquals("DoeUpdated", user.getLastName());
|
||||
assertEquals(origLdapId, user.getAttributes().get(LDAPConstants.LDAP_ID));
|
||||
assertEquals(origLdapEntryDn, user.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN));
|
||||
Assert.assertNull(user.getAttributes());
|
||||
|
||||
// Revert
|
||||
user.setFirstName("John");
|
||||
user.setLastName("Doe");
|
||||
updateProfileExpectSuccess(user);
|
||||
} finally {
|
||||
userProfileRes.update(origConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -231,7 +283,8 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
|||
assertEquals("john-alias@email.org", usernew.getEmail());
|
||||
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
|
||||
usernew.setEmail("john@email.org");
|
||||
|
@ -240,13 +293,22 @@ public class LDAPAccountRestApiTest extends AbstractLDAPTest {
|
|||
org.keycloak.representations.idm.UserRepresentation userRep = testRealm().users()
|
||||
.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);
|
||||
|
||||
testRealm().users().get(userRep.getId()).update(userRep);
|
||||
usernew = SimpleHttp.doGet(getAccountUrl(null), httpClient).auth(tokenUtil.getToken()).asJson(UserRepresentation.class);
|
||||
|
||||
assertTrue(usernew.getAttributes().containsKey(LDAPConstants.LDAP_ID));
|
||||
assertTrue(usernew.getAttributes().containsKey(LDAPConstants.LDAP_ENTRY_DN));
|
||||
// Metadata attributes still not present in account REST
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
|
@ -36,7 +38,11 @@ import org.keycloak.models.LDAPConstants;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
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.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.ldap.idm.model.LDAPObject;
|
||||
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.junit.Assert.assertEquals;
|
||||
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");
|
||||
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john, "Password1");
|
||||
});
|
||||
|
||||
UPConfig cfg = testRealm().users().userProfile().getConfiguration();
|
||||
cfg.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ENABLED);
|
||||
testRealm().users().userProfile().update(cfg);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -245,4 +259,75 @@ public class LDAPAdminRestApiTest extends AbstractLDAPTest {
|
|||
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.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
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.UserAttributeLDAPStorageMapper;
|
||||
import org.keycloak.testsuite.client.KeycloakTestingClient;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.util.LDAPRule;
|
||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||
|
||||
|
@ -85,6 +87,10 @@ public class LDAPBinaryAttributesTest extends AbstractLDAPTest {
|
|||
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.runners.MethodSorters;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.LDAPConstants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserProfileAttributeMetadata;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
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.ldap.idm.model.LDAPObject;
|
||||
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.util.LDAPRule;
|
||||
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>
|
||||
*/
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class LDAPUserProfileTest extends AbstractLDAPTest {
|
||||
|
||||
@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");
|
||||
LDAPTestUtils.updateLDAPPassword(ctx.getLdapProvider(), john2, "Password1");
|
||||
});
|
||||
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
VerifyProfileTest.enableDynamicUserProfile(realm);
|
||||
testRealm().update(realm);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -26,11 +26,13 @@ import jakarta.ws.rs.core.Response;
|
|||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.keycloak.admin.client.resource.ComponentResource;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
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.LDAPTestAsserts;
|
||||
import org.keycloak.testsuite.federation.ldap.LDAPTestContext;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.util.LDAPTestUtils;
|
||||
|
||||
|
||||
|
@ -66,6 +69,12 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati
|
|||
return false;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void enableUserProfileUnmanagedAttributes() {
|
||||
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void assertFederatedUserLink(UserRepresentation user) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.junit.Ignore;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
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.UserMapStorageFactory;
|
||||
import org.keycloak.testsuite.federation.UserPropertyFileStorageFactory;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.RegisterPage;
|
||||
|
@ -160,6 +162,9 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
propProviderRWId = addComponent(newPropProviderRW());
|
||||
|
||||
createAppClientInRealm(testRealmResource().toRepresentation().getRealm());
|
||||
|
||||
UserProfileResource userProfileRes = testRealmResource().users().userProfile();
|
||||
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.authentication.authenticators.access.AllowAccessAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.access.DenyAccessAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.PasswordFormFactory;
|
||||
|
@ -63,6 +65,12 @@ public class ConditionalUserAttributeAuthenticatorTest extends AbstractTestRealm
|
|||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {}
|
||||
|
||||
@Before
|
||||
public void configureUserProfile() {
|
||||
UserProfileResource userProfileRes = testRealm().users().userProfile();
|
||||
VerifyProfileTest.enableUnmanagedAttributes(userProfileRes);
|
||||
}
|
||||
|
||||
private void createUsers() {
|
||||
GroupRepresentation subGroup = GroupBuilder.create().name(SUBGROUP).build();
|
||||
testRealm().groups().add(subGroup);
|
||||
|
|
|
@ -56,6 +56,8 @@ import org.keycloak.testsuite.util.AccountHelper;
|
|||
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,26 +34,34 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
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.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.RegisterPage;
|
||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||
import org.keycloak.testsuite.util.GreenMailRule;
|
||||
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
/**
|
||||
* Test user registration with customized user-profile configurations
|
||||
*
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class RegisterWithUserProfileTest extends RegisterTest {
|
||||
public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
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\": {}},"
|
||||
+ "{\"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
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
||||
super.configureTestRealm(testRealm);
|
||||
|
||||
VerifyProfileTest.enableDynamicUserProfile(testRealm);
|
||||
|
||||
testRealm.setClientScopes(new ArrayList<>());
|
||||
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());
|
||||
|
@ -683,7 +704,11 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected void setUserProfileConfiguration(String configuration) {
|
||||
private void setUserProfileConfiguration(String 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.assertFalse;
|
||||
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_USER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
@ -41,6 +39,7 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -79,7 +78,6 @@ import org.openqa.selenium.By;
|
|||
/**
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
public static final String SCOPE_DEPARTMENT = "department";
|
||||
|
@ -117,9 +115,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Override
|
||||
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 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();
|
||||
|
@ -404,15 +399,8 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testIgnoreCustomAttributeWhenUserProfileIsDisabled() {
|
||||
try {
|
||||
disableDynamicUserProfile(testRealm());
|
||||
testingClient.server(TEST_REALM_NAME).run(setEmptyFirstNameAndCustomAttribute());
|
||||
testDefaultProfile();
|
||||
} finally {
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
enableDynamicUserProfile(realm);
|
||||
testRealm().update(realm);
|
||||
}
|
||||
}
|
||||
|
||||
private static RunOnServer setEmptyFirstNameAndCustomAttribute() {
|
||||
|
@ -1166,7 +1154,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConfigurationRemainsAfterReset() throws IOException {
|
||||
public void testConfigurationPersisted() throws IOException {
|
||||
String customConfig = "{\"attributes\": ["
|
||||
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL + "},"
|
||||
|
@ -1175,13 +1163,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
UPConfig persistedConfig = setUserProfileConfiguration(customConfig);
|
||||
|
||||
RealmResource realmRes = testRealm();
|
||||
disableDynamicUserProfile(realmRes, false);
|
||||
RealmRepresentation realm = realmRes.toRepresentation();
|
||||
enableDynamicUserProfile(realm);
|
||||
testRealm().update(realm);
|
||||
|
||||
JsonTestUtils.assertJsonEquals(JsonSerialization.writeValueAsString(persistedConfig), realmRes.users().userProfile().getConfiguration());
|
||||
JsonTestUtils.assertJsonEquals(JsonSerialization.writeValueAsString(persistedConfig), testRealm().users().userProfile().getConfiguration());
|
||||
}
|
||||
|
||||
protected UserRepresentation getUser(String userId) {
|
||||
|
@ -1211,30 +1193,6 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
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) {
|
||||
try {
|
||||
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) {
|
||||
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.assertTrue;
|
||||
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_LINKS;
|
||||
import static org.keycloak.models.AccountRoles.VIEW_GROUPS;
|
||||
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
|
||||
import static org.keycloak.testsuite.Assert.assertNames;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -1204,7 +1204,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
RealmRepresentation rep = realm.toRepresentation();
|
||||
Map<String, String> attributes = rep.getAttributes();
|
||||
String userProfileEnabled = attributes.get(REALM_USER_PROFILE_ENABLED);
|
||||
assertTrue(Boolean.parseBoolean(userProfileEnabled));
|
||||
assertNull(userProfileEnabled);
|
||||
}
|
||||
|
||||
private void testUnmanagedAttributePolicySet(RealmResource realm, UnmanagedAttributePolicy policy) {
|
||||
|
|
|
@ -17,10 +17,8 @@
|
|||
package org.keycloak.testsuite.migration;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.utils.io.IOUtil;
|
||||
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.
|
||||
*/
|
||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigrationTest {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,9 +19,7 @@ package org.keycloak.testsuite.migration;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.migration.Migration;
|
||||
|
||||
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>
|
||||
*/
|
||||
@EnableFeature(Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class MigrationTest extends AbstractMigrationTest {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -139,7 +139,6 @@ public class ImportTest extends AbstractTestRealmKeycloakTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public void importUserProfile() throws Exception {
|
||||
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.ProtocolMappersResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.Profile;
|
||||
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.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.forms.VerifyProfileTest;
|
||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||
import org.keycloak.testsuite.updaters.ProtocolMappersUpdater;
|
||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||
|
@ -120,6 +123,10 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
|||
* @see AccessTokenTest#testAuthorizationNegotiateHeaderIgnored()
|
||||
*/
|
||||
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.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
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.testsuite.AbstractKeycloakTest;
|
||||
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.RealmBuilder;
|
||||
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>
|
||||
*/
|
||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
public class ServiceAccountUserProfileTest extends AbstractKeycloakTest {
|
||||
|
||||
private static String userId;
|
||||
|
@ -116,7 +112,6 @@ public class ServiceAccountUserProfileTest extends AbstractKeycloakTest {
|
|||
realm.user(serviceAccountUser);
|
||||
|
||||
RealmRepresentation realmRep = realm.build();
|
||||
VerifyProfileTest.enableDynamicUserProfile(realmRep);
|
||||
testRealms.add(realmRep);
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public class LogoutTest extends AbstractSamlTest {
|
|||
.targetAttributeSamlResponse()
|
||||
.targetUri(getSamlBrokerUrl(REALM_NAME))
|
||||
.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()
|
||||
|
||||
// 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.assertFalse;
|
||||
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.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
@ -52,12 +60,19 @@ public class CustomRegistrationTemplateTest extends AbstractTestRealmKeycloakTes
|
|||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
private UPConfig upConfig;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.setRegistrationAllowed(true);
|
||||
testRealm.setLoginTheme("address");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
upConfig = updateUserProfileConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistration() {
|
||||
//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);
|
||||
}
|
||||
|
||||
@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) {
|
||||
for (Entry<String, String> attribute : CUSTOM_ATTRIBUTES.entrySet()) {
|
||||
String name = attribute.getKey();
|
||||
|
@ -93,9 +152,6 @@ public class CustomRegistrationTemplateTest extends AbstractTestRealmKeycloakTes
|
|||
}
|
||||
|
||||
private void navigateToRegistrationPage() {
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
realm.setRegistrationAllowed(true);
|
||||
testRealm().update(realm);
|
||||
loginPage.open();
|
||||
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.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.AbstractMap;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -39,7 +42,11 @@ import org.keycloak.models.Constants;
|
|||
import org.keycloak.models.UserModel;
|
||||
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.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||
|
@ -63,26 +70,31 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
|||
@Page
|
||||
protected AppPage appPage;
|
||||
|
||||
private UPConfig upConfig;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
testRealm.setLoginTheme("address");
|
||||
// the custom theme expects email as username and the username field is not rendered at all
|
||||
testRealm.setRegistrationEmailAsUsername(true);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
UserRepresentation user = UserBuilder.create().enabled(true)
|
||||
.username("tom")
|
||||
.email("tom@keycloak.org")
|
||||
.password("password")
|
||||
.firstName("Tom")
|
||||
.lastName("Brady").build();
|
||||
testRealm.getUsers().add(user);
|
||||
}
|
||||
.lastName("Brady")
|
||||
.requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.name())
|
||||
.build();
|
||||
Response resp = testRealm().users().create(user);
|
||||
String userId = ApiUtil.getCreatedId(resp);
|
||||
resp.close();
|
||||
getCleanup().addUserId(userId);
|
||||
|
||||
@Before
|
||||
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);
|
||||
upConfig = updateUserProfileConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,6 +106,37 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
|||
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() {
|
||||
navigateToUpdateProfilePage();
|
||||
updateProfilePage.update(CUSTOM_ATTRIBUTES.entrySet().stream()
|
||||
|
@ -124,4 +167,16 @@ public class CustomUpdateProfileTemplateTest extends AbstractTestRealmKeycloakTe
|
|||
loginPage.login("tom@keycloak.org", "password");
|
||||
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