/**
* Copyright (c) 2015 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.common.db;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.db.datasource.ModeFactory;
import com.redhat.rhn.common.db.datasource.SelectMode;
import com.redhat.rhn.common.db.datasource.WriteMode;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.common.util.StringUtil;
import com.redhat.rhn.domain.common.ResetPassword;
import com.redhat.rhn.domain.user.User;
/**
* ResetPasswordFactory: API to create, find, delete, and manage ResetPassword
* entries.
*
* @author ggainey
* @Rev:
*
*/
public class ResetPasswordFactory extends HibernateFactory {
public static final String EXPIRE_TIME = "password_token_expiration_hours";
private static ResetPasswordFactory singleton = new ResetPasswordFactory();
private static Logger log = Logger.getLogger(ResetPasswordFactory.class);
private ResetPasswordFactory() {
super();
}
@Override
protected Logger getLogger() {
return log;
}
/**
* Persist a RestPassword entity
* @param rp ResetPassword to be persisted
*/
public static void save(ResetPassword rp) {
WriteMode wm = ModeFactory.getWriteMode("ResetPassword_queries",
"insert_token");
Map<String, Object> params = new HashMap<String, Object>();
params.put("user_id", rp.getUserId());
params.put("token", rp.getToken());
wm.executeUpdate(params);
}
/**
* Find a given ResetPassword entry by token.
* @param token token of interest
* @return ResetPassword, or null if none found
*/
public static ResetPassword lookupByToken(String token) {
SelectMode sm = ModeFactory.getMode("ResetPassword_queries",
"find_by_token");
Map<String, Object> params = new HashMap<String, Object>();
params.put("token", token);
DataResult<ResetPassword> dr = sm.execute(params);
if (dr == null || dr.size() == 0) {
return null;
}
else {
return dr.get(0);
}
}
/**
* Invalidate all tokens for a specified user-id
* @param uid user-id whose tokens are to be marked invalid
* @return number of tokens invalidated
*/
public static int invalidateUserTokens(Long uid) {
WriteMode wm = ModeFactory.getWriteMode("ResetPassword_queries",
"invalidate_user_tokens");
Map<String, Object> params = new HashMap<String, Object>();
params.put("user_id", uid);
return wm.executeUpdate(params);
}
/**
* Remove all tokens for a specified user-id. NOTE: this is generally Not Done,
* for auditing purposes
* @param uid user-id whose tokens are to be deleted
* @return number of deleted rows
*/
public static int deleteUserTokens(Long uid) {
WriteMode wm = ModeFactory.getWriteMode("ResetPassword_queries",
"delete_user_tokens");
Map<String, Object> params = new HashMap<String, Object>();
params.put("user_id", uid);
return wm.executeUpdate(params);
}
/**
* Create a unique one-use token for a specified User
* @param u User whose password is to be reset
* @return unique SHA1 hash
*/
public static String generatePasswordToken(User u) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
// What matters is that the token cannot be guessed from publically-available
// info (like timestamp or login or uid). A random UUID is 'something only
// the server knows'
UUID uuid = UUID.randomUUID();
String hash = StringUtil.getHexString(md.digest(uuid.toString().getBytes()));
while (lookupByToken(hash) != null) {
uuid = UUID.randomUUID();
hash = StringUtil.getHexString(md.digest(uuid.toString().getBytes()));
}
return hash;
}
catch (NoSuchAlgorithmException e) {
log.error("Failed to find SHA-1?!?", e);
return null;
}
}
/**
* Create a new ResetPassword entry for the specified user
* @param u User whose password is to be reset
* @return ResetPassword entity
*/
public static ResetPassword createNewEntryFor(User u) {
ResetPassword rp = new ResetPassword(u.getId(), generatePasswordToken(u));
save(rp);
return rp;
}
/**
* Invalidate the specified token in the DB
* @param token token to be marked as invalid
*/
public static void invalidateToken(String token) {
WriteMode wm = ModeFactory.getWriteMode("ResetPassword_queries",
"invalidate_token");
Map<String, Object> params = new HashMap<String, Object>();
params.put("token", token);
wm.executeUpdate(params);
}
/**
* Generate the URL for the specified ResetPassword token
* @param rp ResetPassword of interest
* @return URL leading to ResetLink on 'this' machine, with the token of interest
*/
public static String generateLink(ResetPassword rp) {
String link = "https://" + ConfigDefaults.get().getHostname() +
"/rhn/ResetLink.do?token=" + rp.getToken();
return link;
}
/**
* Report problems with a ResetPassword entity
* @param rp ResetPassword of interest
* @return ActionErrors list - isEmpty() means "no errors found"
*/
public static ActionErrors findErrors(ResetPassword rp) {
log.debug("findErrors : [" + (rp == null ? "null" : rp.toString()) + "]");
ActionErrors errors = new ActionErrors();
if (rp == null) {
log.debug("findErrors: no RP found");
errors.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("resetpassword.jsp.error.notoken"));
}
else if (!rp.isValid()) {
log.debug("findErrors: invalid RP found");
errors.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("resetpassword.jsp.error.invalidtoken"));
}
else if (rp.isExpired()) {
log.debug("findErrors: expired RP found");
errors.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("resetpassword.jsp.error.expiredtoken"));
}
return errors;
}
}