Skip to content

Commit 7cfd415

Browse files
markusheideneleftherias
authored andcommitted
Start with LDAP Jackson2 mixins
Issue gh-9263
1 parent 74e3abc commit 7cfd415

File tree

11 files changed

+472
-0
lines changed

11 files changed

+472
-0
lines changed

core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public final class SecurityJackson2Modules {
8484

8585
private static final String javaTimeJackson2ModuleClass = "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule";
8686

87+
private static final String ldapJackson2ModuleClass = "org.springframework.security.ldap.jackson2.LdapJackson2Module";
88+
8789
private SecurityJackson2Modules() {
8890
}
8991

@@ -129,6 +131,9 @@ public static List<Module> getModules(ClassLoader loader) {
129131
if (ClassUtils.isPresent(javaTimeJackson2ModuleClass, loader)) {
130132
addToModulesList(loader, modules, javaTimeJackson2ModuleClass);
131133
}
134+
if (ClassUtils.isPresent(ldapJackson2ModuleClass, loader)) {
135+
addToModulesList(loader, modules, ldapJackson2ModuleClass);
136+
}
132137
return modules;
133138
}
134139

ldap/spring-security-ldap.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dependencies {
88
api 'org.springframework:spring-core'
99
api 'org.springframework:spring-tx'
1010

11+
optional 'com.fasterxml.jackson.core:jackson-databind'
1112
optional 'ldapsdk:ldapsdk'
1213
optional "com.unboundid:unboundid-ldapsdk"
1314
optional "org.apache.directory.server:apacheds-core"
@@ -34,6 +35,7 @@ dependencies {
3435
testImplementation "org.mockito:mockito-core"
3536
testImplementation "org.mockito:mockito-junit-jupiter"
3637
testImplementation "org.springframework:spring-test"
38+
testImplementation 'org.skyscreamer:jsonassert'
3739
}
3840

3941
integrationTest {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.ldap.jackson2;
18+
19+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
22+
23+
import org.springframework.security.jackson2.SecurityJackson2Modules;
24+
25+
/**
26+
* This is a Jackson mixin class helps in serialize/deserialize
27+
* {@link org.springframework.security.ldap.userdetails.InetOrgPerson} class. To use this
28+
* class you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}.
29+
*
30+
* <pre>
31+
* ObjectMapper mapper = new ObjectMapper();
32+
* mapper.registerModule(new LdapJackson2Module());
33+
* </pre>
34+
*
35+
* <i>Note: This class will save full class name into a property called @class</i>
36+
*
37+
* @see LdapJackson2Module
38+
* @see SecurityJackson2Modules
39+
*/
40+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
41+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
42+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
43+
@JsonIgnoreProperties(ignoreUnknown = true)
44+
abstract class InetOrgPersonMixin {
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.ldap.jackson2;
18+
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
23+
import com.fasterxml.jackson.annotation.JsonCreator;
24+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
25+
import com.fasterxml.jackson.annotation.JsonProperty;
26+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
27+
28+
import org.springframework.security.jackson2.SecurityJackson2Modules;
29+
30+
/**
31+
* This is a Jackson mixin class helps in serialize/deserialize
32+
* {@link org.springframework.security.ldap.userdetails.LdapAuthority} class. To use this
33+
* class you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}.
34+
*
35+
* <pre>
36+
* ObjectMapper mapper = new ObjectMapper();
37+
* mapper.registerModule(new LdapJackson2Module());
38+
* </pre>
39+
*
40+
* <i>Note: This class will save full class name into a property called @class</i>
41+
*
42+
* @see LdapJackson2Module
43+
* @see SecurityJackson2Modules
44+
*/
45+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
46+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
47+
@JsonIgnoreProperties(ignoreUnknown = true)
48+
abstract class LdapAuthorityMixin {
49+
50+
/**
51+
* Constructor used by Jackson to create object of
52+
* {@link org.springframework.security.ldap.userdetails.LdapAuthority}.
53+
* @param role
54+
* @param dn
55+
* @param attributes
56+
*/
57+
@JsonCreator
58+
LdapAuthorityMixin(@JsonProperty("role") String role, @JsonProperty("dn") String dn,
59+
@JsonProperty("attributes") Map<String, List<String>> attributes) {
60+
}
61+
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.ldap.jackson2;
18+
19+
import com.fasterxml.jackson.core.Version;
20+
import com.fasterxml.jackson.databind.module.SimpleModule;
21+
22+
import org.springframework.security.jackson2.SecurityJackson2Modules;
23+
import org.springframework.security.ldap.userdetails.InetOrgPerson;
24+
import org.springframework.security.ldap.userdetails.LdapAuthority;
25+
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
26+
import org.springframework.security.ldap.userdetails.Person;
27+
28+
/**
29+
* Jackson module for spring-security-ldap. This module registers
30+
* {@link LdapAuthorityMixin}, {@link LdapUserDetailsImplMixin}, {@link PersonMixin},
31+
* {@link InetOrgPersonMixin}. If no default typing enabled by default then it'll enable
32+
* it because typing info is needed to properly serialize/deserialize objects. In order to
33+
* use this module just add this module into your ObjectMapper configuration.
34+
*
35+
* <pre>
36+
* ObjectMapper mapper = new ObjectMapper();
37+
* mapper.registerModule(new LdapJackson2Module());
38+
* </pre>
39+
*
40+
* <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list of all
41+
* security modules.</b>
42+
*
43+
* @see SecurityJackson2Modules
44+
*/
45+
public class LdapJackson2Module extends SimpleModule {
46+
47+
public LdapJackson2Module() {
48+
super(LdapJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
49+
}
50+
51+
@Override
52+
public void setupModule(SetupContext context) {
53+
SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
54+
context.setMixInAnnotations(LdapAuthority.class, LdapAuthorityMixin.class);
55+
context.setMixInAnnotations(LdapUserDetailsImpl.class, LdapUserDetailsImplMixin.class);
56+
context.setMixInAnnotations(Person.class, PersonMixin.class);
57+
context.setMixInAnnotations(InetOrgPerson.class, InetOrgPersonMixin.class);
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.ldap.jackson2;
18+
19+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
22+
23+
import org.springframework.security.jackson2.SecurityJackson2Modules;
24+
25+
/**
26+
* This is a Jackson mixin class helps in serialize/deserialize
27+
* {@link org.springframework.security.ldap.userdetails.LdapUserDetailsImpl} class. To use
28+
* this class you need to register it with
29+
* {@link com.fasterxml.jackson.databind.ObjectMapper}.
30+
*
31+
* <pre>
32+
* ObjectMapper mapper = new ObjectMapper();
33+
* mapper.registerModule(new LdapJackson2Module());
34+
* </pre>
35+
*
36+
* <i>Note: This class will save full class name into a property called @class</i>
37+
*
38+
* @see LdapJackson2Module
39+
* @see SecurityJackson2Modules
40+
*/
41+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
42+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
43+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
44+
@JsonIgnoreProperties(ignoreUnknown = true)
45+
abstract class LdapUserDetailsImplMixin {
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2015-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.ldap.jackson2;
18+
19+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
22+
23+
import org.springframework.security.jackson2.SecurityJackson2Modules;
24+
25+
/**
26+
* This is a Jackson mixin class helps in serialize/deserialize
27+
* {@link org.springframework.security.ldap.userdetails.Person} class. To use this class
28+
* you need to register it with {@link com.fasterxml.jackson.databind.ObjectMapper}.
29+
*
30+
* <pre>
31+
* ObjectMapper mapper = new ObjectMapper();
32+
* mapper.registerModule(new LdapJackson2Module());
33+
* </pre>
34+
*
35+
* <i>Note: This class will save full class name into a property called @class</i>
36+
*
37+
* @see LdapJackson2Module
38+
* @see SecurityJackson2Modules
39+
*/
40+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
41+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
42+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
43+
@JsonIgnoreProperties(ignoreUnknown = true)
44+
abstract class PersonMixin {
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.ldap.jackson2;
17+
18+
import org.springframework.ldap.core.DirContextAdapter;
19+
import org.springframework.ldap.core.DistinguishedName;
20+
import org.springframework.security.jackson2.SecurityJackson2Modules;
21+
import org.springframework.security.ldap.userdetails.InetOrgPerson;
22+
import org.springframework.security.ldap.userdetails.Person;
23+
24+
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Disabled;
27+
import org.junit.jupiter.api.Test;
28+
import org.skyscreamer.jsonassert.JSONAssert;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.junit.jupiter.api.Assertions.*;
32+
33+
/**
34+
* Tests for {@link InetOrgPersonMixin}.
35+
*/
36+
class InetOrgPersonMixinTests {
37+
38+
private ObjectMapper mapper;
39+
40+
@BeforeEach
41+
public void setup() {
42+
ClassLoader loader = getClass().getClassLoader();
43+
this.mapper = new ObjectMapper();
44+
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
45+
}
46+
47+
@Disabled
48+
@Test
49+
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
50+
InetOrgPerson.Essence essence = new InetOrgPerson.Essence(createUserContext());
51+
InetOrgPerson p = (InetOrgPerson) essence.createUserDetails();
52+
53+
String expectedJson = asJson(p);
54+
String json = this.mapper.writeValueAsString(p);
55+
JSONAssert.assertEquals(expectedJson, json, true);
56+
}
57+
58+
private DirContextAdapter createUserContext() {
59+
DirContextAdapter ctx = new DirContextAdapter();
60+
ctx.setDn(new DistinguishedName("ignored=ignored"));
61+
ctx.setAttributeValue("uid", "ghengis");
62+
ctx.setAttributeValue("userPassword", "pillage");
63+
ctx.setAttributeValue("carLicense", "HORS1");
64+
ctx.setAttributeValue("cn", "Ghengis Khan");
65+
ctx.setAttributeValue("description", "Scary");
66+
ctx.setAttributeValue("destinationIndicator", "West");
67+
ctx.setAttributeValue("displayName", "Ghengis McCann");
68+
ctx.setAttributeValue("givenName", "Ghengis");
69+
ctx.setAttributeValue("homePhone", "+467575436521");
70+
ctx.setAttributeValue("initials", "G");
71+
ctx.setAttributeValue("employeeNumber", "00001");
72+
ctx.setAttributeValue("homePostalAddress", "Steppes");
73+
ctx.setAttributeValue("mail", "ghengis@mongolia");
74+
ctx.setAttributeValue("mobile", "always");
75+
ctx.setAttributeValue("o", "Hordes");
76+
ctx.setAttributeValue("ou", "Horde1");
77+
ctx.setAttributeValue("postalAddress", "On the Move");
78+
ctx.setAttributeValue("postalCode", "Changes Frequently");
79+
ctx.setAttributeValue("roomNumber", "Yurt 1");
80+
ctx.setAttributeValue("roomNumber", "Yurt 1");
81+
ctx.setAttributeValue("sn", "Khan");
82+
ctx.setAttributeValue("street", "Westward Avenue");
83+
ctx.setAttributeValue("telephoneNumber", "+442075436521");
84+
return ctx;
85+
}
86+
87+
private String asJson(Person person) {
88+
// @formatter:off
89+
return "{\n" +
90+
" \"@class\": \"org.springframework.security.ldap.userdetails.InetOrgPerson\"\n" +
91+
"}";
92+
// @formatter:on
93+
}
94+
95+
}

0 commit comments

Comments
 (0)