001: package com.technoetic.xplanner.security.module;
002:
003: import java.security.MessageDigest;
004: import java.security.SecureRandom;
005: import java.util.Arrays;
006: import java.util.Map;
007: import javax.security.auth.Subject;
008: import javax.servlet.http.HttpServletRequest;
009:
010: import net.sf.hibernate.Session;
011: import org.apache.log4j.Logger;
012:
013: import com.technoetic.xplanner.db.hibernate.ThreadSession;
014: import com.technoetic.xplanner.domain.Person;
015: import com.technoetic.xplanner.security.AuthenticationException;
016: import com.technoetic.xplanner.security.LoginModule;
017: import com.technoetic.xplanner.security.util.Base64;
018:
019: public class XPlannerLoginModule implements LoginModule {
020: private transient Logger log = Logger.getLogger(getClass());
021: private final transient SecureRandom secureRandom = new SecureRandom();
022: private String name;
023: private LoginSupport loginSupport;
024:
025: public XPlannerLoginModule(LoginSupport support) {
026: loginSupport = support;
027: }
028:
029: public void setOptions(Map options) {
030: }
031:
032: public Subject authenticate(String userId, String password)
033: throws AuthenticationException {
034: log.debug(ATTEMPTING_TO_AUTHENTICATE + this .getName() + " ("
035: + userId + ")");
036: Subject subject = loginSupport.createSubject();
037: Person person = loginSupport
038: .populateSubjectPrincipalFromDatabase(subject, userId);
039: if (!isPasswordMatched(person, password)) {
040: throw new AuthenticationException(
041: MESSAGE_AUTHENTICATION_FAILED_KEY);
042: }
043: log.debug(AUTHENTICATION_SUCCESFULL + this .getName());
044: return subject;
045: }
046:
047: public boolean isCapableOfChangingPasswords() {
048: return true;
049: }
050:
051: boolean isPasswordMatched(Person person, String password)
052: throws AuthenticationException {
053: log
054: .debug("evaluating password match for "
055: + person.getUserId());
056: String storedPassword = person.getPassword();
057: if (storedPassword == null) {
058: throw new AuthenticationException(MESSAGE_NULL_PASSWORD_KEY);
059: }
060: byte[] storedPasswordBytesWithSalt = Base64
061: .decode(storedPassword.getBytes());
062:
063: if (storedPasswordBytesWithSalt.length < 12) {
064: throw new AuthenticationException(
065: MESSAGE_AUTHENTICATION_FAILED_KEY);
066: }
067: byte[] salt = new byte[12];
068: System.arraycopy(storedPasswordBytesWithSalt, 0, salt, 0, 12);
069:
070: byte[] digestForGivenPassword = digestPassword(salt, password);
071: byte[] digestForExistingPassword = new byte[storedPasswordBytesWithSalt.length - 12];
072: System.arraycopy(storedPasswordBytesWithSalt, 12,
073: digestForExistingPassword, 0,
074: storedPasswordBytesWithSalt.length - 12);
075:
076: boolean isMatching = Arrays.equals(digestForGivenPassword,
077: digestForExistingPassword);
078: log.debug("passwords "
079: + (isMatching ? "matched" : "did not match") + " for "
080: + person.getUserId());
081: return isMatching;
082: }
083:
084: private byte[] digestPassword(byte[] salt, String password)
085: throws AuthenticationException {
086: try {
087: MessageDigest md = MessageDigest.getInstance("MD5");
088: md.update(salt);
089: md.update(password.getBytes("UTF8"));
090: return md.digest();
091: } catch (Exception e) {
092: throw new AuthenticationException(
093: MESSAGE_CONFIGURATION_ERROR_KEY, e);
094: }
095: }
096:
097: public void changePassword(String userId, String password)
098: throws AuthenticationException {
099: log.debug("changing password for " + userId);
100: try {
101: Session session = ThreadSession.get();
102: try {
103: Person person = loginSupport.getPerson(userId);
104: if (person != null) {
105: person.setPassword(encodePassword(password, null));
106: session.flush();
107: session.connection().commit();
108: } else {
109: throw new AuthenticationException(
110: "couldn't find person.");
111: }
112: } catch (Throwable ex) {
113: session.connection().rollback();
114: log.error("error during password change.", ex);
115: throw new AuthenticationException("server error.");
116: }
117:
118: } catch (Exception e) {
119: log.error("error", e);
120: throw new AuthenticationException("server error.");
121: }
122: }
123:
124: public void logout(HttpServletRequest request)
125: throws AuthenticationException {
126: request.getSession().invalidate();
127: }
128:
129: public String getName() {
130: return name;
131: }
132:
133: public void setName(String name) {
134: this .name = name;
135: }
136:
137: public String encodePassword(String password, byte[] salt)
138: throws Exception {
139: if (salt == null) {
140: salt = new byte[12];
141: secureRandom.nextBytes(salt);
142: }
143:
144: byte[] digest = digestPassword(salt, password);
145: byte[] storedPassword = new byte[digest.length + 12];
146:
147: System.arraycopy(salt, 0, storedPassword, 0, 12);
148: System.arraycopy(digest, 0, storedPassword, 12, digest.length);
149:
150: return new String(Base64.encode(storedPassword));
151: }
152:
153: // This main can be used to generate a hashed password by hand, if needed.
154: public static void main(String[] args) {
155: try {
156: String password;
157: if (args.length == 0) {
158: password = "admin";
159: } else {
160: password = args[0];
161: }
162: System.out.println(new XPlannerLoginModule(
163: new LoginSupportImpl()).encodePassword(password,
164: null));
165: } catch (Exception e) {
166: e.printStackTrace();
167: }
168: }
169: }
|