From a17b75e862e3dac4ecf9ae3798ed0e2e74cbac0a Mon Sep 17 00:00:00 2001
From: Eleftheria Stein
Date: Tue, 13 Aug 2019 17:09:04 -0400
Subject: [PATCH] Expire as many sessions as exceed maximum allowed
Fixes: gh-7166
---
...tSessionControlAuthenticationStrategy.java | 25 ++++++++-----------
...ionControlAuthenticationStrategyTests.java | 21 +++++++++++++++-
2 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
index 68df38f902..2e7401207c 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -15,6 +15,7 @@
*/
package org.springframework.security.web.authentication.session;
+import java.util.Comparator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
@@ -45,8 +46,9 @@ import org.springframework.util.Assert;
*
*
* If a user has reached the maximum number of permitted sessions, the behaviour depends
- * on the exceptionIfMaxExceeded property. The default behaviour is to expired
- * the least recently used session, which will be invalidated by the
+ * on the exceptionIfMaxExceeded property. The default behaviour is to expire
+ * any sessions that exceed the maximum number of permitted sessions, starting with the
+ * least recently used sessions. The expired sessions will be invalidated by the
* {@link ConcurrentSessionFilter} if accessed again. If exceptionIfMaxExceeded
* is set to true, however, the user will be prevented from starting a new
* authenticated session.
@@ -156,18 +158,13 @@ public class ConcurrentSessionControlAuthenticationStrategy implements
"Maximum sessions of {0} for this principal exceeded"));
}
- // Determine least recently used session, and mark it for invalidation
- SessionInformation leastRecentlyUsed = null;
-
- for (SessionInformation session : sessions) {
- if ((leastRecentlyUsed == null)
- || session.getLastRequest()
- .before(leastRecentlyUsed.getLastRequest())) {
- leastRecentlyUsed = session;
- }
+ // Determine least recently used sessions, and mark them for invalidation
+ sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
+ int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
+ List sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
+ for (SessionInformation session: sessionsToBeExpired) {
+ session.expireNow();
}
-
- leastRecentlyUsed.expireNow();
}
/**
diff --git a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java
index 1082868356..409f11af78 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -134,6 +134,25 @@ public class ConcurrentSessionControlAuthenticationStrategyTests {
assertThat(sessionInformation.isExpired()).isTrue();
}
+ @Test
+ public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() {
+ SessionInformation oldestSessionInfo = new SessionInformation(
+ authentication.getPrincipal(), "unique1", new Date(1374766134214L));
+ SessionInformation secondOldestSessionInfo = new SessionInformation(
+ authentication.getPrincipal(), "unique2", new Date(1374766134215L));
+ when(sessionRegistry.getAllSessions(any(), anyBoolean())).thenReturn(
+ Arrays. asList(oldestSessionInfo,
+ secondOldestSessionInfo,
+ sessionInformation));
+ strategy.setMaximumSessions(2);
+
+ strategy.onAuthentication(authentication, request, response);
+
+ assertThat(oldestSessionInfo.isExpired()).isTrue();
+ assertThat(secondOldestSessionInfo.isExpired()).isTrue();
+ assertThat(sessionInformation.isExpired()).isFalse();
+ }
+
@Test(expected = IllegalArgumentException.class)
public void setMessageSourceNull() {
strategy.setMessageSource(null);