/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.wildfly.clustering.web.undertow.session;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Set;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
import io.undertow.security.api.AuthenticatedSessionManager.AuthenticatedSession;
import io.undertow.security.idm.Account;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.session.SessionConfig;
import io.undertow.server.session.SessionListener;
import io.undertow.server.session.SessionListener.SessionDestroyedReason;
import io.undertow.server.session.SessionListeners;
import io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.wildfly.clustering.ee.Batch;
import org.wildfly.clustering.ee.BatchContext;
import org.wildfly.clustering.ee.Batcher;
import org.wildfly.clustering.web.session.Session;
import org.wildfly.clustering.web.session.SessionAttributes;
import org.wildfly.clustering.web.session.SessionManager;
import org.wildfly.clustering.web.session.SessionMetaData;
/**
* Unit test for {@link DistributableSession}.
*
* @author Paul Ferraro
*/
public class DistributableSessionTestCase {
private final UndertowSessionManager manager = mock(UndertowSessionManager.class);
private final SessionConfig config = mock(SessionConfig.class);
private final Session<LocalSessionContext> session = mock(Session.class);
private final Batch batch = mock(Batch.class);
private final Runnable closeTask = mock(Runnable.class);
private final io.undertow.server.session.Session adapter = new DistributableSession(this.manager, this.session, this.config, this.batch, this.closeTask);
@Test
public void getId() {
String id = "id";
when(this.session.getId()).thenReturn(id);
String result = this.adapter.getId();
assertSame(id, result);
}
@Test
public void requestDone() {
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
HttpServerExchange exchange = new HttpServerExchange(null);
when(this.session.isValid()).thenReturn(true);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
this.adapter.requestDone(exchange);
verify(this.session).close();
verify(this.batch).close();
verify(context).close();
verify(this.closeTask).run();
reset(this.batch, this.session, context, this.closeTask);
when(this.session.isValid()).thenReturn(false);
this.adapter.requestDone(exchange);
verify(this.session, never()).close();
verify(this.batch, never()).close();
verify(context, never()).close();
verify(this.closeTask, never()).run();
}
@Test
public void getCreationTime() {
this.validate(session -> session.getCreationTime());
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionMetaData metaData = mock(SessionMetaData.class);
Instant now = Instant.now();
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getMetaData()).thenReturn(metaData);
when(metaData.getCreationTime()).thenReturn(now);
long result = this.adapter.getCreationTime();
assertEquals(now.toEpochMilli(), result);
verify(context).close();
}
@Test
public void getLastAccessedTime() {
this.validate(session -> session.getLastAccessedTime());
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionMetaData metaData = mock(SessionMetaData.class);
Instant now = Instant.now();
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getMetaData()).thenReturn(metaData);
when(metaData.getLastAccessedTime()).thenReturn(now);
long result = this.adapter.getLastAccessedTime();
assertEquals(now.toEpochMilli(), result);
verify(context).close();
}
@Test
public void getMaxInactiveInterval() {
this.validate(session -> session.getMaxInactiveInterval());
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionMetaData metaData = mock(SessionMetaData.class);
long expected = 3600L;
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getMetaData()).thenReturn(metaData);
when(metaData.getMaxInactiveInterval()).thenReturn(Duration.ofSeconds(expected));
long result = this.adapter.getMaxInactiveInterval();
assertEquals(expected, result);
verify(context).close();
}
@Test
public void setMaxInactiveInterval() {
int interval = 3600;
this.validate(session -> session.setMaxInactiveInterval(interval));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionMetaData metaData = mock(SessionMetaData.class);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getMetaData()).thenReturn(metaData);
this.adapter.setMaxInactiveInterval(interval);
verify(metaData).setMaxInactiveInterval(Duration.ofSeconds(interval));
verify(context).close();
}
@Test
public void getAttributeNames() {
this.validate(session -> session.getAttributeNames());
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
Set<String> expected = Collections.singleton("name");
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.getAttributeNames()).thenReturn(expected);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.getAttributeNames();
assertSame(expected, result);
verify(context).close();
}
@Test
public void getAttribute() {
String name = "name";
this.validate(session -> session.getAttribute(name));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
Object expected = new Object();
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.getAttribute(name)).thenReturn(expected);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.getAttribute(name);
assertSame(expected, result);
verify(context).close();
}
@Test
public void getAuthenticatedSessionAttribute() {
String name = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
this.validate(session -> session.getAttribute(name));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
Account account = mock(Account.class);
AuthenticatedSession auth = new AuthenticatedSession(account, HttpServletRequest.FORM_AUTH);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.getAttribute(name)).thenReturn(auth);
AuthenticatedSession result = (AuthenticatedSession) this.adapter.getAttribute(name);
assertSame(account, result.getAccount());
assertSame(HttpServletRequest.FORM_AUTH, result.getMechanism());
verify(context).close();
reset(context);
LocalSessionContext localContext = mock(LocalSessionContext.class);
AuthenticatedSession expected = new AuthenticatedSession(account, HttpServletRequest.BASIC_AUTH);
when(attributes.getAttribute(name)).thenReturn(null);
when(this.session.getLocalContext()).thenReturn(localContext);
when(localContext.getAuthenticatedSession()).thenReturn(expected);
result = (AuthenticatedSession) this.adapter.getAttribute(name);
assertSame(expected, result);
verify(context).close();
}
@Test
public void setAttribute() {
String name = "name";
Integer value = Integer.valueOf(1);
this.validate(session -> session.setAttribute(name, value));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
Object expected = new Object();
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.setAttribute(name, value)).thenReturn(expected);
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.setAttribute(name, value);
assertSame(expected, result);
verify(listener, never()).attributeAdded(this.adapter, name, value);
verify(listener).attributeUpdated(this.adapter, name, value, expected);
verify(listener, never()).attributeRemoved(same(this.adapter), same(name), any());
verify(context).close();
}
@Test
public void setNewAttribute() {
String name = "name";
Integer value = Integer.valueOf(1);
this.validate(session -> session.setAttribute(name, value));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
Object expected = null;
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.setAttribute(name, value)).thenReturn(expected);
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.setAttribute(name, value);
assertSame(expected, result);
verify(listener).attributeAdded(this.adapter, name, value);
verify(listener, never()).attributeUpdated(same(this.adapter), same(name), same(value), any());
verify(listener, never()).attributeRemoved(same(this.adapter), same(name), any());
verify(context).close();
}
@Test
public void setNullAttribute() {
String name = "name";
Object value = null;
this.validate(session -> session.setAttribute(name, value));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
Object expected = new Object();
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.removeAttribute(name)).thenReturn(expected);
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.setAttribute(name, value);
assertSame(expected, result);
verify(listener, never()).attributeAdded(this.adapter, name, value);
verify(listener, never()).attributeUpdated(same(this.adapter), same(name), same(value), any());
verify(listener).attributeRemoved(this.adapter, name, expected);
verify(context).close();
}
@Test
public void setSameAttribute() {
String name = "name";
Integer value = Integer.valueOf(1);
this.validate(session -> session.setAttribute(name, value));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
Object expected = value;
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.setAttribute(name, value)).thenReturn(expected);
when(this.manager.getSessionListeners()).thenReturn(listeners);
Object result = this.adapter.setAttribute(name, value);
assertSame(expected, result);
verify(listener, never()).attributeAdded(this.adapter, name, value);
verify(listener, never()).attributeUpdated(same(this.adapter), same(name), same(value), any());
verify(listener, never()).attributeRemoved(same(this.adapter), same(name), any());
verify(context).close();
}
@Test
public void setAuthenticatedSessionAttribute() {
String name = CachedAuthenticatedSessionHandler.class.getName() + ".AuthenticatedSession";
Account account = mock(Account.class);
AuthenticatedSession auth = new AuthenticatedSession(account, HttpServletRequest.FORM_AUTH);
this.validate(session -> session.setAttribute(name, "bar"));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
Account oldAccount = mock(Account.class);
AuthenticatedSession oldAuth = new AuthenticatedSession(oldAccount, HttpServletRequest.FORM_AUTH);
ArgumentCaptor<AuthenticatedSession> capturedAuth = ArgumentCaptor.forClass(AuthenticatedSession.class);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.setAttribute(same(name), capturedAuth.capture())).thenReturn(oldAuth);
AuthenticatedSession result = (AuthenticatedSession) this.adapter.setAttribute(name, auth);
assertSame(auth.getAccount(), capturedAuth.getValue().getAccount());
assertSame(auth.getMechanism(), capturedAuth.getValue().getMechanism());
assertSame(oldAccount, result.getAccount());
assertSame(HttpServletRequest.FORM_AUTH, result.getMechanism());
verify(context).close();
reset(context, attributes);
capturedAuth = ArgumentCaptor.forClass(AuthenticatedSession.class);
when(attributes.setAttribute(same(name), capturedAuth.capture())).thenReturn(null);
result = (AuthenticatedSession) this.adapter.setAttribute(name, auth);
assertSame(auth.getAccount(), capturedAuth.getValue().getAccount());
assertSame(auth.getMechanism(), capturedAuth.getValue().getMechanism());
assertNull(result);
verify(context).close();
reset(context, attributes);
auth = new AuthenticatedSession(account, HttpServletRequest.BASIC_AUTH);
AuthenticatedSession oldSession = new AuthenticatedSession(oldAccount, HttpServletRequest.BASIC_AUTH);
LocalSessionContext localContext = mock(LocalSessionContext.class);
when(this.session.getLocalContext()).thenReturn(localContext);
when(localContext.getAuthenticatedSession()).thenReturn(oldSession);
result = (AuthenticatedSession) this.adapter.setAttribute(name, auth);
verify(localContext).setAuthenticatedSession(same(auth));
verify(context).close();
}
@Test
public void removeAttribute() {
String name = "name";
this.validate(session -> session.removeAttribute(name));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
Object expected = new Object();
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.removeAttribute(name)).thenReturn(expected);
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.removeAttribute(name);
assertSame(expected, result);
verify(listener).attributeRemoved(this.adapter, name, expected);
verify(context).close();
}
@Test
public void removeNonExistingAttribute() {
String name = "name";
this.validate(session -> session.removeAttribute(name));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionAttributes attributes = mock(SessionAttributes.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
when(this.session.getAttributes()).thenReturn(attributes);
when(attributes.removeAttribute(name)).thenReturn(null);
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
Object result = this.adapter.removeAttribute(name);
assertNull(result);
verify(listener, never()).attributeRemoved(same(this.adapter), same(name), any());
verify(context).close();
}
@Test
public void invalidate() {
HttpServerExchange exchange = new HttpServerExchange(null);
this.validate(session -> session.invalidate(exchange));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
String sessionId = "session";
when(this.manager.getSessionListeners()).thenReturn(listeners);
when(this.session.getId()).thenReturn(sessionId);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
this.adapter.invalidate(exchange);
verify(this.session).invalidate();
verify(this.config).clearSession(exchange, sessionId);
verify(listener).sessionDestroyed(this.adapter, exchange, SessionDestroyedReason.INVALIDATED);
verify(this.batch).close();
verify(context).close();
verify(this.closeTask).run();
}
@Test
public void getSessionManager() {
assertSame(this.manager, this.adapter.getSessionManager());
}
@Test
public void changeSessionId() {
HttpServerExchange exchange = new HttpServerExchange(null);
SessionConfig config = mock(SessionConfig.class);
this.validate(session -> session.changeSessionId(exchange, config));
SessionManager<LocalSessionContext, Batch> manager = mock(SessionManager.class);
Batcher<Batch> batcher = mock(Batcher.class);
BatchContext context = mock(BatchContext.class);
Session<LocalSessionContext> session = mock(Session.class);
SessionAttributes oldAttributes = mock(SessionAttributes.class);
SessionAttributes newAttributes = mock(SessionAttributes.class);
SessionMetaData oldMetaData = mock(SessionMetaData.class);
SessionMetaData newMetaData = mock(SessionMetaData.class);
LocalSessionContext oldContext = mock(LocalSessionContext.class);
LocalSessionContext newContext = mock(LocalSessionContext.class);
SessionListener listener = mock(SessionListener.class);
SessionListeners listeners = new SessionListeners();
listeners.addSessionListener(listener);
String oldSessionId = "old";
String newSessionId = "new";
String name = "name";
Object value = new Object();
Instant now = Instant.now();
Duration interval = Duration.ofSeconds(10L);
AuthenticatedSession authenticatedSession = new AuthenticatedSession(null, null);
when(this.manager.getSessionManager()).thenReturn(manager);
when(manager.getBatcher()).thenReturn(batcher);
when(batcher.resumeBatch(this.batch)).thenReturn(context);
when(manager.createIdentifier()).thenReturn(newSessionId);
when(manager.createSession(newSessionId)).thenReturn(session);
when(this.session.getAttributes()).thenReturn(oldAttributes);
when(this.session.getMetaData()).thenReturn(oldMetaData);
when(session.getAttributes()).thenReturn(newAttributes);
when(session.getMetaData()).thenReturn(newMetaData);
when(oldAttributes.getAttributeNames()).thenReturn(Collections.singleton(name));
when(oldAttributes.getAttribute(name)).thenReturn(value);
when(newAttributes.setAttribute(name, value)).thenReturn(null);
when(oldMetaData.getLastAccessedTime()).thenReturn(now);
when(oldMetaData.getMaxInactiveInterval()).thenReturn(interval);
when(this.session.getId()).thenReturn(oldSessionId);
when(session.getId()).thenReturn(newSessionId);
when(this.session.getLocalContext()).thenReturn(oldContext);
when(session.getLocalContext()).thenReturn(newContext);
when(oldContext.getAuthenticatedSession()).thenReturn(authenticatedSession);
when(this.manager.getSessionListeners()).thenReturn(listeners);
String result = this.adapter.changeSessionId(exchange, config);
assertSame(newSessionId, result);
verify(newMetaData).setLastAccessedTime(now);
verify(newMetaData).setMaxInactiveInterval(interval);
verify(config).setSessionId(exchange, newSessionId);
verify(newContext).setAuthenticatedSession(same(authenticatedSession));
verify(listener).sessionIdChanged(this.adapter, oldSessionId);
verify(context).close();
}
private <R> void validate(Consumer<io.undertow.server.session.Session> consumer) {
when(this.session.isValid()).thenReturn(false, true);
try {
consumer.accept(this.adapter);
fail("Invalid session should throw IllegalStateException");
} catch (IllegalStateException e) {
// Expected
}
}
}