Commit 89b231b4 authored by Nick Martynenko's avatar Nick Martynenko Committed by Matija Obreza
Browse files

OAuth refresh token cleanup

parent 06a21a10
......@@ -16,14 +16,17 @@
package org.genesys2.server.persistence.domain;
import java.util.Collection;
import org.genesys2.server.model.oauth.OAuthAccessToken;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Transactional
public interface OAuthAccessTokenPersistence extends JpaRepository<OAuthAccessToken, String> {
......@@ -33,7 +36,17 @@ public interface OAuthAccessTokenPersistence extends JpaRepository<OAuthAccessTo
OAuthAccessToken findByAuthenticationId(String authenticationId);
@Query("delete from OAuthAccessToken where tokenId in (:ids)")
@Modifying
void deleteByTokenIds(@Param("ids") List<String> tokenIds);
@Query("delete from OAuthAccessToken where refreshToken = ?1")
@Modifying
void deleteByRefreshToken(String value);
@Query("select tokenId from OAuthAccessToken where createdDate < ?1")
List<String> getIdsOlderThen(Date date);
@Query("select refreshToken from OAuthAccessToken where tokenId in (:ids)")
List<String> getRefreshTokensByIds(@Param("ids") List<String> tokenIds);
}
......@@ -18,9 +18,18 @@ package org.genesys2.server.persistence.domain;
import org.genesys2.server.model.oauth.OAuthRefreshToken;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Transactional
public interface OAuthRefreshTokenPersistence extends JpaRepository<OAuthRefreshToken, String> {
@Query("delete from OAuthRefreshToken where tokenId in (:ids)")
@Modifying
void deleteByTokenIds(@Param("ids") List<String> tokenIds);
}
package org.genesys2.server.service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public interface TokensCleanupService {
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
void cleanupAccessAndRefreshTokens();
}
package org.genesys2.server.service.impl;
import org.genesys2.server.persistence.domain.OAuthAccessTokenPersistence;
import org.genesys2.server.persistence.domain.OAuthRefreshTokenPersistence;
import org.genesys2.server.service.TokensCleanupService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service("tokensCleanupService")
public class TokensCleanupServiceImpl implements TokensCleanupService{
private Logger _logger = LoggerFactory.getLogger(getClass());
@Value("${scheduler.tokens.cleanup.hours}")
private int cleanupHours;
@Autowired
private OAuthAccessTokenPersistence accessTokenPersistence;
@Autowired
private OAuthRefreshTokenPersistence refreshTokenPersistence;
@Scheduled(cron = "${scheduler.tokens.cleanup.expression}")
@Override
public void cleanupAccessAndRefreshTokens() {
_logger.info("Start cleanup of tokens");
//calculate date
Date olderThen = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(cleanupHours));
//get "old" access token ids
List<String> accessTokensIds = accessTokenPersistence.getIdsOlderThen(olderThen);
_logger.debug("Access tokens IDs ready to cleanup {}", accessTokensIds);
if (!accessTokensIds.isEmpty()){
//get corresponding refresh token ids
List<String> refreshTokens = accessTokenPersistence.getRefreshTokensByIds(accessTokensIds);
_logger.debug("Refresh tokens IDs ready to cleanup {}", accessTokensIds);
//remove access token ids
accessTokenPersistence.deleteByTokenIds(accessTokensIds);
//should not be empty
if (refreshTokens.isEmpty() || refreshTokens.size() != accessTokensIds.size()){
_logger.warn("There is difference between refresh and access tokens");
}
if (!refreshTokens.isEmpty()){
//remove refresh token ids
refreshTokenPersistence.deleteByTokenIds(refreshTokens);
}
}
_logger.info("Cleanup of tokens proceeded successfully");
}
}
......@@ -48,6 +48,7 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -60,8 +61,8 @@ public class UserServiceImpl implements UserService {
@Autowired
private UserPersistence userPersistence;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired(required = false)
private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
private long accountLockoutTime = 5 * 60 * 1000;
......
package org.genesys2.server.test;
import org.junit.Ignore;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
@Ignore
@Configuration
@PropertySource("classpath:/spring/spring.properties")
@EnableJpaRepositories(
basePackages = "org.genesys2.server.persistence",
repositoryImplementationPostfix = "CustomImpl",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager"
)
public class JpaDataConfig {
@Autowired
private Environment env;
@Bean(name = "hibernateProperties")
public PropertiesFactoryBean hibernateProperties() throws Exception {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("/spring/hibernate.properties"));
return bean;
}
@Bean(name = "dataSource")
public DataSource dataSource() throws Exception {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driverClassName"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
@Bean(name = "entityManagerFactory")
public FactoryBean<EntityManagerFactory> entityManagerFactory(Properties hibernateProperties) throws Exception {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource());
bean.setPersistenceUnitName("spring-jpa");
bean.setPackagesToScan("org.genesys2.server.model");
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(env.getProperty("db.show.sql", Boolean.TYPE, true));
jpaVendorAdapter.setGenerateDdl(env.getProperty("db.generate.ddl", Boolean.TYPE, true));
bean.setJpaProperties(hibernateProperties);
bean.setJpaVendorAdapter(jpaVendorAdapter);
return bean;
}
@Bean(name = "transactionManager")
public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) throws Exception {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(dataSource());
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public PersistenceExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
package org.genesys2.server.test;
import org.genesys2.server.service.TokensCleanupService;
import org.genesys2.server.service.impl.TokensCleanupServiceImpl;
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.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.fail;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = JpaTest.Config.class,
initializers = PropertyPlacholderInitializer.class
)
public class JpaTest {
@Import(JpaDataConfig.class)
public static class Config {
@Bean
public TokensCleanupService tokensCleanupService(){
return new TokensCleanupServiceImpl();
}
}
//use it without @Scheduled
@Autowired
private TokensCleanupService tokensCleanupService;
@Test
public void testCleanup(){
try {
tokensCleanupService.cleanupAccessAndRefreshTokens();
} catch (Exception e) {
fail("This should not have any exceptions!");
}
}
}
package org.genesys2.server.test;
import org.junit.Ignore;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.PlaceholderConfigurerSupport;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* Initializing class for using "context:property-placeholder" within @Configuration classes
*/
@Ignore
public class PropertyPlacholderInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
PlaceholderConfigurerSupport postProcessor = new PropertySourcesPlaceholderConfigurer();
try {
postProcessor.setLocations(
//add all properties
resourcePatternResolver.getResources("classpath:**/*.properties")
);
} catch (IOException e) {
throw new BeanCreationException("Could not create bean " + postProcessor.toString(), e);
}
applicationContext.addBeanFactoryPostProcessor(postProcessor);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment