This is an automated email from the ASF dual-hosted git repository.
joerghoh pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-models-jacksonexporter.git
The following commit(s) were added to refs/heads/master by this push:
new 74c6edb SLING-11924 disallow the serialization of a ResourceResolver
(#7)
74c6edb is described below
commit 74c6edba675cc6adbea9109d671a6bba016fdd4c
Author: Jörg Hoh <[email protected]>
AuthorDate: Wed Jul 12 14:59:45 2023 +0200
SLING-11924 disallow the serialization of a ResourceResolver (#7)
disallow the serialization of a ResourceResolver; the API surface is
already prepared for other classes as well, but the implementation is not there
yet.
---
pom.xml | 10 +-
.../ConfigurableSerializationModuleProvider.java | 81 ++++++
.../impl/IgnoringResourceResolverMixin.java | 38 +++
.../impl/WarningResourceResolverMixin.java | 72 ++++++
.../JacksonExporterLimitSerializationTest.java | 275 +++++++++++++++++++++
.../impl/example/PojoWithResourceResolver.java | 36 +++
.../jacksonexporter/impl/util/LogCapture.java | 69 ++++++
7 files changed, 579 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 64872f9..1285634 100644
--- a/pom.xml
+++ b/pom.xml
@@ -152,10 +152,16 @@
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.testing.logging-mock</artifactId>
- <version>2.0.0</version>
+ <artifactId>org.apache.sling.testing.sling-mock.junit5</artifactId>
+ <version>3.2.0</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.2.3</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git
a/src/main/java/org/apache/sling/models/jacksonexporter/impl/ConfigurableSerializationModuleProvider.java
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/ConfigurableSerializationModuleProvider.java
new file mode 100644
index 0000000..d9cf2b9
--- /dev/null
+++
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/ConfigurableSerializationModuleProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.models.jacksonexporter.ModuleProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+@Component(service = ModuleProvider.class)
+@Designate(ocd = ConfigurableSerializationModuleProvider.Config.class)
+public class ConfigurableSerializationModuleProvider implements ModuleProvider
{
+
+ @ObjectClassDefinition(name = "Apache Sling Models Jackson Exporter -
Serialization Blocker",
+ description = "Provider of a Jackson Module which can disable the
serialization of classes")
+ static @interface Config {
+
+ @AttributeDefinition(name ="disable serialization",
+ description = "provide a list of the full classnames which
should not get serialized")
+ String[] disable_serialization() default {};
+
+ @AttributeDefinition(name ="warn on serialization",
+ description = "provide a list of the full classnames for which
a warning should be written when serialized")
+ String[] enable_warn_logging() default
{ConfigurableSerializationModuleProvider.RESOURCERESOLVER};
+
+
+ }
+
+ protected static final String RESOURCERESOLVER =
"org.apache.sling.api.resource.ResourceResolver";
+
+
+ SimpleModule moduleInstance;
+
+
+
+
+ @Activate
+ private void activate(Config config) {
+ this.moduleInstance = new SimpleModule();
+
+ // Currently only the Sling ResourceResolver is supported to be
disabled, other classes tbd.
+ List<String> disabled = Arrays.asList(config.disable_serialization());
+ List<String> logging = Arrays.asList(config.enable_warn_logging());
+
+ if (disabled.contains(RESOURCERESOLVER)) {
+ moduleInstance.setMixInAnnotation(ResourceResolver.class,
IgnoringResourceResolverMixin.class);
+ } else if (logging.contains(RESOURCERESOLVER)) {
+ moduleInstance.setMixInAnnotation(ResourceResolver.class,
WarningResourceResolverMixin.class);
+ }
+ }
+
+
+ @Override
+ public Module getModule() {
+ return moduleInstance;
+ }
+
+}
diff --git
a/src/main/java/org/apache/sling/models/jacksonexporter/impl/IgnoringResourceResolverMixin.java
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/IgnoringResourceResolverMixin.java
new file mode 100644
index 0000000..5f4a3c5
--- /dev/null
+++
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/IgnoringResourceResolverMixin.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl;
+
+import org.apache.sling.api.resource.ResourceResolver;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreType;
+
+/**
+ * This mixin disables the serialization of the ResourceResolver.
+ *
+ */
+
+@JsonIgnoreType
+public abstract interface IgnoringResourceResolverMixin extends
ResourceResolver {
+
+
+ /**
+ * TODO: find a way how we can both ignore this type and write a warning
when trying to serialize a ResourceResolver.
+ */
+
+}
diff --git
a/src/main/java/org/apache/sling/models/jacksonexporter/impl/WarningResourceResolverMixin.java
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/WarningResourceResolverMixin.java
new file mode 100644
index 0000000..51b4ac0
--- /dev/null
+++
b/src/main/java/org/apache/sling/models/jacksonexporter/impl/WarningResourceResolverMixin.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl;
+
+import java.io.IOException;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+
+/**
+ * This mixin exports data which Jackson would export by default (so not
change in the default behaviour),
+ * but prints a warning whenever it does that.
+ */
+
+
+@JsonAutoDetect
+public interface WarningResourceResolverMixin extends ResourceResolver {
+
+ public static final String MESSAGE = "A ResourceResolver is serialized
with all its private fields containing "
+ + "implementation details you should not disclose. Please review
your Sling Model implementation(s) and remove "
+ + "all public accessors to a ResourceResolver.";
+
+ public static final Logger LOG =
LoggerFactory.getLogger(JacksonExporter.class);
+
+
+
+ // This method is explicitly mentioned so we provide a custom serializer
which prints the warning
+
+ @Override
+ @JsonSerialize(using=WarningBooleanSerializer.class)
+ boolean isLive();
+
+
+
+ public class WarningBooleanSerializer extends JsonSerializer<Boolean> {
+
+ @Override
+ public void serialize(Boolean value, JsonGenerator jgen,
SerializerProvider provider)
+ throws IOException {
+ LOG.warn(MESSAGE);
+ jgen.writeObject(value);
+
+ }
+
+ }
+
+
+}
diff --git
a/src/test/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporterLimitSerializationTest.java
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporterLimitSerializationTest.java
new file mode 100644
index 0000000..74bdc18
--- /dev/null
+++
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/JacksonExporterLimitSerializationTest.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.models.factory.ExportException;
+import
org.apache.sling.models.jacksonexporter.impl.example.PojoWithResourceResolver;
+import org.apache.sling.models.jacksonexporter.impl.util.LogCapture;
+import org.apache.sling.testing.mock.osgi.junit5.OsgiContext;
+import org.apache.sling.testing.mock.osgi.junit5.OsgiContextExtension;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import ch.qos.logback.classic.Level;
+
+@ExtendWith(OsgiContextExtension.class)
+class JacksonExporterLimitSerializationTest {
+
+ private OsgiContext context = new OsgiContext();
+
+
+
+ @Test
+ void testWarnLogWhenSerializingResourceResolver() throws ExportException {
+
+ LogCapture capture = new
LogCapture(JacksonExporter.class.getName(),false);
+
+ PojoWithResourceResolver pojo = new PojoWithResourceResolver("text",
new EmptyResourceResolver());
+
+ context.registerInjectActivateService(new
ConfigurableSerializationModuleProvider());
+ JacksonExporter underTest =
context.registerInjectActivateService(JacksonExporter.class);
+ Map<String,String> options = Collections.emptyMap();
+
+ String expectedJson = "{\"msg\":\"text\",\"resolver\":{";
+ assertTrue(underTest.export(pojo, String.class,
options).contains(expectedJson));
+ assertTrue(capture.anyMatch(event -> {
+ return
event.getFormattedMessage().equals(WarningResourceResolverMixin.MESSAGE) &&
+ event.getLevel().equals(Level.WARN);
+ }));
+ }
+
+
+ @Test
+ void testNotSerializingResourceResolverWhenDisabled() throws
ExportException {
+
+ LogCapture capture = new
LogCapture(IgnoringResourceResolverMixin.class.getName(),false);
+ PojoWithResourceResolver pojo = new
PojoWithResourceResolver("text",new EmptyResourceResolver());
+
+ Map<String,Object> config =
Collections.singletonMap("disable.serialization",
ResourceResolver.class.getName());
+ context.registerInjectActivateService(new
ConfigurableSerializationModuleProvider(),config);
+
+
+ JacksonExporter underTest =
context.registerInjectActivateService(JacksonExporter.class);
+ Map<String,String> options = Collections.emptyMap();
+
+ String expectedJson = "{\"msg\":\"text\"}";
+ assertEquals(expectedJson, underTest.export(pojo, String.class,
options));
+
+// assertTrue(capture.anyMatch(p ->
p.getFormattedMessage().contains(IgnoringResourceResolverMixin.MESSAGE)));
+ }
+
+
+ /**
+ * A very simple ResourceResolver implementation which does not lead to
any issues with any mocking framework
+ * when trying to export it with Jackson.
+ */
+ public class EmptyResourceResolver implements ResourceResolver {
+
+// public static final String SERIALIZED_STRING =
"\"resolver\":{\"live\":false,\"userID\":null,\"searchPath\":null,\"propertyMap\":null,\"userID\":false}";
+
+
+ @Override
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Resource resolve(HttpServletRequest request, String absPath) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Resource resolve(String absPath) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Resource resolve(HttpServletRequest request) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String map(String resourcePath) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String map(HttpServletRequest request, String resourcePath) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Resource getResource(String path) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Resource getResource(Resource base, String path) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String[] getSearchPath() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Iterator<Resource> listChildren(Resource parent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Iterable<Resource> getChildren(Resource parent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Iterator<Resource> findResources(String query, String language)
{
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Iterator<Map<String, Object>> queryResources(String query,
String language) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+
+ @Override
+ public ResourceResolver clone(Map<String, Object> authenticationInfo)
throws LoginException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isLive() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void close() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public String getUserID() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Iterator<String> getAttributeNames() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void delete(Resource resource) throws PersistenceException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Resource create(Resource parent, String name, Map<String,
Object> properties)
+ throws PersistenceException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void revert() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void commit() throws PersistenceException {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean hasChanges() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public String getParentResourceType(Resource resource) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getParentResourceType(String resourceType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isResourceType(Resource resource, String resourceType) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void refresh() {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+
+}
diff --git
a/src/test/java/org/apache/sling/models/jacksonexporter/impl/example/PojoWithResourceResolver.java
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/example/PojoWithResourceResolver.java
new file mode 100644
index 0000000..3e544b7
--- /dev/null
+++
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/example/PojoWithResourceResolver.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl.example;
+
+import org.apache.sling.api.resource.ResourceResolver;
+
+public class PojoWithResourceResolver {
+
+
+ public String msg;
+ public ResourceResolver resolver;
+
+ public PojoWithResourceResolver (String msg, ResourceResolver resolver) {
+ this.msg = msg;
+ this.resolver = resolver;
+ }
+
+
+
+}
diff --git
a/src/test/java/org/apache/sling/models/jacksonexporter/impl/util/LogCapture.java
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/util/LogCapture.java
new file mode 100644
index 0000000..be328b0
--- /dev/null
+++
b/src/test/java/org/apache/sling/models/jacksonexporter/impl/util/LogCapture.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sling.models.jacksonexporter.impl.util;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.function.Predicate;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+
+public class LogCapture extends ListAppender<ILoggingEvent> implements
Closeable {
+ private final boolean verboseFailure;
+
+ /** Setup the capture and start it */
+ public LogCapture(String loggerName, boolean verboseFailure) {
+ this.verboseFailure = verboseFailure;
+ Logger logger = (Logger) LoggerFactory.getLogger(loggerName);
+ logger.setLevel(Level.ALL);
+ setContext((LoggerContext) LoggerFactory.getILoggerFactory());
+ logger.addAppender(this);
+ start();
+ }
+
+ public boolean anyMatch(Predicate<ILoggingEvent> p) {
+ return this.list.stream().anyMatch(p);
+ }
+
+ public void assertMessage(String message) {
+ assertTrue(anyMatch(event -> {
+ return event.getFormattedMessage().equals(message);
+ }));
+ }
+
+
+
+
+
+ @Override
+ public void close() throws IOException {
+ stop();
+ }
+
+
+
+}