io.hops.hopsworks.common.user.AuthController.java Source code

Java tutorial

Introduction

Here is the source code for io.hops.hopsworks.common.user.AuthController.java

Source

/*
 * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b
 * are released under the following license:
 *
 * This file is part of Hopsworks
 * Copyright (C) 2018, Logical Clocks AB. All rights reserved
 *
 * Hopsworks is free software: you can redistribute it and/or modify it under the terms of
 * the GNU Affero General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 *
 * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b
 * are released under the following license:
 *
 * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package io.hops.hopsworks.common.user;

import io.hops.hopsworks.common.dao.certificates.CertsFacade;
import io.hops.hopsworks.common.dao.certificates.ProjectGenericUserCerts;
import io.hops.hopsworks.common.dao.certificates.UserCerts;
import io.hops.hopsworks.common.dao.project.Project;
import io.hops.hopsworks.common.dao.project.ProjectFacade;
import io.hops.hopsworks.common.dao.user.BbcGroup;
import io.hops.hopsworks.common.dao.user.BbcGroupFacade;
import io.hops.hopsworks.common.dao.user.UserFacade;
import io.hops.hopsworks.common.dao.user.Users;
import io.hops.hopsworks.common.dao.user.ldap.LdapUser;
import io.hops.hopsworks.common.dao.user.ldap.LdapUserFacade;
import io.hops.hopsworks.common.dao.user.security.audit.AccountAuditFacade;
import io.hops.hopsworks.common.dao.user.security.audit.AccountsAuditActions;
import io.hops.hopsworks.common.dao.user.security.audit.RolesAuditAction;
import io.hops.hopsworks.common.dao.user.security.audit.UserAuditActions;
import io.hops.hopsworks.common.dao.user.security.ua.SecurityQuestion;
import io.hops.hopsworks.common.dao.user.security.ua.SecurityUtils;
import io.hops.hopsworks.common.dao.user.security.ua.UserAccountStatus;
import io.hops.hopsworks.common.dao.user.security.ua.UserAccountType;
import io.hops.hopsworks.common.dao.user.security.ua.UserAccountsEmailMessages;
import io.hops.hopsworks.common.exception.RESTCodes;
import io.hops.hopsworks.common.exception.ServiceException;
import io.hops.hopsworks.common.exception.UserException;
import io.hops.hopsworks.common.security.CertificatesMgmService;
import io.hops.hopsworks.common.user.ldap.LdapRealm;
import io.hops.hopsworks.common.util.EmailBean;
import io.hops.hopsworks.common.util.HopsUtils;
import io.hops.hopsworks.common.util.Settings;
import org.apache.commons.codec.digest.DigestUtils;

import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.naming.NamingException;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

@Stateless
@TransactionAttribute(TransactionAttributeType.NEVER)
public class AuthController {

    private final static Logger LOGGER = Logger.getLogger(AuthController.class.getName());
    private final static int SALT_LENGTH = 64;
    private final static int RANDOM_PWD_LEN = 8;
    private final static Random RANDOM = new SecureRandom();

    @EJB
    private UserFacade userFacade;
    @EJB
    private BbcGroupFacade bbcGroupFacade;
    @EJB
    private UserStatusValidator userStatusValidator;
    @EJB
    private Settings settings;
    @EJB
    private AccountAuditFacade accountAuditFacade;
    @EJB
    private EmailBean emailBean;
    @EJB
    private CertsFacade userCertsFacade;
    @EJB
    private ProjectFacade projectFacade;
    @EJB
    private CertificatesMgmService certificatesMgmService;
    @EJB
    private LdapRealm ldapRealm;
    @EJB
    private LdapUserFacade ldapUserFacade;

    /**
     * Pre check for custom realm login.
     *
     * @param user
     * @param password
     * @param otp
     * @param req
     * @return
     */
    public String preCustomRealmLoginCheck(Users user, String password, String otp, HttpServletRequest req)
            throws UserException {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (user.getMode().equals(UserAccountType.LDAP_ACCOUNT_TYPE)) {
            throw new IllegalArgumentException("Can not login ldap user. Use LDAP login.");
        }
        if (isTwoFactorEnabled(user)) {
            if ((otp == null || otp.isEmpty()) && user.getMode().equals(UserAccountType.M_ACCOUNT_TYPE)) {
                if (checkPasswordAndStatus(user, password, req)) {
                    throw new IllegalStateException("Second factor required.");
                }
            }
        }

        // Add padding if custom realm is disabled
        if (otp == null || otp.isEmpty() && user.getMode().equals(UserAccountType.M_ACCOUNT_TYPE)) {
            otp = Settings.MOBILE_OTP_PADDING;
        }
        String newPassword = getPasswordPlusSalt(password, user.getSalt());
        if (otp.length() == Settings.MOBILE_OTP_PADDING.length()
                && user.getMode().equals(UserAccountType.M_ACCOUNT_TYPE)) {
            newPassword = newPassword + otp;
        } else {
            throw new IllegalArgumentException("Could not recognize the account type. Report a bug.");
        }
        return newPassword;
    }

    public String preLdapLoginCheck(Users user, String password) {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (!user.getMode().equals(UserAccountType.LDAP_ACCOUNT_TYPE)) {
            throw new IllegalArgumentException("User is not registerd as ldap user.");
        }
        return getPasswordPlusSalt(password, user.getSalt()) + Settings.MOBILE_OTP_PADDING;
    }

    /**
     * Validates password and update account audit. Use validatePwd if ldap user.
     *
     * @param user
     * @param password
     * @param req
     * @return
     */
    public boolean validatePassword(Users user, String password, HttpServletRequest req) {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (user.getMode().equals(UserAccountType.LDAP_ACCOUNT_TYPE)) {
            throw new IllegalArgumentException("Operation not allowed for LDAP account.");
        }
        String userPwdHash = user.getPassword();
        String pwdHash = getPasswordHash(password, user.getSalt());
        if (!userPwdHash.equals(pwdHash)) {
            registerFalseLogin(user, req);
            LOGGER.log(Level.WARNING, "False login attempt by user: {0}", user.getEmail());
            return false;
        }
        resetFalseLogin(user);
        return true;
    }

    /**
     * Validate password works both for hopsworks and ldap user
     * @param user
     * @param password
     * @param req
     * @return
     */
    public boolean validatePwd(Users user, String password, HttpServletRequest req) {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (user.getMode().equals(UserAccountType.LDAP_ACCOUNT_TYPE)) {
            LdapUser ldapUser = ldapUserFacade.findByUsers(user);
            if (ldapUser == null) {
                return false;
            }
            try {
                ldapRealm.authenticateLdapUser(ldapUser, password);
                return true;
            } catch (LoginException ex) {
                LOGGER.log(Level.WARNING, "False login attempt by ldap user: {0}", user.getEmail());
                LOGGER.log(Level.WARNING, null, ex.getMessage());
                return false;
            } catch (EJBException | NamingException ee) {
                LOGGER.log(Level.WARNING, "Could not reach LDAP server. {0}", ee.getMessage());
                throw new IllegalStateException("Could not reach LDAP server.");
            }
        }
        String userPwdHash = user.getPassword();
        String pwdHash = getPasswordHash(password, user.getSalt());
        if (!userPwdHash.equals(pwdHash)) {
            registerFalseLogin(user, req);
            LOGGER.log(Level.WARNING, "False login attempt by user: {0}", user.getEmail());
            return false;
        }
        resetFalseLogin(user);
        return true;
    }

    /**
     * Validate security question and update false login attempts
     *
     * @param user
     * @param securityQ
     * @param securityAnswer
     * @param req
     * @return
     */
    public boolean validateSecurityQA(Users user, String securityQ, String securityAnswer, HttpServletRequest req) {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (user.getMode().equals(UserAccountType.LDAP_ACCOUNT_TYPE)) {
            throw new IllegalArgumentException("Operation not allowed for LDAP account.");
        }
        if (securityQ == null || securityQ.isEmpty() || securityAnswer == null || securityAnswer.isEmpty()) {
            return false;
        }
        if (!user.getSecurityQuestion().getValue().equalsIgnoreCase(securityQ)
                || !user.getSecurityAnswer().equals(DigestUtils.sha256Hex(securityAnswer.toLowerCase()))) {
            registerFalseLogin(user, req);
            LOGGER.log(Level.WARNING, "False Security Question attempt by user: {0}", user.getEmail());
            return false;
        }
        return true;
    }

    /**
     * Checks password and user status. Also updates false login attempts
     *
     * @param user
     * @param password
     * @param req
     * @return
     */
    public boolean checkPasswordAndStatus(Users user, String password, HttpServletRequest req)
            throws UserException {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (!validatePassword(user, password, req)) {
            return false;
        }
        return userStatusValidator.checkStatus(user.getStatus());
    }

    /**
     * Validates email validation key. Also updates false key validation attempts.
     *
     * @param key
     * @param req
     */
    public void validateKey(String key, HttpServletRequest req) throws UserException {
        if (key == null) {
            throw new IllegalArgumentException("the validation key should not be null");
        }
        if (key.length() <= Settings.USERNAME_LENGTH) {
            throw new IllegalArgumentException(
                    "The validation key is invalid. Minimum length is: " + Settings.USERNAME_LENGTH);
        }
        String userName = key.substring(0, Settings.USERNAME_LENGTH);
        // get the 8 char username
        String secret = key.substring(Settings.USERNAME_LENGTH);
        Users user = userFacade.findByUsername(userName);
        if (user == null) {
            throw new UserException(RESTCodes.UserErrorCode.USER_WAS_NOT_FOUND, Level.FINE);
        }
        if (!secret.equals(user.getValidationKey())) {
            registerFalseKeyValidation(user, req);
            throw new UserException(RESTCodes.UserErrorCode.INCORRECT_VALIDATION_KEY, Level.FINE,
                    "user: " + user.getUsername());
        }

        if (!user.getStatus().equals(UserAccountStatus.NEW_MOBILE_ACCOUNT)) {
            switch (user.getStatus()) {
            case VERIFIED_ACCOUNT:
                throw new UserException(RESTCodes.UserErrorCode.ACCOUNT_INACTIVE, Level.FINE);
            case ACTIVATED_ACCOUNT:
                throw new UserException(RESTCodes.UserErrorCode.ACCOUNT_ALREADY_VERIFIED, Level.FINE);
            default:
                throw new UserException(RESTCodes.UserErrorCode.ACCOUNT_BLOCKED, Level.FINE);
            }
        }

        user.setStatus(UserAccountStatus.VERIFIED_ACCOUNT);
        userFacade.update(user);
        accountAuditFacade.registerRoleChange(user, UserAccountStatus.VERIFIED_ACCOUNT.name(),
                RolesAuditAction.SUCCESS.name(), "Account verification", user, req);
    }

    /**
     * Sends new activation key to the given user.
     *
     * @param user
     * @param req
     * @throws MessagingException
     */
    public void sendNewValidationKey(Users user, HttpServletRequest req) throws MessagingException {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        String activationKey = SecurityUtils.getRandomPassword(RANDOM_PWD_LEN);
        emailBean.sendEmail(user.getEmail(), Message.RecipientType.TO,
                UserAccountsEmailMessages.ACCOUNT_REQUEST_SUBJECT,
                UserAccountsEmailMessages.buildMobileRequestMessageRest(settings.getVerificationEndpoint(),
                        user.getUsername() + activationKey));
        user.setValidationKey(activationKey);
        userFacade.update(user);
    }

    /**
     * Reset password with random string and send email to user with the new password.
     *
     * @param user
     * @param req
     * @throws MessagingException
     * @throws Exception
     */
    public void resetPassword(Users user, HttpServletRequest req) throws ServiceException {
        if (user == null) {
            throw new IllegalArgumentException("User not set.");
        }
        if (userStatusValidator.isBlockedAccount(user)) {
            throw new IllegalStateException("User is blocked.");
        }
        String randomPassword = SecurityUtils.getRandomPassword(UserValidator.PASSWORD_MIN_LENGTH);
        String message = UserAccountsEmailMessages.buildTempResetMessage(randomPassword);
        try {
            emailBean.sendEmail(user.getEmail(), Message.RecipientType.TO,
                    UserAccountsEmailMessages.ACCOUNT_PASSWORD_RESET, message);
        } catch (MessagingException ex) {
            throw new ServiceException(RESTCodes.ServiceErrorCode.EMAIL_SENDING_FAILURE, Level.SEVERE,
                    "user: " + user.getUsername(), ex.getMessage(), ex);
        }
        changePassword(user, randomPassword, req);
        resetFalseLogin(user);
        accountAuditFacade.registerAccountChange(user, AccountsAuditActions.RECOVERY.name(),
                UserAuditActions.SUCCESS.name(), "Password reset.", user, req);
    }

    /**
     * Test if two factor is enabled in the system and by the user
     *
     * @param user
     * @return
     */
    public boolean isTwoFactorEnabled(Users user) {
        String twoFactorAuth = settings.getTwoFactorAuth();
        String twoFactorExclude = settings.getTwoFactorExclude();
        String twoFactorMode = (twoFactorAuth != null ? twoFactorAuth : "");
        String excludes = (twoFactorExclude != null ? twoFactorExclude : null);
        String[] groups = (excludes != null && !excludes.isEmpty() ? excludes.split(";") : new String[] {});

        for (String group : groups) {
            if (isUserInRole(user, group)) {
                return false; //will allow anyone if one of the users groups are in the exclude list
            }
        }
        if (twoFactorMode.equals(Settings.TwoFactorMode.MANDATORY.getName())) {
            return true;
        } else if (twoFactorMode.equals(Settings.TwoFactorMode.OPTIONAL.getName()) && user.getTwoFactor()) {
            return true;
        }

        return false;
    }

    /**
     * Test if two factor is enabled
     *
     * @return
     */
    public boolean isTwoFactorEnabled() {
        String twoFactorAuth = settings.getTwoFactorAuth();
        String twoFactorMode = (twoFactorAuth != null ? twoFactorAuth : "");
        return twoFactorMode.equals(Settings.TwoFactorMode.MANDATORY.getName())
                || twoFactorMode.equals(Settings.TwoFactorMode.OPTIONAL.getName());
    }

    /**
     * Hash password + salt
     *
     * @param password
     * @param salt
     * @return
     */
    public String getPasswordHash(String password, String salt) {
        return getHash(getPasswordPlusSalt(password, salt));
    }

    /**
     * Returns the hash of the value
     *
     * @param val
     * @return
     */
    public String getHash(String val) {
        return DigestUtils.sha256Hex(val);
    }

    /**
     * Change password to the given password. Will generate a new salt
     *
     * @param user
     * @param password
     * @param req
     * @throws Exception
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void changePassword(Users user, String password, HttpServletRequest req) {
        String salt = generateSalt();
        String passwordWithSalt = getPasswordHash(password, salt);
        String oldPassword = user.getPassword();
        user.setPassword(passwordWithSalt);
        user.setSalt(salt);
        user.setPasswordChanged(new Timestamp(new Date().getTime()));
        userFacade.update(user);
        resetProjectCertPassword(user, oldPassword);
    }

    /**
     * Change security question and adds account audit for the operation.
     *
     * @param user
     * @param securityQuestion
     * @param securityAnswer
     * @param req
     */
    public void changeSecQA(Users user, String securityQuestion, String securityAnswer, HttpServletRequest req) {
        user.setSecurityQuestion(SecurityQuestion.getQuestion(securityQuestion));
        user.setSecurityAnswer(DigestUtils.sha256Hex(securityAnswer.toLowerCase()));
        userFacade.update(user);
        accountAuditFacade.registerAccountChange(user, AccountsAuditActions.SECQUESTION.name(),
                AccountsAuditActions.SUCCESS.name(), "Changed Security Question.", user, req);
    }

    /**
     * Concatenates password and salt
     *
     * @param password
     * @param salt
     * @return
     */
    public String getPasswordPlusSalt(String password, String salt) {
        return password + salt;
    }

    private void resetProjectCertPassword(Users p, String oldPass) {
        //For every project, change the certificate secret in the database
        //Get cert password by decrypting it with old password
        List<Project> projects = projectFacade.findAllMemberStudies(p);
        List<ProjectGenericUserCerts> pguCerts = null;
        try {
            for (Project project : projects) {
                UserCerts userCert = userCertsFacade.findUserCert(project.getName(), p.getUsername());
                String masterEncryptionPassword = certificatesMgmService.getMasterEncryptionPassword();
                String certPassword = HopsUtils.decrypt(oldPass, userCert.getUserKeyPwd(),
                        masterEncryptionPassword);
                //Encrypt it with new password and store it in the db
                String newSecret = HopsUtils.encrypt(p.getPassword(), certPassword, masterEncryptionPassword);
                userCert.setUserKeyPwd(newSecret);
                userCertsFacade.update(userCert);

                //If user is owner of the project, update projectgenericuser certs as well
                if (project.getOwner().equals(p)) {
                    if (pguCerts == null) {
                        pguCerts = new ArrayList<>();
                    }
                    ProjectGenericUserCerts pguCert = userCertsFacade
                            .findProjectGenericUserCerts(project.getName() + Settings.PROJECT_GENERIC_USER_SUFFIX);
                    pguCerts.add(userCertsFacade
                            .findProjectGenericUserCerts(project.getName() + Settings.PROJECT_GENERIC_USER_SUFFIX));
                    String pguCertPassword = HopsUtils.decrypt(oldPass, pguCert.getCertificatePassword(),
                            masterEncryptionPassword);
                    //Encrypt it with new password and store it in the db
                    String newPguSecret = HopsUtils.encrypt(p.getPassword(), pguCertPassword,
                            masterEncryptionPassword);
                    pguCert.setCertificatePassword(newPguSecret);
                    userCertsFacade.updatePGUCert(pguCert);
                }
            }
        } catch (Exception ex) {
            LOGGER.log(Level.SEVERE, null, ex);
            throw new EJBException(ex);
        }

    }

    /**
     * Register failed login attempt.
     *
     * @param user
     * @param req
     */
    public void registerFalseLogin(Users user, HttpServletRequest req) {
        if (user != null) {
            int count = user.getFalseLogin() + 1;
            user.setFalseLogin(count);

            // block the user account if more than allowed false logins
            if (count > Settings.ALLOWED_FALSE_LOGINS) {
                user.setStatus(UserAccountStatus.BLOCKED_ACCOUNT);
                try {
                    emailBean.sendEmail(user.getEmail(), Message.RecipientType.TO,
                            UserAccountsEmailMessages.ACCOUNT_BLOCKED__SUBJECT,
                            UserAccountsEmailMessages.accountBlockedMessage());
                } catch (MessagingException ex) {
                    LOGGER.log(Level.SEVERE, "Failed to send email. ", ex);
                }
                accountAuditFacade.registerRoleChange(user, UserAccountStatus.SPAM_ACCOUNT.name(),
                        RolesAuditAction.SUCCESS.name(), "False login retries:" + Integer.toString(count), user,
                        req);
            }
            // notify user about the false attempts
            userFacade.update(user);
        }
    }

    /**
     * Registers failed email validation
     *
     * @param user
     * @param req
     */
    public void registerFalseKeyValidation(Users user, HttpServletRequest req) {
        if (user != null) {
            int count = user.getFalseLogin() + 1;
            user.setFalseLogin(count);

            // make the user spam account if more than allowed tries
            if (count > Settings.ACCOUNT_VALIDATION_TRIES) {
                user.setStatus(UserAccountStatus.SPAM_ACCOUNT);
            }
            userFacade.update(user);
            accountAuditFacade.registerRoleChange(user, UserAccountStatus.SPAM_ACCOUNT.name(),
                    RolesAuditAction.SUCCESS.name(), "Wrong validation key retries: " + Integer.toString(count),
                    user, req);
        }
    }

    /**
     * Set user online, resets false login attempts and register login audit info
     *
     * @param user
     * @param req
     */
    public void registerLogin(Users user, HttpServletRequest req) {
        resetFalseLogin(user);
        setUserOnlineStatus(user, Settings.IS_ONLINE);
        accountAuditFacade.registerLoginInfo(user, UserAuditActions.LOGIN.name(), UserAuditActions.SUCCESS.name(),
                req);
        LOGGER.log(Level.INFO, "Logged in user: {0}. ", user.getEmail());
    }

    /**
     * Set user offline and register login audit info
     *
     * @param user
     * @param req
     */
    public void registerLogout(Users user, HttpServletRequest req) {
        setUserOnlineStatus(user, Settings.IS_OFFLINE);
        accountAuditFacade.registerLoginInfo(user, UserAuditActions.LOGOUT.name(), UserAuditActions.SUCCESS.name(),
                req);
        LOGGER.log(Level.INFO, "Logged out user: {0}. ", user.getEmail());
    }

    /**
     * Register authentication failure and register login audit info
     *
     * @param user
     * @param req
     */
    public void registerAuthenticationFailure(Users user, HttpServletRequest req) {
        registerFalseLogin(user, req);
        accountAuditFacade.registerLoginInfo(user, UserAuditActions.LOGIN.name(), UserAuditActions.FAILED.name(),
                req);
        LOGGER.log(Level.INFO, "Authentication failure user: {0}. ", user.getEmail());
    }

    private void resetFalseLogin(Users user) {
        if (user != null) {
            user.setFalseLogin(0);
            userFacade.update(user);
        }
    }

    private void setUserOnlineStatus(Users user, int status) {
        if (user != null) {
            user.setIsonline(status);
            userFacade.update(user);
        }
    }

    private boolean isUserInRole(Users user, String groupName) {
        if (user == null || groupName == null) {
            return false;
        }
        BbcGroup group = bbcGroupFacade.findByGroupName(groupName);
        if (group == null) {
            return false;
        }
        return user.getBbcGroupCollection().contains(group);
    }

    /**
     * Generates a salt value with SALT_LENGTH and DIGEST
     *
     * @return
     */
    public String generateSalt() {
        byte[] bytes = new byte[SALT_LENGTH];
        RANDOM.nextBytes(bytes);
        byte[] encodedSalt = Base64.getEncoder().encode(bytes);
        String salt = "";
        try {
            salt = new String(encodedSalt, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            LOGGER.log(Level.SEVERE, "Generate salt encoding failed", ex);
        }
        return salt;
    }
}