diff --git a/projects/samskivert/src/java/com/samskivert/servlet/Makefile b/projects/samskivert/src/java/com/samskivert/servlet/Makefile new file mode 100644 index 00000000..2a1361b8 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/servlet/Makefile @@ -0,0 +1,9 @@ +# +# $Id: Makefile,v 1.1 2001/03/02 01:21:06 mdb Exp $ + +ROOT = ../../.. + +SRCS = \ + RedirectException.java \ + +include $(ROOT)/build/Makefile.java diff --git a/projects/samskivert/src/java/com/samskivert/servlet/RedirectException.java b/projects/samskivert/src/java/com/samskivert/servlet/RedirectException.java new file mode 100644 index 00000000..6f7a7384 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/servlet/RedirectException.java @@ -0,0 +1,25 @@ +// +// $Id: RedirectException.java,v 1.1 2001/03/02 01:21:06 mdb Exp $ + +package com.samskivert.servlet; + +/** + * A redirect exception is thrown by servlet services when they require + * that the user be redirected to a different URL rather than continue + * processing this request. It is expected that redirect handling can be + * implemented in a single place such that servlets can simply allow this + * exception to propagate up to the proper handler which will then issue + * the appropriate redirect header. + */ +public class RedirectException extends Exception +{ + public RedirectException (String url) + { + super(url); + } + + public String getRedirectURL () + { + return getMessage(); + } +} diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/InvalidUsernameException.java b/projects/samskivert/src/java/com/samskivert/servlet/user/InvalidUsernameException.java new file mode 100644 index 00000000..033cb8ab --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/InvalidUsernameException.java @@ -0,0 +1,12 @@ +// +// $Id: InvalidUsernameException.java,v 1.1 2001/03/02 01:21:06 mdb Exp $ + +package com.samskivert.servlet.user; + +public class InvalidUsernameException extends Exception +{ + public InvalidUsernameException (String message) + { + super(message); + } +} diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/Makefile b/projects/samskivert/src/java/com/samskivert/servlet/user/Makefile new file mode 100644 index 00000000..362aa764 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/Makefile @@ -0,0 +1,13 @@ +# +# $Id: Makefile,v 1.1 2001/03/02 01:21:06 mdb Exp $ + +ROOT = ../../../.. + +SRCS = \ + InvalidUsernameException.java \ + User.java \ + UserExistsException.java \ + UserManager.java \ + UserRepository.java \ + +include $(ROOT)/build/Makefile.java diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/User.java b/projects/samskivert/src/java/com/samskivert/servlet/user/User.java new file mode 100644 index 00000000..ce3687a5 --- /dev/null +++ b/projects/samskivert/src/java/com/samskivert/servlet/user/User.java @@ -0,0 +1,66 @@ +// +// $Id: User.java,v 1.1 2001/03/02 01:21:06 mdb Exp $ + +package com.samskivert.servlet.user; + +import java.sql.Date; + +/** + * A user object contains information about a registered user in our web + * application environment. Users are stored in the user repository (a + * database table) and loaded during a request based on authentication + * information provided with the request headers. + * + *
Note: Do not modify any of the fields of this object
+ * diretly. Use the set methods to make updates. If no set
+ * methods exist, you shouldn't be modifying that field.
+ */
+public class User
+{
+ /** The user's assigned integer userid. */
+ public int userid;
+
+ /** The user's chosen username. */
+ public String username;
+
+ /** The date this record was created. */
+ public Date created;
+
+ /**
+ * The user's real name (first, last and whatever else they opt to
+ * provide).
+ */
+ public String realname;
+
+ /** The user's chosen password (encrypted). */
+ public String password;
+
+ /** The user's email address. */
+ public String email;
+
+ /**
+ * Updates the user's real name.
+ */
+ public void setRealName (String realname)
+ {
+ this.realname = realname;
+ }
+
+ /**
+ * Updates the user's password.
+ *
+ * @param password The user's new (unencrypted) password.
+ */
+ public void setPassword (String password)
+ {
+ this.password = UserRepository.encryptPassword(username, password);
+ }
+
+ /**
+ * Updates the user's email address.
+ */
+ public void setEmail (String email)
+ {
+ this.email = email;
+ }
+}
diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java
new file mode 100644
index 00000000..e3a33e7c
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserExistsException.java
@@ -0,0 +1,12 @@
+//
+// $Id: UserExistsException.java,v 1.1 2001/03/02 01:21:06 mdb Exp $
+
+package com.samskivert.servlet.user;
+
+public class UserExistsException extends Exception
+{
+ public UserExistsException (String message)
+ {
+ super(message);
+ }
+}
diff --git a/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java
new file mode 100644
index 00000000..e84d4243
--- /dev/null
+++ b/projects/samskivert/src/java/com/samskivert/servlet/user/UserManager.java
@@ -0,0 +1,162 @@
+//
+// $Id: UserManager.java,v 1.1 2001/03/02 01:21:06 mdb Exp $
+
+package com.samskivert.servlet.user;
+
+import java.net.URLEncoder;
+import java.sql.SQLException;
+import java.util.Properties;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpUtils;
+
+import com.samskivert.Log;
+import com.samskivert.servlet.RedirectException;
+import com.samskivert.util.Interval;
+import com.samskivert.util.IntervalManager;
+import com.samskivert.util.StringUtil;
+
+/**
+ * The user manager provides easy access to user objects for servlets. It
+ * takes care of cookie management involved in login, logout and loading a
+ * user record during an authenticated session.
+ */
+public class UserManager
+{
+ /**
+ * A user manager creates a user repository through which to load and
+ * save user records. The properties needed to configure the user
+ * repository must be provided to the user manager at construct time.
+ *
+ *
Presently the user manager requires the following configuration + * information: + *
login_url: Should be set to the URL to which to
+ * redirect a requester if they are required to login before accessing
+ * the requested page. For example:
+ *
+ * + * login_url = /usermgmt/login.ajsp?return=%R + *+ * + * The
%R will be replaced with the URL encoded URL the
+ * user is currently requesting (complete with query parameters) so
+ * that the login code can redirect the user back to this request once
+ * they are authenticated.
+ * + * driver=[jdbc driver class] + * url=[jdbc driver url] + * username=[jdbc username] + * password=[jdbc password] + *+ * + * @param props a properties object containing the configuration + * parameters for the repository. + */ + public UserRepository (Properties props) + throws SQLException + { + super(props); + } + + protected void createTables () + throws SQLException + { + // create our table object + _utable = new Table(User.class.getName(), "users", _session, + "userid"); + } + + /** + * Requests that a new user be created in the repository. + * + * @param username The username of the new user to create. Usernames + * must consist only of characters that match the following regular + * expression:
[_A-Za-z0-9]+ and be longer than two
+ * characters.
+ * @param password The (unencrypted) password for the new user.
+ * @param realname The user's real name.
+ * @param email The user's email address.
+ *
+ * @return The userid of the newly created user.
+ */
+ public int createUser (String username, String password,
+ String realname, String email)
+ throws InvalidUsernameException, UserExistsException, SQLException
+ {
+ // check minimum length
+ if (username.length() < MINIMUM_USERNAME_LENGTH) {
+ throw new InvalidUsernameException("error.username_too_short");
+ }
+
+ // check that it's only valid characters
+ if (!_userre.match(username)) {
+ throw new InvalidUsernameException("error.invalid_username");
+ }
+
+ // create a new user object and stick it into the database
+ final User user = new User();
+ user.username = username;
+ user.password = encryptPassword(username, password);
+ user.realname = realname;
+ user.email = email;
+ user.created = new Date(System.currentTimeMillis());
+
+ try {
+ execute(new Operation () {
+ public void invoke () throws SQLException
+ {
+ _utable.insert(user);
+ // update the userid now that it's known
+ user.userid = lastInsertedId();
+ }
+ });
+
+ } catch (SQLException sqe) {
+ if (isDuplicateRowException(sqe)) {
+ throw new UserExistsException("error.user_exists");
+ } else {
+ throw sqe;
+ }
+ }
+
+ return user.userid;
+ }
+
+ /**
+ * Encrypts the user's password according to our preferred scheme.
+ */
+ public static String encryptPassword (String username, String password)
+ {
+ return Crypt.crypt(username.substring(0, 2), password);
+ }
+
+ /**
+ * Looks up a user by username.
+ *
+ * @return the user with the specified user id or null if no user with
+ * that id exists.
+ */
+ public User getUser (String username)
+ throws SQLException
+ {
+ // look up the user
+ Cursor ec = _utable.select("where username = '" + username + "'");
+
+ // fetch the user from the cursor
+ User user = (User)ec.next();
+ if (user != null) {
+ // call next() again to cause the cursor to close itself
+ ec.next();
+ }
+
+ return user;
+ }
+
+ /**
+ * Looks up a user by userid.
+ *
+ * @return the user with the specified user id or null if no user with
+ * that id exists.
+ */
+ public User getUser (int userid)
+ throws SQLException
+ {
+ // look up the user
+ Cursor ec = _utable.select("where userid = " + userid);
+
+ // fetch the user from the cursor
+ User user = (User)ec.next();
+ if (user != null) {
+ // call next() again to cause the cursor to close itself
+ ec.next();
+ }
+
+ return user;
+ }
+
+ /**
+ * Looks up a user by a session identifier.
+ *
+ * @return the user associated with the specified session or null of
+ * no session exists with the supplied identifier.
+ */
+ public User getUserBySession (String sessionKey)
+ throws SQLException
+ {
+ // look up the user
+ Cursor ec = _utable.select("sessions",
+ "where authcode = '" + sessionKey +
+ "' AND sessions.userid = users.userid");
+
+ // fetch the user from the cursor
+ User user = (User)ec.next();
+ if (user != null) {
+ // call next() again to cause the cursor to close itself
+ ec.next();
+ }
+
+ return user;
+ }
+
+ /**
+ * Updates a user that was previously fetched from the repository.
+ */
+ public void updateUser (final User user)
+ throws SQLException
+ {
+ execute(new Operation () {
+ public void invoke () throws SQLException
+ {
+ _utable.update(user);
+ }
+ });
+ }
+
+ /**
+ * Removes the user from the repository.
+ */
+ public void deleteUser (final User user)
+ throws SQLException
+ {
+ execute(new Operation () {
+ public void invoke () throws SQLException
+ {
+ _utable.delete(user);
+ }
+ });
+ }
+
+ /**
+ * Prunes any expired sessions from the sessions table.
+ */
+ public void pruneSessions ()
+ throws SQLException
+ {
+ execute(new Operation () {
+ public void invoke () throws SQLException
+ {
+ _session.execute("delete from sessions where " +
+ "expires >= CURRENT_DATE()");
+ }
+ });
+ }
+
+ public static void main (String[] args)
+ {
+ Properties props = new Properties();
+ props.put("driver", "org.gjt.mm.mysql.Driver");
+ props.put("url", "jdbc:mysql://localhost:3306/samskivert");
+ props.put("username", "www");
+ props.put("password", "Il0ve2PL@Y");
+
+ try {
+ UserRepository rep = new UserRepository(props);
+
+ System.out.println(rep.getUser("mdb"));
+ System.out.println(rep.getUserBySession("auth"));
+
+ rep.createUser("samskivert", "foobar", "Michael Bayne",
+ "mdb@samskivert.com");
+ rep.createUser("mdb", "foobar", "Michael Bayne",
+ "mdb@samskivert.com");
+
+ rep.shutdown();
+
+ } catch (Throwable t) {
+ t.printStackTrace(System.err);
+ }
+ }
+
+ protected Table _utable;
+
+ protected static final int MINIMUM_USERNAME_LENGTH = 2;
+
+ /** Used to check usernames for invalid characteres. */
+ protected static RE _userre;
+ static {
+ try {
+ _userre = new RE("^[_A-Za-z0-9]+$");
+ } catch (RESyntaxException rese) {
+ Log.warning("Unable to initialize user regexp?! " + rese);
+ }
+ }
+}