package com.github.hburgmeier.jerseyoauth2.authsrv.jpa;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import org.hibernate.StaleStateException;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.hburgmeier.jerseyoauth2.api.token.InvalidTokenException;
import com.github.hburgmeier.jerseyoauth2.api.types.TokenType;
import com.github.hburgmeier.jerseyoauth2.api.user.IUser;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.IConfiguration;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IAuthorizedClientApp;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.IAccessTokenInfo;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.IAccessTokenStorageService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.TokenStorageException;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.user.IUserStorageService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.user.UserStorageServiceException;
import com.google.inject.Inject;
public class DatabaseAccessTokenStorage implements IAccessTokenStorageService {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseAccessTokenStorage.class);
private EntityManagerFactory emf;
private final Duration tokenLifetime;
@Inject(optional=true)
private IUserStorageService userStorageService = null;
@Inject
public DatabaseAccessTokenStorage(final EntityManagerFactory emf, final IConfiguration config)
{
this.emf = emf;
this.tokenLifetime = config.getTokenLifetime();
if (this.tokenLifetime == null)
throw new IllegalArgumentException("token lifetime is null");
}
@Override
public IAccessTokenInfo getTokenInfoByAccessToken(String accessToken) throws InvalidTokenException {
try {
EntityManager em = emf.createEntityManager();
try {
TokenEntity tokenEntity = em.find(TokenEntity.class, accessToken);
if (tokenEntity!=null && !tokenEntity.isExpired())
{
setUser(tokenEntity);
return tokenEntity;
} else {
if (tokenEntity==null)
LOGGER.debug("token {} unknown", accessToken);
else {
removeToken(em, tokenEntity);
LOGGER.debug("token {} expired", accessToken);
}
return null;
}
} finally {
em.close();
}
} catch (UserStorageServiceException e) {
throw new InvalidTokenException(e);
}
}
@Override
public IAccessTokenInfo issueToken(String accessToken, String refreshToken, IAuthorizedClientApp clientApp) {
assert(clientApp instanceof AuthorizedClientApplication);
DateTime now = DateTime.now();
long validUntil = now.plus(tokenLifetime).getMillis();
TokenEntity te = new TokenEntity(accessToken, refreshToken, (AuthorizedClientApplication)clientApp, tokenLifetime.getMillis(),
TokenType.BEARER, validUntil);
saveTokenEntity(te);
LOGGER.debug("token {} saved", accessToken);
return te;
}
@Override
public IAccessTokenInfo getTokenInfoByRefreshToken(String refreshToken) throws InvalidTokenException {
try {
EntityManager em = emf.createEntityManager();
try {
TypedQuery<TokenEntity> query = em.createNamedQuery("findTokenEntityByRefreshToken", TokenEntity.class);
query.setParameter("refreshToken", refreshToken);
TokenEntity te = query.getSingleResult();
if (!te.isExpired())
{
setUser(te);
return te;
} else {
LOGGER.debug("refresh token {} is expired", refreshToken);
throw new InvalidTokenException("expired");
}
} finally {
em.close();
}
} catch (NoResultException e) {
throw new InvalidTokenException(e);
} catch (UserStorageServiceException e) {
throw new InvalidTokenException(e);
}
}
@Override
public IAccessTokenInfo refreshToken(String oldAccessToken, String newAccessToken, String newRefreshToken) throws TokenStorageException {
try {
EntityManager em = emf.createEntityManager();
try {
TokenEntity tokenEntity = em.find(TokenEntity.class, oldAccessToken);
removeToken(em, tokenEntity);
tokenEntity.updateTokens(newAccessToken, newRefreshToken);
saveTokenEntity(tokenEntity);
setUser(tokenEntity);
return tokenEntity;
} finally {
em.close();
}
} catch (UserStorageServiceException | PersistenceException e) {
throw new TokenStorageException(e);
}
}
protected void removeToken(EntityManager em, TokenEntity tokenEntity) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.remove(tokenEntity);
em.flush();
tx.commit();
} catch (StaleStateException ex) {
LOGGER.debug("token was already deleted", ex);
tx.commit();
} catch (PersistenceException ex) {
LOGGER.error("persistence error", ex);
tx.rollback();
throw ex;
}
}
@Override
public List<IAccessTokenInfo> invalidateTokensForUser(IUser user) {
EntityManager em = emf.createEntityManager();
TypedQuery<TokenEntity> query = em.createNamedQuery("findTokenEntityByUsername", TokenEntity.class);
query.setParameter("username", user.getName());
List<TokenEntity> resultList = query.getResultList();
List<IAccessTokenInfo> result = new LinkedList<>();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
for (TokenEntity te : resultList)
{
em.remove(te);
result.add(te);
}
em.flush();
tx.commit();
LOGGER.debug("tokens for user {} invalidated", user.getName());
} catch (PersistenceException ex) {
LOGGER.error("persistence error", ex);
tx.rollback();
throw ex;
} finally {
em.close();
}
return result;
}
protected void saveTokenEntity(TokenEntity te) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.persist(te);
em.flush();
tx.commit();
} catch (PersistenceException ex) {
LOGGER.error("persistence error", ex);
tx.rollback();
throw ex;
} finally {
em.close();
}
}
protected void setUser(TokenEntity tokenEntity) throws UserStorageServiceException {
if (tokenEntity!=null)
{
AuthorizedClientApplication clientApp = (AuthorizedClientApplication) tokenEntity.getClientApp();
if (userStorageService!=null)
{
LOGGER.debug("using UserStorageService");
IUser iUser = userStorageService.loadUser(clientApp.getUsername());
clientApp.setAuthorizedUser(iUser);
} else {
LOGGER.debug("using no UserStorageService");
clientApp.setAuthorizedUser(new User(clientApp.getUsername()));
}
}
}
}