Skip to content

Commit 7f99cc0

Browse files
committed
WIP Remove global default typing
This commits remove global default typing for better security and use instead a custom PolymorphicTypeValidator. See https://cowtowncoder.medium.com/jackson-2-10-safe-default-typing-2d018f0ce2ba for more details. TODO Configure the PolymorphicTypeValidator in each module
1 parent 6ad4821 commit 7f99cc0

File tree

55 files changed

+295
-572
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+295
-572
lines changed

cas/src/main/java/org/springframework/security/cas/jackson/AssertionImplMixin.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* @see CasJacksonModule
4646
* @see org.springframework.security.jackson.SecurityJacksonModules
4747
*/
48-
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
48+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
4949
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
5050
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
5151
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -61,7 +61,8 @@ class AssertionImplMixin {
6161
* @param attributes the key/value pairs for this attribute.
6262
*/
6363
@JsonCreator
64-
AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal,
64+
AssertionImplMixin(
65+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonProperty("principal") AttributePrincipal principal,
6566
@JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate,
6667
@JsonProperty("authenticationDate") Date authenticationDate,
6768
@JsonProperty("attributes") Map<String, Object> attributes) {

cas/src/main/java/org/springframework/security/cas/jackson/AttributePrincipalImplMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
* @see CasJacksonModule
4444
* @see org.springframework.security.jackson.SecurityJacksonModules
4545
*/
46-
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
46+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
4747
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
4848
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
4949
@JsonIgnoreProperties(ignoreUnknown = true)

cas/src/main/java/org/springframework/security/cas/jackson/CasAuthenticationTokenMixin.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
* @see CasJacksonModule
5353
* @see org.springframework.security.jackson.SecurityJacksonModules
5454
*/
55-
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
55+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
5656
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
5757
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
5858
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -75,10 +75,13 @@ class CasAuthenticationTokenMixin {
7575
* principal and how to obtain a proxy ticket for the user.
7676
*/
7777
@JsonCreator
78-
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal,
79-
@JsonProperty("credentials") Object credentials,
80-
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
81-
@JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) {
78+
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
79+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonProperty("principal") Object principal,
80+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonProperty("credentials") Object credentials,
81+
@JsonTypeInfo(
82+
use = JsonTypeInfo.Id.CLASS) @JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
83+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonProperty("userDetails") UserDetails userDetails,
84+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonProperty("assertion") Assertion assertion) {
8285
}
8386

8487
}

cas/src/main/java/org/springframework/security/cas/jackson/CasJacksonModule.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@
1919
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
2020
import org.apereo.cas.client.validation.AssertionImpl;
2121
import tools.jackson.core.Version;
22-
import tools.jackson.databind.cfg.MapperBuilder;
2322
import tools.jackson.databind.module.SimpleModule;
2423

2524
import org.springframework.security.cas.authentication.CasAuthenticationToken;
26-
import org.springframework.security.jackson.AllowlistTypeResolverBuilder;
2725
import org.springframework.security.jackson.SecurityJacksonModules;
2826

2927
/**
@@ -55,7 +53,6 @@ public CasJacksonModule() {
5553

5654
@Override
5755
public void setupModule(SetupContext context) {
58-
((MapperBuilder<?, ?>) context.getOwner()).setDefaultTyping(new AllowlistTypeResolverBuilder());
5956
context.setMixIn(AssertionImpl.class, AssertionImplMixin.class);
6057
context.setMixIn(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);
6158
context.setMixIn(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);

cas/src/test/java/org/springframework/security/cas/jackson/CasAuthenticationTokenMixinTests.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ public class CasAuthenticationTokenMixinTests {
5656

5757
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
5858

59-
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON
60-
+ "]]";
59+
public static final String AUTHORITIES_SET_JSON = "[" + AUTHORITY_JSON + "]";
6160

62-
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", ["
63-
+ AUTHORITY_JSON + "]]";
61+
public static final String AUTHORITIES_ARRAYLIST_JSON = "[" + AUTHORITY_JSON + "]";
6462

6563
// @formatter:off
6664
public static final String USER_JSON = "{"
@@ -81,13 +79,10 @@ public class CasAuthenticationTokenMixinTests {
8179
+ "," + "\"authenticated\": true, " + "\"details\": null," + "\"assertion\": {"
8280
+ "\"@class\": \"org.apereo.cas.client.validation.AssertionImpl\", " + "\"principal\": {"
8381
+ "\"@class\": \"org.apereo.cas.client.authentication.AttributePrincipalImpl\", "
84-
+ "\"name\": \"assertName\", " + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, "
85-
+ "\"proxyGrantingTicket\": null, " + "\"proxyRetriever\": null" + "}, "
86-
+ "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
87-
+ "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "],"
88-
+ "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
89-
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"},"
90-
+ "\"context\": {\"@class\":\"java.util.HashMap\"}" + "}" + "}";
82+
+ "\"name\": \"assertName\", " + "\"attributes\": {}, " + "\"proxyGrantingTicket\": null, "
83+
+ "\"proxyRetriever\": null" + "}, " + "\"validFromDate\":" + START_DATE.getTime() + ", "
84+
+ "\"validUntilDate\":" + END_DATE.getTime() + "," + "\"authenticationDate\":" + START_DATE.getTime() + ", "
85+
+ "\"attributes\": {}," + "\"context\": {}" + "}" + "}";
9186

9287
private static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, "null");
9388

core/src/main/java/org/springframework/security/jackson/AllowlistTypeResolverBuilder.java

Lines changed: 0 additions & 163 deletions
This file was deleted.

core/src/main/java/org/springframework/security/jackson/AnonymousAuthenticationTokenMixin.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2424
import com.fasterxml.jackson.annotation.JsonProperty;
2525
import com.fasterxml.jackson.annotation.JsonTypeInfo;
26+
import org.jspecify.annotations.Nullable;
2627

2728
import org.springframework.security.core.GrantedAuthority;
2829

@@ -47,7 +48,7 @@
4748
* @see CoreJacksonModule
4849
* @see SecurityJacksonModules
4950
*/
50-
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
51+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
5152
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
5253
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
5354
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -63,8 +64,12 @@ class AnonymousAuthenticationTokenMixin {
6364
*/
6465
@JsonCreator
6566
AnonymousAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
66-
@JsonProperty("principal") Object principal,
67-
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
67+
@JsonProperty("principal") @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) Object principal,
68+
@JsonProperty("authorities") @JsonTypeInfo(
69+
use = JsonTypeInfo.Id.CLASS) Collection<? extends GrantedAuthority> authorities) {
6870
}
6971

