package no.niths.services.auth; import java.util.GregorianCalendar; import java.util.UUID; import no.niths.application.rest.exception.ExpiredTokenException; import no.niths.application.rest.exception.UnvalidTokenException; import no.niths.common.constants.SecurityConstants; import no.niths.services.auth.interfaces.StringCryptationService; import no.niths.services.auth.interfaces.TokenGeneratorService; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Service; /** * * Service for generating and verifying tokens. Tokens are used in HTTP headers * when accessing the API from an application. * * Tokens are encrypted and decrypted with @see {@link http://www.jasypt.org/} * Passwords are from application.properties in res/main/resources * * Token structure: <random num sequence> | <token secret> | <date_time_issued> * * For information about the random num sequece @see {@link http://en.wikipedia.org/wiki/Universally_unique_identifier} * */ @Service public class TokenGeneratorServiceImpl implements TokenGeneratorService { private static final Logger logger = LoggerFactory .getLogger(TokenGeneratorServiceImpl.class); @Autowired private StringCryptationService stringCrypt; /** * {@inheritDoc} */ @Override public String generateToken(Long userId) { if (userId == null) { throw new UnvalidTokenException("User id is missing"); } long tokenIssued = getCurrentTime(); String generatedToken = UUID.randomUUID().toString().toUpperCase() + "|" + Long.toString(userId) + "|" + Long.toString(tokenIssued); // Encrypt the token String encryptedToked = stringCrypt.encrypt(generatedToken); logger.debug("Generated token before encryption: " + generatedToken); logger.debug("Generated token after encryption: " + encryptedToked); //Replace all / to avoid any errors using the token in a link encryptedToked = encryptedToked.replace('/', '*'); logger.debug("Generated token after replacing: " + encryptedToked); return encryptedToked; } /** * {@inheritDoc} */ @Override public Long verifyTokenFormat(String token, boolean checkTime) throws AuthenticationException { logger.debug("Verifying token format..."); Long tokenSecret = null; if (token == null) { throw new UnvalidTokenException("Token can not be null"); } try { logger.debug("Token before encryption: " + token); token = token.replace('*', '/'); logger.debug("Token after replace: " + token); //Decrypt the token String decryptedToken = stringCrypt.decrypt(token); logger.debug("Token after decryption: " + decryptedToken); String[] splittet = decryptedToken.split("[|]"); if (splittet.length != 3) { throw new UnvalidTokenException("Token not in a valid format"); } if (checkTime) { long issuedAt = Long.parseLong(splittet[2]); if (System.currentTimeMillis() - issuedAt > SecurityConstants.MAX_SESSION_VALID_TIME) { logger.debug("Token expired"); throw new ExpiredTokenException("Session-token has expired"); } } //Return tokenSecret, in this case, the domain id tokenSecret = Long.parseLong(splittet[1]); } catch (EncryptionOperationNotPossibleException ee) { throw new UnvalidTokenException("Token not in a valid format"); } catch (NumberFormatException nfe) { throw new UnvalidTokenException("Token not in a valid format"); } logger.debug("Verified"); return tokenSecret; } // Private helper private long getCurrentTime() { return new GregorianCalendar().getTimeInMillis(); } public StringCryptationService getStringCrypt() { return stringCrypt; } public void setStringCrypt(StringCryptationService stringCrypt) { this.stringCrypt = stringCrypt; } }