diff --git a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java index ceaed240ab..7bd06f379d 100644 --- a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java +++ b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -209,6 +209,7 @@ public final class SecurityJackson2Modules { names.add("java.util.HashMap"); names.add("java.util.LinkedHashMap"); names.add("org.springframework.security.core.context.SecurityContextImpl"); + names.add("java.util.Arrays$ArrayList"); ALLOWLIST_CLASS_NAMES = Collections.unmodifiableSet(names); } diff --git a/ldap/src/main/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixin.java b/ldap/src/main/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixin.java index bcf7dd4220..fca449114e 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixin.java +++ b/ldap/src/main/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixin.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,19 +21,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.security.jackson2.SecurityJackson2Modules; +import org.springframework.security.ldap.userdetails.InetOrgPerson; /** - * This is a Jackson mixin class helps in serialize/deserialize - * {@link org.springframework.security.ldap.userdetails.InetOrgPerson} class. To use this - * class you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}. - * - *
- * ObjectMapper mapper = new ObjectMapper(); - * mapper.registerModule(new LdapJackson2Module()); - *- * - * Note: This class will save full class name into a property called @class + * This Jackson mixin is used to serialize/deserialize {@link InetOrgPerson}. * + * @since 5.7 * @see LdapJackson2Module * @see SecurityJackson2Modules */ diff --git a/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixin.java b/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixin.java index 151500df82..85fe16f5fd 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixin.java +++ b/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixin.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2020 the original author or authors. + * Copyright 2015-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,19 +26,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.springframework.security.jackson2.SecurityJackson2Modules; +import org.springframework.security.ldap.userdetails.LdapAuthority; /** - * This is a Jackson mixin class helps in serialize/deserialize - * {@link org.springframework.security.ldap.userdetails.LdapAuthority} class. To use this - * class you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}. - * - *
- * ObjectMapper mapper = new ObjectMapper(); - * mapper.registerModule(new LdapJackson2Module()); - *- * - * Note: This class will save full class name into a property called @class + * This Jackson mixin is used to serialize/deserialize {@link LdapAuthority}. * + * @since 5.7 * @see LdapJackson2Module * @see SecurityJackson2Modules */ @@ -47,13 +40,6 @@ import org.springframework.security.jackson2.SecurityJackson2Modules; @JsonIgnoreProperties(ignoreUnknown = true) abstract class LdapAuthorityMixin { - /** - * Constructor used by Jackson to create object of - * {@link org.springframework.security.ldap.userdetails.LdapAuthority}. - * @param role - * @param dn - * @param attributes - */ @JsonCreator LdapAuthorityMixin(@JsonProperty("role") String role, @JsonProperty("dn") String dn, @JsonProperty("attributes") Map
* ObjectMapper mapper = new ObjectMapper();
@@ -40,6 +42,7 @@ import org.springframework.security.ldap.userdetails.Person;
* Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list of all
* security modules.
*
+ * @since 5.7
* @see SecurityJackson2Modules
*/
public class LdapJackson2Module extends SimpleModule {
diff --git a/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixin.java b/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixin.java
index ecf060ba49..a441102e6b 100644
--- a/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixin.java
+++ b/ldap/src/main/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixin.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * Copyright 2015-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,20 +21,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
/**
- * This is a Jackson mixin class helps in serialize/deserialize
- * {@link org.springframework.security.ldap.userdetails.LdapUserDetailsImpl} class. To use
- * this class you need to register it with
- * {@link com.fasterxml.jackson.databind.ObjectMapper}.
- *
- *
- * ObjectMapper mapper = new ObjectMapper();
- * mapper.registerModule(new LdapJackson2Module());
- *
- *
- * Note: This class will save full class name into a property called @class
+ * This Jackson mixin is used to serialize/deserialize {@link LdapUserDetailsImpl}.
*
+ * @since 5.7
* @see LdapJackson2Module
* @see SecurityJackson2Modules
*/
diff --git a/ldap/src/main/java/org/springframework/security/ldap/jackson2/PersonMixin.java b/ldap/src/main/java/org/springframework/security/ldap/jackson2/PersonMixin.java
index c261c253a2..a3a0ddebc5 100644
--- a/ldap/src/main/java/org/springframework/security/ldap/jackson2/PersonMixin.java
+++ b/ldap/src/main/java/org/springframework/security/ldap/jackson2/PersonMixin.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2020 the original author or authors.
+ * Copyright 2015-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,19 +21,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.ldap.userdetails.Person;
/**
- * This is a Jackson mixin class helps in serialize/deserialize
- * {@link org.springframework.security.ldap.userdetails.Person} class. To use this class
- * you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}.
- *
- *
- * ObjectMapper mapper = new ObjectMapper();
- * mapper.registerModule(new LdapJackson2Module());
- *
- *
- * Note: This class will save full class name into a property called @class
+ * This Jackson mixin is used to serialize/deserialize {@link Person}.
*
+ * @since 5.7
* @see LdapJackson2Module
* @see SecurityJackson2Modules
*/
diff --git a/ldap/src/test/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixinTests.java b/ldap/src/test/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixinTests.java
index efd328d812..d9a05e6531 100644
--- a/ldap/src/test/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixinTests.java
+++ b/ldap/src/test/java/org/springframework/security/ldap/jackson2/InetOrgPersonMixinTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,27 +13,74 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.security.ldap.jackson2;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DistinguishedName;
-import org.springframework.security.jackson2.SecurityJackson2Modules;
-import org.springframework.security.ldap.userdetails.InetOrgPerson;
-import org.springframework.security.ldap.userdetails.Person;
+package org.springframework.security.ldap.jackson2;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.ldap.userdetails.InetOrgPerson;
+import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper;
+
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link InetOrgPersonMixin}.
*/
-class InetOrgPersonMixinTests {
+public class InetOrgPersonMixinTests {
+
+ private static final String USER_PASSWORD = "Password1234";
+
+ private static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
+
+ // @formatter:off
+ private static final String INET_ORG_PERSON_JSON = "{\n"
+ + "\"@class\": \"org.springframework.security.ldap.userdetails.InetOrgPerson\","
+ + "\"dn\": \"ignored=ignored\","
+ + "\"uid\": \"ghengis\","
+ + "\"username\": \"ghengis\","
+ + "\"password\": \"" + USER_PASSWORD + "\","
+ + "\"carLicense\": \"HORS1\","
+ + "\"givenName\": \"Ghengis\","
+ + "\"destinationIndicator\": \"West\","
+ + "\"displayName\": \"Ghengis McCann\","
+ + "\"givenName\": \"Ghengis\","
+ + "\"homePhone\": \"+467575436521\","
+ + "\"initials\": \"G\","
+ + "\"employeeNumber\": \"00001\","
+ + "\"homePostalAddress\": \"Steppes\","
+ + "\"mail\": \"ghengis@mongolia\","
+ + "\"mobile\": \"always\","
+ + "\"o\": \"Hordes\","
+ + "\"ou\": \"Horde1\","
+ + "\"postalAddress\": \"On the Move\","
+ + "\"postalCode\": \"Changes Frequently\","
+ + "\"roomNumber\": \"Yurt 1\","
+ + "\"sn\": \"Khan\","
+ + "\"street\": \"Westward Avenue\","
+ + "\"telephoneNumber\": \"+442075436521\","
+ + "\"departmentNumber\": \"5679\","
+ + "\"title\": \"T\","
+ + "\"cn\": [\"java.util.Arrays$ArrayList\",[\"Ghengis Khan\"]],"
+ + "\"description\": \"Scary\","
+ + "\"accountNonExpired\": true, "
+ + "\"accountNonLocked\": true, "
+ + "\"credentialsNonExpired\": true, "
+ + "\"enabled\": true, "
+ + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + ","
+ + "\"graceLoginsRemaining\": " + Integer.MAX_VALUE + ","
+ + "\"timeBeforeExpiration\": " + Integer.MAX_VALUE
+ + "}";
+ // @formatter:on
private ObjectMapper mapper;
@@ -44,22 +91,83 @@ class InetOrgPersonMixinTests {
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
}
- @Disabled
@Test
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
- InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
- InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
+ InetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();
+ InetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
- String expectedJson = asJson(p);
String json = this.mapper.writeValueAsString(p);
- JSONAssert.assertEquals(expectedJson, json, true);
+ JSONAssert.assertEquals(INET_ORG_PERSON_JSON, json, true);
+ }
+
+ @Test
+ public void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()
+ throws JsonProcessingException, JSONException {
+ InetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();
+ InetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
+ p.eraseCredentials();
+ String actualJson = this.mapper.writeValueAsString(p);
+ JSONAssert.assertEquals(INET_ORG_PERSON_JSON.replaceAll("\"" + USER_PASSWORD + "\"", "null"), actualJson, true);
+ }
+
+ @Test
+ public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
+ assertThatExceptionOfType(JsonProcessingException.class)
+ .isThrownBy(() -> new ObjectMapper().readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class));
+ }
+
+ @Test
+ public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
+ InetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();
+ InetOrgPerson expectedAuthentication = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
+
+ InetOrgPerson authentication = this.mapper.readValue(INET_ORG_PERSON_JSON, InetOrgPerson.class);
+ assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
+ assertThat(authentication.getCarLicense()).isEqualTo(expectedAuthentication.getCarLicense());
+ assertThat(authentication.getDepartmentNumber()).isEqualTo(expectedAuthentication.getDepartmentNumber());
+ assertThat(authentication.getDestinationIndicator())
+ .isEqualTo(expectedAuthentication.getDestinationIndicator());
+ assertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());
+ assertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());
+ assertThat(authentication.getDisplayName()).isEqualTo(expectedAuthentication.getDisplayName());
+ assertThat(authentication.getUid()).isEqualTo(expectedAuthentication.getUid());
+ assertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());
+ assertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());
+ assertThat(authentication.getHomePhone()).isEqualTo(expectedAuthentication.getHomePhone());
+ assertThat(authentication.getEmployeeNumber()).isEqualTo(expectedAuthentication.getEmployeeNumber());
+ assertThat(authentication.getHomePostalAddress()).isEqualTo(expectedAuthentication.getHomePostalAddress());
+ assertThat(authentication.getInitials()).isEqualTo(expectedAuthentication.getInitials());
+ assertThat(authentication.getMail()).isEqualTo(expectedAuthentication.getMail());
+ assertThat(authentication.getMobile()).isEqualTo(expectedAuthentication.getMobile());
+ assertThat(authentication.getO()).isEqualTo(expectedAuthentication.getO());
+ assertThat(authentication.getOu()).isEqualTo(expectedAuthentication.getOu());
+ assertThat(authentication.getPostalAddress()).isEqualTo(expectedAuthentication.getPostalAddress());
+ assertThat(authentication.getPostalCode()).isEqualTo(expectedAuthentication.getPostalCode());
+ assertThat(authentication.getRoomNumber()).isEqualTo(expectedAuthentication.getRoomNumber());
+ assertThat(authentication.getStreet()).isEqualTo(expectedAuthentication.getStreet());
+ assertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());
+ assertThat(authentication.getTitle()).isEqualTo(expectedAuthentication.getTitle());
+ assertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());
+ assertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());
+ assertThat(authentication.getGraceLoginsRemaining())
+ .isEqualTo(expectedAuthentication.getGraceLoginsRemaining());
+ assertThat(authentication.getTimeBeforeExpiration())
+ .isEqualTo(expectedAuthentication.getTimeBeforeExpiration());
+ assertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());
+ assertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());
+ assertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());
+ assertThat(authentication.isCredentialsNonExpired())
+ .isEqualTo(expectedAuthentication.isCredentialsNonExpired());
}
private DirContextAdapter createUserContext() {
DirContextAdapter ctx = new DirContextAdapter();
ctx.setDn(new DistinguishedName("ignored=ignored"));
ctx.setAttributeValue("uid", "ghengis");
- ctx.setAttributeValue("userPassword", "pillage");
+ ctx.setAttributeValue("userPassword", USER_PASSWORD);
ctx.setAttributeValue("carLicense", "HORS1");
ctx.setAttributeValue("cn", "Ghengis Khan");
ctx.setAttributeValue("description", "Scary");
@@ -77,19 +185,12 @@ class InetOrgPersonMixinTests {
ctx.setAttributeValue("postalAddress", "On the Move");
ctx.setAttributeValue("postalCode", "Changes Frequently");
ctx.setAttributeValue("roomNumber", "Yurt 1");
- ctx.setAttributeValue("roomNumber", "Yurt 1");
ctx.setAttributeValue("sn", "Khan");
ctx.setAttributeValue("street", "Westward Avenue");
ctx.setAttributeValue("telephoneNumber", "+442075436521");
+ ctx.setAttributeValue("departmentNumber", "5679");
+ ctx.setAttributeValue("title", "T");
return ctx;
}
- private String asJson(Person person) {
- // @formatter:off
- return "{\n" +
- " \"@class\": \"org.springframework.security.ldap.userdetails.InetOrgPerson\"\n" +
- "}";
- // @formatter:on
- }
-
}
diff --git a/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixinTests.java b/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixinTests.java
deleted file mode 100644
index b2e1255cde..0000000000
--- a/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapAuthorityMixinTests.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2002-2020 the original author or authors.
- *
- * 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
- *
- * https://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.springframework.security.ldap.jackson2;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-/**
- * Tests for {@link LdapAuthorityMixin}.
- */
-class LdapAuthorityMixinTests {
-
-}
diff --git a/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixinTests.java b/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixinTests.java
index 70c8b81d02..755623ba8f 100644
--- a/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixinTests.java
+++ b/ldap/src/test/java/org/springframework/security/ldap/jackson2/LdapUserDetailsImplMixinTests.java
@@ -13,13 +13,114 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.security.ldap.jackson2;
-import static org.junit.jupiter.api.Assertions.*;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.json.JSONException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
+import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link LdapUserDetailsImplMixin}.
*/
-class LdapUserDetailsImplMixinTests {
+public class LdapUserDetailsImplMixinTests {
+
+ private static final String USER_PASSWORD = "Password1234";
+
+ private static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
+
+ // @formatter:off
+ private static final String USER_JSON = "{"
+ + "\"@class\": \"org.springframework.security.ldap.userdetails.LdapUserDetailsImpl\", "
+ + "\"dn\": \"ignored=ignored\","
+ + "\"username\": \"ghengis\","
+ + "\"password\": \"" + USER_PASSWORD + "\","
+ + "\"accountNonExpired\": true, "
+ + "\"accountNonLocked\": true, "
+ + "\"credentialsNonExpired\": true, "
+ + "\"enabled\": true, "
+ + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + ","
+ + "\"graceLoginsRemaining\": " + Integer.MAX_VALUE + ","
+ + "\"timeBeforeExpiration\": " + Integer.MAX_VALUE
+ + "}";
+ // @formatter:on
+
+ private ObjectMapper mapper;
+
+ @BeforeEach
+ public void setup() {
+ ClassLoader loader = getClass().getClassLoader();
+ this.mapper = new ObjectMapper();
+ this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
+ }
+
+ @Test
+ public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
+ LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
+ LdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
+
+ String json = this.mapper.writeValueAsString(p);
+ JSONAssert.assertEquals(USER_JSON, json, true);
+ }
+
+ @Test
+ public void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()
+ throws JsonProcessingException, JSONException {
+ LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
+ LdapUserDetailsImpl p = (LdapUserDetailsImpl) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
+ p.eraseCredentials();
+ String actualJson = this.mapper.writeValueAsString(p);
+ JSONAssert.assertEquals(USER_JSON.replaceAll("\"" + USER_PASSWORD + "\"", "null"), actualJson, true);
+ }
+
+ @Test
+ public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
+ assertThatExceptionOfType(JsonProcessingException.class)
+ .isThrownBy(() -> new ObjectMapper().readValue(USER_JSON, LdapUserDetailsImpl.class));
+ }
+
+ @Test
+ public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
+ LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
+ LdapUserDetailsImpl expectedAuthentication = (LdapUserDetailsImpl) mapper
+ .mapUserFromContext(createUserContext(), "ghengis", AuthorityUtils.NO_AUTHORITIES);
+
+ LdapUserDetailsImpl authentication = this.mapper.readValue(USER_JSON, LdapUserDetailsImpl.class);
+ assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
+ assertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());
+ assertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());
+ assertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());
+ assertThat(authentication.getGraceLoginsRemaining())
+ .isEqualTo(expectedAuthentication.getGraceLoginsRemaining());
+ assertThat(authentication.getTimeBeforeExpiration())
+ .isEqualTo(expectedAuthentication.getTimeBeforeExpiration());
+ assertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());
+ assertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());
+ assertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());
+ assertThat(authentication.isCredentialsNonExpired())
+ .isEqualTo(expectedAuthentication.isCredentialsNonExpired());
+ }
+
+ private DirContextAdapter createUserContext() {
+ DirContextAdapter ctx = new DirContextAdapter();
+ ctx.setDn(new DistinguishedName("ignored=ignored"));
+ ctx.setAttributeValue("userPassword", USER_PASSWORD);
+ return ctx;
+ }
}
diff --git a/ldap/src/test/java/org/springframework/security/ldap/jackson2/PersonMixinTests.java b/ldap/src/test/java/org/springframework/security/ldap/jackson2/PersonMixinTests.java
index 7040c73174..018058888e 100644
--- a/ldap/src/test/java/org/springframework/security/ldap/jackson2/PersonMixinTests.java
+++ b/ldap/src/test/java/org/springframework/security/ldap/jackson2/PersonMixinTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,23 +13,55 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.security.ldap.jackson2;
-import org.springframework.security.jackson2.SecurityJackson2Modules;
-import org.springframework.security.ldap.userdetails.Person;
+package org.springframework.security.ldap.jackson2;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
-import static org.junit.jupiter.api.Assertions.*;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.ldap.userdetails.Person;
+import org.springframework.security.ldap.userdetails.PersonContextMapper;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link PersonMixin}.
*/
-class PersonMixinTests {
+public class PersonMixinTests {
+
+ private static final String USER_PASSWORD = "Password1234";
+
+ private static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
+
+ // @formatter:off
+ private static final String PERSON_JSON = "{"
+ + "\"@class\": \"org.springframework.security.ldap.userdetails.Person\", "
+ + "\"dn\": \"ignored=ignored\","
+ + "\"username\": \"ghengis\","
+ + "\"password\": \"" + USER_PASSWORD + "\","
+ + "\"givenName\": \"Ghengis\","
+ + "\"sn\": \"Khan\","
+ + "\"cn\": [\"java.util.Arrays$ArrayList\",[\"Ghengis Khan\"]],"
+ + "\"description\": \"Scary\","
+ + "\"telephoneNumber\": \"+442075436521\","
+ + "\"accountNonExpired\": true, "
+ + "\"accountNonLocked\": true, "
+ + "\"credentialsNonExpired\": true, "
+ + "\"enabled\": true, "
+ + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + ","
+ + "\"graceLoginsRemaining\": " + Integer.MAX_VALUE + ","
+ + "\"timeBeforeExpiration\": " + Integer.MAX_VALUE
+ + "}";
+ // @formatter:on
private ObjectMapper mapper;
@@ -40,20 +72,67 @@ class PersonMixinTests {
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
}
- @Disabled
@Test
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
- Person person = null;
- String expectedJson = asJson(person);
- String json = this.mapper.writeValueAsString(person);
- JSONAssert.assertEquals(expectedJson, json, true);
+ PersonContextMapper mapper = new PersonContextMapper();
+ Person p = (Person) mapper.mapUserFromContext(createUserContext(), "ghengis", AuthorityUtils.NO_AUTHORITIES);
+
+ String json = this.mapper.writeValueAsString(p);
+ JSONAssert.assertEquals(PERSON_JSON, json, true);
}
- private String asJson(Person person) {
- // @formatter:off
- return "{\n" +
- " \"@class\": \"org.springframework.security.ldap.userdetails.Person\"\n" +
- "}";
- // @formatter:on
+ @Test
+ public void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull()
+ throws JsonProcessingException, JSONException {
+ PersonContextMapper mapper = new PersonContextMapper();
+ Person p = (Person) mapper.mapUserFromContext(createUserContext(), "ghengis", AuthorityUtils.NO_AUTHORITIES);
+ p.eraseCredentials();
+ String actualJson = this.mapper.writeValueAsString(p);
+ JSONAssert.assertEquals(PERSON_JSON.replaceAll("\"" + USER_PASSWORD + "\"", "null"), actualJson, true);
+ }
+
+ @Test
+ public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
+ assertThatExceptionOfType(JsonProcessingException.class)
+ .isThrownBy(() -> new ObjectMapper().readValue(PERSON_JSON, Person.class));
}
+
+ @Test
+ public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
+ PersonContextMapper mapper = new PersonContextMapper();
+ Person expectedAuthentication = (Person) mapper.mapUserFromContext(createUserContext(), "ghengis",
+ AuthorityUtils.NO_AUTHORITIES);
+
+ Person authentication = this.mapper.readValue(PERSON_JSON, Person.class);
+ assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
+ assertThat(authentication.getDn()).isEqualTo(expectedAuthentication.getDn());
+ assertThat(authentication.getDescription()).isEqualTo(expectedAuthentication.getDescription());
+ assertThat(authentication.getUsername()).isEqualTo(expectedAuthentication.getUsername());
+ assertThat(authentication.getPassword()).isEqualTo(expectedAuthentication.getPassword());
+ assertThat(authentication.getSn()).isEqualTo(expectedAuthentication.getSn());
+ assertThat(authentication.getGivenName()).isEqualTo(expectedAuthentication.getGivenName());
+ assertThat(authentication.getTelephoneNumber()).isEqualTo(expectedAuthentication.getTelephoneNumber());
+ assertThat(authentication.getGraceLoginsRemaining())
+ .isEqualTo(expectedAuthentication.getGraceLoginsRemaining());
+ assertThat(authentication.getTimeBeforeExpiration())
+ .isEqualTo(expectedAuthentication.getTimeBeforeExpiration());
+ assertThat(authentication.isAccountNonExpired()).isEqualTo(expectedAuthentication.isAccountNonExpired());
+ assertThat(authentication.isAccountNonLocked()).isEqualTo(expectedAuthentication.isAccountNonLocked());
+ assertThat(authentication.isEnabled()).isEqualTo(expectedAuthentication.isEnabled());
+ assertThat(authentication.isCredentialsNonExpired())
+ .isEqualTo(expectedAuthentication.isCredentialsNonExpired());
+ }
+
+ private DirContextAdapter createUserContext() {
+ DirContextAdapter ctx = new DirContextAdapter();
+ ctx.setDn(new DistinguishedName("ignored=ignored"));
+ ctx.setAttributeValue("userPassword", USER_PASSWORD);
+ ctx.setAttributeValue("cn", "Ghengis Khan");
+ ctx.setAttributeValue("description", "Scary");
+ ctx.setAttributeValue("givenName", "Ghengis");
+ ctx.setAttributeValue("sn", "Khan");
+ ctx.setAttributeValue("telephoneNumber", "+442075436521");
+ return ctx;
+ }
+
}