72+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
73+
private final @Nullable Collection<GrantedAuthority> authorities = null;
74+
7075
}

core/src/main/java/org/springframework/security/jackson/BadCredentialsExceptionMixin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
* @since 7.0
4242
* @see CoreJacksonModule
4343
*/
44-
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
44+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
4545
@JsonIgnoreProperties(ignoreUnknown = true, value = { "cause", "stackTrace", "authenticationRequest" })
4646
class BadCredentialsExceptionMixin {
4747

core/src/main/java/org/springframework/security/jackson/CoreJacksonModule.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,22 @@
1616

1717
package org.springframework.security.jackson;
1818

19+
import java.time.Instant;
20+
1921
import tools.jackson.core.Version;
2022
import tools.jackson.databind.cfg.MapperBuilder;
23+
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
24+
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
2125
import tools.jackson.databind.module.SimpleModule;
2226

2327
import org.springframework.security.authentication.AnonymousAuthenticationToken;
2428
import org.springframework.security.authentication.BadCredentialsException;
2529
import org.springframework.security.authentication.RememberMeAuthenticationToken;
2630
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
31+
import org.springframework.security.core.Authentication;
32+
import org.springframework.security.core.GrantedAuthority;
2733
import org.springframework.security.core.authority.SimpleGrantedAuthority;
34+
import org.springframework.security.core.context.SecurityContextImpl;
2835
import org.springframework.security.core.userdetails.User;
2936

3037
/**
@@ -60,13 +67,63 @@ public CoreJacksonModule() {
6067

6168
@Override
6269
public void setupModule(SetupContext context) {
63-
((MapperBuilder<?, ?>) context.getOwner()).setDefaultTyping(new AllowlistTypeResolverBuilder());
70+
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
71+
.allowIfSubType(Instant.class)
72+
// TODO Check if really necessary
73+
.allowIfSubType("java.util.Collections$UnmodifiableSet")
74+
.allowIfBaseType(GrantedAuthority.class)
75+
.allowIfBaseType(Authentication.class)
76+
.allowIfSubType(User.class)
77+
.allowIfSubType(BadCredentialsException.class)
78+
.allowIfSubType(SecurityContextImpl.class)
79+
// TODO Move to the proper cas module
80+
.allowIfSubType("org.apereo.cas.client.validation.AssertionImpl")
81+
.allowIfSubType("org.apereo.cas.client.authentication.AttributePrincipalImpl")
82+
// TODO Move to the proper ldap module
83+
.allowIfSubType("org.springframework.security.ldap.userdetails.InetOrgPerson")
84+
.allowIfSubType("org.springframework.security.ldap.userdetails.LdapUserDetailsImpl")
85+
.allowIfSubType("org.springframework.security.ldap.userdetails.Person")
86+
// TODO Move to the proper oauth2-client module
87+
.allowIfSubType("org.springframework.security.oauth2.core.OAuth2AuthenticationException")
88+
.allowIfSubType("org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser")
89+
.allowIfSubType("org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest")
90+
.allowIfSubType("org.springframework.security.oauth2.core.OAuth2Error")
91+
.allowIfSubType("org.springframework.security.oauth2.client.OAuth2AuthorizedClient")
92+
.allowIfSubType("org.springframework.security.oauth2.core.oidc.OidcIdToken")
93+
.allowIfSubType("org.springframework.security.oauth2.core.oidc.OidcUserInfo")
94+
.allowIfSubType("org.springframework.security.oauth2.core.user.DefaultOAuth2User")
95+
.allowIfSubType("org.springframework.security.oauth2.client.registration.ClientRegistration")
96+
.allowIfSubType("org.springframework.security.oauth2.core.OAuth2AccessToken")
97+
.allowIfSubType("org.springframework.security.oauth2.core.OAuth2RefreshToken")
98+
// TODO Move to the proper saml2-service-provider module
99+
.allowIfSubType("org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion")
100+
.allowIfSubType(
101+
"org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal")
102+
.allowIfSubType(
103+
"org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest")
104+
.allowIfSubType(
105+
"org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest")
106+
.allowIfSubType(
107+
"org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest")
108+
.allowIfSubType(
109+
"org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException")
110+
.allowIfSubType("org.springframework.security.saml2.core.Saml2Error")
111+
// TODO Move to the proper web module
112+
.allowIfSubType("jakarta.servlet.http.Cookie")
113+
.allowIfSubType("org.springframework.security.web.csrf.DefaultCsrfToken")
114+
.allowIfSubType("org.springframework.security.web.savedrequest.DefaultSavedRequest")
115+
.allowIfSubType("org.springframework.security.web.savedrequest.SavedCookie")
116+
.allowIfSubType("org.springframework.security.web.authentication.WebAuthenticationDetails")
117+
.allowIfSubType("org.springframework.security.web.server.csrf.DefaultCsrfToken")
118+
.build();
119+
((MapperBuilder<?, ?>) context.getOwner()).polymorphicTypeValidator(ptv);
64120
context.setMixIn(AnonymousAuthenticationToken.class, AnonymousAuthenticationTokenMixin.class);
65121
context.setMixIn(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);
66122
context.setMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
67123
context.setMixIn(User.class, UserMixin.class);
68124
context.setMixIn(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class);
69125
context.setMixIn(BadCredentialsException.class, BadCredentialsExceptionMixin.class);
126+
context.setMixIn(SecurityContextImpl.class, SecurityContextImplMixin.class);
70127
}
71128

72129
}

0 commit comments

Comments
 (0)