/*******************************************************************************
* Copyright 2006 - 2012 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* 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 eu.scape_project.pw.idp;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.mail.Message.RecipientType;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.persistence.EntityManager;
import org.apache.commons.configuration.Configuration;
import org.slf4j.Logger;
import eu.scape_project.pw.idp.excpetions.CannotSendMailException;
import eu.scape_project.pw.idp.excpetions.UserNotFoundException;
import eu.scape_project.pw.idp.model.IdpRole;
import eu.scape_project.pw.idp.model.IdpUser;
import eu.scape_project.pw.idp.model.IdpUserState;
import eu.scape_project.pw.idp.utils.ConfigurationLoader;
/**
* Class responsible for managing users in the identity provider.
*/
@Stateless
public class UserManager {
/**
* Entity manager.
*/
@Inject
private EntityManager em;
/**
* Logger for this class.
*/
@Inject
private Logger log;
@Inject
private ConfigurationLoader configurationLoader;
/**
* Standard role name for a user.
*/
private static final String STANDARD_ROLE_NAME = "authenticated";
/**
* Properties for activation mail.
*/
private Configuration config;
/**
* Init this class.
*/
@PostConstruct
public void init() {
config = configurationLoader.load();
}
/**
* Adds a new user. Adds the standard role to the use. If the standard role
* does not exist, creates it.
*
* @param user
* User to add.
*/
public void addUser(IdpUser user) {
// Set standard role
IdpRole role = null;
List<IdpRole> standardRoles = em
.createQuery("SELECT r from IdpRole r WHERE rolename = :rolename", IdpRole.class)
.setParameter("rolename", STANDARD_ROLE_NAME).getResultList();
if (standardRoles.size() == 1) {
role = standardRoles.get(0);
} else {
role = new IdpRole();
role.setRoleName(STANDARD_ROLE_NAME);
em.persist(role);
log.warn("Standard role not found, added standard role {}.", STANDARD_ROLE_NAME);
}
List<IdpRole> roles = user.getRoles();
roles.add(role);
// Create a user actionToken which is needed for activation
user.setActionToken(UUID.randomUUID().toString());
user.setStatus(IdpUserState.CREATED);
em.persist(user);
log.info("Added user with username {}.", user.getUsername());
}
/**
* Activates an existing user.
*
* @param user
* the user to activate
* @throws UserNotFoundException
* if the user could not be found
*/
public void activateUser(IdpUser user) throws UserNotFoundException {
IdpUser foundUser = em.find(IdpUser.class, user.getId());
if (foundUser == null) {
log.error("Error activating user. User {} not found.", user.getUsername());
throw new UserNotFoundException("Error activating user. User " + user.getUsername() + "not found.");
}
foundUser.setStatus(IdpUserState.ACTIVE);
foundUser.setActionToken("");
em.persist(foundUser);
log.info("Activated user with username {}.", foundUser.getUsername());
}
/**
* Method responsible for sending a email to the user, including a link to
* activate his user account.
*
* @param user
* User the activation mail should be sent to
* @param serverString
* Name and port of the server the user was added.
* @throws CannotSendMailException
* if the mail could not be sent
*/
public void sendActivationMail(IdpUser user, String serverString) throws CannotSendMailException {
try {
Properties props = System.getProperties();
props.put("mail.smtp.host", config.getString("mail.smtp.host"));
Session session = Session.getDefaultInstance(props, null);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(config.getString("mail.from")));
message.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail()));
message.setSubject("Please confirm your Planningsuite user account");
StringBuilder builder = new StringBuilder();
builder.append("Dear " + user.getFirstName() + " " + user.getLastName() + ", \n\n");
builder.append("Please use the following link to confirm your Planningsuite user account: \n");
builder.append("http://" + serverString + "/idp/activateUser.jsf?uid=" + user.getActionToken());
builder.append("\n\n--\n");
builder.append("Your Planningsuite team");
message.setText(builder.toString());
message.saveChanges();
Transport.send(message);
log.debug("Activation mail sent successfully to {}", user.getEmail());
} catch (Exception e) {
log.error("Error at sending activation mail to {}", user.getEmail());
throw new CannotSendMailException("Error at sending activation mail to " + user.getEmail(), e);
}
}
/**
* Initiates password reset for the user.
*
* @param user
* the user
*/
public void initiateResetPassword(IdpUser user) {
user.setActionToken(UUID.randomUUID().toString());
em.persist(em.merge(user));
log.info("Set action token for password reset mail for user {}", user.getUsername());
}
/**
* Sends the user a link to reset the password.
*
* @param user
* the user
* @param serverString
* host and port of the server
* @throws CannotSendMailException
* if the password reset mail could not be sent
*/
public void sendPasswordResetMail(IdpUser user, String serverString) throws CannotSendMailException {
try {
Properties props = System.getProperties();
props.put("mail.smtp.host", config.getString("mail.smtp.host"));
Session session = Session.getDefaultInstance(props, null);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(config.getString("mail.from")));
message.setRecipient(RecipientType.TO, new InternetAddress(user.getEmail()));
message.setSubject("Planningsuite password recovery");
StringBuilder builder = new StringBuilder();
builder.append("Dear " + user.getFirstName() + " " + user.getLastName() + ", \n\n");
builder.append("You have requested help recovering the password for the Plato user ");
builder.append(user.getUsername()).append(".\n\n");
builder.append("Please use the following link to reset your Planningsuite password: \n");
builder.append("http://" + serverString + "/idp/resetPassword.jsf?uid=" + user.getActionToken());
builder.append("\n\n--\n");
builder.append("Your Planningsuite team");
message.setText(builder.toString());
message.saveChanges();
Transport.send(message);
log.debug("Sent password reset mail to " + user.getEmail());
} catch (Exception e) {
log.error("Error at sending password reset mail to {}", user.getEmail());
throw new CannotSendMailException("Error at sending password reset mail to " + user.getEmail());
}
}
/**
* Resets the password of the user identified by the actionToken.
*
* @param user
* the user
* @throws UserNotFoundException
* if the user could not be found
*/
public void resetPassword(IdpUser user) throws UserNotFoundException {
// We have to find the user because if we use em.merge(user)
// user.plainPassword will be deleted (because it is transient).
IdpUser foundUser = em.find(IdpUser.class, user.getId());
if (foundUser == null) {
log.error("Error resetting password. User not found {}.", user.getUsername());
throw new UserNotFoundException("Error resetting password. User not found " + user.getUsername());
}
foundUser.setPlainPassword(user.getPlainPassword());
foundUser.setActionToken("");
foundUser.setStatus(IdpUserState.ACTIVE);
em.persist(foundUser);
log.info("Reset password for user " + user.getUsername());
}
/**
* Reads the user identified by the provided action token.
*
* @param actionToken
* the action token identifying the user
* @return the user
* @throws UserNotFoundException
* if no user could be found
*/
public IdpUser getUserByActionToken(String actionToken) throws UserNotFoundException {
List<IdpUser> matchingUsers = em
.createQuery("SELECT u FROM IdpUser u WHERE u.actionToken = :actionToken", IdpUser.class)
.setParameter("actionToken", actionToken).getResultList();
if (matchingUsers.size() != 1) {
log.error("{} users matching given actionToken {}", matchingUsers.size(), actionToken);
throw new UserNotFoundException(matchingUsers.size() + " users matching given actionToken " + actionToken);
}
return matchingUsers.get(0);
}
/**
* Reads the user identified by the provided identifier.
*
* @param userIdentifier
* the identifier identifying the user
* @return the user
* @throws UserNotFoundException
* if no user could be found
*/
public IdpUser getUserByIdentifier(String userIdentifier) throws UserNotFoundException {
List<IdpUser> matchingUsers = em
.createQuery("SELECT u FROM IdpUser u WHERE u.username = :userIdentifier OR u.email = :userIdentifier",
IdpUser.class).setParameter("userIdentifier", userIdentifier).getResultList();
if (matchingUsers.size() != 1) {
log.error("{} users matching given identifier {}", matchingUsers.size(), userIdentifier);
throw new UserNotFoundException(matchingUsers.size() + " users matching given identifier " + userIdentifier);
}
return matchingUsers.get(0);
}
// ---------- getter/setter ----------
// Method used to make this class Unit-testable
protected void setEntityManager(EntityManager em) {
this.em = em;
}
// Method used to make this class Unit-testable
protected void setLog(Logger log) {
this.log = log;
}
}