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();
+    }
+    
+    
+    
+}

Reply via email to