/*
* Copyright 2014-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.session.jdbc;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.ExpiringSession;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for {@link JdbcOperationsSessionRepository} integration tests.
*
* @author Vedran Pavic
* @since 1.2.0
*/
@WebAppConfiguration
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractJdbcOperationsSessionRepositoryITests {
private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT";
private static final String INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;
@Autowired
private JdbcOperationsSessionRepository repository;
private SecurityContext context;
private SecurityContext changedContext;
@Before
public void setup() throws Exception {
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(
new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(),
"na", AuthorityUtils.createAuthorityList("ROLE_USER")));
this.changedContext = SecurityContextHolder.createEmptyContext();
this.changedContext.setAuthentication(new UsernamePasswordAuthenticationToken(
"changedContext-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
}
@Test
public void saves() throws InterruptedException {
String username = "saves-" + System.currentTimeMillis();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
String expectedAttributeName = "a";
String expectedAttributeValue = "b";
toSave.setAttribute(expectedAttributeName, expectedAttributeValue);
Authentication toSaveToken = new UsernamePasswordAuthenticationToken(username,
"password", AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext toSaveContext = SecurityContextHolder.createEmptyContext();
toSaveContext.setAuthentication(toSaveToken);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, toSaveContext);
toSave.setAttribute(INDEX_NAME, username);
this.repository.save(toSave);
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getId()).isEqualTo(toSave.getId());
assertThat(session.getAttributeNames()).isEqualTo(toSave.getAttributeNames());
assertThat(session.<String>getAttribute(expectedAttributeName))
.isEqualTo(toSave.getAttribute(expectedAttributeName));
this.repository.delete(toSave.getId());
assertThat(this.repository.getSession(toSave.getId())).isNull();
}
@Test
@Transactional(readOnly = true)
public void savesInReadOnlyTransaction() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
this.repository.save(toSave);
}
@Test
public void putAllOnSingleAttrDoesNotRemoveOld() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute("a", "b");
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("1", "2");
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
Session session = this.repository.getSession(toSave.getId());
assertThat(session.getAttributeNames().size()).isEqualTo(2);
assertThat(session.<String>getAttribute("a")).isEqualTo("b");
assertThat(session.<String>getAttribute("1")).isEqualTo("2");
this.repository.delete(toSave.getId());
}
@Test
public void updateLastAccessedTime() {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setLastAccessedTime(System.currentTimeMillis()
- (MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
this.repository.save(toSave);
long lastAccessedTime = System.currentTimeMillis();
toSave.setLastAccessedTime(lastAccessedTime);
this.repository.save(toSave);
ExpiringSession session = this.repository.getSession(toSave.getId());
assertThat(session).isNotNull();
assertThat(session.isExpired()).isFalse();
assertThat(session.getLastAccessedTime()).isEqualTo(lastAccessedTime);
}
@Test
public void findByPrincipalName() throws Exception {
String principalName = "findByPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
}
@Test
public void findByPrincipalNameExpireRemovesIndex() throws Exception {
String principalName = "findByPrincipalNameExpireRemovesIndex"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
toSave.setLastAccessedTime(System.currentTimeMillis()
- (MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
this.repository.save(toSave);
this.repository.cleanUpExpiredSessions();
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
}
@Test
public void findByPrincipalNameNoPrincipalNameChange() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChange"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByPrincipalNameNoPrincipalNameChangeReload() throws Exception {
String principalName = "findByPrincipalNameNoPrincipalNameChangeReload"
+ UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByDeletedPrincipalName() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, null);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedPrincipalName() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
toSave.setAttribute(INDEX_NAME, principalNameChanged);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByDeletedPrincipalNameReload() throws Exception {
String principalName = "findByDeletedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedPrincipalNameReload() throws Exception {
String principalName = "findByChangedPrincipalName" + UUID.randomUUID();
String principalNameChanged = "findByChangedPrincipalName" + UUID.randomUUID();
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(INDEX_NAME, principalName);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, principalNameChanged);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, principalName);
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
principalNameChanged);
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findBySecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
this.repository.delete(toSave.getId());
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
}
@Test
public void findBySecurityPrincipalNameExpireRemovesIndex() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
toSave.setLastAccessedTime(System.currentTimeMillis()
- (MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS * 1000 + 1000));
this.repository.save(toSave);
this.repository.cleanUpExpiredSessions();
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(0);
assertThat(findByPrincipalName.keySet()).doesNotContain(toSave.getId());
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChange() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByPrincipalNameNoSecurityPrincipalNameChangeReload()
throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
toSave = this.repository.getSession(toSave.getId());
toSave.setAttribute("other", "value");
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByDeletedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, null);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalName() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(toSave);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void findByDeletedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(INDEX_NAME, null);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getChangedSecurityName());
assertThat(findByPrincipalName).isEmpty();
}
@Test
public void findByChangedSecurityPrincipalNameReload() throws Exception {
JdbcOperationsSessionRepository.JdbcSession toSave = this.repository
.createSession();
toSave.setAttribute(SPRING_SECURITY_CONTEXT, this.context);
this.repository.save(toSave);
JdbcOperationsSessionRepository.JdbcSession getSession = this.repository
.getSession(toSave.getId());
getSession.setAttribute(SPRING_SECURITY_CONTEXT, this.changedContext);
this.repository.save(getSession);
Map<String, JdbcOperationsSessionRepository.JdbcSession> findByPrincipalName = this.repository
.findByIndexNameAndIndexValue(INDEX_NAME, getSecurityName());
assertThat(findByPrincipalName).isEmpty();
findByPrincipalName = this.repository.findByIndexNameAndIndexValue(INDEX_NAME,
getChangedSecurityName());
assertThat(findByPrincipalName).hasSize(1);
assertThat(findByPrincipalName.keySet()).containsOnly(toSave.getId());
}
@Test
public void cleanupInactiveSessionsUsingRepositoryDefinedInterval() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
this.repository.save(session);
assertThat(this.repository.getSession(session.getId())).isNotNull();
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNotNull();
long now = System.currentTimeMillis();
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(10));
this.repository.save(session);
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNotNull();
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(30));
this.repository.save(session);
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNull();
}
// gh-580
@Test
public void cleanupInactiveSessionsUsingSessionDefinedInterval() {
JdbcOperationsSessionRepository.JdbcSession session = this.repository
.createSession();
session.setMaxInactiveIntervalInSeconds((int) TimeUnit.MINUTES.toSeconds(45));
this.repository.save(session);
assertThat(this.repository.getSession(session.getId())).isNotNull();
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNotNull();
long now = System.currentTimeMillis();
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(40));
this.repository.save(session);
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNotNull();
session.setLastAccessedTime(now - TimeUnit.MINUTES.toMillis(50));
this.repository.save(session);
this.repository.cleanUpExpiredSessions();
assertThat(this.repository.getSession(session.getId())).isNull();
}
private String getSecurityName() {
return this.context.getAuthentication().getName();
}
private String getChangedSecurityName() {
return this.changedContext.getAuthentication().getName();
}
@EnableJdbcHttpSession
protected static class BaseConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
}