This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new 31aea2b  FELIX-6342 HTTP Session not invalidated over HTTPS (#55)
31aea2b is described below

commit 31aea2b06d5bd0e620f86b5874904a1616683873
Author: Abhishek Garg <[email protected]>
AuthorDate: Sun Oct 4 15:08:33 2020 +0530

    FELIX-6342 HTTP Session not invalidated over HTTPS (#55)
    
    Co-authored-by: abhigarg <[email protected]>
---
 .../felix/http/base/internal/HttpConfig.java       | 91 ++++++++++++++++++++++
 .../base/internal/handler/HttpSessionWrapper.java  | 20 ++++-
 .../internal/handler/HttpSessionWrapperTest.java   | 67 ++++++++++++++++
 .../jetty/internal/ConfigMetaTypeProvider.java     |  8 ++
 .../felix/http/jetty/internal/JettyConfig.java     |  2 +
 5 files changed, 186 insertions(+), 2 deletions(-)

diff --git 
a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpConfig.java 
b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpConfig.java
index b93febd..6af61a7 100644
--- 
a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpConfig.java
+++ 
b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpConfig.java
@@ -16,7 +16,14 @@
  */
 package org.apache.felix.http.base.internal;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 import org.jetbrains.annotations.NotNull;
 
@@ -34,6 +41,10 @@ public class HttpConfig {
 
     private volatile boolean invalidateContainerSession;
 
+    public static final String PROP_CONTAINER_ADDED_ATTRIBUTE = 
"org.apache.felix.http.session.container.attribute";
+
+    private volatile Set<String> containerAddedAttribueSet = null;
+
     public boolean isUniqueSessionId() {
         return uniqueSessionId;
     }
@@ -50,9 +61,15 @@ public class HttpConfig {
         this.invalidateContainerSession = invalidateContainerSession;
     }
 
+    public Set<String> getContainerAddedAttribueSet() { return 
containerAddedAttribueSet; }
+
+    public void setContainerAddedAttribueSet(Set<String> 
containerAddedAttribueSet) { this.containerAddedAttribueSet = 
containerAddedAttribueSet; }
+
+
     public void configure(@NotNull final Dictionary<String, Object> props) {
         this.setUniqueSessionId(this.getBooleanProperty(props, 
PROP_UNIQUE_SESSION_ID, DEFAULT_UNIQUE_SESSION_ID));
         this.setInvalidateContainerSession(this.getBooleanProperty(props, 
PROP_INVALIDATE_SESSION, DEFAULT_INVALIDATE_SESSION));
+        this.setContainerAddedAttribueSet(this.getStringSetProperty(props, 
PROP_CONTAINER_ADDED_ATTRIBUTE));
     }
 
 
@@ -67,4 +84,78 @@ public class HttpConfig {
 
         return defValue;
     }
+
+
+    /**
+     * Get the property value as a string array.
+     * Empty values are filtered out - if the resulting array is empty
+     * the default value is returned.
+     */
+    private String[] getStringArrayProperty(final Dictionary<String, Object> 
props,String name, String[] defValue)
+    {
+        Object value = props.get(name);;
+        if (value instanceof String)
+        {
+            final String stringVal = ((String) value).trim();
+            if (stringVal.length() > 0)
+            {
+                return stringVal.split(",");
+            }
+        }
+        else if (value instanceof String[])
+        {
+            final String[] stringArr = (String[]) value;
+            final List<String> list = new ArrayList<>();
+            for (final String stringVal : stringArr)
+            {
+                if (stringVal.trim().length() > 0)
+                {
+                    list.add(stringVal.trim());
+                }
+            }
+            if (list.size() > 0)
+            {
+                return list.toArray(new String[list.size()]);
+            }
+        }
+        else if (value instanceof Collection)
+        {
+            final ArrayList<String> conv = new ArrayList<>();
+            for (Iterator<?> vi = ((Collection<?>) value).iterator(); 
vi.hasNext();)
+            {
+                Object object = vi.next();
+                if (object != null)
+                {
+                    conv.add(String.valueOf(object));
+                }
+            }
+            if (conv.size() > 0)
+            {
+                return conv.toArray(new String[conv.size()]);
+            }
+        }
+        return defValue;
+    }
+
+    /**
+     * get Property values in set format,so that it can directly compare with 
remaining
+     * attributes of session.using set, as it will take O(1) time for 
searching.
+     * @param props
+     * @param name
+     * @return
+     */
+    private Set<String> getStringSetProperty(final Dictionary<String, Object> 
props,String name) {
+
+        String array[] = getStringArrayProperty(props,name,new String[0]) ;
+        Set<String> propertySet = new HashSet<>();
+
+        for(String property : array){
+
+            if(property != null && !"".equals(property.trim())){
+                propertySet.add(property);
+            }
+        }
+
+        return  propertySet;
+    }
 }
diff --git 
a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
 
b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
index e446c55..896ec9b 100644
--- 
a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
+++ 
b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapper.java
@@ -346,15 +346,31 @@ public class HttpSessionWrapper implements HttpSession
         {
             // if the session is empty we can invalidate
             final Enumeration<String> remainingNames = 
this.delegate.getAttributeNames();
-            if ( !remainingNames.hasMoreElements() )
+            if ( (!remainingNames.hasMoreElements()) || 
(isRemainingAttributeAddedByContainer(remainingNames)))
             {
                 this.delegate.invalidate();
             }
         }
-
         this.isInvalid = true;
     }
 
+    private boolean isRemainingAttributeAddedByContainer(Enumeration<String> 
names){
+        final Set<String> attributeAddedByContainerSet = 
this.config.getContainerAddedAttribueSet() ;
+
+        if(attributeAddedByContainerSet != null && 
!attributeAddedByContainerSet.isEmpty()) {
+
+            while (names.hasMoreElements()) {
+
+                final String name = names.nextElement();
+                if (name == null || 
!attributeAddedByContainerSet.contains(name.trim())) {
+                    return false;
+                }
+            }
+            return true ;
+        }
+        return false ;
+    }
+
     @Override
     public boolean isNew()
     {
diff --git 
a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapperTest.java
 
b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapperTest.java
index be276bf..eac0c95 100644
--- 
a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapperTest.java
+++ 
b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HttpSessionWrapperTest.java
@@ -28,8 +28,10 @@ import static org.mockito.Mockito.when;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionListener;
@@ -144,4 +146,69 @@ public class HttpSessionWrapperTest
         Mockito.verify(containerSession).invalidate();
     }
 
+    @Test
+    public void testContainerSessionInvalidationWithContainerAddedAttribute()
+    {
+        // create container session
+        final ConcurrentHashMap<String, Object> attributes = new 
ConcurrentHashMap<>();
+        attributes.put("org.eclipse.jetty.security.sessionCreatedSecure",true);
+        final HttpSession containerSession = mock(HttpSession.class);
+        when(containerSession.getAttributeNames()).thenAnswer(new 
Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable 
{
+                Map<String, Object> clonedAttributes = new 
ConcurrentHashMap<>(attributes);
+                return Collections.enumeration(clonedAttributes.keySet());
+            }
+        });
+        
when(containerSession.getAttribute(Mockito.anyString())).thenAnswer(new 
Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable 
{
+                return attributes.get(invocation.getArgument(0));
+            }
+        });
+        
when(containerSession.getAttribute(Mockito.anyString())).thenAnswer(new 
Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable 
{
+                return attributes.get(invocation.getArgument(0));
+            }
+        });
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable 
{
+                attributes.put((String)invocation.getArgument(0), 
invocation.getArgument(1));
+                return null;
+            }
+        }).when(containerSession).setAttribute(Mockito.anyString(), 
Mockito.any());
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable 
{
+                attributes.remove(invocation.getArgument(0));
+                return null;
+            }
+        }).when(containerSession).removeAttribute(Mockito.anyString());
+
+        final HttpSessionListener listener = mock(HttpSessionListener.class);
+
+        // create context session
+        final ExtServletContext context = mock(ExtServletContext.class);
+        when(context.getServletContextName()).thenReturn("default");
+        when(context.getHttpSessionListener()).thenReturn(listener);
+
+        final HttpConfig config = new HttpConfig();
+        config.setInvalidateContainerSession(false);
+        Set<String> continerAddedAttributesset = new HashSet<>();
+        
continerAddedAttributesset.add("org.eclipse.jetty.security.sessionCreatedSecure")
 ;
+        config.setContainerAddedAttribueSet(continerAddedAttributesset);
+        final HttpSession contextSession = new 
HttpSessionWrapper(containerSession, context, config, false);
+        // invalidate context session and verify that invalidate is not called 
on the container session
+        contextSession.invalidate();
+        Mockito.verify(containerSession, Mockito.never()).invalidate();
+
+        config.setInvalidateContainerSession(true);
+        final HttpSession newSession = new 
HttpSessionWrapper(containerSession, context, config, false);
+        // invalidate context session and verify that invalidate is called on 
the container session
+        newSession.invalidate();
+        Mockito.verify(containerSession).invalidate();
+    }
+
 }
diff --git 
a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
 
b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
index db08887..6ba3966 100644
--- 
a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
+++ 
b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
@@ -423,6 +423,14 @@ class ConfigMetaTypeProvider implements MetaTypeProvider
                 "If this property is set, the container session is 
automatically validated.",
                 HttpConfig.DEFAULT_INVALIDATE_SESSION,
                 
bundle.getBundleContext().getProperty(HttpConfig.PROP_INVALIDATE_SESSION)));
+        adList.add(new 
AttributeDefinitionImpl(HttpConfig.PROP_CONTAINER_ADDED_ATTRIBUTE,
+                "Attributes added by server.",
+                "The atrrtibutes added by underlying session.Use this to 
invalidate session.",
+                AttributeDefinition.STRING,
+                null,
+                2147483647,
+                null, null,
+                
getStringArray(bundle.getBundleContext().getProperty(HttpConfig.PROP_CONTAINER_ADDED_ATTRIBUTE))));
         adList.add(new 
AttributeDefinitionImpl(HttpConfig.PROP_UNIQUE_SESSION_ID,
                 "Unique Session Id",
                 "If this property is set, each http context gets a unique 
session id (derived from the container session).",
diff --git 
a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
 
b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
index aa340d7..470aa59 100644
--- 
a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
+++ 
b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
@@ -735,6 +735,8 @@ public final class JettyConfig
                 HttpConfig.DEFAULT_INVALIDATE_SESSION));
         props.put(HttpConfig.PROP_UNIQUE_SESSION_ID, 
getBooleanProperty(HttpConfig.PROP_UNIQUE_SESSION_ID,
                 HttpConfig.DEFAULT_UNIQUE_SESSION_ID));
+        props.put(HttpConfig.PROP_CONTAINER_ADDED_ATTRIBUTE, 
getStringArrayProperty(HttpConfig.PROP_CONTAINER_ADDED_ATTRIBUTE,
+                new String[0]));
 
         addCustomServiceProperties(props);
     }

Reply via email to