package com.github.hburgmeier.jerseyoauth2.authsrv.jpa; import java.util.Set; import javax.inject.Inject; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.hburgmeier.jerseyoauth2.api.user.IUser; import com.github.hburgmeier.jerseyoauth2.authsrv.api.IConfiguration; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.ClientServiceException; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.ClientType; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IAuthorizedClientApp; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IClientIdGenerator; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IClientService; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IPendingClientToken; import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IRegisteredClientApp; import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.ITokenGenerator; import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.TokenGenerationException; import com.github.hburgmeier.jerseyoauth2.authsrv.api.user.IUserStorageService; import com.github.hburgmeier.jerseyoauth2.authsrv.api.user.UserStorageServiceException; public class DatabaseClientService implements IClientService { private static final String PERSISTENCE_ERROR = "persistence error - rollback"; private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseClientService.class); private final EntityManagerFactory emf; @com.google.inject.Inject(optional=true) private IUserStorageService userStorageService = null; private final ITokenGenerator tokenGenerator; private final IClientIdGenerator clientIdGenerator; private final IConfiguration configuration; @Inject public DatabaseClientService(EntityManagerFactory emf, ITokenGenerator tokenGenerator, final IClientIdGenerator clientIdGenerator, final IConfiguration configuration) { this.emf = emf; this.tokenGenerator = tokenGenerator; this.clientIdGenerator = clientIdGenerator; this.configuration = configuration; } @Override public IRegisteredClientApp registerClient(String appName, String callbackUrl, ClientType clientType) throws ClientServiceException { String clientId = clientIdGenerator.generateClientId(); String clientSecret = null; if (clientType != ClientType.PUBLIC || configuration.getGenerateSecretForPublicClients()) { clientSecret = clientIdGenerator.generateClientSecret(); } RegisteredClient client = new RegisteredClient(clientId, clientSecret); client.setApplicationName(appName); client.setCallbackUrl(callbackUrl); client.setClientType(clientType); persist(client); LOGGER.debug("registered client {}", clientId); return client; } @Override public IRegisteredClientApp getRegisteredClient(String clientId) { EntityManager entityManager = emf.createEntityManager(); try { return entityManager.find(RegisteredClient.class, clientId); } finally { entityManager.close(); } } @Override public IAuthorizedClientApp authorizeClient(IUser user, IRegisteredClientApp clientApp, Set<String> allowedScopes) throws ClientServiceException { assert(clientApp instanceof RegisteredClient); EntityManager entityManager = emf.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); try { AuthorizedClientApplication clApp = findAuthorizedClient(user, clientApp.getClientId(), entityManager); if (clApp == null) { clApp = new AuthorizedClientApplication((RegisteredClient)clientApp, user, allowedScopes); } else { entityManager.merge(clApp); clApp.setScopes(allowedScopes); } try { tx.begin(); entityManager.persist(clApp); entityManager.flush(); tx.commit(); } catch (PersistenceException e) { LOGGER.error(PERSISTENCE_ERROR, e); tx.rollback(); throw e; } return clApp; } finally { entityManager.close(); } } @Override public IAuthorizedClientApp isAuthorized(IUser user, String clientId, Set<String> scopes) { EntityManager entityManager = emf.createEntityManager(); try { AuthorizedClientApplication result = findAuthorizedClient(user, clientId, entityManager); if (result == null) return null; if (!result.getAuthorizedScopes().containsAll(scopes)) { LOGGER.debug("scopes do not match authorized scopes {} auth {}", scopes, result.getAuthorizedScopes()); return null; } setUser(result); return result; } catch (UserStorageServiceException e) { return null; } finally { entityManager.close(); } } protected AuthorizedClientApplication findAuthorizedClient(IUser user, String clientId, EntityManager entityManager) { try { TypedQuery<AuthorizedClientApplication> query = entityManager.createNamedQuery("findAuthorizedClient", AuthorizedClientApplication.class); query.setParameter("username", user.getName()); query.setParameter("clientId", clientId); AuthorizedClientApplication result = query.getSingleResult(); return result; } catch (NoResultException e) { return null; } } @Override public IPendingClientToken createPendingClientToken(IAuthorizedClientApp clientApp) throws ClientServiceException { assert(clientApp instanceof AuthorizedClientApplication); try { String code = tokenGenerator.createAuthenticationCode(); PendingClientToken pendingClientAuth = new PendingClientToken((AuthorizedClientApplication) clientApp, code); persist(pendingClientAuth); return pendingClientAuth; } catch (TokenGenerationException e) { LOGGER.error("Error creating authorization code", e); throw new ClientServiceException(e); } } @Override public IPendingClientToken findPendingClientToken(String clientId, String clientSecret, String code) { EntityManager entityManager = emf.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); try { TypedQuery<PendingClientToken> query = entityManager.createNamedQuery("findPendingByCode", PendingClientToken.class); query.setParameter("code", code); query.setParameter("clientId", clientId); query.setParameter("clientSecret", clientSecret); PendingClientToken result = query.getSingleResult(); try { tx.begin(); entityManager.remove(result); tx.commit(); } catch (PersistenceException e) { LOGGER.error(PERSISTENCE_ERROR, e); tx.rollback(); result = null; } setUser((AuthorizedClientApplication)result.getAuthorizedClient()); return result; } catch (NoResultException e) { return null; } catch (UserStorageServiceException e) { return null; } finally { entityManager.close(); } } @Override public void removePendingClientToken(IPendingClientToken pendingClientToken) { EntityManager entityManager = emf.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); try { tx.begin(); IPendingClientToken dbPendingClientToken = entityManager.merge(pendingClientToken); entityManager.remove(dbPendingClientToken); tx.commit(); } catch (PersistenceException e) { LOGGER.error(PERSISTENCE_ERROR, e); tx.rollback(); throw e; } finally { entityManager.close(); } } @Override public void removePendingTokensForUser(IUser user) { EntityManager entityManager = emf.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); try { tx.begin(); TypedQuery<PendingClientToken> query = entityManager.createNamedQuery("findPendingByUser", PendingClientToken.class); query.setParameter("username", user.getName()); for (PendingClientToken token : query.getResultList()) { entityManager.remove(token); } tx.commit(); } catch (PersistenceException e) { LOGGER.error(PERSISTENCE_ERROR, e); tx.rollback(); throw e; } finally { entityManager.close(); } } protected void persist(Object obj) { EntityManager entityManager = emf.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); try { tx.begin(); entityManager.persist(obj); entityManager.flush(); tx.commit(); } catch (PersistenceException e) { LOGGER.error(PERSISTENCE_ERROR, e); tx.rollback(); throw e; } finally { entityManager.close(); } } protected void setUser(AuthorizedClientApplication result) throws UserStorageServiceException { if (userStorageService!=null) { LOGGER.debug("using userStorageService to load user"); IUser iUser = userStorageService.loadUser(result.getUsername()); result.setAuthorizedUser(iUser); } else { LOGGER.debug("using no user storage service"); result.setAuthorizedUser(new User(result.getUsername())); } } }