001: /*
002: * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
003: *
004: * This file is part of TransferCM.
005: *
006: * TransferCM is free software; you can redistribute it and/or modify it under the
007: * terms of the GNU General Public License as published by the Free Software
008: * Foundation; either version 2 of the License, or (at your option) any later
009: * version.
010: *
011: * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
012: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
014: * details.
015: *
016: * You should have received a copy of the GNU General Public License along with
017: * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
018: * Fifth Floor, Boston, MA 02110-1301 USA
019: */
020:
021: package com.methodhead.reg;
022:
023: import com.methodhead.persistable.Persistable;
024: import com.methodhead.persistable.Key;
025: import com.methodhead.aikp.AutoIntKeyPersistable;
026: import com.methodhead.aikp.IntKey;
027: import com.methodhead.sitecontext.SiteContext;
028: import com.methodhead.auth.AuthUser;
029: import com.methodhead.util.MhfStringUtils;
030:
031: import org.apache.commons.beanutils.DynaClass;
032: import org.apache.commons.beanutils.DynaProperty;
033: import org.apache.commons.beanutils.BasicDynaClass;
034: import java.util.ArrayList;
035: import java.util.List;
036: import java.util.Iterator;
037: import java.sql.ResultSet;
038: import java.sql.SQLException;
039: import com.methodhead.persistable.ConnectionSingleton;
040: import org.apache.commons.lang.exception.ExceptionUtils;
041: import java.security.MessageDigest;
042: import java.security.NoSuchAlgorithmException;
043: import org.apache.log4j.Logger;
044: import org.apache.commons.lang.exception.ExceptionUtils;
045:
046: /**
047: * A User. The following fields are defined:
048: * <ul>
049: * <li><tt>int id = 0</tt></li>
050: * <li><tt>String password = ""</tt></li>
051: * <li><tt>int contact_id = 0</tt></li>
052: * </ul>
053: */
054: public class User extends AutoIntKeyPersistable implements AuthUser,
055: Comparable {
056:
057: private static DynaClass dynaClass_ = null;
058: private static DynaClass roleDynaClass_ = null;
059: private static DynaClass siteContextDynaClass_ = null;
060:
061: static {
062: //
063: // user
064: //
065: DynaProperty[] dynaProperties = new DynaProperty[] {
066: new DynaProperty("id", Integer.class),
067: new DynaProperty("password", String.class),
068: new DynaProperty("contact_id", Integer.class) };
069:
070: dynaClass_ = new BasicDynaClass("reg_user", User.class,
071: dynaProperties);
072:
073: //
074: // role
075: //
076: dynaProperties = new DynaProperty[] {
077: new DynaProperty("user_id", Integer.class),
078: new DynaProperty("sitecontext_id", Integer.class),
079: new DynaProperty("name", String.class) };
080:
081: roleDynaClass_ = new BasicDynaClass("reg_role",
082: Persistable.class, dynaProperties);
083: }
084:
085: // constructors /////////////////////////////////////////////////////////////
086:
087: public User() {
088: super (dynaClass_);
089: init();
090: }
091:
092: public User(DynaClass dynaClass) {
093: super (dynaClass);
094: init();
095: }
096:
097: // constants ////////////////////////////////////////////////////////////////
098:
099: // classes //////////////////////////////////////////////////////////////////
100:
101: // methods //////////////////////////////////////////////////////////////////
102:
103: public int compareTo(Object o) {
104:
105: if (o == null) {
106: throw new NullPointerException();
107: }
108:
109: User user = (User) o;
110:
111: return this .getContact().getFullName().compareTo(
112: user.getContact().getFullName());
113: }
114:
115: protected void init() {
116: setInt("id", 0);
117: setString("password", "");
118: setInt("contact_id", 0);
119: }
120:
121: public String toString() {
122: if ("".equals(getContact().getString("email")))
123: return "User (no email address)";
124: else
125: return getContact().getString("email");
126: }
127:
128: /**
129: * Extends default behaviour to handle password encryption if
130: * passwordEncrypted flag is set.
131: */
132: public void set(String name, Object value) {
133:
134: if (!loading_) {
135: if ("password".equals(name)) {
136: if (getPasswordEncrypted()) {
137: super .set("password",
138: encryptPassword((String) value));
139: return;
140: }
141: }
142: }
143:
144: super .set(name, value);
145: }
146:
147: public boolean authenticate(String password) {
148:
149: if (getPasswordEncrypted()) {
150:
151: return getString("password").equals(
152: encryptPassword(password));
153: } else {
154: return getString("password").equals(password);
155: }
156: }
157:
158: public String getLogin() {
159: return getContact().getString("email");
160: }
161:
162: public String getPublicSecret() {
163: return encryptPassword(getString("password"));
164: }
165:
166: public boolean loadForLogin(String login) {
167:
168: ResultSet rs = null;
169: try {
170: String sql = "SELECT " + " reg_user.id AS id " + "FROM "
171: + " reg_user " + "LEFT JOIN "
172: + " reg_contact ON "
173: + " reg_user.contact_id = reg_contact.id "
174: + "WHERE " + " reg_contact.email="
175: + getSqlLiteral(login);
176:
177: rs = ConnectionSingleton.runQuery(sql);
178:
179: if (rs == null) {
180: throw new SQLException("Null result set.");
181: }
182:
183: if (!rs.next()) {
184: return false;
185: }
186:
187: load(new IntKey(rs.getInt("id")));
188: return true;
189: } catch (SQLException e) {
190: String msg = "Doing something. "
191: + ExceptionUtils.getStackTrace(e);
192: logger_.error(msg);
193: throw new RuntimeException(msg);
194: } finally {
195: ConnectionSingleton.close(rs);
196: }
197: }
198:
199: /**
200: * Returns true if this user has the role <tt>name</tt> for the site
201: * <tt>siteContext</tt>.
202: */
203: public boolean hasRole(SiteContext siteContext, String name) {
204:
205: for (Iterator iter = roles_.iterator(); iter.hasNext();) {
206: Role role = (Role) iter.next();
207:
208: if (role.getSiteContext().equals(siteContext)
209: && role.getName().equals(name))
210: return true;
211: }
212:
213: return false;
214: }
215:
216: private SiteContext getSiteContext(int id) {
217:
218: SiteContext siteContext = new SiteContext();
219: siteContext.load(new IntKey(id));
220: return siteContext;
221: }
222:
223: private void saveRoles() {
224: Persistable p = new Persistable(roleDynaClass_);
225: p.setInt("user_id", getInt("id"));
226:
227: for (Iterator iter = roles_.iterator(); iter.hasNext();) {
228: Role role = (Role) iter.next();
229: p.setString("name", role.getName());
230: p.setInt("sitecontext_id", role.getSiteContext().getInt(
231: "id"));
232: p.saveNew();
233: }
234: }
235:
236: private void loadRoles() {
237: List l = Persistable.loadAll(roleDynaClass_, "user_id="
238: + getInt("id"), null);
239:
240: roles_.clear();
241: for (Iterator iter = l.iterator(); iter.hasNext();) {
242: Persistable p = (Persistable) iter.next();
243:
244: Role role = new Role();
245: role.setSiteContext(getSiteContext(p
246: .getInt("sitecontext_id")));
247: role.setName(p.getString("name"));
248:
249: roles_.add(role);
250: }
251: }
252:
253: private void deleteRoles() {
254: Persistable p = new Persistable(roleDynaClass_);
255: p.deleteAll(roleDynaClass_, "user_id=" + getInt("id"));
256: }
257:
258: public void saveNew() {
259: contact_.saveNew();
260: setInt("contact_id", contact_.getInt("id"));
261: super .saveNew();
262: saveRoles();
263: }
264:
265: public void load(Key key) {
266:
267: loading_ = true;
268: super .load(key);
269: contact_.load(new IntKey(getInt("contact_id")));
270: loadRoles();
271: loading_ = false;
272: }
273:
274: public void save() {
275: deleteRoles();
276: saveRoles();
277:
278: super .save();
279: contact_.save();
280: }
281:
282: public void delete() {
283: deleteRoles();
284: super .delete();
285: contact_.delete();
286: }
287:
288: /**
289: * Returns a list of all {@link com.methodhead.reg.User User}s associated
290: * with <tt>siteContext</tt>.
291: */
292: public List loadAllForSiteContext(SiteContext siteContext) {
293:
294: ResultSet rs = null;
295:
296: List list = new ArrayList();
297:
298: try {
299: rs = ConnectionSingleton
300: .runQuery("SELECT DISTINCT user_id FROM reg_role WHERE sitecontext_id="
301: + siteContext.getInt("id"));
302:
303: while (rs.next()) {
304: User user = new User();
305: user.load(new IntKey(rs.getInt("user_id")));
306: list.add(user);
307: }
308:
309: ConnectionSingleton.close(rs);
310: } catch (SQLException e) {
311: ConnectionSingleton.close(rs);
312: throw new RuntimeException(
313: "Unexpected SQLException while loading all users for site context "
314: + siteContext + ":\n"
315: + ExceptionUtils.getStackTrace(e));
316: }
317:
318: return list;
319: }
320:
321: /**
322: * Encrypts <tt>password</tt> by MD5 hashing it and Base64 encoding the
323: * result.
324: */
325: protected String encryptPassword(String password) {
326:
327: return MhfStringUtils.hashAndEncode(password);
328: }
329:
330: // properties ///////////////////////////////////////////////////////////////
331:
332: /**
333: * Returns a list of {@link Role Role}s for the user.
334: */
335: public List getRoles() {
336: return roles_;
337: }
338:
339: public Contact getContact() {
340: return contact_;
341: }
342:
343: public boolean getPasswordEncrypted() {
344: return passwordEncrypted_;
345: }
346:
347: /**
348: * Sets the password encrypted flag. When this flag is set, passwords are
349: * hashed with MD5 and Base64 encoded before being saved in the database or
350: * used in {@link #authenticate authenticate()}.
351: */
352: public void setPasswordEncrypted(boolean passwordEncrypted) {
353: passwordEncrypted_ = passwordEncrypted;
354: }
355:
356: // attributes ///////////////////////////////////////////////////////////////
357:
358: private List roles_ = new ArrayList();
359: private Contact contact_ = new Contact();
360: private boolean passwordEncrypted_ = false;
361: private boolean loading_ = false;
362:
363: private static Logger logger_ = Logger.getLogger(User.class);
364: }
|