diff --git a/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle b/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle index 6d678e29b..dff042674 100644 --- a/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle +++ b/oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle @@ -10,6 +10,7 @@ dependencies { compile 'com.nimbusds:nimbus-jose-jwt' compile 'com.fasterxml.jackson.core:jackson-databind' + optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' optional 'org.springframework:spring-jdbc' testCompile 'org.springframework.security:spring-security-test' diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java index 3608fc277..824eb0e34 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java @@ -29,6 +29,7 @@ import java.util.Set; import java.util.function.Function; +import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.dao.DataRetrievalFailureException; @@ -41,6 +42,7 @@ import org.springframework.jdbc.support.lob.LobCreator; import org.springframework.jdbc.support.lob.LobHandler; import org.springframework.lang.Nullable; +import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.security.oauth2.core.AbstractOAuth2Token; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; @@ -51,6 +53,7 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken; 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.jackson2.OAuth2ServerJackson2Module; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -312,6 +315,11 @@ public static class OAuth2AuthorizationRowMapper implements RowMapper securityModules = SecurityJackson2Modules.getModules(classLoader); + this.objectMapper.registerModules(securityModules); + this.objectMapper.registerModule(new OAuth2ServerJackson2Module()); } @Override @@ -446,6 +454,13 @@ private Map parseMap(String data) { public static class OAuth2AuthorizationParametersMapper implements Function> { private ObjectMapper objectMapper = new ObjectMapper(); + public OAuth2AuthorizationParametersMapper() { + ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader(); + List securityModules = SecurityJackson2Modules.getModules(classLoader); + this.objectMapper.registerModules(securityModules); + this.objectMapper.registerModule(new OAuth2ServerJackson2Module()); + } + @Override public List apply(OAuth2Authorization authorization) { List parameters = new ArrayList<>(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/HashSetMixin.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/HashSetMixin.java new file mode 100644 index 000000000..0ed489302 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/HashSetMixin.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * This mixin class is used to serialize/deserialize {@link HashSet}. + * + * @author Steve Riesenberg + * @see HashSet + * @see OAuth2ServerJackson2Module + * @since 0.1.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +abstract class HashSetMixin { + + @JsonCreator + HashSetMixin(Set set) { + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JsonNodeUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JsonNodeUtils.java new file mode 100644 index 000000000..b2d978847 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JsonNodeUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Utility class for {@code JsonNode}. + * + * @author Joe Grandja + * @since 0.1.2 + */ +abstract class JsonNodeUtils { + + static final TypeReference> STRING_SET = new TypeReference>() { + }; + + static final TypeReference> 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.isTextual()) ? value.asText() : null; + } + + static T findValue(JsonNode jsonNode, String fieldName, TypeReference valueTypeReference, + ObjectMapper mapper) { + if (jsonNode == null) { + return null; + } + JsonNode value = jsonNode.findValue(fieldName); + return (value != null && value.isContainerNode()) ? mapper.convertValue(value, 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; + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java new file mode 100644 index 000000000..923d73c80 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.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 Joe Grandja + * @since 0.1.2 + * @see OAuth2AuthorizationRequest + * @see OAuth2AuthorizationRequestMixin + */ +final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer { + + private static final StdConverter AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter(); + + @Override + public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context) + throws IOException { + ObjectMapper mapper = (ObjectMapper) parser.getCodec(); + JsonNode root = mapper.readTree(parser); + return deserialize(parser, mapper, root); + } + + private OAuth2AuthorizationRequest deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root) + throws JsonParseException { + 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, mapper)); + builder.state(JsonNodeUtils.findStringValue(root, "state")); + builder.additionalParameters( + JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, mapper)); + builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri")); + builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, mapper)); + return builder.build(); + } + + private Builder getBuilder(JsonParser parser, + AuthorizationGrantType authorizationGrantType) throws JsonParseException { + if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) { + return OAuth2AuthorizationRequest.authorizationCode(); + } + if (AuthorizationGrantType.IMPLICIT.equals(authorizationGrantType)) { + return OAuth2AuthorizationRequest.implicit(); + } + throw new JsonParseException(parser, "Invalid authorizationGrantType"); + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestMixin.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestMixin.java new file mode 100644 index 000000000..5f0dd3f67 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestMixin.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.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 0.1.2 + * @see OAuth2AuthorizationRequest + * @see OAuth2AuthorizationRequestDeserializer + * @see OAuth2ServerJackson2Module + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(ignoreUnknown = true) +abstract class OAuth2AuthorizationRequestMixin { + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ClientAuthenticationTokenMixin.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ClientAuthenticationTokenMixin.java new file mode 100644 index 000000000..d113bb29b --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ClientAuthenticationTokenMixin.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.util.Map; + +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.ClientAuthenticationMethod; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; + +/** + * This mixin class is used to serialize/deserialize {@link OAuth2ClientAuthenticationToken}. + * + * @author Joe Grandja + * @since 0.1.2 + * @see OAuth2ClientAuthenticationToken + * @see OAuth2ServerJackson2Module + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true) +abstract class OAuth2ClientAuthenticationTokenMixin { + + @JsonCreator + OAuth2ClientAuthenticationTokenMixin(@JsonProperty("clientId") String clientId, + @JsonProperty("clientSecret") String clientSecret, + @JsonProperty("clientAuthenticationMethod") ClientAuthenticationMethod clientAuthenticationMethod, + @JsonProperty("additionalParameters") Map additionalParameters) { + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ServerJackson2Module.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ServerJackson2Module.java new file mode 100644 index 000000000..7aaca39bf --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2ServerJackson2Module.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.util.Collections; +import java.util.HashSet; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import org.springframework.security.jackson2.SecurityJackson2Modules; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; + +/** + * Jackson {@code Module} for {@code spring-authorization-server}, that registers the + * following mix-in annotations: + * + *
    + *
  • {@link UnmodifiableMapMixin}
  • + *
  • {@link HashSetMixin}
  • + *
  • {@link OAuth2AuthorizationRequestMixin}
  • + *
  • {@link OAuth2ClientAuthenticationTokenMixin}
  • + *
+ * + * 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 ObjectMapper} configuration. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new OAuth2ServerJackson2Module());
+ * 
+ * + * @author Steve Riesenberg + * @since 0.1.2 + * @see SecurityJackson2Modules + * @see UnmodifiableMapMixin + * @see HashSetMixin + * @see OAuth2AuthorizationRequestMixin + * @see OAuth2ClientAuthenticationTokenMixin + */ +public class OAuth2ServerJackson2Module extends SimpleModule { + + public OAuth2ServerJackson2Module() { + super(OAuth2ServerJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null)); + } + + @Override + public void setupModule(SetupContext context) { + SecurityJackson2Modules.enableDefaultTyping(context.getOwner()); + context.setMixInAnnotations(Collections.unmodifiableMap(Collections.emptyMap()).getClass(), + UnmodifiableMapMixin.class); + context.setMixInAnnotations(HashSet.class, HashSetMixin.class); + context.setMixInAnnotations(OAuth2AuthorizationRequest.class, OAuth2AuthorizationRequestMixin.class); + context.setMixInAnnotations(OAuth2ClientAuthenticationToken.class, OAuth2ClientAuthenticationTokenMixin.class); + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/StdConverters.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/StdConverters.java new file mode 100644 index 000000000..fc82f4985 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/StdConverters.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.util.StdConverter; + +import org.springframework.security.oauth2.core.AuthorizationGrantType; + +/** + * {@code StdConverter} implementations. + * + * @author Joe Grandja + * @since 0.1.2 + */ +abstract class StdConverters { + + static final class AuthorizationGrantTypeConverter extends StdConverter { + + @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.IMPLICIT.getValue().equalsIgnoreCase(value)) { + return AuthorizationGrantType.IMPLICIT; + } + if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equalsIgnoreCase(value)) { + return AuthorizationGrantType.CLIENT_CREDENTIALS; + } + if (AuthorizationGrantType.PASSWORD.getValue().equalsIgnoreCase(value)) { + return AuthorizationGrantType.PASSWORD; + } + return null; + } + + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapDeserializer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapDeserializer.java new file mode 100644 index 000000000..8703b6126 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapDeserializer.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * A {@code JsonDeserializer} for {@link Collections#unmodifiableMap(Map)}. + * + * @author Joe Grandja + * @since 0.1.2 + * @see Collections#unmodifiableMap(Map) + * @see UnmodifiableMapMixin + */ +final class UnmodifiableMapDeserializer extends JsonDeserializer> { + + @Override + public Map deserialize(JsonParser parser, DeserializationContext context) throws IOException { + ObjectMapper mapper = (ObjectMapper) parser.getCodec(); + JsonNode mapNode = mapper.readTree(parser); + Map result = new LinkedHashMap<>(); + if (mapNode != null && mapNode.isObject()) { + Iterable> fields = mapNode::fields; + for (Map.Entry field : fields) { + result.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class)); + } + } + return Collections.unmodifiableMap(result); + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapMixin.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapMixin.java new file mode 100644 index 000000000..d6e48f70c --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapMixin.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.authorization.jackson2; + +import java.util.Collections; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +/** + * This mixin class is used to serialize/deserialize + * {@link Collections#unmodifiableMap(Map)}. It also registers a custom deserializer + * {@link UnmodifiableMapDeserializer}. + * + * @author Joe Grandja + * @since 0.1.2 + * @see Collections#unmodifiableMap(Map) + * @see UnmodifiableMapDeserializer + * @see OAuth2ServerJackson2Module + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +@JsonDeserialize(using = UnmodifiableMapDeserializer.class) +abstract class UnmodifiableMapMixin { + + @JsonCreator + UnmodifiableMapMixin(Map map) { + } + +} diff --git a/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql b/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql index c426c7682..8fc2d08f6 100644 --- a/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql +++ b/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql @@ -3,7 +3,7 @@ CREATE TABLE oauth2_authorization ( registered_client_id varchar(100) NOT NULL, principal_name varchar(200) NOT NULL, authorization_grant_type varchar(100) NOT NULL, - attributes varchar(1000) DEFAULT NULL, + attributes varchar(4000) DEFAULT NULL, state varchar(1000) DEFAULT NULL, authorization_code_value blob DEFAULT NULL, authorization_code_issued_at timestamp DEFAULT NULL,