package de.rwth.idsg.bikeman.ixsi.repository; import com.google.common.base.Optional; import de.rwth.idsg.bikeman.domain.CardAccount; import de.rwth.idsg.bikeman.domain.ixsi.IxsiToken; import de.rwth.idsg.bikeman.web.rest.exception.DatabaseException; import lombok.extern.slf4j.Slf4j; import org.hibernate.jpa.internal.QueryImpl; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nullable; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author Sevket Goekay <goekay@dbis.rwth-aachen.de> * @since 28.10.2014 */ @Slf4j @Repository public class IxsiUserRepositoryImpl implements IxsiUserRepository { @Inject private PasswordEncoder passwordEncoder; @PersistenceContext private EntityManager em; @Override @Transactional(readOnly = false) public String setUserToken(String cardId, String cardPin) throws DatabaseException { CardAccount cardAccount = getCardAccount(cardId, cardPin); final String q = "SELECT t FROM IxsiToken t WHERE t.cardAccount = :cardAccount"; String encodedPin = passwordEncoder.encode(cardPin); // If there is already a token, update it. try { IxsiToken t = em.createQuery(q, IxsiToken.class) .setParameter("cardAccount", cardAccount) .getSingleResult(); t.setTokenValue(encodedPin); t.setCreated(new Date()); t.setLastUsed(null); em.merge(t); log.debug("Updated IxsiToken for {}", cardAccount); // Insert a new token for cardAccount } catch (NoResultException e) { log.error("Error occurred", e); IxsiToken newToken = new IxsiToken(); newToken.setCardAccount(cardAccount); newToken.setTokenValue(encodedPin); em.persist(newToken); log.debug("Inserted IxsiToken for {}", cardAccount); } return encodedPin; } @Override @Transactional(readOnly = false) public boolean validateUserToken(String cardId, String ixsiToken) { final String q = "SELECT t FROM IxsiToken t " + "WHERE t.tokenValue = :ixsiToken " + "AND t.cardAccount.cardId = :cardId"; try { IxsiToken t = em.createQuery(q, IxsiToken.class) .setParameter("cardId", cardId) .setParameter("ixsiToken", ixsiToken) .getSingleResult(); log.info("IxsiToken: {}", t); t.setLastUsed(new Date()); em.merge(t); return true; } catch (Exception e) { log.error("Error occurred", e); return false; } } @Override public Optional<String> getMajorCustomerName(String cardId) { final String p = "SELECT mj.name FROM MajorCustomer mj " + "WHERE (SELECT ca FROM CardAccount ca WHERE ca.cardId = :cardId) " + "MEMBER OF mj.cardAccounts"; try { List<String> nameResult = em.createQuery(p, String.class) .setParameter("cardId", cardId) .getResultList(); return Optional.fromNullable(getSingleFromList(nameResult)); } catch (Exception e) { log.error("Error occurred", e); return Optional.absent(); } } /** * Does the CardAccount exist, at all? And is the pin correct? */ private CardAccount getCardAccount(String cardId, String cardPin) throws DatabaseException { final String p = "SELECT ca FROM CardAccount ca WHERE ca.cardId = :cardId"; try { CardAccount cardAccount = em.createQuery(p, CardAccount.class) .setParameter("cardId", cardId) .getSingleResult(); if (!cardAccount.getCardPin().equals(cardPin)) { throw new DatabaseException("CardAccount pin is not correct"); } return cardAccount; } catch (NoResultException e) { throw new DatabaseException("No CardAccount exists with cardId " + cardId, e); } } /** * Captures the logic and essence of {@link QueryImpl#getSingleResult()} minus the exception throwing. */ @Nullable private static <T> T getSingleFromList(List<T> result) { if (result.size() == 0) { return null; } else if (result.size() > 1) { Set<T> uniqueResult = new HashSet<>(result); if (uniqueResult.size() > 1) { log.warn("Query result returns more than one elements"); } return uniqueResult.iterator().next(); } else { return result.get(0); } } }