Skip to content

Commit cd5139a

Browse files
authored
Enhanced Session Management in Apache Roller (#148)
* Roller session improvements. * Add a test and fixes for problems revealed. * Restore listener methods. * Session manager only manages logged-in user sessions. * Use default methods rather than adapter, also add test for session manager (a wip).
1 parent faafe37 commit cd5139a

File tree

6 files changed

+396
-42
lines changed

6 files changed

+396
-42
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.apache.roller.weblogger.ui.core;
2+
3+
import org.apache.commons.logging.Log;
4+
import org.apache.commons.logging.LogFactory;
5+
import org.apache.roller.weblogger.pojos.User;
6+
import org.apache.roller.weblogger.util.cache.Cache;
7+
import org.apache.roller.weblogger.util.cache.CacheHandler;
8+
import org.apache.roller.weblogger.util.cache.CacheManager;
9+
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
public class RollerLoginSessionManager {
14+
private static final Log log = LogFactory.getLog(RollerLoginSessionManager.class);
15+
private static final String CACHE_ID = "roller.session.cache";
16+
private final Cache sessionCache;
17+
18+
public static RollerLoginSessionManager getInstance() {
19+
return RollerLoginSessionManager.SingletonHolder.INSTANCE;
20+
}
21+
22+
private static class SingletonHolder {
23+
private static final RollerLoginSessionManager INSTANCE = new RollerLoginSessionManager();
24+
}
25+
26+
class SessionCacheHandler implements CacheHandler {
27+
@Override
28+
public void invalidate(User user) {
29+
if (user != null && user.getUserName() != null) {
30+
sessionCache.remove(user.getUserName());
31+
}
32+
}
33+
}
34+
35+
/** Testing purpose only */
36+
RollerLoginSessionManager(Cache cache) {
37+
this.sessionCache = cache;
38+
CacheManager.registerHandler(new SessionCacheHandler());
39+
}
40+
41+
private RollerLoginSessionManager() {
42+
Map<String, String> cacheProps = new HashMap<>();
43+
cacheProps.put("id", CACHE_ID);
44+
cacheProps.put("size", "1000"); // Cache up to 1000 sessions
45+
cacheProps.put("timeout", "3600"); // Session timeout in seconds (1 hour)
46+
this.sessionCache = CacheManager.constructCache(null, cacheProps);
47+
CacheManager.registerHandler(new SessionCacheHandler());
48+
}
49+
50+
public void register(String userName, RollerSession session) {
51+
if (userName != null && session != null) {
52+
this.sessionCache.put(userName, session);
53+
log.debug("Registered session for user: " + userName);
54+
}
55+
}
56+
57+
public RollerSession get(String userName) {
58+
if (userName != null) {
59+
return (RollerSession) this.sessionCache.get(userName);
60+
}
61+
return null;
62+
}
63+
64+
public void invalidate(String userName) {
65+
if (userName != null) {
66+
this.sessionCache.remove(userName);
67+
log.debug("Invalidated session for user: " + userName);
68+
}
69+
}
70+
}

app/src/main/java/org/apache/roller/weblogger/ui/core/RollerSession.java

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,19 @@
3838

3939
/**
4040
* Roller session handles session startup and shutdown.
41-
*
42-
* @web.listener
4341
*/
4442
public class RollerSession
4543
implements HttpSessionListener, HttpSessionActivationListener, Serializable {
4644

47-
static final long serialVersionUID = 5890132909166913727L;
48-
45+
private static final long serialVersionUID = 5890132909166913727L;
46+
4947
// the id of the user represented by this session
5048
private String userName = null;
5149

5250
private static final Log log;
5351

5452
public static final String ROLLER_SESSION = "org.apache.roller.weblogger.rollersession";
55-
public static final String ERROR_MESSAGE = "rollererror_message";
56-
public static final String STATUS_MESSAGE = "rollerstatus_message";
57-
53+
5854
static{
5955
WebloggerConfig.init(); // must be called before calls to logging APIs
6056
log = LogFactory.getLog(RollerSession.class);
@@ -68,14 +64,20 @@ public static RollerSession getRollerSession(HttpServletRequest request) {
6864
HttpSession session = request.getSession(false);
6965
if (session != null) {
7066
rollerSession = (RollerSession)session.getAttribute(ROLLER_SESSION);
71-
67+
7268
if (rollerSession == null) {
73-
// HttpSession with no RollerSession?
74-
// Must be a session that was de-serialized from a previous run.
69+
// Create new session if none exists
7570
rollerSession = new RollerSession();
7671
session.setAttribute(ROLLER_SESSION, rollerSession);
72+
} else if (rollerSession.getAuthenticatedUser() != null) {
73+
// Check if session is still valid in cache
74+
RollerLoginSessionManager manager = RollerLoginSessionManager.getInstance();
75+
String username = rollerSession.getAuthenticatedUser().getUserName();
76+
if (manager.get(username) == null) {
77+
rollerSession = new RollerSession();
78+
session.setAttribute(ROLLER_SESSION, rollerSession);
79+
}
7780
}
78-
7981
Principal principal = request.getUserPrincipal();
8082

8183
// If we've got a principal but no user object, then attempt to get
@@ -124,8 +126,7 @@ public static RollerSession getRollerSession(HttpServletRequest request) {
124126

125127
return rollerSession;
126128
}
127-
128-
129+
129130
/** Create session's Roller instance */
130131
@Override
131132
public void sessionCreated(HttpSessionEvent se) {
@@ -138,15 +139,8 @@ public void sessionCreated(HttpSessionEvent se) {
138139
public void sessionDestroyed(HttpSessionEvent se) {
139140
clearSession(se);
140141
}
141-
142-
143-
/** Init session as if it was new */
144-
@Override
145-
public void sessionDidActivate(HttpSessionEvent se) {
146-
}
147-
148-
149-
/**
142+
143+
/**
150144
* Purge session before passivation. Because Roller currently does not
151145
* support session recovery, failover, migration, or whatever you want
152146
* to call it when sessions are saved and then restored at some later
@@ -156,15 +150,14 @@ public void sessionDidActivate(HttpSessionEvent se) {
156150
public void sessionWillPassivate(HttpSessionEvent se) {
157151
clearSession(se);
158152
}
159-
160-
153+
161154
/**
162155
* Authenticated user associated with this session.
163156
*/
164157
public User getAuthenticatedUser() {
165158

166159
User authenticUser = null;
167-
if(userName != null) {
160+
if (userName != null) {
168161
try {
169162
UserManager mgr = WebloggerFactory.getWeblogger().getUserManager();
170163
authenticUser = mgr.getUserByUserName(userName);
@@ -175,16 +168,16 @@ public User getAuthenticatedUser() {
175168

176169
return authenticUser;
177170
}
178-
179-
171+
180172
/**
181173
* Authenticated user associated with this session.
182174
*/
183175
public void setAuthenticatedUser(User authenticatedUser) {
184176
this.userName = authenticatedUser.getUserName();
177+
RollerLoginSessionManager sessionManager = RollerLoginSessionManager.getInstance();
178+
sessionManager.register(authenticatedUser.getUserName(), this);
185179
}
186-
187-
180+
188181
private void clearSession(HttpSessionEvent se) {
189182
HttpSession session = se.getSession();
190183
try {
@@ -196,5 +189,4 @@ private void clearSession(HttpSessionEvent se) {
196189
}
197190
}
198191
}
199-
200192
}

app/src/main/java/org/apache/roller/weblogger/ui/struts2/admin/UserEdit.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.roller.weblogger.pojos.GlobalPermission;
3838
import org.apache.roller.weblogger.pojos.User;
3939
import org.apache.roller.weblogger.pojos.WeblogPermission;
40+
import org.apache.roller.weblogger.ui.core.RollerLoginSessionManager;
4041
import org.apache.roller.weblogger.ui.struts2.core.Register;
4142
import org.apache.roller.weblogger.ui.struts2.util.UIAction;
4243
import org.apache.struts2.interceptor.validation.SkipValidation;
@@ -165,6 +166,18 @@ public String save() {
165166
// reset password if set
166167
if (!StringUtils.isEmpty(getBean().getPassword())) {
167168
user.resetPassword(getBean().getPassword());
169+
170+
// invalidate user's session if it's not user executing this action
171+
if (!getAuthenticatedUser().getUserName().equals(user.getUserName())) {
172+
RollerLoginSessionManager sessionManager = RollerLoginSessionManager.getInstance();
173+
sessionManager.invalidate(user.getUserName());
174+
}
175+
}
176+
177+
// if user is disabled and not the same as the user executing this action, then invalidate their session
178+
if (!user.getEnabled() && !getAuthenticatedUser().getUserName().equals(user.getUserName())) {
179+
RollerLoginSessionManager sessionManager = RollerLoginSessionManager.getInstance();
180+
sessionManager.invalidate(user.getUserName());
168181
}
169182

170183
try {

app/src/main/java/org/apache/roller/weblogger/util/cache/CacheHandler.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,20 @@
3939
*/
4040
public interface CacheHandler {
4141

42-
void invalidate(WeblogEntry entry);
43-
44-
void invalidate(Weblog website);
45-
46-
void invalidate(WeblogBookmark bookmark);
47-
48-
void invalidate(WeblogBookmarkFolder folder);
42+
default void invalidate(WeblogEntry entry) {}
4943

50-
void invalidate(WeblogEntryComment comment);
44+
default void invalidate(Weblog website) {}
5145

52-
void invalidate(User user);
46+
default void invalidate(WeblogBookmark bookmark) {}
5347

54-
void invalidate(WeblogCategory category);
48+
default void invalidate(WeblogBookmarkFolder folder) {}
49+
50+
default void invalidate(WeblogEntryComment comment) {}
51+
52+
default void invalidate(User user) {}
53+
54+
default void invalidate(WeblogCategory category) {}
55+
56+
default void invalidate(WeblogTemplate template) {}
5557

56-
void invalidate(WeblogTemplate template);
57-
5858
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. The ASF licenses this file to You
4+
* under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License. For additional information regarding
15+
* copyright in this work, please see the NOTICE file in the top level
16+
* directory of this distribution.
17+
*/
18+
19+
package org.apache.roller.weblogger.ui.core;
20+
21+
import org.apache.roller.weblogger.pojos.User;
22+
import org.apache.roller.weblogger.util.cache.Cache;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
import static org.junit.jupiter.api.Assertions.assertNull;
28+
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.Mockito.*;
30+
31+
class RollerLoginSessionManagerTest {
32+
private RollerLoginSessionManager sessionManager;
33+
private Cache mockCache;
34+
35+
@BeforeEach
36+
void setUp() {
37+
mockCache = mock(Cache.class);
38+
sessionManager = new RollerLoginSessionManager(mockCache);
39+
}
40+
41+
@Test
42+
void testRegisterSession() {
43+
RollerSession mockSession = mock(RollerSession.class);
44+
String userName = "testUser";
45+
46+
sessionManager.register(userName, mockSession);
47+
48+
verify(mockCache).put(userName, mockSession);
49+
}
50+
51+
@Test
52+
void testGetSession() {
53+
RollerSession mockSession = mock(RollerSession.class);
54+
String userName = "testUser";
55+
when(mockCache.get(userName)).thenReturn(mockSession);
56+
57+
RollerSession result = sessionManager.get(userName);
58+
59+
assertEquals(mockSession, result);
60+
verify(mockCache).get(userName);
61+
}
62+
63+
@Test
64+
void testInvalidateSession() {
65+
String userName = "testUser";
66+
67+
sessionManager.invalidate(userName);
68+
69+
verify(mockCache).remove(userName);
70+
}
71+
72+
@Test
73+
void testCacheHandlerInvalidation() {
74+
User mockUser = mock(User.class);
75+
String userName = "testUser";
76+
when(mockUser.getUserName()).thenReturn(userName);
77+
78+
sessionManager.new SessionCacheHandler().invalidate(mockUser);
79+
80+
verify(mockCache).remove(userName);
81+
}
82+
83+
@Test
84+
void testNullInputHandling() {
85+
RollerSession mockSession = mock(RollerSession.class);
86+
87+
sessionManager.register(null, mockSession);
88+
sessionManager.invalidate(null);
89+
sessionManager.get(null);
90+
91+
verify(mockCache, never()).put(any(), any());
92+
verify(mockCache, never()).remove(any());
93+
verify(mockCache, never()).get(any());
94+
}
95+
96+
@Test
97+
void testSessionTimeout() {
98+
String userName = "testUser";
99+
when(mockCache.get(userName)).thenReturn(null);
100+
101+
RollerSession result = sessionManager.get(userName);
102+
103+
assertNull(result);
104+
verify(mockCache).get(userName);
105+
}
106+
}

0 commit comments

Comments
 (0)