mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-21 01:38:50 +00:00
Add Jackson 3 support
This commit adds support for Jackson 3 which has the following major differences with the Jackson 2 one: - jackson subpackage instead of jackson2 - Jackson type prefix instead of Jackson2 - JsonMapper instead of ObjectMapper - For configuration, JsonMapper.Builder instead of ObjectMapper since the latter is now immutable - Remove custom support for unmodifiable collections - Use safe default typing via a PolymorphicTypeValidator Jackson 3 changes compared to Jackson 2 are documented in https://cowtowncoder.medium.com/jackson-3-0-0-ga-released-1f669cda529a and https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md. This commit does not cover webauthn which is a special case (uses jackson sub-package for Jackson 2 support) which will be handled in a distinct commit. See gh-17832 Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
This commit is contained in:
parent
916a687b29
commit
65a14d6c6d
@ -15,6 +15,7 @@ dependencies {
|
||||
api 'org.springframework:spring-web'
|
||||
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.cas.jackson;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.apereo.cas.client.authentication.AttributePrincipal;
|
||||
|
||||
/**
|
||||
* Helps in jackson deserialization of class
|
||||
* {@link org.apereo.cas.client.validation.AssertionImpl}, which is used with
|
||||
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CasJacksonModule
|
||||
* @see org.springframework.security.jackson.SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
class AssertionImplMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize
|
||||
* {@link org.apereo.cas.client.validation.AssertionImpl}
|
||||
* @param principal the Principal to associate with the Assertion.
|
||||
* @param validFromDate when the assertion is valid from.
|
||||
* @param validUntilDate when the assertion is valid to.
|
||||
* @param authenticationDate when the assertion is authenticated.
|
||||
* @param attributes the key/value pairs for this attribute.
|
||||
*/
|
||||
@JsonCreator
|
||||
AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal,
|
||||
@JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate,
|
||||
@JsonProperty("authenticationDate") Date authenticationDate,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.cas.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.apereo.cas.client.proxy.ProxyRetriever;
|
||||
|
||||
/**
|
||||
* Helps in deserialize
|
||||
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with
|
||||
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CasJacksonModule
|
||||
* @see org.springframework.security.jackson.SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
class AttributePrincipalImplMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize
|
||||
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl}
|
||||
* @param name the unique identifier for the principal.
|
||||
* @param attributes the key/value pairs for this principal.
|
||||
* @param proxyGrantingTicket the ticket associated with this principal.
|
||||
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS
|
||||
* server.
|
||||
*/
|
||||
@JsonCreator
|
||||
AttributePrincipalImplMixin(@JsonProperty("name") String name,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes,
|
||||
@JsonProperty("proxyGrantingTicket") String proxyGrantingTicket,
|
||||
@JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.cas.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.apereo.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* Mixin class which helps in deserialize {@link CasAuthenticationToken} using jackson.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CasJacksonModule
|
||||
* @see org.springframework.security.jackson.SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
class CasAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize {@link CasAuthenticationToken}
|
||||
* @param keyHash hashCode of provided key to identify if this object made by a given
|
||||
* {@link CasAuthenticationProvider}
|
||||
* @param principal typically the UserDetails object (cannot be <code>null</code>)
|
||||
* @param credentials the service/proxy ticket ID from CAS (cannot be
|
||||
* <code>null</code>)
|
||||
* @param authorities the authorities granted to the user (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param userDetails the user details (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param assertion the assertion returned from the CAS servers. It contains the
|
||||
* principal and how to obtain a proxy ticket for the user.
|
||||
*/
|
||||
@JsonCreator
|
||||
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal,
|
||||
@JsonProperty("credentials") Object credentials,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.cas.jackson;
|
||||
|
||||
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.apereo.cas.client.validation.AssertionImpl;
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
import org.springframework.security.jackson.SecurityJacksonModule;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-cas. This module register
|
||||
* {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and
|
||||
* {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll
|
||||
* enable it because typing info is needed to properly serialize/deserialize objects. In
|
||||
* order to use this module just add this module into your JsonMapper configuration.
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
public class CasJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public CasJacksonModule() {
|
||||
super(CasJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(AssertionImpl.class)
|
||||
.allowIfSubType(AttributePrincipalImpl.class)
|
||||
.allowIfSubType(CasAuthenticationToken.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixIn(AssertionImpl.class, AssertionImplMixin.class);
|
||||
context.setMixIn(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);
|
||||
context.setMixIn(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support for CAS.
|
||||
*/
|
||||
package org.springframework.security.cas.jackson;
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson support for CAS.
|
||||
* Jackson 2 support for CAS.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.cas.jackson2;
|
||||
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.cas.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.apereo.cas.client.validation.Assertion;
|
||||
import org.apereo.cas.client.validation.AssertionImpl;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class CasAuthenticationTokenMixinTests {
|
||||
|
||||
private static final String KEY = "casKey";
|
||||
|
||||
private static final String PASSWORD = "\"1234\"";
|
||||
|
||||
private static final Date START_DATE = new Date();
|
||||
|
||||
private static final Date END_DATE = new Date();
|
||||
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
|
||||
|
||||
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON
|
||||
+ "]]";
|
||||
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", ["
|
||||
+ AUTHORITY_JSON + "]]";
|
||||
|
||||
// @formatter:off
|
||||
public static final String USER_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.core.userdetails.User\", "
|
||||
+ "\"username\": \"admin\","
|
||||
+ " \"password\": " + PASSWORD + ", "
|
||||
+ "\"accountNonExpired\": true, "
|
||||
+ "\"accountNonLocked\": true, "
|
||||
+ "\"credentialsNonExpired\": true, "
|
||||
+ "\"enabled\": true, "
|
||||
+ "\"authorities\": " + AUTHORITIES_SET_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
private static final String CAS_TOKEN_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.cas.authentication.CasAuthenticationToken\", "
|
||||
+ "\"keyHash\": " + KEY.hashCode() + "," + "\"principal\": " + USER_JSON + ", " + "\"credentials\": "
|
||||
+ PASSWORD + ", " + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + "," + "\"userDetails\": " + USER_JSON
|
||||
+ "," + "\"authenticated\": true, " + "\"details\": null," + "\"assertion\": {"
|
||||
+ "\"@class\": \"org.apereo.cas.client.validation.AssertionImpl\", " + "\"principal\": {"
|
||||
+ "\"@class\": \"org.apereo.cas.client.authentication.AttributePrincipalImpl\", "
|
||||
+ "\"name\": \"assertName\", " + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, "
|
||||
+ "\"proxyGrantingTicket\": null, " + "\"proxyRetriever\": null" + "}, "
|
||||
+ "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
|
||||
+ "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "],"
|
||||
+ "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
|
||||
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"},"
|
||||
+ "\"context\": {\"@class\":\"java.util.HashMap\"}" + "}" + "}";
|
||||
|
||||
private static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, "null");
|
||||
|
||||
protected JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeCasAuthenticationTest() throws JSONException {
|
||||
CasAuthenticationToken token = createCasAuthenticationToken();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeCasAuthenticationTestAfterEraseCredentialInvoked() throws JSONException {
|
||||
CasAuthenticationToken token = createCasAuthenticationToken();
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() {
|
||||
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class);
|
||||
assertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeCasAuthenticationTest() throws IOException {
|
||||
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin");
|
||||
assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234");
|
||||
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
|
||||
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
|
||||
assertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority)
|
||||
.containsOnly("ROLE_USER");
|
||||
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
|
||||
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
|
||||
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
|
||||
assertThat(token.getAssertion().getPrincipal().getName()).isEqualTo("assertName");
|
||||
assertThat(token.getAssertion().getAttributes()).hasSize(0);
|
||||
}
|
||||
|
||||
private CasAuthenticationToken createCasAuthenticationToken() {
|
||||
User principal = new User("admin", "1234", Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
Collection<? extends GrantedAuthority> authorities = Collections
|
||||
.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
Assertion assertion = new AssertionImpl(new AttributePrincipalImpl("assertName"), START_DATE, END_DATE,
|
||||
START_DATE, Collections.<String, Object>emptyMap());
|
||||
return new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities,
|
||||
new User("admin", "1234", authorities), assertion);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -167,7 +168,8 @@ public class JwkSetTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -176,7 +178,8 @@ public class JwkSetTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -1300,7 +1301,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1309,7 +1311,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -573,7 +574,8 @@ public class OAuth2ClientCredentialsGrantTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -582,7 +584,8 @@ public class OAuth2ClientCredentialsGrantTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -516,7 +517,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -525,7 +527,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -553,7 +554,8 @@ public class OAuth2TokenIntrospectionTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -562,7 +564,8 @@ public class OAuth2TokenIntrospectionTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -351,7 +352,8 @@ public class OAuth2TokenRevocationTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -360,7 +362,8 @@ public class OAuth2TokenRevocationTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -695,7 +696,8 @@ public class OidcTests {
|
||||
|
||||
RowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
super(registeredClientRepository);
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -704,7 +706,8 @@ public class OidcTests {
|
||||
|
||||
ParametersMapper() {
|
||||
super();
|
||||
getObjectMapper().addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class);
|
||||
setMapper(new JdbcOAuth2AuthorizationService.JacksonDelegate(JsonMapper.builder()
|
||||
.addMixIn(TestingAuthenticationToken.class, TestingAuthenticationTokenMixin.class)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ dependencies {
|
||||
optional 'org.springframework:spring-jdbc'
|
||||
optional 'org.springframework:spring-tx'
|
||||
optional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
|
||||
testImplementation 'commons-collections:commons-collections'
|
||||
testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This is a Jackson mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
class AnonymousAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create object of
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.
|
||||
* @param keyHash hashCode of key provided at the time of token creation by using
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
AnonymousAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.BadCredentialsException} class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Yannick Lombardi
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonIgnoreProperties({ "cause", "stackTrace", "authenticationRequest" })
|
||||
class BadCredentialsExceptionMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create
|
||||
* {@link org.springframework.security.authentication.BadCredentialsException} object.
|
||||
* @param message the detail message
|
||||
*/
|
||||
@JsonCreator
|
||||
BadCredentialsExceptionMixin(@JsonProperty("message") String message) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.cfg.DateTimeFeature;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-core. This module register
|
||||
* {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin},
|
||||
* {@link SimpleGrantedAuthorityMixin}, {@link FactorGrantedAuthorityMixin},
|
||||
* {{@link UserMixin}, {@link UsernamePasswordAuthenticationTokenMixin} and
|
||||
* {@link UsernamePasswordAuthenticationTokenMixin}.
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.O
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
public class CoreJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public CoreJacksonModule() {
|
||||
super(CoreJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
protected CoreJacksonModule(String name, Version version) {
|
||||
super(name, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(Instant.class)
|
||||
.allowIfSubType(Duration.class)
|
||||
.allowIfSubType(SimpleGrantedAuthority.class)
|
||||
.allowIfSubType(FactorGrantedAuthority.class)
|
||||
.allowIfSubType(UsernamePasswordAuthenticationToken.class)
|
||||
.allowIfSubType(RememberMeAuthenticationToken.class)
|
||||
.allowIfSubType(AnonymousAuthenticationToken.class)
|
||||
.allowIfSubType(User.class)
|
||||
.allowIfSubType(BadCredentialsException.class)
|
||||
.allowIfSubType(SecurityContextImpl.class)
|
||||
.allowIfSubType(TestingAuthenticationToken.class)
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableSet")
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableRandomAccessList")
|
||||
.allowIfSubType("java.util.Collections$EmptyList")
|
||||
.allowIfSubType("java.util.ArrayList")
|
||||
.allowIfSubType("java.util.HashMap")
|
||||
.allowIfSubType("java.util.Collections$EmptyMap")
|
||||
.allowIfSubType("java.util.Date")
|
||||
.allowIfSubType("java.util.Arrays$ArrayList")
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableMap")
|
||||
.allowIfSubType("java.util.LinkedHashMap")
|
||||
.allowIfSubType("java.util.Collections$SingletonList")
|
||||
.allowIfSubType("java.util.TreeMap")
|
||||
.allowIfSubType("java.util.HashSet")
|
||||
.allowIfSubType("java.util.LinkedHashSet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
((MapperBuilder<?, ?>) context.getOwner()).enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
context.setMixIn(AnonymousAuthenticationToken.class, AnonymousAuthenticationTokenMixin.class);
|
||||
context.setMixIn(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);
|
||||
context.setMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
|
||||
context.setMixIn(FactorGrantedAuthority.class, FactorGrantedAuthorityMixin.class);
|
||||
context.setMixIn(User.class, UserMixin.class);
|
||||
context.setMixIn(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class);
|
||||
context.setMixIn(BadCredentialsException.class, BadCredentialsExceptionMixin.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rob Winch
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class FactorGrantedAuthorityMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor.
|
||||
* @param authority the authority
|
||||
*/
|
||||
@JsonCreator
|
||||
FactorGrantedAuthorityMixin(@JsonProperty("authority") String authority,
|
||||
@JsonProperty("issuedAt") Instant issuedAt) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class RememberMeAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* object.
|
||||
* @param keyHash hashCode of above given key.
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
RememberMeAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import tools.jackson.databind.module.SimpleModule;
|
||||
|
||||
/**
|
||||
* Jackson module allowing to contribute {@link PolymorphicTypeValidator} configuration.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
*/
|
||||
public abstract class SecurityJacksonModule extends SimpleModule {
|
||||
|
||||
public SecurityJacksonModule() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SecurityJacksonModule(String name, Version version) {
|
||||
super(name, version, null);
|
||||
}
|
||||
|
||||
public abstract void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder);
|
||||
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tools.jackson.databind.DefaultTyping;
|
||||
import tools.jackson.databind.JacksonModule;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import tools.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* This utility class will find all the Jackson modules contributed by Spring Security in
|
||||
* the classpath (except {@code OAuth2AuthorizationServerJacksonModule} and
|
||||
* {@code WebauthnJacksonModule}), enable automatic inclusion of type information and
|
||||
* configure a {@link PolymorphicTypeValidator} that handles the validation of class
|
||||
* names.
|
||||
*
|
||||
* <p>
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* If needed, you can add custom classes to the validation handling.
|
||||
* <p>
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
* .allowIfSubType(MyCustomType.class);
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader, builder))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
*/
|
||||
public final class SecurityJacksonModules {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SecurityJacksonModules.class);
|
||||
|
||||
private static final List<String> securityJacksonModuleClasses = Arrays.asList(
|
||||
"org.springframework.security.jackson.CoreJacksonModule",
|
||||
"org.springframework.security.web.jackson.WebJacksonModule",
|
||||
"org.springframework.security.web.server.jackson.WebServerJacksonModule");
|
||||
|
||||
private static final String webServletJacksonModuleClass = "org.springframework.security.web.jackson.WebServletJacksonModule";
|
||||
|
||||
private static final String oauth2ClientJacksonModuleClass = "org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule";
|
||||
|
||||
private static final String ldapJacksonModuleClass = "org.springframework.security.ldap.jackson.LdapJacksonModule";
|
||||
|
||||
private static final String saml2JacksonModuleClass = "org.springframework.security.saml2.jackson.Saml2JacksonModule";
|
||||
|
||||
private static final String casJacksonModuleClass = "org.springframework.security.cas.jackson.CasJacksonModule";
|
||||
|
||||
private static final boolean webServletPresent;
|
||||
|
||||
private static final boolean oauth2ClientPresent;
|
||||
|
||||
private static final boolean ldapJacksonPresent;
|
||||
|
||||
private static final boolean saml2JacksonPresent;
|
||||
|
||||
private static final boolean casJacksonPresent;
|
||||
|
||||
static {
|
||||
|
||||
ClassLoader classLoader = SecurityJacksonModules.class.getClassLoader();
|
||||
webServletPresent = ClassUtils.isPresent("jakarta.servlet.http.Cookie", classLoader);
|
||||
oauth2ClientPresent = ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
|
||||
classLoader);
|
||||
ldapJacksonPresent = ClassUtils.isPresent(ldapJacksonModuleClass, classLoader);
|
||||
saml2JacksonPresent = ClassUtils.isPresent(saml2JacksonModuleClass, classLoader);
|
||||
casJacksonPresent = ClassUtils.isPresent(casJacksonModuleClass, classLoader);
|
||||
}
|
||||
|
||||
private SecurityJacksonModules() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static @Nullable SecurityJacksonModule loadAndGetInstance(String className, ClassLoader loader) {
|
||||
try {
|
||||
Class<? extends SecurityJacksonModule> securityModule = (Class<? extends SecurityJacksonModule>) ClassUtils
|
||||
.forName(className, loader);
|
||||
logger.debug(LogMessage.format("Loaded module %s, now registering", className));
|
||||
return securityModule.getConstructor().newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.debug(LogMessage.format("Cannot load module %s", className), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available security modules in classpath, enable automatic
|
||||
* inclusion of type information and configure a default
|
||||
* {@link PolymorphicTypeValidator} that handles the validation of class names.
|
||||
* @param loader the ClassLoader to use
|
||||
* @return List of available security modules in classpath
|
||||
* @see #getModules(ClassLoader, BasicPolymorphicTypeValidator.Builder)
|
||||
*/
|
||||
public static List<JacksonModule> getModules(ClassLoader loader) {
|
||||
return getModules(loader, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available security modules in classpath, enable automatic
|
||||
* inclusion of type information and configure a default
|
||||
* {@link PolymorphicTypeValidator} customizable with the provided builder that
|
||||
* handles the validation of class names.
|
||||
* @param loader the ClassLoader to use
|
||||
* @param typeValidatorBuilder the builder to configure custom types allowed in
|
||||
* addition to Spring Security ones
|
||||
* @return List of available security modules in classpath.
|
||||
*/
|
||||
public static List<JacksonModule> getModules(ClassLoader loader,
|
||||
BasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {
|
||||
|
||||
List<JacksonModule> modules = new ArrayList<>();
|
||||
for (String className : securityJacksonModuleClasses) {
|
||||
addToModulesList(loader, modules, className);
|
||||
}
|
||||
if (webServletPresent) {
|
||||
addToModulesList(loader, modules, webServletJacksonModuleClass);
|
||||
}
|
||||
if (oauth2ClientPresent) {
|
||||
addToModulesList(loader, modules, oauth2ClientJacksonModuleClass);
|
||||
}
|
||||
if (ldapJacksonPresent) {
|
||||
addToModulesList(loader, modules, ldapJacksonModuleClass);
|
||||
}
|
||||
if (saml2JacksonPresent) {
|
||||
addToModulesList(loader, modules, saml2JacksonModuleClass);
|
||||
}
|
||||
if (casJacksonPresent) {
|
||||
addToModulesList(loader, modules, casJacksonModuleClass);
|
||||
}
|
||||
applyPolymorphicTypeValidator(modules, typeValidatorBuilder);
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static void applyPolymorphicTypeValidator(List<JacksonModule> modules,
|
||||
BasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {
|
||||
|
||||
BasicPolymorphicTypeValidator.Builder builder = (typeValidatorBuilder != null) ? typeValidatorBuilder
|
||||
: BasicPolymorphicTypeValidator.builder();
|
||||
for (JacksonModule module : modules) {
|
||||
if (module instanceof SecurityJacksonModule securityModule) {
|
||||
securityModule.configurePolymorphicTypeValidator(builder);
|
||||
}
|
||||
}
|
||||
modules.add(new SimpleModule() {
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(),
|
||||
DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loader the ClassLoader to use
|
||||
* @param modules list of the modules to add
|
||||
* @param className name of the class to instantiate
|
||||
*/
|
||||
private static void addToModulesList(ClassLoader loader, List<JacksonModule> modules, String className) {
|
||||
SecurityJacksonModule module = loadAndGetInstance(className, loader);
|
||||
if (module != null) {
|
||||
modules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public abstract class SimpleGrantedAuthorityMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor.
|
||||
* @param role the role
|
||||
*/
|
||||
@JsonCreator
|
||||
public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Custom Deserializer for {@link User} class. This is already registered with
|
||||
* {@link UserMixin}. You can also use it directly with your mixin class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see UserMixin
|
||||
*/
|
||||
class UserDeserializer extends ValueDeserializer<User> {
|
||||
|
||||
private static final TypeReference<Set<GrantedAuthority>> GRANTED_AUTHORITY_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will create {@link User} object. It will ensure successful object
|
||||
* creation even if password key is null in serialized json, because credentials may
|
||||
* be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In
|
||||
* that case there won't be any password key in serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws JacksonException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public User deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException {
|
||||
JsonNode jsonNode = ctxt.readTree(jp);
|
||||
JsonNode authoritiesNode = readJsonNode(jsonNode, "authorities");
|
||||
Set<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,
|
||||
ctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_SET));
|
||||
JsonNode passwordNode = readJsonNode(jsonNode, "password");
|
||||
String username = readJsonNode(jsonNode, "username").asString();
|
||||
String password = (passwordNode.isMissingNode()) ? null : passwordNode.stringValue();
|
||||
boolean enabled = readJsonNode(jsonNode, "enabled").asBoolean();
|
||||
boolean accountNonExpired = readJsonNode(jsonNode, "accountNonExpired").asBoolean();
|
||||
boolean credentialsNonExpired = readJsonNode(jsonNode, "credentialsNonExpired").asBoolean();
|
||||
boolean accountNonLocked = readJsonNode(jsonNode, "accountNonLocked").asBoolean();
|
||||
User result = new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,
|
||||
authorities);
|
||||
if (passwordNode.asString(null) == null) {
|
||||
result.eraseCredentials();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.userdetails.User}. This class also register a
|
||||
* custom deserializer {@link UserDeserializer} to deserialize User object successfully.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see UserDeserializer
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = UserDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class UserMixin {
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DatabindException;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of
|
||||
* deserialization it will invoke suitable constructor depending on the value of
|
||||
* <b>authenticated</b> property. It will ensure that the token's state must not change.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 7.0
|
||||
* @see UsernamePasswordAuthenticationTokenMixin
|
||||
*/
|
||||
class UsernamePasswordAuthenticationTokenDeserializer extends ValueDeserializer<UsernamePasswordAuthenticationToken> {
|
||||
|
||||
private static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* This method construct {@link UsernamePasswordAuthenticationToken} object from
|
||||
* serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws JacksonException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)
|
||||
throws JacksonException {
|
||||
JsonNode jsonNode = ctxt.readTree(jp);
|
||||
boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = getPrincipal(ctxt, principalNode);
|
||||
JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
|
||||
Object credentials = getCredentials(credentialsNode);
|
||||
JsonNode authoritiesNode = readJsonNode(jsonNode, "authorities");
|
||||
List<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,
|
||||
ctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_LIST));
|
||||
UsernamePasswordAuthenticationToken token = (!authenticated)
|
||||
? UsernamePasswordAuthenticationToken.unauthenticated(principal, credentials)
|
||||
: UsernamePasswordAuthenticationToken.authenticated(principal, credentials, authorities);
|
||||
JsonNode detailsNode = readJsonNode(jsonNode, "details");
|
||||
if (detailsNode.isNull() || detailsNode.isMissingNode()) {
|
||||
token.setDetails(null);
|
||||
}
|
||||
else {
|
||||
Object details = ctxt.readTreeAsValue(detailsNode, Object.class);
|
||||
token.setDetails(details);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private @Nullable Object getCredentials(JsonNode credentialsNode) {
|
||||
if (credentialsNode.isNull() || credentialsNode.isMissingNode()) {
|
||||
return null;
|
||||
}
|
||||
return credentialsNode.asString();
|
||||
}
|
||||
|
||||
private Object getPrincipal(DeserializationContext ctxt, JsonNode principalNode)
|
||||
throws StreamReadException, DatabindException {
|
||||
if (principalNode.isObject()) {
|
||||
return ctxt.readTreeAsValue(principalNode, Object.class);
|
||||
}
|
||||
return principalNode.asString();
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize / deserialize
|
||||
* {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.
|
||||
* This class register a custom deserializer
|
||||
* {@link UsernamePasswordAuthenticationTokenDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class)
|
||||
abstract class UsernamePasswordAuthenticationTokenMixin {
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.jackson;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
@ -71,7 +71,7 @@ class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<U
|
||||
throws IOException, JsonProcessingException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = getPrincipal(mapper, principalNode);
|
||||
JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
|
||||
|
@ -15,10 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mix-in classes to add Jackson serialization support.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* Jackson 2 serialization support.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.jackson2;
|
||||
|
@ -34,9 +34,9 @@ import java.util.TreeSet;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
@ -340,13 +340,14 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
||||
assertThat(factory.proxy(35)).isEqualTo(35);
|
||||
}
|
||||
|
||||
// TODO Find why callbacks property is serialized with Jackson 3, not with Jackson 2
|
||||
@Disabled("callbacks property is serialized with Jackson 3, not with Jackson 2")
|
||||
@Test
|
||||
public void serializeWhenAuthorizationProxyObjectThenOnlyIncludesProxiedProperties()
|
||||
throws JsonProcessingException {
|
||||
public void serializeWhenAuthorizationProxyObjectThenOnlyIncludesProxiedProperties() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.admin);
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
User user = proxy(factory, this.alan);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
String serialized = mapper.writeValueAsString(user);
|
||||
Map<String, Object> properties = mapper.readValue(serialized, Map.class);
|
||||
assertThat(properties).hasSize(3).containsKeys("id", "firstName", "lastName");
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* @author Jitenra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public abstract class AbstractMixinTests {
|
||||
|
||||
protected JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType(
|
||||
"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal");
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader, builder)).build();
|
||||
}
|
||||
|
||||
User createDefaultUser() {
|
||||
return createUser("admin", "1234", "ROLE_USER");
|
||||
}
|
||||
|
||||
User createUser(String username, String password, String authority) {
|
||||
return new User(username, password, AuthorityUtils.createAuthorityList(authority));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class AnonymousAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String HASH_KEY = "key";
|
||||
|
||||
// @formatter:off
|
||||
private static final String ANONYMOUS_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.AnonymousAuthenticationToken\", "
|
||||
+ "\"details\": null,"
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ","
|
||||
+ "\"authenticated\": true, "
|
||||
+ "\"keyHash\": " + HASH_KEY.hashCode() + ","
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeAnonymousAuthenticationTokenTest() throws JSONException {
|
||||
User user = createDefaultUser();
|
||||
AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(ANONYMOUS_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAnonymousAuthenticationTokenTest() {
|
||||
AnonymousAuthenticationToken token = this.mapper.readValue(ANONYMOUS_JSON, AnonymousAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getKeyHash()).isEqualTo(HASH_KEY.hashCode());
|
||||
assertThat(token.getAuthorities()).isNotNull().hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAnonymousAuthenticationTokenWithoutAuthoritiesTest() {
|
||||
String jsonString = "{\"@class\": \"org.springframework.security.authentication.AnonymousAuthenticationToken\", \"details\": null,"
|
||||
+ "\"principal\": \"user\", \"authenticated\": true, \"keyHash\": " + HASH_KEY.hashCode() + ","
|
||||
+ "\"authorities\": [\"java.util.ArrayList\", []]}";
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(jsonString, AnonymousAuthenticationToken.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAnonymousAuthenticationTokenMixinAfterEraseCredentialTest() throws JSONException {
|
||||
User user = createDefaultUser();
|
||||
AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(ANONYMOUS_JSON.replace(UserDeserializerTests.USER_PASSWORD, "null"), actualJson, true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Yannick Lombardi
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BadCredentialsExceptionMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
private static final String EXCEPTION_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.BadCredentialsException\","
|
||||
+ "\"localizedMessage\": \"message\", "
|
||||
+ "\"message\": \"message\", "
|
||||
+ "\"suppressed\": [\"[Ljava.lang.Throwable;\",[]]"
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeBadCredentialsExceptionMixinTest() throws JsonProcessingException, JSONException {
|
||||
BadCredentialsException exception = new BadCredentialsException("message");
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
JSONAssert.assertEquals(EXCEPTION_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeBadCredentialsExceptionMixinTest() throws IOException {
|
||||
BadCredentialsException exception = this.mapper.readValue(EXCEPTION_JSON, BadCredentialsException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isEqualTo("message");
|
||||
assertThat(exception.getLocalizedMessage()).isEqualTo("message");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 7.0
|
||||
*/
|
||||
class FactorGrantedAuthorityMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.FactorGrantedAuthority\", \"authority\": \"FACTOR_PASSWORD\", \"issuedAt\": 1759177143.043000000 }";
|
||||
|
||||
private Instant issuedAt = Instant.ofEpochMilli(1759177143043L);
|
||||
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
void serializeSimpleGrantedAuthorityTest() throws JSONException {
|
||||
GrantedAuthority authority = FactorGrantedAuthority.withAuthority("FACTOR_PASSWORD")
|
||||
.issuedAt(this.issuedAt)
|
||||
.build();
|
||||
String serializeJson = this.mapper.writeValueAsString(authority);
|
||||
JSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deserializeGrantedAuthorityTest() {
|
||||
FactorGrantedAuthority authority = (FactorGrantedAuthority) this.mapper.readValue(AUTHORITY_JSON, Object.class);
|
||||
assertThat(authority).isNotNull();
|
||||
assertThat(authority.getAuthority()).isEqualTo("FACTOR_PASSWORD");
|
||||
assertThat(authority.getIssuedAt()).isEqualTo(this.issuedAt);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class RememberMeAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String REMEMBERME_KEY = "rememberMe";
|
||||
|
||||
// @formatter:off
|
||||
private static final String REMEMBERME_AUTH_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.RememberMeAuthenticationToken\", "
|
||||
+ "\"keyHash\": " + REMEMBERME_KEY.hashCode() + ", "
|
||||
+ "\"authenticated\": true, \"details\": null" + ", "
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ", "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String REMEMBERME_AUTH_STRINGPRINCIPAL_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.RememberMeAuthenticationToken\","
|
||||
+ "\"keyHash\": " + REMEMBERME_KEY.hashCode() + ", "
|
||||
+ "\"authenticated\": true, "
|
||||
+ "\"details\": null,"
|
||||
+ "\"principal\": \"admin\", "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
public void testWithNullPrincipal() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new RememberMeAuthenticationToken("key", null, Collections.<GrantedAuthority>emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNullKey() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new RememberMeAuthenticationToken(null, "principal", Collections.<GrantedAuthority>emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationToken() throws JsonProcessingException, JSONException {
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, "admin",
|
||||
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationWithUserToken() throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,
|
||||
user.getAuthorities());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(String.format(REMEMBERME_AUTH_JSON, "\"password\""), actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationWithUserTokenAfterEraseCredential()
|
||||
throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,
|
||||
user.getAuthorities());
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(REMEMBERME_AUTH_JSON.replace(UserDeserializerTests.USER_PASSWORD, "null"), actualJson,
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeRememberMeAuthenticationToken() throws IOException {
|
||||
RememberMeAuthenticationToken token = this.mapper.readValue(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON,
|
||||
RememberMeAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isEqualTo("admin").isEqualTo(token.getName());
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeRememberMeAuthenticationTokenWithUserTest() throws IOException {
|
||||
RememberMeAuthenticationToken token = this.mapper.readValue(String.format(REMEMBERME_AUTH_JSON, "\"password\""),
|
||||
RememberMeAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin");
|
||||
assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234");
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(((User) token.getPrincipal()).isEnabled()).isEqualTo(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class SecurityContextMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String SECURITY_CONTEXT_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.core.context.SecurityContextImpl\", "
|
||||
+ "\"authentication\": " + UsernamePasswordAuthenticationTokenMixinTests.AUTHENTICATED_STRINGPRINCIPAL_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void securityContextSerializeTest() throws JsonProcessingException, JSONException {
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
context.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("admin", "1234",
|
||||
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))));
|
||||
String actualJson = this.mapper.writeValueAsString(context);
|
||||
JSONAssert.assertEquals(SECURITY_CONTEXT_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityContextDeserializeTest() throws IOException {
|
||||
SecurityContext context = this.mapper.readValue(SECURITY_CONTEXT_JSON, SecurityContextImpl.class);
|
||||
assertThat(context).isNotNull();
|
||||
assertThat(context.getAuthentication()).isNotNull().isInstanceOf(UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(context.getAuthentication().getPrincipal()).isEqualTo("admin");
|
||||
assertThat(context.getAuthentication().getCredentials()).isEqualTo("1234");
|
||||
assertThat(context.getAuthentication().isAuthenticated()).isTrue();
|
||||
Collection authorities = context.getAuthentication().getAuthorities();
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(authorities).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.databind.JacksonModule;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
public class SecurityJacksonModulesTests {
|
||||
|
||||
@Test
|
||||
public void addModulesWithNoTypeValidatorBuilder() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader);
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new SimpleGrantedAuthority("SCOPE_message:read")));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addModulesWithDefaultTypeValidatorBuilder() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader,
|
||||
BasicPolymorphicTypeValidator.builder());
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new SimpleGrantedAuthority("SCOPE_message:read")));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addModulesWithCustomTypeValidator() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType(TestGrantedAuthority.class);
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader, builder);
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new TestGrantedAuthority()));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
private static class TestGrantedAuthority implements GrantedAuthority {
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
|
||||
public static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$EmptyList\", []]";
|
||||
public static final String NO_AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", []]";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeSimpleGrantedAuthorityTest() throws JsonProcessingException, JSONException {
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
|
||||
String serializeJson = this.mapper.writeValueAsString(authority);
|
||||
JSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeGrantedAuthorityTest() throws IOException {
|
||||
SimpleGrantedAuthority authority = this.mapper.readValue(AUTHORITY_JSON, SimpleGrantedAuthority.class);
|
||||
assertThat(authority).isNotNull();
|
||||
assertThat(authority.getAuthority()).isNotNull().isEqualTo("ROLE_USER");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeGrantedAuthorityWithoutRoleTest() throws IOException {
|
||||
String json = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\"}";
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(json, SimpleGrantedAuthority.class));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UnmodifiableMapTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
private static final String DEFAULT_MAP_JSON = "{"
|
||||
+ "\"@class\": \"java.util.Collections$UnmodifiableMap\","
|
||||
+ "\"Key\": \"Value\""
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
void shouldSerialize() throws Exception {
|
||||
String mapJson = mapper
|
||||
.writeValueAsString(Collections.unmodifiableMap(Collections.singletonMap("Key", "Value")));
|
||||
|
||||
JSONAssert.assertEquals(DEFAULT_MAP_JSON, mapJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeserialize() throws Exception {
|
||||
Map<String, String> map = mapper.readValue(DEFAULT_MAP_JSON,
|
||||
Collections.unmodifiableMap(Collections.emptyMap()).getClass());
|
||||
|
||||
assertThat(map).isNotNull()
|
||||
.isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass())
|
||||
.containsAllEntriesOf(Collections.singletonMap("Key", "Value"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.MismatchedInputException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class UserDeserializerTests extends AbstractMixinTests {
|
||||
|
||||
public static final String USER_PASSWORD = "\"1234\"";
|
||||
|
||||
// @formatter:off
|
||||
public static final String USER_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.core.userdetails.User\", "
|
||||
+ "\"username\": \"admin\","
|
||||
+ " \"password\": " + USER_PASSWORD + ", "
|
||||
+ "\"accountNonExpired\": true, "
|
||||
+ "\"accountNonLocked\": true, "
|
||||
+ "\"credentialsNonExpired\": true, "
|
||||
+ "\"enabled\": true, "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeUserTest() throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
String userJson = this.mapper.writeValueAsString(user);
|
||||
JSONAssert.assertEquals(userWithPasswordJson(user.getPassword()), userJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeUserWithoutAuthority() throws JsonProcessingException, JSONException {
|
||||
User user = new User("admin", "1234", Collections.<GrantedAuthority>emptyList());
|
||||
String userJson = this.mapper.writeValueAsString(user);
|
||||
JSONAssert.assertEquals(userWithNoAuthoritiesJson(), userJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNullPasswordEmptyAuthorityTest() throws IOException {
|
||||
String userJsonWithoutPasswordString = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
"[]");
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(userJsonWithoutPasswordString, User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNullPasswordNoAuthorityTest() throws Exception {
|
||||
String userJsonWithoutPasswordString = removeNode(userWithNoAuthoritiesJson(), this.mapper, "password");
|
||||
User user = this.mapper.readValue(userJsonWithoutPasswordString, User.class);
|
||||
assertThat(user).isNotNull();
|
||||
assertThat(user.getUsername()).isEqualTo("admin");
|
||||
assertThat(user.getPassword()).isNull();
|
||||
assertThat(user.getAuthorities()).isEmpty();
|
||||
assertThat(user.isEnabled()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNoClassIdInAuthoritiesTest() throws Exception {
|
||||
String userJson = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
"[{\"authority\": \"ROLE_USER\"}]");
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(userJson, User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithClassIdInAuthoritiesTest() {
|
||||
User user = this.mapper.readValue(userJson(), User.class);
|
||||
assertThat(user).isNotNull();
|
||||
assertThat(user.getUsername()).isEqualTo("admin");
|
||||
assertThat(user.getPassword()).isEqualTo("1234");
|
||||
assertThat(user.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
private String removeNode(String json, JsonMapper mapper, String toRemove) throws Exception {
|
||||
ObjectNode node = mapper.createParser(json).readValueAsTree();
|
||||
node.remove(toRemove);
|
||||
String result = mapper.writeValueAsString(node);
|
||||
JSONAssert.assertNotEquals(json, result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String userJson() {
|
||||
return USER_JSON;
|
||||
}
|
||||
|
||||
public static String userWithPasswordJson(String password) {
|
||||
return userJson().replaceAll(Pattern.quote(USER_PASSWORD), "\"" + password + "\"");
|
||||
}
|
||||
|
||||
public static String userWithNoAuthoritiesJson() {
|
||||
return userJson().replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_SET_JSON);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonClassDescription;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Value;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 4.2
|
||||
*/
|
||||
public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String AUTHENTICATED_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\","
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ", " + "\"credentials\": \"1234\", "
|
||||
+ "\"authenticated\": true, " + "\"details\": null, " + "\"authorities\": "
|
||||
+ SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON + "}";
|
||||
|
||||
public static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON
|
||||
.replace(UserDeserializerTests.USER_JSON, "\"admin\"");
|
||||
|
||||
private static final String NON_USER_PRINCIPAL_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\", "
|
||||
+ "\"username\": \"admin\"" + "}";
|
||||
|
||||
private static final String AUTHENTICATED_STRINGDETAILS_JSON = AUTHENTICATED_JSON.replace("\"details\": null, ",
|
||||
"\"details\": \"details\", ");
|
||||
|
||||
private static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON
|
||||
.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)
|
||||
.replaceAll(UserDeserializerTests.USER_PASSWORD, "null")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);
|
||||
|
||||
private static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON
|
||||
.replace("\"authenticated\": true, ", "\"authenticated\": false, ")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON);
|
||||
|
||||
@Test
|
||||
public void serializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated("admin",
|
||||
"1234");
|
||||
String serializedJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(UNAUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.authenticated(user.getUsername(), user.getPassword(), user.getAuthorities());
|
||||
String serializedJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest() {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(UNAUTHENTICATED_STRINGPRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.isAuthenticated()).isEqualTo(false);
|
||||
assertThat(token.getAuthorities()).isNotNull().hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() {
|
||||
UsernamePasswordAuthenticationToken expectedToken = createToken();
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGPRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.isAuthenticated()).isTrue();
|
||||
assertThat(token.getAuthorities()).isEqualTo(expectedToken.getAuthorities());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithUserTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = createToken();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithUserTest() throws IOException {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()
|
||||
.hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.isAuthenticated()).isEqualTo(true);
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinAfterEraseCredentialInvoked()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = createToken();
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, "null"), actualJson,
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
NonUserPrincipal principal = new NonUserPrincipal();
|
||||
principal.setUsername("admin");
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(principal, null,
|
||||
new ArrayList<>());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest()
|
||||
throws IOException {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithDetailsTest() {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGDETAILS_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()
|
||||
.hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.isAuthenticated()).isEqualTo(true);
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.getDetails()).isExactlyInstanceOf(String.class).isEqualTo("details");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializingThenDeserializingWithNoCredentialsOrDetailsShouldWork() {
|
||||
UsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated("Frodo",
|
||||
null);
|
||||
String serialized = this.mapper.writeValueAsString(original);
|
||||
UsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializingThenDeserializingWithConfiguredJsontMapperShouldWork() {
|
||||
JsonMapper jsonMapper = this.mapper.rebuild()
|
||||
.changeDefaultPropertyInclusion((p) -> Value.construct(Include.NON_ABSENT, Include.NON_ABSENT))
|
||||
.build();
|
||||
|
||||
UsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated("Frodo",
|
||||
null);
|
||||
String serialized = jsonMapper.writeValueAsString(original);
|
||||
UsernamePasswordAuthenticationToken deserialized = jsonMapper.readValue(serialized,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
}
|
||||
|
||||
private UsernamePasswordAuthenticationToken createToken() {
|
||||
User user = createDefaultUser();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(user,
|
||||
user.getPassword(), user.getAuthorities());
|
||||
return token;
|
||||
}
|
||||
|
||||
@JsonClassDescription
|
||||
public static class NonUserPrincipal {
|
||||
|
||||
private String username;
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
Spring Security provides Jackson support for persisting Spring Security related classes.
|
||||
This can improve the performance of serializing Spring Security related classes when working with distributed sessions (i.e. session replication, Spring Session, etc).
|
||||
|
||||
To use it, register the `SecurityJackson2Modules.getModules(ClassLoader)` with `ObjectMapper` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
|
||||
To use it, register the `SecurityJacksonModules.getModules(ClassLoader)` with `JsonMapper.Builder` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@ -12,12 +12,12 @@ Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<Module> modules = SecurityJackson2Modules.getModules(loader);
|
||||
mapper.registerModules(modules);
|
||||
JsonMapper mapper = JsonMapper.builder()
|
||||
.addModules(SecurityJacksonModules.getModules(loader))
|
||||
.build();
|
||||
|
||||
// ... use ObjectMapper as normally ...
|
||||
// ... use JsonMapper as normally ...
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
// ...
|
||||
String json = mapper.writeValueAsString(context);
|
||||
@ -27,12 +27,12 @@ Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
val mapper = ObjectMapper()
|
||||
val loader = javaClass.classLoader
|
||||
val modules: MutableList<Module> = SecurityJackson2Modules.getModules(loader)
|
||||
mapper.registerModules(modules)
|
||||
val mapper = JsonMapper.builder()
|
||||
.addModules(SecurityJacksonModules.getModules(loader))
|
||||
.build()
|
||||
|
||||
// ... use ObjectMapper as normally ...
|
||||
// ... use JsonMapper as normally ...
|
||||
val context: SecurityContext = SecurityContextImpl()
|
||||
// ...
|
||||
val json: String = mapper.writeValueAsString(context)
|
||||
@ -43,8 +43,8 @@ val json: String = mapper.writeValueAsString(context)
|
||||
====
|
||||
The following Spring Security modules provide Jackson support:
|
||||
|
||||
- spring-security-core (`CoreJackson2Module`)
|
||||
- spring-security-web (`WebJackson2Module`, `WebServletJackson2Module`, `WebServerJackson2Module`)
|
||||
- xref:servlet/oauth2/client/index.adoc#oauth2client[ spring-security-oauth2-client] (`OAuth2ClientJackson2Module`)
|
||||
- spring-security-cas (`CasJackson2Module`)
|
||||
- spring-security-core (`CoreJacksonModule`)
|
||||
- spring-security-web (`WebJacksonModule`, `WebServletJacksonModule`, `WebServerJacksonModule`)
|
||||
- xref:servlet/oauth2/client/index.adoc#oauth2client[ spring-security-oauth2-client] (`OAuth2ClientJacksonModule`)
|
||||
- spring-security-cas (`CasJacksonModule`)
|
||||
====
|
||||
|
@ -4,16 +4,16 @@
|
||||
Spring Security provides Jackson support for persisting Spring Security-related classes.
|
||||
This can improve the performance of serializing Spring Security-related classes when working with distributed sessions (session replication, Spring Session, and so on).
|
||||
|
||||
To use it, register the `SecurityJackson2Modules.getModules(ClassLoader)` with `ObjectMapper` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
|
||||
To use it, register the `SecurityJacksonModules.getModules(ClassLoader)` with `JsonMapper.Builder` (https://github.com/FasterXML/jackson-databind[jackson-databind]):
|
||||
|
||||
[source,java]
|
||||
----
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<Module> modules = SecurityJackson2Modules.getModules(loader);
|
||||
mapper.registerModules(modules);
|
||||
JsonMapper mapper = JsonMapper.builder()
|
||||
.addModules(SecurityJacksonModules.getModules(loader))
|
||||
.build();
|
||||
|
||||
// ... use ObjectMapper as normally ...
|
||||
// ... use JsonMapper as normally ...
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
// ...
|
||||
String json = mapper.writeValueAsString(context);
|
||||
@ -23,8 +23,8 @@ String json = mapper.writeValueAsString(context);
|
||||
====
|
||||
The following Spring Security modules provide Jackson support:
|
||||
|
||||
- spring-security-core (javadoc:org.springframework.security.jackson2.CoreJackson2Module[])
|
||||
- spring-security-web (javadoc:org.springframework.security.web.jackson2.WebJackson2Module[], javadoc:org.springframework.security.web.jackson2.WebServletJackson2Module[], javadoc:org.springframework.security.web.server.jackson2.WebServerJackson2Module[])
|
||||
- <<oauth2client, spring-security-oauth2-client>> (javadoc:org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module[])
|
||||
- spring-security-cas (javadoc:org.springframework.security.cas.jackson2.CasJackson2Module[])
|
||||
- spring-security-core (javadoc:org.springframework.security.jackson.CoreJacksonModule[])
|
||||
- spring-security-web (javadoc:org.springframework.security.web.jackson.WebJacksonModule[], javadoc:org.springframework.security.web.jackson.WebServletJacksonModule[], javadoc:org.springframework.security.web.server.jackson.WebServerJacksonModule[])
|
||||
- <<oauth2client, spring-security-oauth2-client>> (javadoc:org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule[])
|
||||
- spring-security-cas (javadoc:org.springframework.security.cas.jackson.CasJacksonModule[])
|
||||
====
|
||||
|
@ -11,6 +11,7 @@ dependencies {
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'ldapsdk:ldapsdk'
|
||||
optional "com.unboundid:unboundid-ldapsdk"
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
api ('org.springframework.ldap:spring-ldap-core') {
|
||||
exclude(group: 'commons-logging', module: 'commons-logging')
|
||||
exclude(group: 'org.springframework', module: 'spring-beans')
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPerson;
|
||||
|
||||
/**
|
||||
* This Jackson mixin is used to serialize/deserialize {@link InetOrgPerson}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
* @see LdapJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class InetOrgPersonMixin {
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthority;
|
||||
|
||||
/**
|
||||
* This Jackson mixin is used to serialize/deserialize {@link LdapAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
* @see LdapJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class LdapAuthorityMixin {
|
||||
|
||||
@JsonCreator
|
||||
LdapAuthorityMixin(@JsonProperty("role") String role, @JsonProperty("dn") String dn,
|
||||
@JsonProperty("attributes") Map<String, List<String>> attributes) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModule;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.ldap.userdetails.InetOrgPerson;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthority;
|
||||
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
|
||||
import org.springframework.security.ldap.userdetails.Person;
|
||||
|
||||
/**
|
||||
* Jackson module for {@code spring-security-ldap}. This module registers
|
||||
* {@link LdapAuthorityMixin}, {@link LdapUserDetailsImplMixin}, {@link PersonMixin},
|
||||
* {@link InetOrgPersonMixin}.
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class LdapJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public LdapJacksonModule() {
|
||||
super(LdapJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(InetOrgPerson.class)
|
||||
.allowIfSubType(LdapUserDetailsImpl.class)
|
||||
.allowIfSubType(Person.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixIn(LdapAuthority.class, LdapAuthorityMixin.class);
|
||||
context.setMixIn(LdapUserDetailsImpl.class, LdapUserDetailsImplMixin.class);
|
||||
context.setMixIn(Person.class, PersonMixin.class);
|
||||
context.setMixIn(InetOrgPerson.class, InetOrgPersonMixin.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.ldap.userdetails.LdapUserDetailsImpl;
|
||||
|
||||
/**
|
||||
* This Jackson mixin is used to serialize/deserialize {@link LdapUserDetailsImpl}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
* @see LdapJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class LdapUserDetailsImplMixin {
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.ldap.userdetails.Person;
|
||||
|
||||
/**
|
||||
* This Jackson mixin is used to serialize/deserialize {@link Person}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
* @see LdapJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class PersonMixin {
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support for LDAP.
|
||||
*/
|
||||
package org.springframework.security.ldap.jackson;
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 2 serialization support for LDAP.
|
||||
*/
|
||||
package org.springframework.security.ldap.jackson2;
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
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.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.springframework.security.ldap.jackson.InetOrgPersonMixin}.
|
||||
*/
|
||||
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 JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
InetOrgPersonContextMapper mapper = new InetOrgPersonContextMapper();
|
||||
InetOrgPerson p = (InetOrgPerson) mapper.mapUserFromContext(createUserContext(), "ghengis",
|
||||
AuthorityUtils.NO_AUTHORITIES);
|
||||
|
||||
String json = this.mapper.writeValueAsString(p);
|
||||
JSONAssert.assertEquals(INET_ORG_PERSON_JSON, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull() throws JacksonException, 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(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().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(LdapNameBuilder.newInstance("ignored=ignored").build());
|
||||
ctx.setAttributeValue("uid", "ghengis");
|
||||
ctx.setAttributeValue("userPassword", USER_PASSWORD);
|
||||
ctx.setAttributeValue("carLicense", "HORS1");
|
||||
ctx.setAttributeValue("cn", "Ghengis Khan");
|
||||
ctx.setAttributeValue("description", "Scary");
|
||||
ctx.setAttributeValue("destinationIndicator", "West");
|
||||
ctx.setAttributeValue("displayName", "Ghengis McCann");
|
||||
ctx.setAttributeValue("givenName", "Ghengis");
|
||||
ctx.setAttributeValue("homePhone", "+467575436521");
|
||||
ctx.setAttributeValue("initials", "G");
|
||||
ctx.setAttributeValue("employeeNumber", "00001");
|
||||
ctx.setAttributeValue("homePostalAddress", "Steppes");
|
||||
ctx.setAttributeValue("mail", "ghengis@mongolia");
|
||||
ctx.setAttributeValue("mobile", "always");
|
||||
ctx.setAttributeValue("o", "Hordes");
|
||||
ctx.setAttributeValue("ou", "Horde1");
|
||||
ctx.setAttributeValue("postalAddress", "On the Move");
|
||||
ctx.setAttributeValue("postalCode", "Changes Frequently");
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
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 org.springframework.security.ldap.jackson.LdapUserDetailsImplMixin}.
|
||||
*/
|
||||
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 JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@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 JacksonException, 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(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().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(LdapNameBuilder.newInstance("ignored=ignored").build());
|
||||
ctx.setAttributeValue("userPassword", USER_PASSWORD);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.jackson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
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 org.springframework.security.ldap.jackson.PersonMixin}.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
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 JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenEraseCredentialInvokedThenUserPasswordIsNull() throws JacksonException, 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(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().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(LdapNameBuilder.newInstance("ignored=ignored").build());
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -11,10 +11,11 @@ dependencies {
|
||||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
api "com.nimbusds:nimbus-jose-jwt"
|
||||
api "com.fasterxml.jackson.core:jackson-databind"
|
||||
api 'tools.jackson.core:jackson-databind'
|
||||
|
||||
optional "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
|
||||
optional "org.springframework:spring-jdbc"
|
||||
optional "com.fasterxml.jackson.core:jackson-databind"
|
||||
|
||||
testImplementation project(":spring-security-test")
|
||||
testImplementation project(path : ':spring-security-oauth2-jose', configuration : 'tests')
|
||||
|
@ -33,13 +33,15 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
@ -64,8 +66,10 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson.OAuth2AuthorizationServerJacksonModule;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@ -469,16 +473,12 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
private Mapper mapper = (ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper",
|
||||
OAuth2AuthorizationRowMapper.class.getClassLoader())) ? new JacksonDelegate() : new Jackson2Delegate();
|
||||
|
||||
public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
this.registeredClientRepository = registeredClientRepository;
|
||||
|
||||
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -623,9 +623,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
this.lobHandler = lobHandler;
|
||||
}
|
||||
|
||||
public final void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper cannot be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public final void setMapper(Mapper mapper) {
|
||||
Assert.notNull(mapper, "objectMapper cannot be null");
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
protected final RegisteredClientRepository getRegisteredClientRepository() {
|
||||
@ -636,13 +636,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return this.lobHandler;
|
||||
}
|
||||
|
||||
protected final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
protected final Mapper getMapper() {
|
||||
return this.mapper;
|
||||
}
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return this.objectMapper.readValue(data, new TypeReference<>() {
|
||||
return this.mapper.readValue(data, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@ -659,13 +659,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
public static class OAuth2AuthorizationParametersMapper
|
||||
implements Function<OAuth2Authorization, List<SqlParameterValue>> {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
private Mapper mapper = (ClassUtils.isPresent("tools.jackson.databind.json.JsonMapper",
|
||||
OAuth2AuthorizationRowMapper.class.getClassLoader())) ? new JacksonDelegate() : new Jackson2Delegate();
|
||||
|
||||
public OAuth2AuthorizationParametersMapper() {
|
||||
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -737,13 +734,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public final void setObjectMapper(ObjectMapper objectMapper) {
|
||||
Assert.notNull(objectMapper, "objectMapper cannot be null");
|
||||
this.objectMapper = objectMapper;
|
||||
public final void setMapper(Mapper mapper) {
|
||||
Assert.notNull(mapper, "mapper cannot be null");
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
protected final ObjectMapper getObjectMapper() {
|
||||
return this.objectMapper;
|
||||
protected final Mapper getMapper() {
|
||||
return this.mapper;
|
||||
}
|
||||
|
||||
private <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(String tokenColumnName,
|
||||
@ -774,7 +771,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
return this.mapper.writeValueAsString(data);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
@ -851,4 +848,74 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
}
|
||||
|
||||
public interface Mapper {
|
||||
|
||||
String writeValueAsString(Object data);
|
||||
|
||||
<T> T readValue(String value, ParameterizedTypeReference<T> typeReference);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public static class Jackson2Delegate implements Mapper {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public Jackson2Delegate() {
|
||||
ClassLoader classLoader = Jackson2Delegate.class.getClassLoader();
|
||||
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
|
||||
this.objectMapper.registerModules(securityModules);
|
||||
this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeValueAsString(Object data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
}
|
||||
catch (JsonProcessingException ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readValue(String value, ParameterizedTypeReference<T> typeReference) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.JavaType javaType = this.objectMapper.getTypeFactory()
|
||||
.constructType(typeReference.getType());
|
||||
return this.objectMapper.readValue(value, javaType);
|
||||
}
|
||||
catch (JsonProcessingException ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class JacksonDelegate implements Mapper {
|
||||
|
||||
private final JsonMapper jsonMapper;
|
||||
|
||||
public JacksonDelegate() {
|
||||
this.jsonMapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
public JacksonDelegate(JsonMapper.Builder builder) {
|
||||
this.jsonMapper = builder.addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeValueAsString(Object data) {
|
||||
return this.jsonMapper.writeValueAsString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T readValue(String value, ParameterizedTypeReference<T> typeReference) {
|
||||
tools.jackson.databind.JavaType javaType = this.jsonMapper.getTypeFactory()
|
||||
.constructType(typeReference.getType());
|
||||
return this.jsonMapper.readValue(value, javaType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* Utility class for {@code JsonNode}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class JsonNodeUtils {
|
||||
|
||||
static final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isString()) ? value.stringValue() : null;
|
||||
}
|
||||
|
||||
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
||||
DeserializationContext context) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isContainer())
|
||||
? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;
|
||||
}
|
||||
|
||||
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isObject()) ? value : null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link SignatureAlgorithm}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see SignatureAlgorithm
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class JwsAlgorithmMixin {
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.exc.InvalidFormatException;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestMixin
|
||||
*/
|
||||
final class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {
|
||||
|
||||
@Override
|
||||
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode root = context.readTree(parser);
|
||||
return deserialize(parser, context, root);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {
|
||||
AuthorizationGrantType authorizationGrantType = convertAuthorizationGrantType(
|
||||
JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
|
||||
Builder builder = getBuilder(parser, authorizationGrantType);
|
||||
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
|
||||
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
|
||||
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
|
||||
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, context));
|
||||
builder.state(JsonNodeUtils.findStringValue(root, "state"));
|
||||
builder.additionalParameters(
|
||||
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
|
||||
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||
return OAuth2AuthorizationRequest.authorizationCode();
|
||||
}
|
||||
throw new InvalidFormatException(parser, "Invalid authorizationGrantType", authorizationGrantType,
|
||||
AuthorizationGrantType.class);
|
||||
}
|
||||
|
||||
private static AuthorizationGrantType convertAuthorizationGrantType(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
|
||||
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestDeserializer
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizationRequestMixin {
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.DefaultTyping;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.jackson.CoreJacksonModule;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
|
||||
|
||||
/**
|
||||
* Jackson {@code Module} for {@code spring-security-oauth2-authorization-server}, that
|
||||
* registers the following mix-in annotations:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link OAuth2TokenExchangeActor}</li>
|
||||
* <li>{@link OAuth2AuthorizationRequestMixin}</li>
|
||||
* <li>{@link OAuth2TokenExchangeCompositeAuthenticationTokenMixin}</li>
|
||||
* <li>{@link JwsAlgorithmMixin}</li>
|
||||
* <li>{@link OAuth2TokenFormatMixin}</li>
|
||||
* </ul>
|
||||
*
|
||||
* If not already enabled, default typing will be automatically enabled as type info is
|
||||
* required to properly serialize/deserialize objects. In order to use this module just
|
||||
* add it to your {@code JsonMapper.Builder} configuration.
|
||||
*
|
||||
* <pre>
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(new OAuth2AuthorizationServerJacksonModule()).build;
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OAuth2AuthorizationServerJacksonModule extends CoreJacksonModule {
|
||||
|
||||
public OAuth2AuthorizationServerJacksonModule() {
|
||||
super(OAuth2AuthorizationServerJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
super.configurePolymorphicTypeValidator(builder);
|
||||
builder.allowIfSubType(OAuth2TokenFormat.class)
|
||||
.allowIfSubType(OAuth2TokenExchangeActor.class)
|
||||
.allowIfSubType(OAuth2TokenExchangeCompositeAuthenticationToken.class)
|
||||
.allowIfSubType(SignatureAlgorithm.class)
|
||||
.allowIfSubType(MacAlgorithm.class)
|
||||
.allowIfSubType(OAuth2AuthorizationRequest.class)
|
||||
.allowIfSubType(URL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
super.setupModule(context);
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder();
|
||||
this.configurePolymorphicTypeValidator(builder);
|
||||
((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(), DefaultTyping.NON_FINAL,
|
||||
JsonTypeInfo.As.PROPERTY);
|
||||
context.setMixIn(OAuth2TokenExchangeActor.class, OAuth2TokenExchangeActorMixin.class);
|
||||
context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
|
||||
context.setMixIn(OAuth2TokenExchangeCompositeAuthenticationToken.class,
|
||||
OAuth2TokenExchangeCompositeAuthenticationTokenMixin.class);
|
||||
context.setMixIn(SignatureAlgorithm.class, JwsAlgorithmMixin.class);
|
||||
context.setMixIn(MacAlgorithm.class, JwsAlgorithmMixin.class);
|
||||
context.setMixIn(OAuth2TokenFormat.class, OAuth2TokenFormatMixin.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeActor}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenExchangeActor
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenExchangeActorMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenExchangeActorMixin(@JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize
|
||||
* {@link OAuth2TokenExchangeCompositeAuthenticationToken}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenExchangeCompositeAuthenticationToken
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenExchangeCompositeAuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenExchangeCompositeAuthenticationTokenMixin(@JsonProperty("subject") Authentication subject,
|
||||
@JsonProperty("actors") List<Authentication> actors) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2TokenFormat}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2TokenFormat
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2TokenFormatMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2TokenFormatMixin(@JsonProperty("value") String value) {
|
||||
}
|
||||
|
||||
}
|
@ -29,11 +29,11 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
@ -747,7 +747,7 @@ public class JdbcOAuth2AuthorizationServiceTests {
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return getObjectMapper().readValue(data, new TypeReference<>() {
|
||||
return getMapper().readValue(data, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@ -852,7 +852,7 @@ public class JdbcOAuth2AuthorizationServiceTests {
|
||||
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return getObjectMapper().writeValueAsString(data);
|
||||
return getMapper().writeValueAsString(data);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
|
@ -365,6 +365,7 @@ public class JdbcRegisteredClientRepositoryTests {
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static final class CustomRegisteredClientRowMapper implements RowMapper<RegisteredClient> {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2AuthorizationServerJackson2Module}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public class OAuth2AuthorizationServerJacksonModuleTests {
|
||||
|
||||
private static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.mapper = JsonMapper.builder().addModules(new OAuth2AuthorizationServerJacksonModule()).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2AuthorizationAttributesThenSuccess() {
|
||||
Authentication principal = new UsernamePasswordAuthenticationToken("principal", "credentials");
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization()
|
||||
.attributes((attrs) -> attrs.put(Principal.class.getName(), principal))
|
||||
.build();
|
||||
Map<String, Object> attributes = authorization.getAttributes();
|
||||
String json = this.mapper.writeValueAsString(attributes);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(attributes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2AccessTokenMetadataThenSuccess() {
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();
|
||||
Map<String, Object> metadata = authorization.getAccessToken().getMetadata();
|
||||
String json = this.mapper.writeValueAsString(metadata);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(metadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenClientSettingsThenSuccess() {
|
||||
ClientSettings clientSettings = ClientSettings.builder()
|
||||
.tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256)
|
||||
.build();
|
||||
Map<String, Object> clientSettingsMap = clientSettings.getSettings();
|
||||
String json = this.mapper.writeValueAsString(clientSettingsMap);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(clientSettingsMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenTokenSettingsThenSuccess() {
|
||||
TokenSettings tokenSettings = TokenSettings.builder().build();
|
||||
Map<String, Object> tokenSettingsMap = tokenSettings.getSettings();
|
||||
String json = this.mapper.writeValueAsString(tokenSettingsMap);
|
||||
assertThat(this.mapper.readValue(json, STRING_OBJECT_MAP)).isEqualTo(tokenSettingsMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readValueWhenOAuth2TokenExchangeCompositeAuthenticationTokenThenSuccess() {
|
||||
Authentication subject = new UsernamePasswordAuthenticationToken("principal", "credentials");
|
||||
OAuth2TokenExchangeActor actor1 = new OAuth2TokenExchangeActor(
|
||||
Map.of(JwtClaimNames.ISS, "issuer-1", JwtClaimNames.SUB, "actor1"));
|
||||
OAuth2TokenExchangeActor actor2 = new OAuth2TokenExchangeActor(
|
||||
Map.of(JwtClaimNames.ISS, "issuer-2", JwtClaimNames.SUB, "actor2"));
|
||||
OAuth2TokenExchangeCompositeAuthenticationToken authentication = new OAuth2TokenExchangeCompositeAuthenticationToken(
|
||||
subject, List.of(actor1, actor2));
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
assertThat(this.mapper.readValue(json, OAuth2TokenExchangeCompositeAuthenticationToken.class))
|
||||
.isEqualTo(authentication);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.server.authorization.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link TestingAuthenticationToken}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see TestingAuthenticationToken
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true)
|
||||
public class TestingAuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
TestingAuthenticationTokenMixin(@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("credentials") Object credentials,
|
||||
@JsonProperty("authorities") List<GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,7 @@ dependencies {
|
||||
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
optional 'org.springframework:spring-jdbc'
|
||||
optional 'org.springframework:spring-r2dbc'
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
|
||||
testImplementation project(path: ':spring-security-oauth2-core', configuration: 'tests')
|
||||
testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link ClientRegistration}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see ClientRegistration
|
||||
* @see ClientRegistrationMixin
|
||||
*/
|
||||
final class ClientRegistrationDeserializer extends ValueDeserializer<ClientRegistration> {
|
||||
|
||||
private static final StdConverter<JsonNode, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_CONVERTER = new StdConverters.ClientAuthenticationMethodConverter();
|
||||
|
||||
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();
|
||||
|
||||
private static final StdConverter<JsonNode, AuthenticationMethod> AUTHENTICATION_METHOD_CONVERTER = new StdConverters.AuthenticationMethodConverter();
|
||||
|
||||
@Override
|
||||
public ClientRegistration deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode clientRegistrationNode = context.readTree(parser);
|
||||
JsonNode providerDetailsNode = JsonNodeUtils.findObjectNode(clientRegistrationNode, "providerDetails");
|
||||
JsonNode userInfoEndpointNode = JsonNodeUtils.findObjectNode(providerDetailsNode, "userInfoEndpoint");
|
||||
return ClientRegistration
|
||||
.withRegistrationId(JsonNodeUtils.findStringValue(clientRegistrationNode, "registrationId"))
|
||||
.clientId(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientId"))
|
||||
.clientSecret(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientSecret"))
|
||||
.clientAuthenticationMethod(CLIENT_AUTHENTICATION_METHOD_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "clientAuthenticationMethod")))
|
||||
.authorizationGrantType(AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(clientRegistrationNode, "authorizationGrantType")))
|
||||
.redirectUri(JsonNodeUtils.findStringValue(clientRegistrationNode, "redirectUri"))
|
||||
.scope(JsonNodeUtils.findValue(clientRegistrationNode, "scopes", JsonNodeUtils.STRING_SET, context))
|
||||
.clientName(JsonNodeUtils.findStringValue(clientRegistrationNode, "clientName"))
|
||||
.authorizationUri(JsonNodeUtils.findStringValue(providerDetailsNode, "authorizationUri"))
|
||||
.tokenUri(JsonNodeUtils.findStringValue(providerDetailsNode, "tokenUri"))
|
||||
.userInfoUri(JsonNodeUtils.findStringValue(userInfoEndpointNode, "uri"))
|
||||
.userInfoAuthenticationMethod(AUTHENTICATION_METHOD_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(userInfoEndpointNode, "authenticationMethod")))
|
||||
.userNameAttributeName(JsonNodeUtils.findStringValue(userInfoEndpointNode, "userNameAttributeName"))
|
||||
.jwkSetUri(JsonNodeUtils.findStringValue(providerDetailsNode, "jwkSetUri"))
|
||||
.issuerUri(JsonNodeUtils.findStringValue(providerDetailsNode, "issuerUri"))
|
||||
.providerConfigurationMetadata(JsonNodeUtils.findValue(providerDetailsNode, "configurationMetadata",
|
||||
JsonNodeUtils.STRING_OBJECT_MAP, context))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link ClientRegistration}. It also
|
||||
* registers a custom deserializer {@link ClientRegistrationDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see ClientRegistration
|
||||
* @see ClientRegistrationDeserializer
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = ClientRegistrationDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class ClientRegistrationMixin {
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see DefaultOAuth2User
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class DefaultOAuth2UserMixin {
|
||||
|
||||
@JsonCreator
|
||||
DefaultOAuth2UserMixin(@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes,
|
||||
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see DefaultOidcUser
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "attributes" })
|
||||
abstract class DefaultOidcUserMixin {
|
||||
|
||||
@JsonCreator
|
||||
DefaultOidcUserMixin(@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("idToken") OidcIdToken idToken, @JsonProperty("userInfo") OidcUserInfo userInfo,
|
||||
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* Utility class for {@code JsonNode}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class JsonNodeUtils {
|
||||
|
||||
static final TypeReference<Set<String>> STRING_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||
};
|
||||
|
||||
static String findStringValue(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isString()) ? value.stringValue() : null;
|
||||
}
|
||||
|
||||
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
|
||||
DeserializationContext context) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isContainer())
|
||||
? context.readTreeAsValue(value, context.getTypeFactory().constructType(valueTypeReference)) : null;
|
||||
}
|
||||
|
||||
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
|
||||
if (jsonNode == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = jsonNode.findValue(fieldName);
|
||||
return (value != null && value.isObject()) ? value : null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AccessToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AccessToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AccessTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AccessTokenMixin(
|
||||
@JsonProperty("tokenType") @JsonDeserialize(
|
||||
converter = StdConverters.AccessTokenTypeConverter.class) OAuth2AccessToken.TokenType tokenType,
|
||||
@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt,
|
||||
@JsonProperty("expiresAt") Instant expiresAt, @JsonProperty("scopes") Set<String> scopes) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize
|
||||
* {@link OAuth2AuthenticationException}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Dennis Neufeld
|
||||
* @author Steve Riesenberg
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthenticationException
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "cause", "stackTrace", "suppressedExceptions" })
|
||||
abstract class OAuth2AuthenticationExceptionMixin {
|
||||
|
||||
@JsonProperty("error")
|
||||
abstract OAuth2Error getError();
|
||||
|
||||
@JsonProperty("detailMessage")
|
||||
abstract String getMessage();
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthenticationExceptionMixin(@JsonProperty("error") OAuth2Error error,
|
||||
@JsonProperty("detailMessage") String message) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthenticationToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0O
|
||||
* @see OAuth2AuthenticationToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "authenticated" })
|
||||
abstract class OAuth2AuthenticationTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthenticationTokenMixin(@JsonProperty("principal") OAuth2User principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("authorizedClientRegistrationId") String authorizedClientRegistrationId) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
|
||||
|
||||
/**
|
||||
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestMixin
|
||||
*/
|
||||
final class OAuth2AuthorizationRequestDeserializer extends ValueDeserializer<OAuth2AuthorizationRequest> {
|
||||
|
||||
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();
|
||||
|
||||
@Override
|
||||
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) {
|
||||
JsonNode root = context.readTree(parser);
|
||||
return deserialize(parser, context, root);
|
||||
}
|
||||
|
||||
private OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context, JsonNode root) {
|
||||
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER
|
||||
.convert(JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
|
||||
Builder builder = getBuilder(parser, authorizationGrantType);
|
||||
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
|
||||
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
|
||||
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
|
||||
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, context));
|
||||
builder.state(JsonNodeUtils.findStringValue(root, "state"));
|
||||
builder.additionalParameters(
|
||||
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
|
||||
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, context));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType) {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||
return OAuth2AuthorizationRequest.authorizationCode();
|
||||
}
|
||||
throw new StreamReadException(parser, "Invalid authorizationGrantType");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
|
||||
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizationRequest
|
||||
* @see OAuth2AuthorizationRequestDeserializer
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizationRequestMixin {
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizedClient}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2AuthorizedClient
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2AuthorizedClientMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2AuthorizedClientMixin(@JsonProperty("clientRegistration") ClientRegistration clientRegistration,
|
||||
@JsonProperty("principalName") String principalName,
|
||||
@JsonProperty("accessToken") OAuth2AccessToken accessToken,
|
||||
@JsonProperty("refreshToken") OAuth2RefreshToken refreshToken) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModule;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
/**
|
||||
* Jackson {@code Module} for {@code spring-security-oauth2-client}, that registers the
|
||||
* following mix-in annotations:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link OAuth2AuthorizationRequestMixin}</li>
|
||||
* <li>{@link ClientRegistrationMixin}</li>
|
||||
* <li>{@link OAuth2AccessTokenMixin}</li>
|
||||
* <li>{@link OAuth2RefreshTokenMixin}</li>
|
||||
* <li>{@link OAuth2AuthorizedClientMixin}</li>
|
||||
* <li>{@link OAuth2UserAuthorityMixin}</li>
|
||||
* <li>{@link DefaultOAuth2UserMixin}</li>
|
||||
* <li>{@link OidcIdTokenMixin}</li>
|
||||
* <li>{@link OidcUserInfoMixin}</li>
|
||||
* <li>{@link OidcUserAuthorityMixin}</li>
|
||||
* <li>{@link DefaultOidcUserMixin}</li>
|
||||
* <li>{@link OAuth2AuthenticationTokenMixin}</li>
|
||||
* <li>{@link OAuth2AuthenticationExceptionMixin}</li>
|
||||
* <li>{@link OAuth2ErrorMixin}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OAuth2ClientJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public OAuth2ClientJacksonModule() {
|
||||
super(OAuth2ClientJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(OAuth2AuthenticationException.class)
|
||||
.allowIfSubType(DefaultOidcUser.class)
|
||||
.allowIfSubType(OAuth2AuthorizationRequest.class)
|
||||
.allowIfSubType(OAuth2Error.class)
|
||||
.allowIfSubType(OAuth2AuthorizedClient.class)
|
||||
.allowIfSubType(OidcIdToken.class)
|
||||
.allowIfSubType(OidcUserInfo.class)
|
||||
.allowIfSubType(DefaultOAuth2User.class)
|
||||
.allowIfSubType(ClientRegistration.class)
|
||||
.allowIfSubType(OAuth2AccessToken.class)
|
||||
.allowIfSubType(OAuth2RefreshToken.class)
|
||||
.allowIfSubType(OAuth2AuthenticationToken.class)
|
||||
.allowIfSubType(OidcUserAuthority.class)
|
||||
.allowIfSubType(OAuth2UserAuthority.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixIn(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class);
|
||||
context.setMixIn(ClientRegistration.class, ClientRegistrationMixin.class);
|
||||
context.setMixIn(OAuth2AccessToken.class, OAuth2AccessTokenMixin.class);
|
||||
context.setMixIn(OAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);
|
||||
context.setMixIn(OAuth2AuthorizedClient.class, OAuth2AuthorizedClientMixin.class);
|
||||
context.setMixIn(OAuth2UserAuthority.class, OAuth2UserAuthorityMixin.class);
|
||||
context.setMixIn(DefaultOAuth2User.class, DefaultOAuth2UserMixin.class);
|
||||
context.setMixIn(OidcIdToken.class, OidcIdTokenMixin.class);
|
||||
context.setMixIn(OidcUserInfo.class, OidcUserInfoMixin.class);
|
||||
context.setMixIn(OidcUserAuthority.class, OidcUserAuthorityMixin.class);
|
||||
context.setMixIn(DefaultOidcUser.class, DefaultOidcUserMixin.class);
|
||||
context.setMixIn(OAuth2AuthenticationToken.class, OAuth2AuthenticationTokenMixin.class);
|
||||
context.setMixIn(OAuth2AuthenticationException.class, OAuth2AuthenticationExceptionMixin.class);
|
||||
context.setMixIn(OAuth2Error.class, OAuth2ErrorMixin.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2Error} as part of
|
||||
* {@link org.springframework.security.oauth2.core.OAuth2AuthenticationException}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Dennis Neufeld
|
||||
* @since 7.0
|
||||
* @see OAuth2Error
|
||||
* @see OAuth2AuthenticationExceptionMixin
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2ErrorMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2ErrorMixin(@JsonProperty("errorCode") String errorCode, @JsonProperty("description") String description,
|
||||
@JsonProperty("uri") String uri) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2RefreshToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2RefreshToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2RefreshTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2RefreshTokenMixin(@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OAuth2UserAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OAuth2UserAuthority
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OAuth2UserAuthorityMixin {
|
||||
|
||||
@JsonCreator
|
||||
OAuth2UserAuthorityMixin(@JsonProperty("authority") String authority,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcIdToken}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcIdToken
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OidcIdTokenMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcIdTokenMixin(@JsonProperty("tokenValue") String tokenValue, @JsonProperty("issuedAt") Instant issuedAt,
|
||||
@JsonProperty("expiresAt") Instant expiresAt, @JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcUserAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcUserAuthority
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties({ "attributes" })
|
||||
abstract class OidcUserAuthorityMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcUserAuthorityMixin(@JsonProperty("authority") String authority, @JsonProperty("idToken") OidcIdToken idToken,
|
||||
@JsonProperty("userInfo") OidcUserInfo userInfo) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize/deserialize {@link OidcUserInfo}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
* @see OidcUserInfo
|
||||
* @see OAuth2ClientJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class OidcUserInfoMixin {
|
||||
|
||||
@JsonCreator
|
||||
OidcUserInfoMixin(@JsonProperty("claims") Map<String, Object> claims) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.AuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
|
||||
/**
|
||||
* {@code StdConverter} implementations.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Joe Grandja
|
||||
* @since 7.0
|
||||
*/
|
||||
abstract class StdConverters {
|
||||
|
||||
static final class AccessTokenTypeConverter extends StdConverter<JsonNode, OAuth2AccessToken.TokenType> {
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken.TokenType convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(value)) {
|
||||
return OAuth2AccessToken.TokenType.BEARER;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class ClientAuthenticationMethodConverter extends StdConverter<JsonNode, ClientAuthenticationMethod> {
|
||||
|
||||
@Override
|
||||
public ClientAuthenticationMethod convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
return ClientAuthenticationMethod.valueOf(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class AuthorizationGrantTypeConverter extends StdConverter<JsonNode, AuthorizationGrantType> {
|
||||
|
||||
@Override
|
||||
public AuthorizationGrantType convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
}
|
||||
if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthorizationGrantType.CLIENT_CREDENTIALS;
|
||||
}
|
||||
return new AuthorizationGrantType(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class AuthenticationMethodConverter extends StdConverter<JsonNode, AuthenticationMethod> {
|
||||
|
||||
@Override
|
||||
public AuthenticationMethod convert(JsonNode jsonNode) {
|
||||
String value = JsonNodeUtils.findStringValue(jsonNode, "value");
|
||||
if (AuthenticationMethod.HEADER.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.HEADER;
|
||||
}
|
||||
if (AuthenticationMethod.FORM.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.FORM;
|
||||
}
|
||||
if (AuthenticationMethod.QUERY.getValue().equalsIgnoreCase(value)) {
|
||||
return AuthenticationMethod.QUERY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support for OAuth2 client.
|
||||
*/
|
||||
package org.springframework.security.oauth2.client.jackson;
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 2 serialization support for OAuth2 client.
|
||||
*/
|
||||
package org.springframework.security.oauth2.client.jackson2;
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationExceptionMixin}.
|
||||
*
|
||||
* @author Dennis Neufeld
|
||||
* @since 5.3.4
|
||||
*/
|
||||
public class OAuth2AuthenticationExceptionMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]", "Authorization Request Not Found", "/foo/bar"),
|
||||
"Authorization Request Not Found");
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
String expected = asJson(exception);
|
||||
JSONAssert.assertEquals(expected, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]"));
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
String expected = asJson(exception);
|
||||
JSONAssert.assertEquals(expected, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
String json = asJson(new OAuth2AuthenticationException(new OAuth2Error("[authorization_request_not_found]")));
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
OAuth2AuthenticationException expected = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]", "Authorization Request Not Found", "/foo/bar"),
|
||||
"Authorization Request Not Found");
|
||||
OAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),
|
||||
OAuth2AuthenticationException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isEqualTo(expected.getMessage());
|
||||
OAuth2Error oauth2Error = exception.getError();
|
||||
assertThat(oauth2Error).isNotNull();
|
||||
assertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());
|
||||
assertThat(oauth2Error.getDescription()).isEqualTo(expected.getError().getDescription());
|
||||
assertThat(oauth2Error.getUri()).isEqualTo(expected.getError().getUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
OAuth2AuthenticationException expected = new OAuth2AuthenticationException(
|
||||
new OAuth2Error("[authorization_request_not_found]"));
|
||||
OAuth2AuthenticationException exception = this.mapper.readValue(asJson(expected),
|
||||
OAuth2AuthenticationException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isNull();
|
||||
OAuth2Error oauth2Error = exception.getError();
|
||||
assertThat(oauth2Error).isNotNull();
|
||||
assertThat(oauth2Error.getErrorCode()).isEqualTo(expected.getError().getErrorCode());
|
||||
assertThat(oauth2Error.getDescription()).isNull();
|
||||
assertThat(oauth2Error.getUri()).isNull();
|
||||
}
|
||||
|
||||
private String asJson(OAuth2AuthenticationException exception) {
|
||||
OAuth2Error error = exception.getError();
|
||||
// @formatter:off
|
||||
return "\n{"
|
||||
+ "\n \"@class\": \"org.springframework.security.oauth2.core.OAuth2AuthenticationException\","
|
||||
+ "\n \"error\":"
|
||||
+ "\n {"
|
||||
+ "\n \"@class\":\"org.springframework.security.oauth2.core.OAuth2Error\","
|
||||
+ "\n \"errorCode\":\"" + error.getErrorCode() + "\","
|
||||
+ "\n \"description\":" + jsonStringOrNull(error.getDescription()) + ","
|
||||
+ "\n \"uri\":" + jsonStringOrNull(error.getUri())
|
||||
+ "\n },"
|
||||
+ "\n \"detailMessage\":" + jsonStringOrNull(exception.getMessage())
|
||||
+ "\n}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private String jsonStringOrNull(String input) {
|
||||
return (input != null) ? "\"" + input + "\"" : "null";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.DeserializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.authentication.TestOAuth2AuthenticationTokens;
|
||||
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
|
||||
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthenticationTokenMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthenticationTokenMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder()
|
||||
.addModules(SecurityJacksonModules.getModules(loader))
|
||||
// see https://github.com/FasterXML/jackson-databind/issues/3052 for details
|
||||
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
// OidcUser
|
||||
OAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String expectedJson = asJson(authentication);
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
// OAuth2User
|
||||
authentication = TestOAuth2AuthenticationTokens.authenticated();
|
||||
expectedJson = asJson(authentication);
|
||||
json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
DefaultOidcUser principal = TestOidcUsers.create();
|
||||
principal = new DefaultOidcUser(principal.getAuthorities(), principal.getIdToken());
|
||||
OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(),
|
||||
"registration-id");
|
||||
String expectedJson = asJson(authentication);
|
||||
String json = this.mapper.writeValueAsString(authentication);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
OAuth2AuthenticationToken authentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String json = asJson(authentication);
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthenticationToken.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
// OidcUser
|
||||
OAuth2AuthenticationToken expectedAuthentication = TestOAuth2AuthenticationTokens.oidcAuthenticated();
|
||||
String json = asJson(expectedAuthentication);
|
||||
OAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOidcUser expectedOidcUser = (DefaultOidcUser) expectedAuthentication.getPrincipal();
|
||||
DefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal();
|
||||
assertThat(oidcUser.getAuthorities().containsAll(expectedOidcUser.getAuthorities())).isTrue();
|
||||
assertThat(oidcUser.getAttributes()).containsExactlyEntriesOf(expectedOidcUser.getAttributes());
|
||||
assertThat(oidcUser.getName()).isEqualTo(expectedOidcUser.getName());
|
||||
OidcIdToken expectedIdToken = expectedOidcUser.getIdToken();
|
||||
OidcIdToken idToken = oidcUser.getIdToken();
|
||||
assertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());
|
||||
assertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());
|
||||
assertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());
|
||||
assertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());
|
||||
OidcUserInfo expectedUserInfo = expectedOidcUser.getUserInfo();
|
||||
OidcUserInfo userInfo = oidcUser.getUserInfo();
|
||||
assertThat(userInfo.getClaims()).containsExactlyEntriesOf(expectedUserInfo.getClaims());
|
||||
// OAuth2User
|
||||
expectedAuthentication = TestOAuth2AuthenticationTokens.authenticated();
|
||||
json = asJson(expectedAuthentication);
|
||||
authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).containsExactlyElementsOf(expectedAuthentication.getAuthorities());
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOAuth2User expectedOauth2User = (DefaultOAuth2User) expectedAuthentication.getPrincipal();
|
||||
DefaultOAuth2User oauth2User = (DefaultOAuth2User) authentication.getPrincipal();
|
||||
assertThat(oauth2User.getAuthorities().containsAll(expectedOauth2User.getAuthorities())).isTrue();
|
||||
assertThat(oauth2User.getAttributes()).containsExactlyEntriesOf(expectedOauth2User.getAttributes());
|
||||
assertThat(oauth2User.getName()).isEqualTo(expectedOauth2User.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
DefaultOidcUser expectedPrincipal = TestOidcUsers.create();
|
||||
expectedPrincipal = new DefaultOidcUser(expectedPrincipal.getAuthorities(), expectedPrincipal.getIdToken());
|
||||
OAuth2AuthenticationToken expectedAuthentication = new OAuth2AuthenticationToken(expectedPrincipal,
|
||||
Collections.emptyList(), "registration-id");
|
||||
String json = asJson(expectedAuthentication);
|
||||
OAuth2AuthenticationToken authentication = this.mapper.readValue(json, OAuth2AuthenticationToken.class);
|
||||
assertThat(authentication.getAuthorities()).isEmpty();
|
||||
assertThat(authentication.getDetails()).isEqualTo(expectedAuthentication.getDetails());
|
||||
assertThat(authentication.isAuthenticated()).isEqualTo(expectedAuthentication.isAuthenticated());
|
||||
assertThat(authentication.getAuthorizedClientRegistrationId())
|
||||
.isEqualTo(expectedAuthentication.getAuthorizedClientRegistrationId());
|
||||
DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
|
||||
assertThat(principal.getAuthorities().containsAll(expectedPrincipal.getAuthorities())).isTrue();
|
||||
assertThat(principal.getAttributes()).containsExactlyEntriesOf(expectedPrincipal.getAttributes());
|
||||
assertThat(principal.getName()).isEqualTo(expectedPrincipal.getName());
|
||||
OidcIdToken expectedIdToken = expectedPrincipal.getIdToken();
|
||||
OidcIdToken idToken = principal.getIdToken();
|
||||
assertThat(idToken.getTokenValue()).isEqualTo(expectedIdToken.getTokenValue());
|
||||
assertThat(idToken.getIssuedAt()).isEqualTo(expectedIdToken.getIssuedAt());
|
||||
assertThat(idToken.getExpiresAt()).isEqualTo(expectedIdToken.getExpiresAt());
|
||||
assertThat(idToken.getClaims()).containsExactlyEntriesOf(expectedIdToken.getClaims());
|
||||
assertThat(principal.getUserInfo()).isNull();
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthenticationToken authentication) {
|
||||
String principalJson = (authentication.getPrincipal() instanceof DefaultOidcUser)
|
||||
? asJson((DefaultOidcUser) authentication.getPrincipal())
|
||||
: asJson((DefaultOAuth2User) authentication.getPrincipal());
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken\",\n" +
|
||||
" \"principal\": " + principalJson + ",\n" +
|
||||
" \"authorities\": " + asJson(authentication.getAuthorities(), "java.util.Collections$UnmodifiableRandomAccessList") + ",\n" +
|
||||
" \"authorizedClientRegistrationId\": \"" + authentication.getAuthorizedClientRegistrationId() + "\",\n" +
|
||||
" \"details\": null\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(DefaultOAuth2User oauth2User) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.user.DefaultOAuth2User\",\n" +
|
||||
" \"authorities\": " + asJson(oauth2User.getAuthorities(), "java.util.Collections$UnmodifiableSet") + ",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"username\": \"user\"\n" +
|
||||
" },\n" +
|
||||
" \"nameAttributeKey\": \"username\"\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(DefaultOidcUser oidcUser) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser\",\n" +
|
||||
" \"authorities\": " + asJson(oidcUser.getAuthorities(), "java.util.Collections$UnmodifiableSet") + ",\n" +
|
||||
" \"idToken\": " + asJson(oidcUser.getIdToken()) + ",\n" +
|
||||
" \"userInfo\": " + asJson(oidcUser.getUserInfo()) + ",\n" +
|
||||
" \"nameAttributeKey\": \"" + IdTokenClaimNames.SUB + "\"\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(Collection<? extends GrantedAuthority> authorities, String classTypeInfo) {
|
||||
OAuth2UserAuthority oauth2UserAuthority = null;
|
||||
OidcUserAuthority oidcUserAuthority = null;
|
||||
List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
if (authority instanceof OidcUserAuthority) {
|
||||
oidcUserAuthority = (OidcUserAuthority) authority;
|
||||
}
|
||||
else if (authority instanceof OAuth2UserAuthority) {
|
||||
oauth2UserAuthority = (OAuth2UserAuthority) authority;
|
||||
}
|
||||
else if (authority instanceof SimpleGrantedAuthority) {
|
||||
simpleAuthorities.add((SimpleGrantedAuthority) authority);
|
||||
}
|
||||
}
|
||||
String authoritiesJson = (oidcUserAuthority != null) ? asJson(oidcUserAuthority)
|
||||
: (oauth2UserAuthority != null) ? asJson(oauth2UserAuthority) : "";
|
||||
if (!simpleAuthorities.isEmpty()) {
|
||||
if (StringUtils.hasLength(authoritiesJson)) {
|
||||
authoritiesJson += ",";
|
||||
}
|
||||
authoritiesJson += asJson(simpleAuthorities);
|
||||
}
|
||||
// @formatter:off
|
||||
return "[\n" +
|
||||
" \"" + classTypeInfo + "\",\n" +
|
||||
" [" + authoritiesJson + "]\n" +
|
||||
" ]";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2UserAuthority oauth2UserAuthority) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\",\n" +
|
||||
" \"authority\": \"" + oauth2UserAuthority.getAuthority() + "\",\n" +
|
||||
" \"userNameAttributeName\": \"username\",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"username\": \"user\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcUserAuthority oidcUserAuthority) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\",\n" +
|
||||
" \"authority\": \"" + oidcUserAuthority.getAuthority() + "\",\n" +
|
||||
" \"userNameAttributeName\": \"" + oidcUserAuthority.getUserNameAttributeName() + "\",\n" +
|
||||
" \"idToken\": " + asJson(oidcUserAuthority.getIdToken()) + ",\n" +
|
||||
" \"userInfo\": " + asJson(oidcUserAuthority.getUserInfo()) + "\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(List<SimpleGrantedAuthority> simpleAuthorities) {
|
||||
// @formatter:off
|
||||
return simpleAuthorities.stream()
|
||||
.map((authority) -> "{\n" +
|
||||
" \"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\",\n" +
|
||||
" \"authority\": \"" + authority.getAuthority() + "\"\n" +
|
||||
" }")
|
||||
.collect(Collectors.joining(","));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcIdToken idToken) {
|
||||
String aud = "";
|
||||
if (!CollectionUtils.isEmpty(idToken.getAudience())) {
|
||||
aud = StringUtils.collectionToDelimitedString(idToken.getAudience(), ",", "\"", "\"");
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.OidcIdToken\",\n" +
|
||||
" \"tokenValue\": \"" + idToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(idToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(idToken.getExpiresAt()) + ",\n" +
|
||||
" \"claims\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"iat\": [\n" +
|
||||
" \"java.time.Instant\",\n" +
|
||||
" " + toString(idToken.getIssuedAt()) + "\n" +
|
||||
" ],\n" +
|
||||
" \"exp\": [\n" +
|
||||
" \"java.time.Instant\",\n" +
|
||||
" " + toString(idToken.getExpiresAt()) + "\n" +
|
||||
" ],\n" +
|
||||
" \"sub\": \"" + idToken.getSubject() + "\",\n" +
|
||||
" \"iss\": \"" + idToken.getIssuer() + "\",\n" +
|
||||
" \"aud\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + aud + "]\n" +
|
||||
" ],\n" +
|
||||
" \"azp\": \"" + idToken.getAuthorizedParty() + "\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OidcUserInfo userInfo) {
|
||||
if (userInfo == null) {
|
||||
return null;
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.oidc.OidcUserInfo\",\n" +
|
||||
" \"claims\": {\n" +
|
||||
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
|
||||
" \"sub\": \"" + userInfo.getSubject() + "\",\n" +
|
||||
" \"name\": \"" + userInfo.getClaim(StandardClaimNames.NAME) + "\"\n" +
|
||||
" }\n" +
|
||||
" }";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String toString(Instant instant) {
|
||||
if (instant == null) {
|
||||
return null;
|
||||
}
|
||||
return DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizationRequestMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthorizationRequestMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
private OAuth2AuthorizationRequest.Builder authorizationRequestBuilder;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
Map<String, Object> additionalParameters = new LinkedHashMap<>();
|
||||
additionalParameters.put("param1", "value1");
|
||||
additionalParameters.put("param2", "value2");
|
||||
// @formatter:off
|
||||
this.authorizationRequestBuilder = TestOAuth2AuthorizationRequests.request()
|
||||
.scope("read", "write")
|
||||
.additionalParameters(additionalParameters);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String expectedJson = asJson(authorizationRequest);
|
||||
String json = this.mapper.writeValueAsString(authorizationRequest);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
// @formatter:off
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder
|
||||
.scopes(null)
|
||||
.state(null)
|
||||
.additionalParameters(Map::clear)
|
||||
.attributes(Map::clear)
|
||||
.build();
|
||||
// @formatter:on
|
||||
String expectedJson = asJson(authorizationRequest);
|
||||
String json = this.mapper.writeValueAsString(authorizationRequest);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
String json = asJson(this.authorizationRequestBuilder.build());
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizationRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
OAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String json = asJson(expectedAuthorizationRequest);
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);
|
||||
assertThat(authorizationRequest.getAuthorizationUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());
|
||||
assertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());
|
||||
assertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());
|
||||
assertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());
|
||||
assertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());
|
||||
assertThat(authorizationRequest.getScopes()).isEqualTo(expectedAuthorizationRequest.getScopes());
|
||||
assertThat(authorizationRequest.getState()).isEqualTo(expectedAuthorizationRequest.getState());
|
||||
assertThat(authorizationRequest.getAdditionalParameters())
|
||||
.containsExactlyEntriesOf(expectedAuthorizationRequest.getAdditionalParameters());
|
||||
assertThat(authorizationRequest.getAuthorizationRequestUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());
|
||||
assertThat(authorizationRequest.getAttributes())
|
||||
.containsExactlyEntriesOf(expectedAuthorizationRequest.getAttributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
// @formatter:off
|
||||
OAuth2AuthorizationRequest expectedAuthorizationRequest = this.authorizationRequestBuilder.scopes(null)
|
||||
.state(null)
|
||||
.additionalParameters(Map::clear)
|
||||
.attributes(Map::clear)
|
||||
.build();
|
||||
// @formatter:on
|
||||
String json = asJson(expectedAuthorizationRequest);
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.mapper.readValue(json, OAuth2AuthorizationRequest.class);
|
||||
assertThat(authorizationRequest.getAuthorizationUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationUri());
|
||||
assertThat(authorizationRequest.getGrantType()).isEqualTo(expectedAuthorizationRequest.getGrantType());
|
||||
assertThat(authorizationRequest.getResponseType()).isEqualTo(expectedAuthorizationRequest.getResponseType());
|
||||
assertThat(authorizationRequest.getClientId()).isEqualTo(expectedAuthorizationRequest.getClientId());
|
||||
assertThat(authorizationRequest.getRedirectUri()).isEqualTo(expectedAuthorizationRequest.getRedirectUri());
|
||||
assertThat(authorizationRequest.getScopes()).isEmpty();
|
||||
assertThat(authorizationRequest.getState()).isNull();
|
||||
assertThat(authorizationRequest.getAdditionalParameters()).isEmpty();
|
||||
assertThat(authorizationRequest.getAuthorizationRequestUri())
|
||||
.isEqualTo(expectedAuthorizationRequest.getAuthorizationRequestUri());
|
||||
assertThat(authorizationRequest.getAttributes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenInvalidAuthorizationGrantTypeThenThrowJsonParseException() {
|
||||
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestBuilder.build();
|
||||
String json = asJson(authorizationRequest).replace("authorization_code", "client_credentials");
|
||||
assertThatExceptionOfType(StreamReadException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(json, OAuth2AuthorizationRequest.class))
|
||||
.withMessageContaining("Invalid authorizationGrantType");
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthorizationRequest authorizationRequest) {
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(authorizationRequest.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String additionalParameters = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getAdditionalParameters())) {
|
||||
additionalParameters += "," + authorizationRequest.getAdditionalParameters()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + authorizationRequest.getAdditionalParameters().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
String attributes = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(authorizationRequest.getAttributes())) {
|
||||
attributes += "," + authorizationRequest.getAttributes()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + authorizationRequest.getAttributes().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest\",\n" +
|
||||
" \"authorizationUri\": \"" + authorizationRequest.getAuthorizationUri() + "\",\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + authorizationRequest.getGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"responseType\": {\n" +
|
||||
" \"value\": \"" + authorizationRequest.getResponseType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"clientId\": \"" + authorizationRequest.getClientId() + "\",\n" +
|
||||
" \"redirectUri\": \"" + authorizationRequest.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"state\": " + ((authorizationRequest.getState() != null) ? "\"" + authorizationRequest.getState() + "\"" : "null") + ",\n" +
|
||||
" \"additionalParameters\": {\n" +
|
||||
" " + additionalParameters + "\n" +
|
||||
" },\n" +
|
||||
" \"authorizationRequestUri\": \"" + authorizationRequest.getAuthorizationRequestUri() + "\",\n" +
|
||||
" \"attributes\": {\n" +
|
||||
" " + attributes + "\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.datatype.jsr310.DecimalUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.jackson.SecurityJacksonModules;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.TestOAuth2AccessTokens;
|
||||
import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for
|
||||
* {@link org.springframework.security.oauth2.client.jackson.OAuth2AuthorizedClientMixin}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class OAuth2AuthorizedClientMixinTests {
|
||||
|
||||
private JsonMapper mapper;
|
||||
|
||||
private ClientRegistration.Builder clientRegistrationBuilder;
|
||||
|
||||
private OAuth2AccessToken accessToken;
|
||||
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
|
||||
private String principalName;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
|
||||
Map<String, Object> providerConfigurationMetadata = new LinkedHashMap<>();
|
||||
providerConfigurationMetadata.put("config1", "value1");
|
||||
providerConfigurationMetadata.put("config2", "value2");
|
||||
// @formatter:off
|
||||
this.clientRegistrationBuilder = TestClientRegistrations.clientRegistration()
|
||||
.authorizationGrantType(new AuthorizationGrantType("custom-grant"))
|
||||
.scope("read", "write")
|
||||
.providerConfigurationMetadata(providerConfigurationMetadata);
|
||||
// @formatter:on
|
||||
this.accessToken = TestOAuth2AccessTokens.scopes("read", "write");
|
||||
this.refreshToken = TestOAuth2RefreshTokens.refreshToken();
|
||||
this.principalName = "principal-name";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenMixinRegisteredThenSerializes() throws Exception {
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),
|
||||
this.principalName, this.accessToken, this.refreshToken);
|
||||
String expectedJson = asJson(authorizedClient);
|
||||
String json = this.mapper.writeValueAsString(authorizedClient);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenRequiredAttributesOnlyThenSerializes() throws Exception {
|
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration()
|
||||
.clientSecret(null)
|
||||
.clientName(null)
|
||||
.userInfoUri(null)
|
||||
.userNameAttributeName(null)
|
||||
.jwkSetUri(null)
|
||||
.issuerUri(null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, this.principalName,
|
||||
TestOAuth2AccessTokens.noScopes());
|
||||
String expectedJson = asJson(authorizedClient);
|
||||
String json = this.mapper.writeValueAsString(authorizedClient);
|
||||
JSONAssert.assertEquals(expectedJson, json, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinNotRegisteredThenThrowJsonProcessingException() {
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.clientRegistrationBuilder.build(),
|
||||
this.principalName, this.accessToken);
|
||||
String json = asJson(authorizedClient);
|
||||
assertThatExceptionOfType(JacksonException.class)
|
||||
.isThrownBy(() -> new JsonMapper().readValue(json, OAuth2AuthorizedClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenMixinRegisteredThenDeserializes() throws Exception {
|
||||
ClientRegistration expectedClientRegistration = this.clientRegistrationBuilder.build();
|
||||
OAuth2AccessToken expectedAccessToken = this.accessToken;
|
||||
OAuth2RefreshToken expectedRefreshToken = this.refreshToken;
|
||||
OAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,
|
||||
this.principalName, expectedAccessToken, expectedRefreshToken);
|
||||
String json = asJson(expectedAuthorizedClient);
|
||||
OAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);
|
||||
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
||||
assertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());
|
||||
assertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());
|
||||
assertThat(clientRegistration.getClientSecret()).isEqualTo(expectedClientRegistration.getClientSecret());
|
||||
assertThat(clientRegistration.getClientAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());
|
||||
assertThat(clientRegistration.getAuthorizationGrantType())
|
||||
.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());
|
||||
assertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());
|
||||
assertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());
|
||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getTokenUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isEqualTo(
|
||||
expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName());
|
||||
assertThat(clientRegistration.getProviderDetails().getJwkSetUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getJwkSetUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getIssuerUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata())
|
||||
.containsExactlyEntriesOf(clientRegistration.getProviderDetails().getConfigurationMetadata());
|
||||
assertThat(clientRegistration.getClientName()).isEqualTo(expectedClientRegistration.getClientName());
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
assertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());
|
||||
assertThat(accessToken.getScopes()).isEqualTo(expectedAccessToken.getScopes());
|
||||
assertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());
|
||||
assertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());
|
||||
assertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());
|
||||
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();
|
||||
assertThat(refreshToken.getTokenValue()).isEqualTo(expectedRefreshToken.getTokenValue());
|
||||
assertThat(refreshToken.getIssuedAt()).isEqualTo(expectedRefreshToken.getIssuedAt());
|
||||
assertThat(refreshToken.getExpiresAt()).isEqualTo(expectedRefreshToken.getExpiresAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeWhenRequiredAttributesOnlyThenDeserializes() throws Exception {
|
||||
// @formatter:off
|
||||
ClientRegistration expectedClientRegistration = TestClientRegistrations.clientRegistration()
|
||||
.clientSecret(null)
|
||||
.clientName(null)
|
||||
.userInfoUri(null)
|
||||
.userNameAttributeName(null)
|
||||
.jwkSetUri(null)
|
||||
.issuerUri(null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
OAuth2AccessToken expectedAccessToken = TestOAuth2AccessTokens.noScopes();
|
||||
OAuth2AuthorizedClient expectedAuthorizedClient = new OAuth2AuthorizedClient(expectedClientRegistration,
|
||||
this.principalName, expectedAccessToken);
|
||||
String json = asJson(expectedAuthorizedClient);
|
||||
OAuth2AuthorizedClient authorizedClient = this.mapper.readValue(json, OAuth2AuthorizedClient.class);
|
||||
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
|
||||
assertThat(clientRegistration.getRegistrationId()).isEqualTo(expectedClientRegistration.getRegistrationId());
|
||||
assertThat(clientRegistration.getClientId()).isEqualTo(expectedClientRegistration.getClientId());
|
||||
assertThat(clientRegistration.getClientSecret()).isEmpty();
|
||||
assertThat(clientRegistration.getClientAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getClientAuthenticationMethod());
|
||||
assertThat(clientRegistration.getAuthorizationGrantType())
|
||||
.isEqualTo(expectedClientRegistration.getAuthorizationGrantType());
|
||||
assertThat(clientRegistration.getRedirectUri()).isEqualTo(expectedClientRegistration.getRedirectUri());
|
||||
assertThat(clientRegistration.getScopes()).isEqualTo(expectedClientRegistration.getScopes());
|
||||
assertThat(clientRegistration.getProviderDetails().getAuthorizationUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getAuthorizationUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getTokenUri())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getTokenUri());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod())
|
||||
.isEqualTo(expectedClientRegistration.getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod());
|
||||
assertThat(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getJwkSetUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getIssuerUri()).isNull();
|
||||
assertThat(clientRegistration.getProviderDetails().getConfigurationMetadata()).isEmpty();
|
||||
assertThat(clientRegistration.getClientName()).isEqualTo(clientRegistration.getRegistrationId());
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(expectedAuthorizedClient.getPrincipalName());
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
assertThat(accessToken.getTokenType()).isEqualTo(expectedAccessToken.getTokenType());
|
||||
assertThat(accessToken.getScopes()).isEmpty();
|
||||
assertThat(accessToken.getTokenValue()).isEqualTo(expectedAccessToken.getTokenValue());
|
||||
assertThat(accessToken.getIssuedAt()).isEqualTo(expectedAccessToken.getIssuedAt());
|
||||
assertThat(accessToken.getExpiresAt()).isEqualTo(expectedAccessToken.getExpiresAt());
|
||||
assertThat(authorizedClient.getRefreshToken()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deserializeWhenClientSettingsPropertyDoesNotExistThenDefaulted() throws JacksonException {
|
||||
// ClientRegistration.clientSettings was added later, so old values will be
|
||||
// serialized without that property
|
||||
// this test checks for passivity
|
||||
ClientRegistration clientRegistration = this.clientRegistrationBuilder.build();
|
||||
ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();
|
||||
ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String configurationMetadata = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {
|
||||
configurationMetadata += "," + providerDetails.getConfigurationMetadata()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + providerDetails.getConfigurationMetadata().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
String json = "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n" +
|
||||
" \"registrationId\": \"" + clientRegistration.getRegistrationId() + "\",\n" +
|
||||
" \"clientId\": \"" + clientRegistration.getClientId() + "\",\n" +
|
||||
" \"clientSecret\": \"" + clientRegistration.getClientSecret() + "\",\n" +
|
||||
" \"clientAuthenticationMethod\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getClientAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getAuthorizationGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"redirectUri\": \"" + clientRegistration.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"providerDetails\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\",\n" +
|
||||
" \"authorizationUri\": \"" + providerDetails.getAuthorizationUri() + "\",\n" +
|
||||
" \"tokenUri\": \"" + providerDetails.getTokenUri() + "\",\n" +
|
||||
" \"userInfoEndpoint\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\",\n" +
|
||||
" \"uri\": " + ((userInfoEndpoint.getUri() != null) ? "\"" + userInfoEndpoint.getUri() + "\"" : null) + ",\n" +
|
||||
" \"authenticationMethod\": {\n" +
|
||||
" \"value\": \"" + userInfoEndpoint.getAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"userNameAttributeName\": " + ((userInfoEndpoint.getUserNameAttributeName() != null) ? "\"" + userInfoEndpoint.getUserNameAttributeName() + "\"" : null) + "\n" +
|
||||
" },\n" +
|
||||
" \"jwkSetUri\": " + ((providerDetails.getJwkSetUri() != null) ? "\"" + providerDetails.getJwkSetUri() + "\"" : null) + ",\n" +
|
||||
" \"issuerUri\": " + ((providerDetails.getIssuerUri() != null) ? "\"" + providerDetails.getIssuerUri() + "\"" : null) + ",\n" +
|
||||
" \"configurationMetadata\": {\n" +
|
||||
" " + configurationMetadata + "\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"clientName\": \"" + clientRegistration.getClientName() + "\"\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
// validate the test input
|
||||
assertThat(json).doesNotContain("clientSettings");
|
||||
ClientRegistration registration = this.mapper.readValue(json, ClientRegistration.class);
|
||||
// the default value of requireProofKey is false
|
||||
assertThat(registration.getClientSettings().isRequireProofKey()).isFalse();
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AuthorizedClient authorizedClient) {
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.OAuth2AuthorizedClient\",\n" +
|
||||
" \"clientRegistration\": " + asJson(authorizedClient.getClientRegistration()) + ",\n" +
|
||||
" \"principalName\": \"" + authorizedClient.getPrincipalName() + "\",\n" +
|
||||
" \"accessToken\": " + asJson(authorizedClient.getAccessToken()) + ",\n" +
|
||||
" \"refreshToken\": " + asJson(authorizedClient.getRefreshToken()) + "\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(ClientRegistration clientRegistration) {
|
||||
ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails();
|
||||
ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
String configurationMetadata = "\"@class\": \"java.util.Collections$UnmodifiableMap\"";
|
||||
if (!CollectionUtils.isEmpty(providerDetails.getConfigurationMetadata())) {
|
||||
configurationMetadata += "," + providerDetails.getConfigurationMetadata()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map((key) -> "\"" + key + "\": \"" + providerDetails.getConfigurationMetadata().get(key) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration\",\n" +
|
||||
" \"registrationId\": \"" + clientRegistration.getRegistrationId() + "\",\n" +
|
||||
" \"clientId\": \"" + clientRegistration.getClientId() + "\",\n" +
|
||||
" \"clientSecret\": \"" + clientRegistration.getClientSecret() + "\",\n" +
|
||||
" \"clientAuthenticationMethod\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getClientAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"authorizationGrantType\": {\n" +
|
||||
" \"value\": \"" + clientRegistration.getAuthorizationGrantType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"redirectUri\": \"" + clientRegistration.getRedirectUri() + "\",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ],\n" +
|
||||
" \"providerDetails\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\",\n" +
|
||||
" \"authorizationUri\": \"" + providerDetails.getAuthorizationUri() + "\",\n" +
|
||||
" \"tokenUri\": \"" + providerDetails.getTokenUri() + "\",\n" +
|
||||
" \"userInfoEndpoint\": {\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\",\n" +
|
||||
" \"uri\": " + ((userInfoEndpoint.getUri() != null) ? "\"" + userInfoEndpoint.getUri() + "\"" : null) + ",\n" +
|
||||
" \"authenticationMethod\": {\n" +
|
||||
" \"value\": \"" + userInfoEndpoint.getAuthenticationMethod().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"userNameAttributeName\": " + ((userInfoEndpoint.getUserNameAttributeName() != null) ? "\"" + userInfoEndpoint.getUserNameAttributeName() + "\"" : null) + "\n" +
|
||||
" },\n" +
|
||||
" \"jwkSetUri\": " + ((providerDetails.getJwkSetUri() != null) ? "\"" + providerDetails.getJwkSetUri() + "\"" : null) + ",\n" +
|
||||
" \"issuerUri\": " + ((providerDetails.getIssuerUri() != null) ? "\"" + providerDetails.getIssuerUri() + "\"" : null) + ",\n" +
|
||||
" \"configurationMetadata\": {\n" +
|
||||
" " + configurationMetadata + "\n" +
|
||||
" }\n" +
|
||||
" },\n" +
|
||||
" \"clientName\": \"" + clientRegistration.getClientName() + "\",\n" +
|
||||
" \"clientSettings\": {\n" +
|
||||
" \"requireProofKey\": " + clientRegistration.getClientSettings().isRequireProofKey() + "\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2AccessToken accessToken) {
|
||||
String scopes = "";
|
||||
if (!CollectionUtils.isEmpty(accessToken.getScopes())) {
|
||||
scopes = StringUtils.collectionToDelimitedString(accessToken.getScopes(), ",", "\"", "\"");
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.OAuth2AccessToken\",\n" +
|
||||
" \"tokenType\": {\n" +
|
||||
" \"value\": \"" + accessToken.getTokenType().getValue() + "\"\n" +
|
||||
" },\n" +
|
||||
" \"tokenValue\": \"" + accessToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(accessToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(accessToken.getExpiresAt()) + ",\n" +
|
||||
" \"scopes\": [\n" +
|
||||
" \"java.util.Collections$UnmodifiableSet\",\n" +
|
||||
" [" + scopes + "]\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String asJson(OAuth2RefreshToken refreshToken) {
|
||||
if (refreshToken == null) {
|
||||
return null;
|
||||
}
|
||||
// @formatter:off
|
||||
return "{\n" +
|
||||
" \"@class\": \"org.springframework.security.oauth2.core.OAuth2RefreshToken\",\n" +
|
||||
" \"tokenValue\": \"" + refreshToken.getTokenValue() + "\",\n" +
|
||||
" \"issuedAt\": " + toString(refreshToken.getIssuedAt()) + ",\n" +
|
||||
" \"expiresAt\": " + toString(refreshToken.getExpiresAt()) + "\n" +
|
||||
"}";
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static String toString(Instant instant) {
|
||||
if (instant == null) {
|
||||
return null;
|
||||
}
|
||||
return DecimalUtils.toBigDecimal(instant.getEpochSecond(), instant.getNano()).toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.oauth2.client.jackson;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.node.JsonNodeFactory;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
import tools.jackson.databind.util.StdConverter;
|
||||
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class StdConvertersTests {
|
||||
|
||||
private final StdConverter<JsonNode, ClientAuthenticationMethod> clientAuthenticationMethodConverter = new org.springframework.security.oauth2.client.jackson.StdConverters.ClientAuthenticationMethodConverter();
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("convertWhenClientAuthenticationMethodConvertedThenDeserializes")
|
||||
void convertWhenClientAuthenticationMethodConvertedThenDeserializes(String clientAuthenticationMethod) {
|
||||
ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
|
||||
jsonNode.put("value", clientAuthenticationMethod);
|
||||
ClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode);
|
||||
assertThat(actual.getValue()).isEqualTo(clientAuthenticationMethod);
|
||||
}
|
||||
|
||||
static Stream<Arguments> convertWhenClientAuthenticationMethodConvertedThenDeserializes() {
|
||||
return Stream.of(Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
|
||||
Arguments.of(ClientAuthenticationMethod.NONE.getValue()), Arguments.of("custom_method"));
|
||||
}
|
||||
|
||||
}
|
@ -20,8 +20,6 @@ import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
@ -29,6 +27,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -111,7 +111,7 @@ public class ClientRegistrationsTests {
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
private JsonMapper mapper = new JsonMapper();
|
||||
|
||||
private Map<String, Object> response;
|
||||
|
||||
|
@ -15,7 +15,7 @@ dependencies {
|
||||
testImplementation "jakarta.servlet:jakarta.servlet-api"
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver'
|
||||
testImplementation 'io.projectreactor.netty:reactor-netty'
|
||||
testImplementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
testImplementation 'tools.jackson.core:jackson-databind'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
|
@ -21,10 +21,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
@ -33,6 +29,8 @@ import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -191,8 +189,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponse(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@ -203,8 +200,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOidcFallbackResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOidc(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@ -216,8 +212,7 @@ public class JwtDecodersTests {
|
||||
|
||||
// gh-7512
|
||||
@Test
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException()
|
||||
throws JsonMappingException, JsonProcessingException {
|
||||
public void issuerWhenOAuth2ResponseDoesNotContainJwksUriThenThrowsIllegalArgumentException() {
|
||||
prepareConfigurationResponseOAuth2(this.buildResponseWithMissingJwksUri());
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
@ -384,8 +379,8 @@ public class JwtDecodersTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public String buildResponseWithMissingJwksUri() throws JsonMappingException, JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
public String buildResponseWithMissingJwksUri() {
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
Map<String, Object> response = mapper.readValue(DEFAULT_RESPONSE_TEMPLATE,
|
||||
new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user