Java tutorial
/* * Copyright (C) 2016 FormKiQ Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.formkiq.core.service; import static com.formkiq.core.domain.type.UserStatus.ACTIVE; import static com.formkiq.core.domain.type.UserStatus.INVITE; import java.util.Date; import java.util.List; import java.util.UUID; import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.validator.routines.EmailValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.util.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.formkiq.core.dao.FolderDao; import com.formkiq.core.dao.UserDao; import com.formkiq.core.domain.Folder; import com.formkiq.core.domain.FolderAccess; import com.formkiq.core.domain.User; import com.formkiq.core.domain.UserSetting; import com.formkiq.core.domain.type.FolderPermission; import com.formkiq.core.domain.type.FolderStatus; import com.formkiq.core.domain.type.UserDTO; import com.formkiq.core.domain.type.UserListDTO; import com.formkiq.core.domain.type.UserNotificationListDTO; import com.formkiq.core.domain.type.UserRole; import com.formkiq.core.domain.type.UserSettings; import com.formkiq.core.domain.type.UserStatus; import com.formkiq.core.form.dto.NotificationMethod; import com.formkiq.core.form.dto.Workflow; import com.formkiq.core.util.DateService; import com.formkiq.core.util.Strings; /** * UserService Implementation. * */ public class UserServiceImpl implements UserService { /** User Disabled Message. */ private static final String DISABLED_MESSAGE = "Authentication " + "failed. User account is disabled."; /** Default User Token expiry time. */ private static final int DEFAULT_EXPIRY_MINUTES = 24 * 60; /** DateService. */ @Autowired private DateService dateservice; /** FolderDao. */ @Autowired private FolderDao folderDao; /** PasswordEncoder. */ @Autowired private PasswordEncoder passwordEncoder; /** SpringSecurityService. */ @Autowired private SpringSecurityService securityService; /** SystemPropertyService. */ @Autowired private SystemPropertyService systemPropertyService; /** UserDao. */ @Autowired private UserDao userDao; /** * default constructor. */ public UserServiceImpl() { } @Override public User createUser(final String clientid, final String email, final String password, final UserStatus status, final UserRole role, final String loginToken) throws PreconditionFailedException { if (!this.securityService.isAdmin()) { if (this.systemPropertyService.isInviteOnly() && !UserStatus.INVITE.equals(status)) { throw new PreconditionFailedException("User can only be created via invite"); } } if (!EmailValidator.getInstance().isValid(email)) { throw new PreconditionFailedException("Invalid Email " + email); } if (StringUtils.isEmpty(password)) { throw new PreconditionFailedException("Password required"); } if (status == null) { throw new PreconditionFailedException("UserStatus required"); } if (role == null) { throw new PreconditionFailedException("UserRole required"); } if (this.userDao.findUser(email) != null) { throw new PreconditionFailedException("Email already registered"); } User user = new User(); user.setClientid(clientid); user.setEmail(email); user.setStatus(status); user.setRole(role); setUserPassword(user, password); setUserLoginToken(user, loginToken); user = this.userDao.saveUser(user); return user; } @Override public void deleteUser(final String email) { if (!StringUtils.isEmpty(email)) { User user = this.userDao.findUser(email); if (user != null) { if (!(UserRole.ROLE_ADMIN.equals(user.getRole()) && this.userDao.getAdminUserCount() == 1)) { this.userDao.deleteUser(user); } else { throw new PreconditionFailedException("Cannot delete, only admin"); } } else { throw new PreconditionFailedException("Email " + email + " not found"); } } else { throw new PreconditionFailedException("Invalid Email"); } } @Override public void deleteUserNotification(final String email, final String uuid) { User user = this.userDao.findUser(email); this.userDao.deleteNotification(user, uuid); } @Override public void deleteUserSettings(final String email, final UserSettings setting) { User user = this.userDao.findUser(email); if (user != null) { UserSetting us = this.userDao.findUserSetting(user, setting); if (us != null) { this.userDao.deleteUserSetting(us); } else { throw new PreconditionFailedException("Setting not found"); } } else { throw new PreconditionFailedException("User not found"); } } @Override public UserDetails findActiveUser(final String email, final String password) throws AuthenticationFailureException { boolean matched = false; User user = null; if (!StringUtils.isEmpty(email) && !StringUtils.isEmpty(password)) { try { user = findActiveUserByEmail(email); matched = isMatch(password, user.getPassword()); if (!matched && password.startsWith("Basic")) { user = findActiveUserByLoginToken(email, password); } } catch (AuthenticationFailureException e) { if (password.startsWith("Basic")) { user = findActiveUserByLoginToken(email, password); matched = true; } else { throw e; } } if (!matched) { user = null; } } if (user == null) { throw new AuthenticationFailureException("Authentication failed. " + "Please verify your email address " + "and password and try again."); } return user; } /** * Find Active User. * @param email {@link String} * @return {@link User} */ @Override public User findActiveUserByEmail(final String email) { User user = this.userDao.findUser(email); return verifyUserActive(user); } /** * Finds Active User by Login Token. * @param loginToken {@link String} * @param password {@link String} * @return {@link User} */ private User findActiveUserByLoginToken(final String loginToken, final String password) { String clientid = null; String s = password.replaceAll("Basic ", ""); String[] decode = Strings.decode(s, ":"); if (decode.length == 2) { clientid = decode[0]; } User user = this.userDao.findUserByLoginToken(clientid, loginToken); return verifyUserActive(user); } @Override public UserNotificationListDTO findNotifications(final String email, final String token) { User user = this.userDao.findUser(email); if (user != null) { return this.userDao.findNotifications(user, token); } throw new PreconditionFailedException("User not found"); } @Override public UserDTO findUser(final String email, final boolean includePassword) throws AuthenticationFailureException { UserDTO user = null; if (!StringUtils.isEmpty(email)) { user = this.userDao.findUserDTO(email); if (user != null && !includePassword) { user.setPassword(null); } } if (user == null) { throw new AuthenticationFailureException("Authentication failed. " + "Please verify your email address " + "and password and try again."); } return user; } /** * Find User. * @param email {@link String} * @return {@link User} */ @Override public UserDetails findUserByEmail(final String email) { return this.userDao.findUser(email); } @Override public UserListDTO findUsers(final String token, final String text) { return this.userDao.findUsers(token, text); } /** * Generate secured Password Hash. * @param password String * @return {@link String} */ String generatedSecuredPasswordHash(final String password) { String securedPasswordHash = this.passwordEncoder.encode(password); return securedPasswordHash; } @Override public String generateResetToken(final String email) throws AuthenticationFailureException { User u = this.userDao.findUser(email); if (u != null && (ACTIVE.equals(u.getStatus()) || INVITE.equals(u.getStatus()))) { String token = generateSecurityToken(); String securedTokenHash = generatedSecuredPasswordHash(token); Date now = this.dateservice.now(); u.setResetToken(securedTokenHash); u.setResetInsertedDate(now); u.setUpdatedDate(now); this.userDao.saveUser(u); return token; } throw new AuthenticationFailureException(DISABLED_MESSAGE); } @Override public String generateSecurityToken() { String securityToken = UUID.randomUUID().toString().replaceAll("-", ""); return securityToken; } /** * Gets the User Reset Token expiry in minutes. * @return int */ protected int getUserTokenExpiryInMinutes() { return DEFAULT_EXPIRY_MINUTES; } /** * Checks password match. * @param rawPassword String * @param encodedPassword String * @return boolean */ private boolean isMatch(final String rawPassword, final String encodedPassword) { return this.passwordEncoder.matches(rawPassword, encodedPassword); } @Override public void saveNotification(final String email, final String folder, final Workflow workflow, final NotificationMethod type) throws JsonProcessingException { User user = this.userDao.findUser(email); if (user != null) { this.userDao.saveUserNotification(user, folder, workflow, type); } else { throw new PreconditionFailedException("User not found"); } } @Override public User saveUser(final String clientid, final String email, final String password, final UserRole role, final UserStatus status, final String loginToken) { User user = this.userDao.findUser(email); if (user != null) { if (role != null) { user.setRole(role); } if (status != null) { user.setStatus(status); if (UserStatus.DELETED.equals(status) || UserStatus.DISABLE.equals(status)) { this.userDao.deleteUserAccessToken(user.getEmail()); } } setUserPassword(user, password); setUserLoginToken(user, loginToken); user = this.userDao.saveUser(user); } else { user = createUser(clientid, email, password, status, role, loginToken); } return user; } @Override public void saveUserSettings(final String email, final UserSettings setting, final String value) { User user = this.userDao.findUser(email); if (user != null) { UserSetting us = this.userDao.findUserSetting(user, setting); if (us == null) { us = new UserSetting(); us.setUserid(user.getUserid()); us.setSetting(setting); } us.setValue(value); this.userDao.saveUserSetting(us); } else { throw new PreconditionFailedException("User not found"); } } /** * Encrypts the User Login Token and set it into the object. * @param user User * @param loginToken unencrypted login token */ private void setUserLoginToken(final User user, final String loginToken) { if (!StringUtils.isEmpty(loginToken)) { user.setLoginToken(loginToken); } } /** * Encrypts the User Password and set it into the object. * @param user User * @param password unencrypted password */ private void setUserPassword(final User user, final String password) { if (!StringUtils.isEmpty(password)) { String securedPassHash = generatedSecuredPasswordHash(password); user.setPassword(securedPassHash); } } @Override public void shareFolderWithUser(final UserDetails ud, final String email, final String folderId, final List<FolderPermission> permissions) { User user = (User) ud; FolderAccess access = this.folderDao.findFolderAccess(user, folderId); if (this.securityService.hasPermission(access, FolderPermission.PERM_FORM_ADMIN)) { User inviteUser = this.userDao.findUser(email); if (inviteUser == null) { throw new PreconditionFailedException("User " + email + " does not exist"); } FolderAccess fa = this.folderDao.findFolderAccess(inviteUser, folderId); if (fa == null || fa.getFolderaccessid() == null) { fa = new FolderAccess(); fa.setStatus(FolderStatus.ACTIVE); } Folder folder = this.folderDao.findFolder(folderId); fa.setFolderid(folder.getFolderid()); fa.setUserid(inviteUser.getUserid()); fa.setPermissions(permissions); this.folderDao.saveFolderAccess(fa); } else { throw new FormAccessDeniedException(); } } @Override public void updateLastLogin(final String email, final Date date) { this.userDao.updateLastLogin(email, date); } @Override public void updateLastUserAgent(final String email, final String useragent) { if (StringUtils.hasText(useragent)) { User user = (User) this.securityService.getUserDetails(); if (!useragent.equals(user.getLastUserAgent())) { this.userDao.updateLastUserAgent(email, useragent); } } } @Override public void updatePassword(final String email, final String resettoken, final String newPassword) { boolean updated = false; if (!StringUtils.isEmpty(resettoken)) { User user = this.userDao.findUser(email); if (user != null) { if (isMatch(resettoken, user.getResetToken())) { Date now = this.dateservice.now(); Date date = DateUtils.addMinutes(now, -getUserTokenExpiryInMinutes()); if (user.getResetInsertedDate().after(date)) { user.setResetInsertedDate(null); user.setResetToken(null); updated = true; } } else { updated = isMatch(resettoken, user.getPassword()); } if (updated) { if (UserStatus.INVITE.equals(user.getStatus())) { user.setStatus(UserStatus.ACTIVE); } setUserPassword(user, newPassword); this.userDao.saveUser(user); } } } if (!updated) { throw new PreconditionFailedException("Invalid Old Password or Reset Token"); } } /** * Checks to make sure User is Active. * @param user {@link User} * @return {@link User} */ private User verifyUserActive(final User user) { if (user != null) { if (UserStatus.ACTIVE.equals(user.getStatus())) { return user; } throw new AuthenticationFailureException(DISABLED_MESSAGE); } throw new AuthenticationFailureException( "Authentication failed. " + "Please verify your email address " + "and password and try again."); } @Override public UserDetails findUser(final String userid) { return this.userDao.findUser(UUID.fromString(userid)); } }