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