diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 108388393b..e1ae205af0 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -55,6 +55,49 @@ import java.util.stream.Collectors; * @version $Revision: 1 $ */ public class ModelToRepresentation { + + public static Set REALM_EXCLUDED_ATTRIBUTES = new HashSet<>(); + static { + REALM_EXCLUDED_ATTRIBUTES.add("displayName"); + REALM_EXCLUDED_ATTRIBUTES.add("displayNameHtml"); + REALM_EXCLUDED_ATTRIBUTES.add("defaultSignatureAlgorithm"); + REALM_EXCLUDED_ATTRIBUTES.add("bruteForceProtected"); + REALM_EXCLUDED_ATTRIBUTES.add("permanentLockout"); + REALM_EXCLUDED_ATTRIBUTES.add("maxFailureWaitSeconds"); + REALM_EXCLUDED_ATTRIBUTES.add("waitIncrementSeconds"); + REALM_EXCLUDED_ATTRIBUTES.add("quickLoginCheckMilliSeconds"); + REALM_EXCLUDED_ATTRIBUTES.add("minimumQuickLoginWaitSeconds"); + REALM_EXCLUDED_ATTRIBUTES.add("maxDeltaTimeSeconds"); + REALM_EXCLUDED_ATTRIBUTES.add("failureFactor"); + REALM_EXCLUDED_ATTRIBUTES.add("actionTokenGeneratedByAdminLifespan"); + REALM_EXCLUDED_ATTRIBUTES.add("actionTokenGeneratedByUserLifespan"); + REALM_EXCLUDED_ATTRIBUTES.add("offlineSessionMaxLifespanEnabled"); + REALM_EXCLUDED_ATTRIBUTES.add("offlineSessionMaxLifespan"); + + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRpEntityName"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicySignatureAlgorithms"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRpId"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAttestationConveyancePreference"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAuthenticatorAttachment"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRequireResidentKey"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyUserVerificationRequirement"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyCreateTimeout"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAvoidSameAuthenticatorRegister"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAcceptableAaguids"); + + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRpEntityNamePasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicySignatureAlgorithmsPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRpIdPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAttestationConveyancePreferencePasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAuthenticatorAttachmentPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyRequireResidentKeyPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyUserVerificationRequirementPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyCreateTimeoutPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAvoidSameAuthenticatorRegisterPasswordless"); + REALM_EXCLUDED_ATTRIBUTES.add("webAuthnPolicyAcceptableAaguidsPasswordless"); + } + + public static void buildGroupPath(StringBuilder sb, GroupModel group) { if (group.getParent() != null) { buildGroupPath(sb, group.getParent()); @@ -417,8 +460,7 @@ public class ModelToRepresentation { exportGroups(realm, rep); } - Map attributes = realm.getAttributes(); - rep.setAttributes(attributes); + rep.setAttributes(stripRealmAttributesIncludedAsFields(realm.getAttributes())); if (!internal) { rep = StripSecretsUtils.strip(rep); @@ -427,6 +469,24 @@ public class ModelToRepresentation { return rep; } + public static Map stripRealmAttributesIncludedAsFields(Map attributes) { + Map a = new HashMap<>(); + + for (Map.Entry e : attributes.entrySet()) { + if (REALM_EXCLUDED_ATTRIBUTES.contains(e.getKey())) { + continue; + } + + if (e.getKey().startsWith("_browser_header")) { + continue; + } + + a.put(e.getKey(), e.getValue()); + } + + return a; + } + public static void exportGroups(RealmModel realm, RealmRepresentation rep) { List groups = toGroupHierarchy(realm, true); rep.setGroups(groups); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index dd33351664..ed0f3e3b4e 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -158,6 +158,25 @@ public class RealmTest extends AbstractAdminTest { Assert.assertNames(adminClient.realms().findAll(), "master", AuthRealm.TEST, REALM_NAME); } + /** + * Checks attributes exposed as fields are not also included as attributes + */ + @Test + public void excludesFieldsFromAttributes() { + RealmRepresentation rep = new RealmRepresentation(); + rep.setRealm("attributes"); + + adminClient.realms().create(rep); + + try { + RealmRepresentation rep2 = adminClient.realm("attributes").toRepresentation(); + + assertTrue("Attributes was expected to be empty, but was: " + String.join(", ", rep2.getAttributes().keySet()), rep2.getAttributes().isEmpty()); + } finally { + adminClient.realm("attributes").remove(); + } + } + @Test public void smtpPasswordSecret() { RealmRepresentation rep = RealmBuilder.create().testEventListener().testMail().build();