All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.summerb.microservices.users.impl.AuthTokenServiceImpl Maven / Gradle / Ivy

package org.summerb.microservices.users.impl;

import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.summerb.approaches.validation.FieldValidationException;
import org.summerb.approaches.validation.ValidationError;
import org.summerb.microservices.users.api.AuthTokenService;
import org.summerb.microservices.users.api.PasswordService;
import org.summerb.microservices.users.api.UserService;
import org.summerb.microservices.users.api.dto.AuthToken;
import org.summerb.microservices.users.api.dto.User;
import org.summerb.microservices.users.api.exceptions.AuthTokenNotFoundException;
import org.summerb.microservices.users.api.exceptions.InvalidPasswordException;
import org.summerb.microservices.users.api.exceptions.UserNotFoundException;
import org.summerb.microservices.users.api.exceptions.UserServiceUnexpectedException;
import org.summerb.microservices.users.impl.dao.AuthTokenDao;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;

public class AuthTokenServiceImpl implements AuthTokenService {
	// private static Logger log =
	// Logger.getLogger(AuthTokenServiceImpl.class);

	private UserService userService;
	private PasswordService passwordService;
	private AuthTokenDao authTokenDao;
	/**
	 * How long token may live even if it's frequently used. After that period
	 * of time token will be deleted and user will have to re-login
	 * 
	 * 14 days by default
	 */
	private long authTokenTimeToLiveSeconds = 60 * 60 * 24 * 14;

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public AuthToken authenticate(String userEmail, String passwordPlain, String clientIp)
			throws UserNotFoundException, FieldValidationException, InvalidPasswordException {
		Preconditions.checkArgument(userEmail != null);
		Preconditions.checkArgument(passwordPlain != null);
		Preconditions.checkArgument(clientIp != null);

		try {
			User user = validateAndGetUser(userEmail, passwordPlain);
			return createAuthToken(user.getEmail(), clientIp, UUID.randomUUID().toString(),
					UUID.randomUUID().toString());
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, UserNotFoundException.class);
			Throwables.throwIfInstanceOf(t, FieldValidationException.class);
			Throwables.throwIfInstanceOf(t, InvalidPasswordException.class);

			String msg = String.format("Failed to create auth otken for user '%s'", userEmail);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	private User validateAndGetUser(String userEmail, String passwordPlain)
			throws UserNotFoundException, FieldValidationException, InvalidPasswordException {
		try {
			User user = userService.getUserByEmail(userEmail);

			boolean isPasswordValid = passwordService.isUserPasswordValid(user.getUuid(), passwordPlain);
			if (!isPasswordValid) {
				throw new InvalidPasswordException();
			}

			return user;
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, UserNotFoundException.class);
			Throwables.throwIfInstanceOf(t, FieldValidationException.class);
			Throwables.throwIfInstanceOf(t, InvalidPasswordException.class);

			String msg = String.format("Failed to validate user '%s' and password '%s'", userEmail, passwordPlain);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public AuthToken createAuthToken(String userEmail, String clientIp, String tokenUuid, String tokenValueUuid)
			throws UserNotFoundException, FieldValidationException {
		Preconditions.checkArgument(userEmail != null);
		Preconditions.checkArgument(clientIp != null);
		Preconditions.checkArgument(StringUtils.hasText(tokenUuid));
		Preconditions.checkArgument(StringUtils.hasText(tokenValueUuid));

		try {
			User user = userService.getUserByEmail(userEmail);
			AuthToken authToken = buildNewAuthToken(user, clientIp, tokenUuid, tokenValueUuid);
			authTokenDao.createAuthToken(authToken);
			return authToken;
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, UserNotFoundException.class);
			Throwables.throwIfInstanceOf(t, FieldValidationException.class);

			String msg = String.format("Failed to create auth otken for user '%s'", userEmail);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	private AuthToken buildNewAuthToken(User user, String clientIp, String tokenUuid, String tokenValueUuid) {
		long now = getNow();
		AuthToken ret = new AuthToken();
		ret.setClientIp(clientIp);
		ret.setCreatedAt(now);
		ret.setExpiresAt(calculateAuthTokenExpirationPoint(now));
		ret.setLastVerifiedAt(now);
		ret.setUserUuid(user.getUuid());
		ret.setUuid(tokenUuid);
		ret.setTokenValue(tokenValueUuid);
		return ret;
	}

	private long calculateAuthTokenExpirationPoint(long now) {
		return now + authTokenTimeToLiveSeconds * 1000;
	}

	private long getNow() {
		return new Date().getTime();
	}

	@Override
	public AuthToken getAuthTokenByUuid(String authTokenUuid) throws AuthTokenNotFoundException {
		Preconditions.checkArgument(authTokenUuid != null);

		try {
			AuthToken ret = authTokenDao.findAuthTokenByUuid(authTokenUuid);
			if (ret == null) {
				throw new AuthTokenNotFoundException();
			}
			return ret;
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, AuthTokenNotFoundException.class);

			String msg = String.format("Failed to get auth token by id '%s'", authTokenUuid);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public AuthToken isAuthTokenValid(String userUuid, String authTokenUuid, String tokenValue)
			throws UserNotFoundException {
		Preconditions.checkArgument(userUuid != null);
		Preconditions.checkArgument(authTokenUuid != null);
		Preconditions.checkArgument(StringUtils.hasText(tokenValue), "TokenValue is mandatory");

		try {
			// First - check token itself
			AuthToken authToken = getAuthTokenByUuid(authTokenUuid);
			if (authToken.getExpiresAt() < getNow()) {
				authTokenDao.deleteAuthToken(authTokenUuid);
				return null;
			}

			if (!tokenValue.equals(authToken.getTokenValue())) {
				return null;
			}

			// Check reference to user
			User user = userService.getUserByUuid(userUuid);
			if (!authToken.getUserUuid().equals(user.getUuid())) {
				return null;
			}

			// Now we need to update time when token was checked
			authToken.setTokenValue(UUID.randomUUID().toString());
			authToken.setLastVerifiedAt(getNow());
			authTokenDao.updateToken(authTokenUuid, authToken.getLastVerifiedAt(), authToken.getTokenValue());

			return authToken;
		} catch (AuthTokenNotFoundException nfe) {
			return null;
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, UserNotFoundException.class);

			String msg = String.format("Failed to check auth token '%s' validity for user '%s'", authTokenUuid,
					userUuid);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void updateToken(String authTokenUuid, long lastVerifiedAt, String newTokenValue)
			throws AuthTokenNotFoundException, FieldValidationException {
		Preconditions.checkArgument(authTokenUuid != null);
		Preconditions.checkArgument(StringUtils.hasText(newTokenValue), "TokenValue is mandatory");

		try {
			// First - check token itself
			AuthToken authToken = getAuthTokenByUuid(authTokenUuid);
			if (newTokenValue.equals(authToken.getTokenValue())) {
				throw new FieldValidationException(new ValidationError("validation.newValueExpected", "newTokenValue"));
			}

			// Now we need to update time when token was checked
			authTokenDao.updateToken(authTokenUuid, lastVerifiedAt, newTokenValue);
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, FieldValidationException.class);
			Throwables.throwIfInstanceOf(t, AuthTokenNotFoundException.class);

			String msg = String.format("Failed to update token '%s'", authTokenUuid);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void deleteAuthToken(String authTokenUuid) throws AuthTokenNotFoundException {
		Preconditions.checkArgument(authTokenUuid != null);

		try {
			getAuthTokenByUuid(authTokenUuid);
			authTokenDao.deleteAuthToken(authTokenUuid);
		} catch (AuthTokenNotFoundException nfe) {
			// it's ok
			return;
		} catch (Throwable t) {
			String msg = String.format("Failed to delete auth token '%s'", authTokenUuid);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	@Override
	public List findUserAuthTokens(String userUuid) throws UserNotFoundException {
		Preconditions.checkArgument(StringUtils.hasText(userUuid));

		try {
			User user = userService.getUserByUuid(userUuid);
			return authTokenDao.findAuthTokensByUser(user.getUuid());
		} catch (Throwable t) {
			Throwables.throwIfInstanceOf(t, UserNotFoundException.class);

			String msg = String.format("Failed to find user '%s' authtokens", userUuid);
			throw new UserServiceUnexpectedException(msg, t);
		}
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public PasswordService getPasswordService() {
		return passwordService;
	}

	public void setPasswordService(PasswordService passwordService) {
		this.passwordService = passwordService;
	}

	public AuthTokenDao getAuthTokenDao() {
		return authTokenDao;
	}

	public void setAuthTokenDao(AuthTokenDao authTokenDao) {
		this.authTokenDao = authTokenDao;
	}

	public long getAuthTokenTimeToLiveSeconds() {
		return authTokenTimeToLiveSeconds;
	}

	public void setAuthTokenTimeToLiveSeconds(long authTokenTimeToLiveSeconds) {
		this.authTokenTimeToLiveSeconds = authTokenTimeToLiveSeconds;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy