Commit 8eb6fd60 authored by Matt Tucker's avatar Matt Tucker Committed by matt

Refactored user classes.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@691 b35dd754-fafc-0310-a699-88a17e54d16e
parent d0ac74dc
......@@ -8,6 +8,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/i18n" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/plugins/broadcast/src/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
......
......@@ -32,17 +32,14 @@
<adminDN></adminDN>
<adminPassword></adminPassword>
</ldap>
<UserProvider>
<properties>
<className>org.jivesoftware.messenger.ldap.LdapUserPropertiesProvider</className>
</properties>
<info>
<className>org.jivesoftware.messenger.ldap.LdapUserInfoProvider</className>
</info>
</UserProvider>
<AuthProvider>
<className>org.jivesoftware.messenger.ldap.LdapAuthProvider</className>
</AuthProvider>
<provider>
<user>
<className>org.jivesoftware.messenger.ldap.LdapUserProvider</className>
</user>
<auth>
<className>org.jivesoftware.messenger.ldap.LdapAuthProvider</className>
</auth>
</provider>
-->
<!-- End Example LDAP Settings -->
</jive>
\ No newline at end of file
......@@ -160,26 +160,6 @@ public class DbConnectionManager {
}
/**
* Close the prepared statement and connection
* @param pstmt the <code>PreparedStatement</code> to close.
* @param con the <code>Connection</code> to close.
*/
public static void close(PreparedStatement pstmt, Connection con) {
try {
if (pstmt != null) pstmt.close();
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) con.close();
}
catch (Exception e) {
Log.error(e);
}
}
/**
* Creates a scroll insensitive Statement if the JDBC driver supports it, or a normal
* Statement otherwise.
......
......@@ -9,7 +9,7 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.auth;
package org.jivesoftware.messenger;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.StringUtils;
......
......@@ -12,7 +12,7 @@
package org.jivesoftware.messenger;
import org.xmpp.packet.JID;
import org.jivesoftware.messenger.user.RosterManager;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.handler.IQRegisterHandler;
import org.jivesoftware.messenger.handler.PresenceUpdateHandler;
......@@ -24,6 +24,7 @@ import org.jivesoftware.messenger.disco.ServerFeaturesProvider;
import org.jivesoftware.messenger.disco.ServerItemsProvider;
import org.jivesoftware.messenger.disco.IQDiscoInfoHandler;
import org.jivesoftware.messenger.muc.MultiUserChatServer;
import org.jivesoftware.messenger.roster.RosterManager;
import java.util.List;
......
......@@ -11,118 +11,70 @@
package org.jivesoftware.messenger.auth;
import org.jivesoftware.messenger.user.UserNotFoundException;
/**
* The essential interface to implement when creating an
* authentication service plug-in.<p>
* Provider interface for authentication. Users that wish to integrate with
* their own authentication system must implement this class and then register
* the implementation with Jive Messenger in the <tt>jive-messenger.xml</tt>
* file. An entry in that file would look like the following:
*
* Implementations of auth provider strictly handles the persistent
* storage access for authentication in Messenger. The auth provider
* is the only Messenger system dealing with users that does not necessarily
* have to associate a username with a user ID. This keeps the auth
* provider completely separate from the user system.
* <pre>
* &lt;provider&gt;
* &lt;auth&gt;
* &lt;className&gt;com.foo.auth.CustomAuthProvider&lt;/className&gt;
* &lt;/auth&gt;
* &lt;/provider&gt;</pre>
*
* @author Iain Shigeoka
* @author Matt Tucker
*/
public interface AuthProvider {
/**
* <p>Determines if the authentication system supports the use of
* plain-text passwords.</p>
* <p/>
* <p>Some servers may wish to disable plain text password
* support because the passwords are easy to steal in
* transit unless SSL is used. Plain-text passwords will be verified via the
* getAuthToken(String username, String password) method.</p>
* <p/>
* <p>Notice that this flag is used to indicate to clients
* whether the server will accept plain text passwords. It
* does not have to imply that passwords are stored as plain text.
* For example, passwords may be stored as MD5 hashes of the
* password in the database. Plain text passwords can be supported
* by simply taking the plain-text password sent from the client,
* MD5 hashing it, and seeing if it matches the hash stored in the
* database.</p>
* Returns true if this AuthProvider supports authentication using plain-text
* passwords according to JEP--0078. Plain text authentication is not secure
* and should generally only be used for a TLS/SSL connection.
*
* @return True if plain text passwords are supported on the server
* @return true if plain text password authentication is supported by
* this AuthProvider.
*/
boolean isPlainSupported();
/**
* <p>Determines if the authentication system supports the use
* of digest authentication.</p>
* <p/>
* <p>Some servers may wish to only enable plain text password
* support because the passwords are easy to steal using
* plain-text in transit unless SSL is used.</p>
* <p/>
* <p>Perhaps ironically, digest authentication requires plain-text
* passwords to be stored for each user. The digest protocol
* protects the password sent over the network by SHA-1 digesting
* it with a unique token. To check the digest, the plain-text copy
* of the password must be available on the server (thus increasing
* the need for keeping the backend store secure). If your user
* system cannot store passwords in plain text, then you should return
* false so digest authentication is not used. If you must use plain-text
* on an insecure network, it is recommend that users connect with
* SSL to protect the passwords in transit.</p>
* Returns true if this AuthProvider supports digest authentication
* according to JEP-0078.
*
* @return true if digest authentication is supported on the server.
* @return true if digest authentication is supported by this
* AuthProvider.
*/
boolean isDigestSupported();
/**
* <p>Returns if the username and password are valid otherwise the method throws an
* UnauthorizedException.<p>
* Returns if the username and password are valid; otherwise this
* method throws an UnauthorizedException.<p>
*
* @param username the username to create an AuthToken with.
* @param password the password to create an AuthToken with.
* @throws UnauthorizedException if the username and password
* do not match any existing user.
* If {@link #isPlainSupported()} returns false, this method should
* throw an UnsupportedOperationException.
*
* @param username the username.
* @param password the passwordl
* @throws UnauthorizedException if the username and password do
* not match any existing user.
*/
void authenticate(String username, String password) throws UnauthorizedException;
/**
* <p>Returns the AuthToken token associated with the specified
* username, unique session token, and digest generated from the
* password and token according to the Jabber digest auth protocol.</p>
* <p/>
* <p>If the username and digest do not match the record of
* any user in the system, the method throws an UnauthorizedException.<p>
* Returns if the username, token, and digest are valid; otherwise this
* method throws an UnauthorizedException.<p>
*
* If {@link #isDigestSupported()} returns false, this method should
* throw an UnsupportedOperationException.
*
* @param username the username to create an AuthToken with.
* @param token the token that was used with plain-text
* password to generate the digest
* @param digest The digest generated from plain-text password and unique token
* @param username the username.
* @param token the token that was used with plain-text password to
* generate the digest.
* @param digest the digest generated from plain-text password and unique token.
* @throws UnauthorizedException if the username and password
* do not match any existing user.
* do not match any existing user.
*/
void authenticate(String username, String token, String digest)
throws UnauthorizedException;
/**
* <p>Update the password for the given user.</p>
* <p/>
* <p>Sets the users's password. The password should be passed
* in as plain text. The way the password is stored is
* implementation dependent. However, it is recommended
* to at least hash passwords with an algorithm such as
* MD5 if you don't need to support digest authentication.</p>
* <p/>
* <p>If you don't want people to change their password through
* Messenger just throw UnauthorizedException.</p>
*
* @param username The username of the user who's password is changing
* @param password The new password for the user
* @throws UnauthorizedException If the password is invalid or
* the caller does not have permission to make the change
* @throws UserNotFoundException If the given user could not be located
* @throws UnsupportedOperationException If the provider does not
* support the operation (this is an optional operation)
*/
public void updatePassword(String username, String password)
throws UserNotFoundException,
UnauthorizedException,
UnsupportedOperationException;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.auth;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.JiveGlobals;
/**
* Provides a centralized source of the various auth providers.<p>
*
* The auth system has one provider. The provider allows you to
* integrate Messenger with various backend authenication systems without
* necessarily replacing the Messenger user management system.
* In other words, you can verify usernames and passwords against a common
* user directory, but allow messenger to manage copies of the user account
* data in it's own database. This results in a redundant storage of data
* and can cause 'data skew' where values are not updated in sync. However,
* it is the simplest and least amount of work to integrate Messenger with
* existing authentication systems.<p>
*
* Users of Jive that wish to change the AuthProvider implementation
* used to generate users can set the <code>AuthProvider.className</code>
* Jive property. For example, if you have altered Jive to use LDAP for
* user information, you'd want to send a custom implementation of
* AuthProvider classes to make LDAP authetnication queries. After changing the
* <code>AuthProvider.className</code> Jive property, you must restart
* Messenger. The valid properties are:<ul>
* <li>AuthProvider.className - specifies an AuthProvider class.</li>
* <li>GroupProvider.className - specifies a GroupProvider class.</li>
* </ul>
*
* @author Iain Shigeoka
*/
public class AuthProviderFactory {
/**
* The default class to instantiate is database implementation.
*/
private static String authClassName =
"org.jivesoftware.messenger.auth.spi.DbAuthProvider";
private static String groupClassName =
"org.jivesoftware.messenger.auth.spi.DbGroupProvider";
private static AuthProvider authProvider = null;
/**
* Returns a concrete AuthProvider. By default, the implementation
* used will be an instance of DbAuthProvider -- the standard database
* implementation that uses the Jive user table. A different authProvider
* can be specified by setting the Jive property "AuthProvider.className".
* However, you must restart Jive for any change to take effect.
*
* @return an AuthProvider instance.
*/
public static AuthProvider getAuthProvider() {
if (authProvider == null) {
// Use className as a convenient object to get a lock on.
synchronized (authClassName) {
if (authProvider == null) {
//See if the classname has been set as a Jive property.
String classNameProp =
JiveGlobals.getProperty("AuthProvider.className");
if (classNameProp != null) {
authClassName = classNameProp;
}
try {
Class c = ClassUtils.forName(authClassName);
authProvider = (AuthProvider)c.newInstance();
}
catch (Exception e) {
Log.error("Exception loading class: " + authClassName, e);
}
}
}
}
return authProvider;
}
}
......@@ -12,32 +12,40 @@
package org.jivesoftware.messenger.auth;
/**
* Proves that a user has successfully logged in. The existence of an AuthToken object indicates
* that a person has logged in correctly and has authentication to act as the user associated with
* the authentication. An instance of this object can be obtained from the AuthFactory and
* must be passed in to to get an instance of KBFactory or ForumFactory.<p>
* <p/>
* In the case of using the core faq or forum services through a web interface, the expected
* behavior is to have a user login and then store the AuthToken object in their session. In
* some app servers, all objects put in the session must be serializable. The default AuthToken
* implementation obeys this rule, but ensure that custom AuthToken classes do as well.
* A token that proves that a user has successfully authenticated.
*
* @author Iain Shigeoka
* @author Matt Tucker
* @see AuthFactory
*/
public interface AuthToken {
public class AuthToken {
private static final long serialVersionUID = 01L;
private String username;
/**
* Constucts a new AuthToken with the specified username.
*
* @param username the username to create an authToken token with.
*/
public AuthToken(String username) {
this.username = username;
}
/**
* Returns the username associated with this AuthToken.
*
* @return the username associated with this AuthToken.
*/
public String getUsername();
public String getUsername() {
return username;
}
/**
* Returns true if this AuthToken is the Anonymous auth token.
*
* @return true if this token is the anonymous AuthToken.
*/
public boolean isAnonymous();
public boolean isAnonymous() {
return username == null;
}
}
\ No newline at end of file
......@@ -9,13 +9,10 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.auth.spi;
package org.jivesoftware.messenger.auth;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.auth.AuthFactory;
import org.jivesoftware.messenger.auth.AuthProvider;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
......@@ -26,49 +23,21 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Implements the default Jive authenticator implementation. It makes an
* SQL query to the Jive user table to see if the supplied username and password
* match a user record. If they do, the appropriate user ID is returned.
* If no matching User record is found an UnauthorizedException is
* thrown.<p>
* Default AuthProvider implementation. It authenticates against the <tt>jiveUser</tt>
* database table and supports plain text and digest authentication.
*
* Because each call to authenticate() makes a database
* connection, the results of authentication should be cached whenever possible. When
* using a servlet or JSP skins, a good method is to cache a token in the
* session named AuthFactory.SESSION_AUTHORIZATION. The default
* AuthFactory.createAuthorization(HttpServletRequest request,
* HttpServletResponse response) method automatically handles this logic.<p>
*
* If you wish to integrate Jive Messenger with your own authentication or
* single sign-on system, you'll need your own implementation of the
* AuthProvider interface. See that interface for further details.<p>
*
* This implementation relies on the DbUserIDProvider to map usernames to IDs in
* order to lookup the password in the jiveUser table. This relationship is not required
* (authentication providers may be entirely independent of user management.
* Because each call to authenticate() makes a database connection, the
* results of authentication should be cached whenever possible.
*
* @author Matt Tucker
* @author Iain Shigeoka
*/
public class DbAuthProvider implements AuthProvider {
public class DefaultAuthProvider implements AuthProvider {
private static final String AUTHORIZE =
"SELECT username FROM jiveUser WHERE username=? AND password=?";
private static final String SELECT_PASSWORD =
"SELECT password FROM jiveUser WHERE username=?";
private static final String UPDATE_PASSWORD =
"UPDATE jiveUser set password=? WHERE username=?";
/**
* <p>Implementation requires the password to be stored in the user table in plain text.</p>
* <p>You can store the password in the database in another format and modify this method
* to check the given plain-text password (e.g. if the passwords are stored as MD5 hashes,
* MD5 hash the password and compare that with the database).</p>
*
* @param username the username to create an AuthToken with.
* @param password the password to create an AuthToken with.
* @throws UnauthorizedException if the username and password do not match any existing user.
*/
public void authenticate(String username, String password) throws UnauthorizedException {
if (username == null || password == null) {
throw new UnauthorizedException();
......@@ -86,10 +55,8 @@ public class DbAuthProvider implements AuthProvider {
pstmt = con.prepareStatement(AUTHORIZE);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
// If the query had no results, the username and password
// If the query has no results, the username and password
// did not match a user record. Therefore, throw an exception.
if (!rs.next()) {
throw new UnauthorizedException();
......@@ -109,18 +76,6 @@ public class DbAuthProvider implements AuthProvider {
// Got this far, so the user must be authorized.
}
/**
* Implementation requires the password to be stored in the user table in plain text.<p>
*
* There is no way to support digest authentication without the plain text password in the
* database. If you can't support plain-text passwords in the backend, then always throw
* UnauthorizedException immediately and be sure to return false in isDigestSupported().
*
* @param username The username of the user to check
* @param token The unique token (XMPP stream id) used to generate the digest
* @param digest The digest to be checked
* @throws UnauthorizedException If the login attempt failed
*/
public void authenticate(String username, String token, String digest) throws UnauthorizedException {
if (username == null || token == null || digest == null) {
throw new UnauthorizedException();
......@@ -146,7 +101,7 @@ public class DbAuthProvider implements AuthProvider {
throw new UnauthorizedException();
}
String pass = rs.getString(1);
String anticipatedDigest = AuthFactory.createTokenPasswordDigest(token, pass);
String anticipatedDigest = AuthFactory.createDigest(token, pass);
if (!digest.equalsIgnoreCase(anticipatedDigest)) {
throw new UnauthorizedException();
}
......@@ -165,46 +120,6 @@ public class DbAuthProvider implements AuthProvider {
// Got this far, so the user must be authorized.
}
/**
* Update the password for the given user. If you don't want people to change their
* password through Messenger just throw UnauthorizedException.
*
* @param username The username of the user who's password is changing
* @param password The new password for the user
* @throws UnauthorizedException If the password is invalid or the provider doesn't allow password updates
* @throws UserNotFoundException If the given user could not be located
*/
public void updatePassword(String username, String password) throws UserNotFoundException, UnauthorizedException {
if (username == null || password == null) {
throw new UnauthorizedException();
}
try {
username = Stringprep.nodeprep(username);
}
catch (StringprepException se) {
throw new UnauthorizedException("Illegal username: " + se.getMessage());
}
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(UPDATE_PASSWORD);
pstmt.setString(1, password);
pstmt.setString(2, username);
pstmt.executeUpdate();
}
catch (SQLException e) {
Log.error("Exception in DbAuthProvider", e);
throw new UnauthorizedException();
}
finally {
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
catch (Exception e) { Log.error(e); }
}
}
public boolean isPlainSupported() {
return true;
}
......
......@@ -11,53 +11,26 @@
package org.jivesoftware.messenger.auth;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Thrown if a User does not have permission to access a particular method.
* Thrown if a user does not have permission to access a particular method.
*
* @author Iain Shigeoka
*/
public class UnauthorizedException extends Exception {
private Throwable nestedThrowable = null;
public UnauthorizedException() {
super();
}
public UnauthorizedException(String msg) {
super(msg);
}
public UnauthorizedException(Throwable nestedThrowable) {
this.nestedThrowable = nestedThrowable;
}
public UnauthorizedException(String msg, Throwable nestedThrowable) {
super(msg);
this.nestedThrowable = nestedThrowable;
}
public void printStackTrace() {
super.printStackTrace();
if (nestedThrowable != null) {
nestedThrowable.printStackTrace();
}
public UnauthorizedException(String message) {
super(message);
}
public void printStackTrace(PrintStream ps) {
super.printStackTrace(ps);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(ps);
}
public UnauthorizedException(Throwable cause) {
super(cause);
}
public void printStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(pw);
}
public UnauthorizedException(String message, Throwable cause) {
super(message, cause);
}
}
\ No newline at end of file
......@@ -3,20 +3,7 @@
<head>
</head>
<body>
<p>Provides the interfaces and classes necessary to create custom
authentication data providers for Messenger.</p>
<p>Authentication can be separately provided from user accounts and is
only used during initial XMPP session connection (an infrequent
operation considering the persistent nature of most XMPP connections).
Implementors should concentrate on the AuthProvider interface.</p>
<p>Messenger also includes the concept of user groups, used to assign
permissions to sets of users rather than having to assign permissions
to many individuals which can be tedious with large user populations.
The group concept is heavily used in Jive Forums upon which the
Messenger user system is based. Messenger 1.1 does not actively use
groups but group support was kept in Messenger to ease future
integration with Forums. Custom data provider implementors are
discouraged from implementing custom GroupProvider classes as the
interface may change.</p>
<p>Authentication service interfaces and classes. Custom authentication
implementations can be created by extending the {@link AuthProvider} interface.
</body>
</html>
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.auth.spi;
import org.jivesoftware.messenger.auth.AuthToken;
import java.io.Serializable;
/**
* Database implementation of the AuthToken interface.
*
* @author Iain Shigeoka
*/
public final class AuthTokenImpl implements AuthToken, Serializable {
private static final long serialVersionUID = 01L;
private String username;
/**
* Constucts a new AuthTokenImpl with the specified username.
*
* @param username the username to create an authToken token with.
*/
public AuthTokenImpl(String username) {
this.username = username;
}
// AuthToken Interface
public String getUsername() {
return username;
}
public boolean isAnonymous() {
return username == null;
}
}
\ No newline at end of file
......@@ -28,7 +28,7 @@ import java.util.Collection;
*
* @author Iain Shigeoka
*/
public class DbGroupProvider implements GroupProvider {
public class DefaultGroupProvider implements GroupProvider {
private static final String INSERT_GROUP =
"INSERT INTO jiveGroup (groupName, description) VALUES (?, ?)";
......
......@@ -11,9 +11,6 @@
package org.jivesoftware.messenger.group;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
* Thrown when attempting to create a group that already exists.
*
......@@ -21,43 +18,20 @@ import java.io.PrintWriter;
*/
public class GroupAlreadyExistsException extends Exception {
private Throwable nestedThrowable = null;
public GroupAlreadyExistsException() {
super();
}
public GroupAlreadyExistsException(String msg) {
super(msg);
}
public GroupAlreadyExistsException(Throwable nestedThrowable) {
this.nestedThrowable = nestedThrowable;
}
public GroupAlreadyExistsException(String msg, Throwable nestedThrowable) {
super(msg);
this.nestedThrowable = nestedThrowable;
}
public void printStackTrace() {
super.printStackTrace();
if (nestedThrowable != null) {
nestedThrowable.printStackTrace();
}
public GroupAlreadyExistsException(String message) {
super(message);
}
public void printStackTrace(PrintStream ps) {
super.printStackTrace(ps);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(ps);
}
public GroupAlreadyExistsException(Throwable cause) {
super(cause);
}
public void printStackTrace(PrintWriter pw) {
super.printStackTrace(pw);
if (nestedThrowable != null) {
nestedThrowable.printStackTrace(pw);
}
public GroupAlreadyExistsException(String message, Throwable cause) {
super(message, cause);
}
}
\ No newline at end of file
......@@ -22,7 +22,7 @@ import java.util.Collection;
import java.util.ArrayList;
/**
* Manages groups
* Manages groups.
*
* @see Group
* @author Matt Tucker
......@@ -51,15 +51,15 @@ public class GroupManager {
groupCache = CacheManager.getCache("group");
groupMemberCache = CacheManager.getCache("group member");
// Load a group provider.
String className = JiveGlobals.getXMLProperty("group.provider.className",
"org.jivesoftware.messenger.group.DbGroupProvider");
String className = JiveGlobals.getXMLProperty("provider.group.className",
"org.jivesoftware.messenger.group.DefaultGroupProvider");
try {
Class c = ClassUtils.forName(className);
provider = (GroupProvider)c.newInstance();
}
catch (Exception e) {
Log.error("Error loading group provider: " + className, e);
provider = new DbGroupProvider();
provider = new DefaultGroupProvider();
}
}
......
......@@ -16,8 +16,17 @@ import org.jivesoftware.messenger.user.User;
import java.util.Collection;
/**
* Group providers load and store group information from a back-end store
* such as a database table, LDAP, etc.
* Provider interface for groups. Users that wish to integrate with
* their own group system must implement this class and then register
* the implementation with Jive Messenger in the <tt>jive-messenger.xml</tt>
* file. An entry in that file would look like the following:
*
* <pre>
* &lt;provider&gt;
* &lt;group&gt;
* &lt;className&gt;com.foo.auth.CustomGroupProvider&lt;/className&gt;
* &lt;/group&gt;
* &lt;/provider&gt;</pre>
*
* @author Matt Tucker
*/
......
......@@ -178,10 +178,11 @@ public class IQAuthHandler extends IQHandler implements IQAuthInfo {
if (response == null) {
AuthToken token = null;
if (password != null && AuthFactory.isPlainSupported()) {
token = AuthFactory.getAuthToken(username, password);
token = AuthFactory.authenticate(username, password);
}
else if (digest != null && AuthFactory.isDigestSupported()) {
token = AuthFactory.getAuthToken(username, session.getStreamID().toString(), digest);
token = AuthFactory.authenticate(username, session.getStreamID().toString(),
digest);
}
if (token == null) {
throw new UnauthorizedException();
......
......@@ -18,7 +18,8 @@ import org.jivesoftware.messenger.forms.spi.XDataFormImpl;
import org.jivesoftware.messenger.forms.spi.XFormFieldImpl;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.Permissions;
import org.jivesoftware.messenger.Permissions;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
......@@ -163,7 +164,7 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
currentRegistration.addElement("registered");
currentRegistration.element("username").setText(user.getUsername());
currentRegistration.element("password").setText("");
currentRegistration.element("email").setText(user.getInfo().getEmail());
currentRegistration.element("email").setText(user.getEmail());
Element form = currentRegistration.element(QName.get("x", "jabber:x:data"));
Iterator fields = form.elementIterator("field");
......@@ -174,10 +175,10 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
field.addElement("value").addText(user.getUsername());
}
else if ("name".equals(field.attributeValue("var"))) {
field.addElement("value").addText(user.getInfo().getName());
field.addElement("value").addText(user.getName());
}
else if ("email".equals(field.attributeValue("var"))) {
field.addElement("value").addText(user.getInfo().getEmail());
field.addElement("value").addText(user.getEmail());
}
}
reply.setChildElement(currentRegistration);
......@@ -279,17 +280,12 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
if (user != null) {
if (user.getUsername().equalsIgnoreCase(username)) {
user.setPassword(password);
user.getInfo().setEmail(email);
user.setEmail(email);
newUser = user;
}
else {
// An admin can create new accounts when logged in.
if (user.isAuthorized(Permissions.SYSTEM_ADMIN)) {
newUser = userManager.createUser(username, password, email);
}
else {
throw new UnauthorizedException();
}
newUser = userManager.createUser(username, password, null, email);
}
}
else {
......@@ -297,9 +293,9 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
}
}
else {
newUser = userManager.createUser(username, password, email);
newUser = userManager.createUser(username, password, null, email);
}
// Set and save the extra user info (e.g. Full name, name visible, etc.)
// Set and save the extra user info (e.g. full name, etc.)
if (newUser != null && registrationForm != null) {
Iterator<String> values;
// Get the full name sent in the form
......@@ -307,9 +303,8 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
if (field != null) {
values = field.getValues();
String name = (values.hasNext() ? values.next() : " ");
newUser.getInfo().setName(name);
newUser.setName(name);
}
newUser.saveInfo();
}
reply = IQ.createResultIQ(packet);
......
......@@ -15,10 +15,12 @@ import org.jivesoftware.messenger.disco.ServerFeaturesProvider;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.messenger.user.Roster;
import org.jivesoftware.messenger.roster.Roster;
import org.xmpp.packet.*;
import java.util.ArrayList;
import java.util.Iterator;
......@@ -204,7 +206,7 @@ public class IQRosterHandler extends IQHandler implements ServerFeaturesProvider
* @param sender The JID of the sender of the removal request
* @param item The removal item element
*/
private void removeItem(Roster roster, JID sender, org.xmpp.packet.Roster.Item item)
private void removeItem(org.jivesoftware.messenger.roster.Roster roster, JID sender, org.xmpp.packet.Roster.Item item)
throws UnauthorizedException
{
......
......@@ -19,6 +19,8 @@ import org.jivesoftware.messenger.user.UserNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Collection;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
......@@ -90,12 +92,12 @@ public class IQvCardHandler extends IQHandler {
Element vcard = DocumentHelper.createElement(QName.get("vCard", "vcard-temp"));
result.setChildElement(vcard);
Iterator names = user.getVCardPropertyNames();
while (names.hasNext()) {
String name = (String)names.next();
VCardManager vManager = VCardManager.getInstance();
Collection<String> names = vManager.getVCardPropertyNames(user.getUsername());
for (String name : names) {
String path = name.replace(':', '/');
Element node = DocumentHelper.makeElement(vcard, path);
node.setText(user.getVCardProperty(name));
node.setText(vManager.getVCardProperty(user.getUsername(), name));
}
}
else {
......@@ -125,7 +127,8 @@ public class IQvCardHandler extends IQHandler {
String value = child.getTextTrim();
if (value != null) {
if (!"".equals(value)) {
user.setVCardProperty(createName(nameStack), value);
VCardManager.getInstance().setVCardProperty(user.getUsername(),
createName(nameStack), value);
}
}
readVCard(child, nameStack, user);
......
......@@ -16,9 +16,11 @@ import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.Roster;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.messenger.user.spi.CachedRosterImpl;
import org.jivesoftware.messenger.roster.spi.CachedRosterImpl;
import org.xmpp.packet.Presence;
import org.xmpp.packet.Packet;
import org.xmpp.packet.JID;
......
......@@ -15,11 +15,13 @@ import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.spi.SessionImpl;
import org.jivesoftware.messenger.user.CachedRoster;
import org.jivesoftware.messenger.user.RosterItem;
import org.jivesoftware.messenger.user.RosterManager;
import org.jivesoftware.messenger.roster.CachedRoster;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.roster.CachedRoster;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.xmpp.packet.*;
......
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.ldap;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserInfo;
import org.jivesoftware.messenger.user.UserInfoProvider;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.UserInfo;
import org.jivesoftware.util.Log;
import java.util.Date;
import javax.naming.directory.*;
import javax.naming.NamingException;
/**
* LDAP implementation of the UserInfoProvider interface. The LdapUserIDProvider
* can operate in two modes -- in the pure LDAP mode, all user data is stored in
* the LDAP store. This mode generally requires modifications to the LDAP schema
* to accommodate data that Messenger needs. In the mixed mode, data that Messenger
* needs is stored locally.
*
* @author Jim Berrettini
*/
public class LdapUserInfoProvider implements UserInfoProvider {
private LdapManager manager;
/**
* Constructor initializes the internal LdapManager instance.
*/
public LdapUserInfoProvider() {
manager = LdapManager.getInstance();
}
/**
* <p>Obtain the UserInfo of a user. Will retrieve either from LDAP or locally, depending on mode of operation.</p>
*
* @param username the username.
* @return a user info object.
* @throws UserNotFoundException
*/
public UserInfo getInfo(String username) throws UserNotFoundException {
return getInfoFromLdap(username);
}
/**
* <p>Sets the user's info. In pure LDAP mode, this is unsupported.</p>
*
* @param username username for setting info.
* @param info to set.
* @throws UserNotFoundException
* @throws UnauthorizedException
* @throws UnsupportedOperationException
*/
public void setInfo(String username, UserInfo info)
throws UserNotFoundException, UnauthorizedException, UnsupportedOperationException {
throw new UnsupportedOperationException("All LDAP mode: Cannot modify data in LDAP.");
}
/**
* Pure LDAP method for getting info for a given userID.
*
* @param username the username.
* @return UserInfo for that user.
* @throws UserNotFoundException
*/
private UserInfo getInfoFromLdap(String username) throws UserNotFoundException {
UserInfo userInfo = null;
DirContext ctx = null;
try {
String userDN = manager.findUserDN(username);
// Load record.
String[] attributes = new String[]{
manager.getUsernameField(), manager.getNameField(),
manager.getEmailField()
};
ctx = manager.getContext();
Attributes attrs = ctx.getAttributes(userDN, attributes);
String name = null;
String email = null;
Attribute nameField = attrs.get(manager.getNameField());
if (nameField != null) {
name = (String)nameField.get();
}
Attribute emailField = attrs.get(manager.getEmailField());
if (emailField != null) {
email = (String)emailField.get();
}
userInfo = new UserInfo(username, name, email, new Date(), new Date());
}
catch (Exception e) {
throw new UserNotFoundException(e);
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
return userInfo;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.ldap;
import org.jivesoftware.messenger.user.UserPropertiesProvider;
import java.util.Collections;
import java.util.Map;
/**
* <p>Implement this provider to store user and vcard properties somewhere
* other than the Jive tables, or to capture jive property events. Currently this is unimplemented.</p>
*
* @author Jim Berrettini
*/
public class LdapUserPropertiesProvider implements UserPropertiesProvider {
public void deleteVcardProperty(String username, String name) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public void deleteUserProperty(String username, String name) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public void insertVcardProperty(String username, String name, String value) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public void insertUserProperty(String username, String name, String value) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public void updateVcardProperty(String username, String name, String value) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public void updateUserProperty(String username, String name, String value) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
public Map getVcardProperties(String username) {
return Collections.EMPTY_MAP;
}
public Map getUserProperties(String username) {
return Collections.EMPTY_MAP;
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.ldap;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.util.Log;
import javax.naming.directory.*;
import javax.naming.NamingEnumeration;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
/**
* LDAP implementation of the UserProvider interface. All data in the directory is
* treated as read-only so any set operations will result in an exception.
*
* @author Matt Tucker
*/
public class LdapUserProvider implements UserProvider {
private LdapManager manager;
public LdapUserProvider() {
manager = LdapManager.getInstance();
}
public User loadUser(String username) throws UserNotFoundException {
DirContext ctx = null;
try {
String userDN = manager.findUserDN(username);
// Load record.
String[] attributes = new String[]{
manager.getUsernameField(), manager.getNameField(),
manager.getEmailField()
};
ctx = manager.getContext();
Attributes attrs = ctx.getAttributes(userDN, attributes);
String name = null;
String email = null;
Attribute nameField = attrs.get(manager.getNameField());
if (nameField != null) {
name = (String)nameField.get();
}
Attribute emailField = attrs.get(manager.getEmailField());
if (emailField != null) {
email = (String)emailField.get();
}
return new User(username, name, email, new Date(), new Date());
}
catch (Exception e) {
throw new UserNotFoundException(e);
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
}
public User createUser(String username, String password, String name, String email)
throws UserAlreadyExistsException
{
throw new UnsupportedOperationException();
}
public void deleteUser(String username) {
throw new UnsupportedOperationException();
}
public int getUserCount() {
int count = 0;
DirContext ctx = null;
try {
ctx = manager.getContext();
// Search for the dn based on the username.
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = "(" + manager.getUsernameField() + "=*)";
NamingEnumeration answer = ctx.search("", filter, constraints);
while (answer.hasMoreElements()) {
count++;
answer.nextElement();
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
return count;
}
public Collection<User> getUsers() {
List<String> usernames = new ArrayList<String>();
DirContext ctx = null;
try {
ctx = manager.getContext();
// Search for the dn based on the username.
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = "(" + manager.getUsernameField() + "=*)";
NamingEnumeration answer = ctx.search("", filter, constraints);
while (answer.hasMoreElements()) {
// Get the next userID.
usernames.add(
(String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get()
);
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
return new UserCollection((String[])usernames.toArray(new String[usernames.size()]));
}
public Collection<User> getUsers(int startIndex, int numResults) {
List<String> usernames = new ArrayList<String>();
DirContext ctx = null;
try {
ctx = manager.getContext();
// Search for the dn based on the username.
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[] { "jiveUserID" });
String filter = "(jiveUserID=*)";
NamingEnumeration answer = ctx.search("", filter, constraints);
for (int i = 0; i < startIndex; i++) {
answer.next();
}
// Now read in desired number of results (or stop if we run out of results).
for (int i = 0; i < numResults; i++) {
if (answer.hasMoreElements()) {
// Get the next userID.
usernames.add(
(String)((SearchResult)answer.next()).getAttributes().get(
"jiveUserID").get()
);
}
else {
break;
}
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
return new UserCollection((String[])usernames.toArray(new String[usernames.size()]));
}
public void setPassword(String username, String password) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setName(String username, String name) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setEmail(String username, String email) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException {
throw new UnsupportedOperationException();
}
}
\ No newline at end of file
......@@ -9,7 +9,7 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import java.util.Hashtable;
import java.util.Iterator;
......@@ -18,6 +18,10 @@ import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.roster.Roster;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable;
import org.xmpp.packet.JID;
......
......@@ -9,12 +9,13 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.xmpp.packet.JID;
import org.jivesoftware.messenger.roster.RosterItem;
/**
* <p>Implements the basic RosterItem interface storing all data into simple fields.</p>
......
......@@ -9,11 +9,13 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.roster.*;
import org.jivesoftware.util.Cacheable;
import org.xmpp.packet.*;
import org.xmpp.packet.Roster;
/**
* <p>A Roster that is cached in memory and persisted to some backend storage system.</p>
......@@ -25,7 +27,7 @@ import org.xmpp.packet.*;
* <p/>
*
*/
public interface CachedRoster extends Roster, Cacheable {
public interface CachedRoster extends org.jivesoftware.messenger.roster.Roster, Cacheable {
/**
* <p>Return the username of the user or chatbot that owns this roster.</p>
......
......@@ -9,9 +9,10 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import org.jivesoftware.util.Cacheable;
import org.jivesoftware.messenger.roster.RosterItem;
/**
* <p>Represents a persistently stored roster item.</p>
......
......@@ -9,9 +9,11 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.xmpp.packet.JID;
import java.util.Iterator;
......
......@@ -9,7 +9,7 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
package org.jivesoftware.messenger.roster;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.util.IntEnum;
......
......@@ -9,25 +9,45 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
package org.jivesoftware.messenger.roster;
import org.xmpp.packet.JID;
import org.jivesoftware.util.Cache;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.user.*;
import org.jivesoftware.messenger.roster.spi.CachedRosterImpl;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.xmpp.packet.JID;
import java.util.Iterator;
public class RosterManagerImpl extends BasicModule implements RosterManager {
/**
* A simple service that allows components to retrieve a roster based solely on the ID
* of the owner. Users have convenience methods for obtaining a roster associated with
* the owner. However there are many components that need to retrieve the roster
* based solely on the generic ID owner key. This interface defines a service that can
* do that. This allows classes that generically manage resource for resource owners
* (such as presence updates) to generically offer their services without knowing or
* caring if the roster owner is a user, chatbot, etc.
*
* @author Iain Shigeoka
*/
public class RosterManager extends BasicModule {
private Cache rosterCache = null;
public RosterManagerImpl() {
public RosterManager() {
super("Roster Manager");
}
/**
* Returns the roster for the given username.
*
* @param username the username to search for.
* @return the roster associated with the ID.
* @throws org.jivesoftware.messenger.user.UserNotFoundException if the ID does not correspond to a known
* entity on the server.
*/
public CachedRoster getRoster(String username) throws UserNotFoundException {
if (rosterCache == null) {
rosterCache = CacheManager.getCache("username2roster");
......@@ -47,6 +67,12 @@ public class RosterManagerImpl extends BasicModule implements RosterManager {
return roster;
}
/**
* Removes the entire roster of a given user. This is necessary when a user
* account is being deleted from the server.
*
* @param user the user.
*/
public void deleteRoster(JID user) {
try {
String username = user.getNode();
......@@ -65,7 +91,7 @@ public class RosterManagerImpl extends BasicModule implements RosterManager {
CacheManager.getCache("username2roster").remove(username);
// Get the rosters that have a reference to the deleted user
RosterItemProvider rosteItemProvider = UserProviderFactory.getRosterItemProvider();
RosterItemProvider rosteItemProvider = RosterItemProvider.getInstance();
Iterator<String> usernames = rosteItemProvider.getUsernames(user.toBareJID());
while (usernames.hasNext()) {
username = usernames.next();
......
......@@ -9,7 +9,7 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
package org.jivesoftware.messenger.roster.spi;
import java.util.Iterator;
import java.util.List;
......@@ -22,15 +22,14 @@ import org.jivesoftware.messenger.SessionManager;
import org.jivesoftware.messenger.XMPPServer;
import org.jivesoftware.messenger.spi.BasicServer;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.BasicRoster;
import org.jivesoftware.messenger.user.BasicRosterItem;
import org.jivesoftware.messenger.user.CachedRoster;
import org.jivesoftware.messenger.user.CachedRosterItem;
import org.jivesoftware.messenger.user.RosterItem;
import org.jivesoftware.messenger.user.RosterItemProvider;
import org.jivesoftware.messenger.roster.BasicRoster;
import org.jivesoftware.messenger.roster.BasicRosterItem;
import org.jivesoftware.messenger.roster.CachedRoster;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.roster.CachedRosterItem;
import org.jivesoftware.messenger.roster.*;
import org.jivesoftware.messenger.user.UserAlreadyExistsException;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.UserProviderFactory;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
......@@ -68,7 +67,7 @@ public class CachedRosterImpl extends BasicRoster implements CachedRoster {
sessionManager = SessionManager.getInstance();
this.username = username;
rosterItemProvider = UserProviderFactory.getRosterItemProvider();
rosterItemProvider = RosterItemProvider.getInstance();
Iterator items = rosterItemProvider.getItems(username);
while (items.hasNext()) {
RosterItem item = (RosterItem)items.next();
......
......@@ -9,14 +9,16 @@
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
package org.jivesoftware.messenger.roster.spi;
import java.util.List;
import java.util.LinkedList;
import org.jivesoftware.messenger.user.BasicRosterItem;
import org.jivesoftware.messenger.user.CachedRosterItem;
import org.jivesoftware.messenger.user.RosterItem;
import org.jivesoftware.messenger.roster.BasicRosterItem;
import org.jivesoftware.messenger.roster.CachedRosterItem;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.roster.RosterItem;
import org.jivesoftware.messenger.roster.CachedRosterItem;
import org.jivesoftware.util.CacheSizes;
import org.xmpp.packet.JID;
......
......@@ -15,6 +15,7 @@ import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.audit.AuditManager;
import org.jivesoftware.messenger.audit.spi.AuditManagerImpl;
import org.jivesoftware.messenger.container.Module;
......@@ -27,10 +28,8 @@ import org.jivesoftware.messenger.handler.*;
import org.jivesoftware.messenger.muc.spi.MultiUserChatServerImpl;
import org.jivesoftware.messenger.muc.MultiUserChatServer;
import org.jivesoftware.messenger.transport.TransportHandler;
import org.jivesoftware.messenger.user.RosterManager;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.spi.RosterManagerImpl;
import org.jivesoftware.messenger.user.spi.UserManagerImpl;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.Version;
......@@ -193,8 +192,7 @@ public class BasicServer implements XMPPServer {
// Load boot modules
loadModule(RoutingTableImpl.class.getName());
loadModule(AuditManagerImpl.class.getName());
loadModule(UserManagerImpl.class.getName());
loadModule(RosterManagerImpl.class.getName());
loadModule(RosterManager.class.getName());
loadModule(PrivateStorage.class.getName());
// Load core modules
loadModule(ConnectionManagerImpl.class.getName());
......@@ -541,7 +539,7 @@ public class BasicServer implements XMPPServer {
}
public RosterManager getRosterManager() {
return (RosterManager) modules.get(RosterManagerImpl.class);
return (RosterManager) modules.get(RosterManager.class);
}
public PresenceManager getPresenceManager() {
......@@ -603,7 +601,7 @@ public class BasicServer implements XMPPServer {
}
public UserManager getUserManager() {
return (UserManager) modules.get(UserManagerImpl.class);
return UserManager.getInstance();
}
public AuditManager getAuditManager() {
......
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import java.util.Iterator;
/**
* <p>Defines the provider methods required for creating, reading, updating and deleting roster items.</p>
* <p/>
* <p>Rosters are another user resource accessed via the user or chatbot's long ID. A user/chatbot may have
* zero or more roster items and each roster item may have zero or more groups. Each roster item is
* additionaly keyed on a XMPP jid. In most cases, the entire roster will be read in from memory and manipulated
* or sent to the user. However some operations will need to retrive specific roster items rather than the
* entire roster.</p>
*
* @author Iain Shigeoka
*/
public interface RosterItemProvider {
/**
* <p>Creates a new roster item for the given user (optional operation).</p>
* <p/>
* <p><b>Important!</b> The item passed as a parameter to this method is strictly a convenience for passing all
* of the data needed for a new roster item. The roster item returned from the method will be cached by Messenger.
* In some cases, the roster item passed in will be passed back out. However, if an implementation may
* return RosterItems as a separate class (for example, a RosterItem that directly accesses the backend
* storage, or one that is an object in an object database).
* <p/>
* <p>If you don't want roster items edited through messenger, throw UnsupportedOperationException.</p>
*
* @param username the username of the user/chatbot that owns the roster item
* @param item the settings for the roster item to create
* @return The created roster item
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
CachedRosterItem createItem(String username, RosterItem item) throws UserAlreadyExistsException, UnsupportedOperationException;
/**
* <p>Update the roster item in storage with the information contained in the given item (optional operation).</p>
* <p/>
* <p>If you don't want roster items edited through messenger, throw UnsupportedOperationException.</p>
*
* @param username the username of the user/chatbot that owns the roster item
* @param item The roster item to update
* @throws UserNotFoundException If no entry could be found to update
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
void updateItem(String username, CachedRosterItem item) throws UserNotFoundException, UnsupportedOperationException;
/**
* <p>Delete the roster item with the given itemJID for the user (optional operation).</p>
* <p/>
* <p>If you don't want roster items deleted through messenger, throw UnsupportedOperationException.</p>
*
* @param username the long ID of the user/chatbot that owns the roster item
* @param rosterItemID The roster item to delete
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
void deleteItem(String username, long rosterItemID) throws UnsupportedOperationException;
/**
* Returns an iterator on the usernames whose roster includes the specified JID.
*
* @param jid the jid that the rosters should include.
* @return an iterator on the usernames whose roster includes the specified JID.
*/
Iterator<String> getUsernames(String jid);
/**
* <p>Obtain a count of the number of roster items available for the given user.</p>
*
* @param username the username of the user/chatbot that owns the roster items
* @return The number of roster items available for the user
*/
int getItemCount(String username);
/**
* <p>Retrieve an iterator of RosterItems for the given user.</p>
* <p/>
* <p>This method will commonly be called when a user logs in. The data will be cached
* in memory when possible. However, some rosters may be very large so items may need
* to be retrieved from the provider more frequently than usual for provider data.
*
* @param username the username of the user/chatbot that owns the roster items
* @return An iterator of all RosterItems owned by the user
*/
Iterator getItems(String username);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import org.xmpp.packet.JID;
/**
* <p>A simple service that allows components to retrieve a roster based solely on the ID of the owner.</p>
* <p/>
* <p>The User, Chatbot, and other ID based 'resources owners' have convenience methods for obtaining
* a roster associated with the owner. However there are many components that need to retrieve the
* roster based solely on the generic ID owner key. This interface defines a service that can do that.
* This allows classes that generically manage resource for resource owners (such as presence updates)
* to generically offer their services without knowing or caring if the roster owner is a user, chatbot, etc.</p>
*
* @author Iain Shigeoka
*/
public interface RosterManager {
/**
* <p>Obtain the roster for the given username.</p>
*
* @param username the username to search for
* @return The roster associated with the ID
* @throws UserNotFoundException If the ID does not correspond to a known entity on the server
*/
CachedRoster getRoster(String username) throws UserNotFoundException;
/**
* Removes the entire roster of a given user. This is necessary when a user account is being
* deleted from the server.
*
* @param user the user to remove his roster.
*/
void deleteRoster(JID user);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.AbstractCollection;
/**
* Provides a view of an array of usernames as a Collection of User objects. If
* any of the usernames cannot be loaded, they are transparently skipped when
* iteratating over the collection.
*
* @author Matt Tucker
*/
public class UserCollection extends AbstractCollection {
private String[] elements;
private int currentIndex = -1;
private Object nextElement = null;
/**
* Constructs a new UserIterator.
*/
public UserCollection(String [] elements) {
this.elements = elements;
}
public Iterator iterator() {
return new UserIterator();
}
public int size() {
return elements.length;
}
private class UserIterator implements Iterator {
public boolean hasNext() {
// If we are at the end of the list, there can't be any more elements
// to iterate through.
if (currentIndex + 1 >= elements.length && nextElement == null) {
return false;
}
// Otherwise, see if nextElement is null. If so, try to load the next
// element to make sure it exists.
if (nextElement == null) {
nextElement = getNextElement();
if (nextElement == null) {
return false;
}
}
return true;
}
public Object next() throws java.util.NoSuchElementException {
Object element = null;
if (nextElement != null) {
element = nextElement;
nextElement = null;
}
else {
element = getNextElement();
if (element == null) {
throw new NoSuchElementException();
}
}
return element;
}
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
/**
* Returns the next available element, or null if there are no more elements to return.
*
* @return the next available element.
*/
private Object getNextElement() {
while (currentIndex + 1 < elements.length) {
currentIndex++;
Object element = null;
try {
element = UserManager.getInstance().getUser(elements[currentIndex]);
}
catch (UserNotFoundException unfe) { }
if (element != null) {
return element;
}
}
return null;
}
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.StringUtils;
/**
* <p>The simplest implementation of UserInfo.</p>
* <p>Useful if you are retrieving user info from a database and just need
* to stuff it into a data structure.</p>
*
* @author Iain Shigeoka
* @author Derek DeMoro
* @see User
*/
public class UserInfo implements Cacheable {
private String username = null;
private String name = " ";
private String email;
private java.util.Date creationDate;
private java.util.Date modificationDate;
/**
* Create a new UserInfo with default fields.
*
* @param username the username.
*/
public UserInfo(String username) {
this.username = username;
creationDate = new java.util.Date();
modificationDate = new java.util.Date();
}
/**
* <p>Create a new UserInfo given field values.</p>
*
* @param username The username
* @param name The user's full name
* @param email The user's email address
*/
public UserInfo(String username, String name, String email,
java.util.Date creationDate, java.util.Date modificationDate)
{
this.username = username;
this.creationDate = creationDate;
this.modificationDate = modificationDate;
if (email == null || "".equals(email)) {
this.email = " ";
}
else {
this.email = email;
}
if (name != null) {
this.name = name;
}
}
public String getUsername() {
return username;
}
public String getName() {
return name;
}
public void setName(String name) throws UnauthorizedException {
if (name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
this.name = name;
// Update modification date
modificationDate.setTime(System.currentTimeMillis());
}
public String getEmail() {
return StringUtils.escapeHTMLTags(email);
}
public void setEmail(String email) throws UnauthorizedException {
if (email == null || "".equals(email)) {
email = " ";
}
this.email = email;
// Update modification date
modificationDate.setTime(System.currentTimeMillis());
}
public java.util.Date getCreationDate() {
return creationDate;
}
public void setCreationDate(java.util.Date creationDate) throws UnauthorizedException {
if (creationDate == null) {
throw new IllegalArgumentException("Creation date cannot be null");
}
this.creationDate.setTime(creationDate.getTime());
}
public java.util.Date getModificationDate() {
return modificationDate;
}
public void setModificationDate(java.util.Date modificationDate) throws UnauthorizedException {
if (modificationDate == null) {
throw new IllegalArgumentException("Modification date cannot be null");
}
this.modificationDate.setTime(modificationDate.getTime());
}
public int getCachedSize() {
// Approximate the size of the object in bytes by calculating the size
// of each field.
int size = 0;
size += CacheSizes.sizeOfObject(); // overhead of object
size += CacheSizes.sizeOfLong(); // id
size += CacheSizes.sizeOfString(name); // name
size += CacheSizes.sizeOfString(email); // email
size += CacheSizes.sizeOfBoolean(); // nameVisible
size += CacheSizes.sizeOfBoolean(); // emailVisible
size += CacheSizes.sizeOfDate(); // creationDate
size += CacheSizes.sizeOfDate(); // modificationDate
return size;
}
/**
* Returns a String representation of the User object using the username.
*
* @return a String representation of the User object.
*/
public String toString() {
return name;
}
public int hashCode() {
return username.hashCode();
}
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object != null && object instanceof User) {
return username.equals(((User)object).getUsername());
}
else {
return false;
}
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import org.jivesoftware.messenger.auth.UnauthorizedException;
/**
* <p>A common interface to implement when creating a user management service plug-in.</p>
* <p/>
* <p>Provide meta-information about a user that's useful in server behavior. Implementation
* of this provider is optional and systems where user information is stored and managed in
* other systems may want to provide partial implementations or use the Jive dummy implementation
* that returns no values.</p>
* <p/>
* <p>Messenger will cache much of the information it obtains from calling this provider. If you will be modifying
* the underlying data outside of Messenger, please consult with Jive for information on maintaining a valid
* cache.</p>
*
* @author Iain Shigeoka
*/
public interface UserInfoProvider {
/**
* <p>Obtain the UserInfo of a user.</p>
* <p>If your implementation doesn't support user info, simply return a UserInfo object filled with default
* values.</p>
*
* @param username the username of the user.
* @return The user's info
* @throws UserNotFoundException If a user with the given ID couldn't be found
*/
UserInfo getInfo(String username) throws UserNotFoundException;
/**
* <p>Sets the user's info (optional operation).</p>
*
* @param username the username of the user.
* @param info The user's new info
* @throws UserNotFoundException If a user with the given ID couldn't be found
* @throws UnauthorizedException If this operation is not allowed for the caller's permissions
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
void setInfo(String username, UserInfo info)
throws UserNotFoundException, UnauthorizedException, UnsupportedOperationException;
}
......@@ -11,45 +11,109 @@
package org.jivesoftware.messenger.user;
import org.jivesoftware.messenger.container.Module;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import java.util.Iterator;
import org.jivesoftware.messenger.JiveGlobals;
import org.jivesoftware.util.Cache;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
import java.util.Collection;
/**
* Centralized management of users in the Jive system including creating, retrieving, and deleting
* User objects.<p>
* <p/>
* In some cases, you may wish to plug in your own user system implementation. In that case, you
* should set the Jive property <tt>UserManager.className</tt> with the name of your UserManager
* class. Your class must have a public, no-argument constructor. The class must also create and
* return User object implementations as necessary.
* Manages users, including loading, creating and deleting.
*
* @author Iain Shigeoka
* @author Matt Tucker
* @see User
*/
public interface UserManager extends Module {
public class UserManager {
private static Cache userCache;
private static UserProvider provider;
private static UserManager instance = new UserManager();
static {
// Initialize caches.
CacheManager.initializeCache("userCache", 512 * 1024);
CacheManager.initializeCache("username2roster", 512 * 1024);
userCache = CacheManager.getCache("userCache");
// Load a group provider.
String className = JiveGlobals.getXMLProperty("provider.user.className",
"org.jivesoftware.messenger.user.DefaultUserProvider");
try {
Class c = ClassUtils.forName(className);
provider = (UserProvider)c.newInstance();
}
catch (Exception e) {
Log.error("Error loading user provider: " + className, e);
provider = new DefaultUserProvider();
}
}
/**
* Returns the currently-installed UserProvider.
*
* @return the current UserProvider.
*/
static UserProvider getUserProvider() {
return provider;
}
public static UserManager getInstance() {
return instance;
}
private UserManager() {
}
/**
* Factory method for creating a new User with all required values: a password, and a unique username.
* Creates a new User. Required values are username and password. The email address
* can optionally be <tt>null</tt>.
*
* @param username the new and unique username for the account.
* @param password the password for the account as plain text.
* @param email The email address to associate with the new account
* @param password the password for the account (plain text).
* @param email the email address to associate with the new account, which can
* be <tt>null</tt>.
* @return a new User.
* @throws UserAlreadyExistsException if the username already exists in the system.
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
* @throws UserAlreadyExistsException if the username already exists in the system.
* @throws UnsupportedOperationException if the provider does not support the
* operation.
*/
public User createUser(String username, String password, String email)
throws UserAlreadyExistsException, UnauthorizedException, UnsupportedOperationException;
public User createUser(String username, String password, String name, String email)
throws UserAlreadyExistsException
{
// Make sure that the username is valid.
try {
username = Stringprep.nameprep(username, true);
}
catch (StringprepException se) {
throw new IllegalArgumentException(se);
}
User user = provider.createUser(username, password, name, email);
userCache.put(username, user);
return user;
}
/**
* Deletes a user (optional operation).
*
* @param user the user to delete.
* @throws UnauthorizedException If the caller doesn't have permission to take the given action
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void deleteUser(User user) throws UnauthorizedException, UnsupportedOperationException;
public void deleteUser(User user) {
String username = user.getUsername();
// Make sure that the username is valid.
try {
username = Stringprep.nameprep(username, true);
}
catch (StringprepException se) {
throw new IllegalArgumentException(se);
}
provider.deleteUser(user.getUsername());
// Remove the user from cache.
userCache.remove(user.getUsername());
}
/**
* Returns the User specified by username.
......@@ -58,32 +122,53 @@ public interface UserManager extends Module {
* @return the User that matches <tt>username</tt>.
* @throws UserNotFoundException if the user does not exist.
*/
public User getUser(String username) throws UserNotFoundException;
public User getUser(String username) throws UserNotFoundException {
// Make sure that the username is valid.
try {
username = Stringprep.nameprep(username, true);
}
catch (StringprepException se) {
throw new IllegalArgumentException(se);
}
User user = (User)userCache.get(username);
if (user == null) {
user = provider.loadUser(username);
userCache.put(username, user);
}
return user;
}
/**
* Returns the number of users in the system.
* Returns the total number of users in the system.
*
* @return the total number of users.
*/
public int getUserCount();
public int getUserCount() {
return provider.getUserCount();
}
/**
* Returns an iterator for all users in the system.
* Returns an unmodifiable Collection of all users in the system.
*
* @return an Iterator for all users.
* @return an unmodifiable Collection of all users.
*/
public Iterator users() throws UnauthorizedException;
public Collection<User> getUsers() {
return provider.getUsers();
}
/**
* Returns an iterator for all users starting at <tt>startIndex</tt> with the given number of
* results. This is useful to support pagination in a GUI where you may only want to display a
* certain number of results per page. It is possible that the number of results returned will
* be less than that specified by numResults if numResults is greater than the number of records
* left in the system to display.
* Returns an unmodifiable Collection of all users starting at <tt>startIndex</tt>
* with the given number of results. This is useful to support pagination in a GUI
* where you may only want to display a certain number of results per page. It is
* possible that the number of results returned will be less than that specified
* by <tt>numResults</tt> if <tt>numResults</tt> is greater than the number of
* records left to display.
*
* @param startIndex the beginning index to start the results at.
* @param numResults the total number of results to return.
* @return an Iterator for all users in the specified range.
* @return a Collection of users in the specified range.
*/
public Iterator users(int startIndex, int numResults) throws UnauthorizedException;
public Collection<User> getUsers(int startIndex, int numResults) {
return provider.getUsers(startIndex, numResults);
}
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import java.util.Map;
/**
* <p>Implement this provider to store user and vcard properties somewhere
* other than the Jive tables, or to capture jive property events.</p>
* <p/>
* <p>Implementors: Most integrators will not need to create their own
* user property providers. In almost all cases, it's best to let Messenger
* store these values in the Jive tables.</p>
*
* @author Iain Shigeoka
*/
public interface UserPropertiesProvider {
/**
* <p>Delete a user's vcard property (optional operation).</p>
*
* @param username the username of the user
* @param name The name of the property to delete
* @throws UnauthorizedException If the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void deleteVcardProperty(String username, String name) throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Delete a user's user property (optional operation).</p>
*
* @param username the username of the user
* @param name The name of the property to delete
* @throws UnauthorizedException If the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void deleteUserProperty(String username, String name) throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Insert a new vcard property (optional operation).</p>
*
* @param username the username of the user
* @param name The name of the property
* @param value The value of the property
* @throws UnauthorizedException If the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void insertVcardProperty(String username, String name, String value) throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Insert a new user property (optional operation).</p>
*
* @param username the username of the user
* @param name The name of the property
* @param value The value of the property
* @throws UnauthorizedException If the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void insertUserProperty(String username, String name, String value) throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Update a vcard property (optional operation).</p>
*
* @param username the username of the user.
* @param name The name of the property.
* @param value The value of the property.
* @throws UnauthorizedException if the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException if the provider does not support the operation (this is an optional operation).
*/
public void updateVcardProperty(String username, String name, String value)
throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Update an Existing user property (optional operation).</p>
*
* @param username the username of the user
* @param name The name of the property
* @param value The value of the property
* @throws UnauthorizedException If the caller does not have permission to carry out the operation
* @throws UnsupportedOperationException If the provider does not support the operation (this is an optional operation)
*/
public void updateUserProperty(String username, String name, String value) throws UnauthorizedException, UnsupportedOperationException;
/**
* <p>Obtain a map containing all vcard properties for a user.</p>
* <p/>
* <p>If the provider doesn't support vcard properties, return an empty map.</p>
*
* @param username the username of the user to retrieve the vcard properties
* @return A map of property name-value pairs
*/
public Map getVcardProperties(String username);
/**
* <p>Obtain a map containing all user properties for a user.</p>
* <p/>
* <p>If the provider doesn't support user properties, return an empty map.</p>
*
* @param username the username of the user to retrieve the user properties
* @return A map of property name-value pairs
*/
public Map getUserProperties(String username);
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import java.util.Date;
import java.util.Collection;
/**
* Provider interface for the user system.
*
* @author Matt Tucker
*/
public interface UserProvider {
/**
* Loads the specified user by username.
*
* @param username the username
* @return the User.
* @throws UserNotFoundException if the User could not be loaded.
*/
User loadUser(String username) throws UserNotFoundException;
/**
* Creates a new user. This method should throw an
* UnsupportedOperationException if this operation is not
* supporte by the backend user store.
*
* @param username the username.
* @param password the plain-text password.
* @param name the user's name, which can be <tt>null</tt>.
* @param email the user's email address, which can be <tt>null</tt>.
* @return a new User.
* @throws UserAlreadyExistsException if the username is already in use.
*/
User createUser(String username, String password, String name, String email)
throws UserAlreadyExistsException;
/**
* Delets a user. This method should throw an
* UnsupportedOperationException if this operation is not
* supported by the backend user store.
*
* @param username the username to delete.
*/
void deleteUser(String username);
/**
* Returns the number of users in the system.
*
* @return the total number of users.
*/
public int getUserCount();
/**
* Returns an unmodifiable Collections of all users in the system. The
* {@link UserCollection} class can be used to assist in the implementation
* of this method. It takes a String [] of usernames and presents it as a
* Collection of User objects (obtained with calls to
* {@link UserManager#getUser(String)}.
*
* @return an unmodifiable Collection of all users.
*/
public Collection<User> getUsers();
/**
* Returns an unmodifiable Collections of users in the system within the
* specified range. The {@link UserCollection} class can be used to assist
* in the implementation of this method. It takes a String [] of usernames
* and presents it as a Collection of User objects (obtained with calls to
* {@link UserManager#getUser(String)}.<p>
*
* It is possible that the number of results returned will be less than that
* specified by <tt>numResults</tt> if <tt>numResults</tt> is greater than the
* number of records left to display.
*
* @param startIndex the beginning index to start the results at.
* @param numResults the total number of results to return.
* @return an unmodifiable Collection of users within the specified range.
*/
public Collection<User> getUsers(int startIndex, int numResults);
/**
* Sets the users's password. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username of the user.
* @param password the new plaintext password for the user.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
public void setPassword(String username, String password)
throws UserNotFoundException;
/**
* Sets the user's name. This method should throw an UnsupportedOperationException
* if this operation is not supported by the backend user store.
*
* @param username the username.
* @param name the name.
* @throws UserNotFoundException if the user could not be found.
*/
public void setName(String username, String name) throws UserNotFoundException;
/**
* Sets the user's email address. This method should throw an
* UnsupportedOperationException if this operation is not supported
* by the backend user store.
*
* @param username the username.
* @param email the email address.
* @throws UserNotFoundException if the user could not be found.
*/
public void setEmail(String username, String email) throws UserNotFoundException;
/**
* Sets the date the user was created. This method should throw an
* UnsupportedOperationException if this operation is not supported
* by the backend user store.
*
* @param username the username.
* @param creationDate the date the user was created.
* @throws UserNotFoundException if the user could not be found.
*/
public void setCreationDate(String username, Date creationDate) throws UserNotFoundException;
/**
* Sets the date the user was last modified. This method should throw an
* UnsupportedOperationException if this operation is not supported
* by the backend user store.
*
* @param username the username.
* @param modificationDate the date the user was last modified.
* @throws UserNotFoundException if the user could not be found.
*/
public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException;
}
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.messenger.JiveGlobals;
/**
* <p>Provides a centralized source of the various user providers.</p>
* <p/>
* <p>The user system has many providers. These providers allow you to
* integrate Messenger with various backend user management systems on a
* 'pay as you go' basis. In otherwords, a simple integration only requires
* a very small amount of customization (modifying a few providers) while
* a more complex integration can modify many providers.</p>
* <p/>
* <p>Users of Jive that wish to change the User*Provider implementation used to generate
* users can set the <code>UserProvider.*.className</code> Jive properties. For example, if
* you have altered Jive to use LDAP for user information, you'd want to send a custom
* implementation of User*Provider classes to make LDAP user queries. After changing the
* <code>UserProvider.*.className</code> Jive properties, you must restart Messenger. The
* valid properties are:<p>
* <p/>
* <ul>
* <li>UserProvider.id.className - specifies a UserIDProvider class.</li>
* <li>UserProvider.properties.className - specifies a UserPropertiesProvider class.</li>
* <li>UserProvider.info.className - specifies a UserInfoProvider class.</li>
* <li>UserProvider.account.className - specifies a UserAccountProvider class.</li>
* </ul>
*
* @author Iain Shigeoka
*/
public class UserProviderFactory {
private static UserPropertiesProvider userPropertiesProvider;
private static UserInfoProvider userInfoProvider;
private static RosterItemProvider rosterItemProvider;
/**
* The default class to instantiate is database implementation.
*/
private static String[] classNames = {"org.jivesoftware.messenger.user.spi.DbUserPropertiesProvider",
"org.jivesoftware.messenger.user.spi.DbUserInfoProvider",
"org.jivesoftware.messenger.user.spi.DbRosterItemProvider"};
private static String[] propNames = {"UserProvider.id.className",
"UserProvider.properties.className",
"UserProvider.info.className",
"UserProvider.account.className",
"UserProvider.roster.className"};
private static void setProviders(Class[] providers) throws IllegalAccessException, InstantiationException {
userPropertiesProvider = (UserPropertiesProvider)providers[0].newInstance();
userInfoProvider = (UserInfoProvider)providers[1].newInstance();
rosterItemProvider = (RosterItemProvider)providers[2].newInstance();
}
public static UserPropertiesProvider getUserPropertiesProvider() {
loadProviders();
return userPropertiesProvider;
}
public static UserInfoProvider getUserInfoProvider() {
loadProviders();
return userInfoProvider;
}
public static RosterItemProvider getRosterItemProvider() {
loadProviders();
return rosterItemProvider;
}
private static void loadProviders() {
if (userInfoProvider == null) {
// Use className as a convenient object to get a lock on.
synchronized (classNames) {
if (userInfoProvider == null) {
try {
Class[] providers = new Class[classNames.length];
for (int i = 0; i < classNames.length; i++) {
String className = classNames[i];
//See if the classname has been set as a Jive property.
String classNameProp = JiveGlobals.getXMLProperty(propNames[i]);
if (classNameProp != null) {
className = classNameProp;
}
try {
providers[i] = ClassUtils.forName(className);
}
catch (Exception e) {
Log.error("Exception loading class: " + className, e);
}
}
setProviders(providers);
}
catch (Exception e) {
Log.error("Exception loading class: " + classNames, e);
}
}
}
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserInfo;
import org.jivesoftware.messenger.user.UserInfoProvider;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
/**
* Catabase implementation of the UserInfoProvider interface.
*
* @author Matt Tucker
* @author Bruce Ritchie
* @author Iain Shigeoka
*
* @see User
*/
public class DbUserInfoProvider implements UserInfoProvider {
private static final String LOAD_USER_BY_USERNAME =
"SELECT name, email, creationDate, modificationDate FROM jiveUser WHERE username=?";
public UserInfo getInfo(String username) throws UserNotFoundException {
UserInfo userInfo = null;
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(LOAD_USER_BY_USERNAME);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
throw new UserNotFoundException();
}
// We trim() the dates before trying to parse them because some
// databases pad with extra characters when returning the data.
userInfo = new UserInfo(username,
rs.getString(1), // name
rs.getString(2), // email
new java.util.Date(Long.parseLong(rs.getString(3).trim())), // creation date
new java.util.Date(Long.parseLong(rs.getString(4).trim()))); // modification date
}
catch (SQLException e) {
throw new UserNotFoundException("Failed to read user " + username + " from database.", e);
}
catch (NumberFormatException nfe) {
Log.error("WARNING: There was an error parsing the dates " +
"returned from the database. Ensure that they're being stored " +
"correctly.");
throw new UserNotFoundException("User "
+ username + " could not be loaded from the database.");
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
}
return userInfo;
}
private static final String SAVE_USER =
"UPDATE jiveUser SET name=?,email=?,creationDate=?,modificationDate=? " +
"WHERE username=?";
public void setInfo(String username, UserInfo info) throws UserNotFoundException, UnauthorizedException {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(SAVE_USER);
pstmt.setString(1, info.getName());
pstmt.setString(2, info.getEmail());
pstmt.setString(3, StringUtils.dateToMillis(info.getCreationDate()));
pstmt.setString(4, StringUtils.dateToMillis(info.getModificationDate()));
pstmt.setString(5, username);
pstmt.executeUpdate();
}
catch (SQLException e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
throw new UnauthorizedException();
}
finally {
try { if (pstmt != null) { pstmt.close(); } }
catch (Exception e) { Log.error(e); }
try { if (con != null) { con.close(); } }
catch (Exception e) { Log.error(e); }
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.spi.BasicServer;
/**
* An class that defines the logic to iterate through an array of long unique ID's of Jive objects.
* <p/>
* <p/>
* One feature of the class is the ability to recover from underlying modifications to the dataset
* in some cases. Consider the following sequence of events:
* <ul>
* <li> Time 00: An Iterator for users in a group is obtained.
* <li> Time 01: 3 of the 8 users in the group are deleted.
* <li> Time 02: Iteration of users begins.
* </ul>
* <p/>
* In the above example, the underlying users in the group were changed after the initial
* iterator was obtained. The logic in this class will attempt to automatically compensate for
* these changes by skipping over items that cannot be loaded. In the above example, that would
* translate to the iterator returning 5 users instead of 8.
*
* @author Iain Shigeoka
*/
public class UserIterator implements Iterator {
private String[] elements;
private int currentIndex = -1;
private Object nextElement = null;
private UserDOFactory objectFactory;
/**
* Creates a new UserIterator.
*/
public UserIterator(String[] elements) {
this.elements = elements;
// Create an objectFactory to load users.
this.objectFactory = new UserDOFactory();
}
private class UserDOFactory {
private UserManager userManager;
public UserDOFactory() {
userManager = BasicServer.getInstance().getUserManager();
}
public Object loadObject(String username) {
try {
User user = userManager.getUser(username);
return user;
}
catch (UserNotFoundException e) { /* ignore */
}
return null;
}
}
/**
* Returns true if there are more elements in the iteration.
*
* @return true if the iterator has more elements.
*/
public boolean hasNext() {
// If we are at the end of the list, there can't be any more elements
// to iterate through.
if (currentIndex + 1 >= elements.length && nextElement == null) {
return false;
}
// Otherwise, see if nextElement is null. If so, try to load the next
// element to make sure it exists.
if (nextElement == null) {
nextElement = getNextElement();
if (nextElement == null) {
return false;
}
}
return true;
}
/**
* Returns the next element.
*
* @return the next element.
* @throws java.util.NoSuchElementException
* if there are no more elements.
*/
public Object next() throws java.util.NoSuchElementException {
Object element = null;
if (nextElement != null) {
element = nextElement;
nextElement = null;
}
else {
element = getNextElement();
if (element == null) {
throw new NoSuchElementException();
}
}
return element;
}
/**
* Not supported for security reasons.
*/
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
/**
* Returns the next available element, or null if there are no more elements to return.
*
* @return the next available element.
*/
public Object getNextElement() {
while (currentIndex + 1 < elements.length) {
currentIndex++;
Object element = objectFactory.loadObject(elements[currentIndex]);
if (element != null) {
return element;
}
}
return null;
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@
package org.jivesoftware.util;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.auth.Permissions;
import org.jivesoftware.messenger.Permissions;
/**
* An interface that defines a method to create proxy objects based on an authToken and permissions.
......
......@@ -15,12 +15,13 @@ import org.jivesoftware.messenger.muc.MultiUserChatServer;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.RosterManager;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.XMPPServer;
import org.jivesoftware.messenger.PrivateStorage;
import org.jivesoftware.messenger.PresenceManager;
import org.jivesoftware.messenger.SessionManager;
import org.jivesoftware.messenger.XMPPServerInfo;
import org.jivesoftware.messenger.roster.RosterManager;
import org.jivesoftware.messenger.spi.BasicServer;
import org.jivesoftware.messenger.group.GroupManager;
......
......@@ -105,15 +105,16 @@
<% } %>
<table class="box" cellpadding="3" cellspacing="1" border="0" width="600">
<form name="f" action="group-create.jsp" method="post">
<tr><td class="text" colspan="2">
Use the form below to create a new group in the system.
</td></tr>
<p>Use the form below to create a new group.</p>
<tr class="jive-even">
<form name="f" action="group-create.jsp" method="post">
<fieldset>
<legend>Create New Group</legend>
<div>
<table class="box" cellpadding="3" cellspacing="1" border="0" width="600">
<tr valign="middle" class="jive-even">
<td>
Group: *
Group Name: *
</td>
<td>
<input type="text" name="name" size="30" maxlength="75"
......@@ -134,7 +135,7 @@ Use the form below to create a new group in the system.
<% } %>
</td>
</tr>
<tr class="jive-odd">
<tr valign="middle" class="jive-odd">
<td>
Description:
</td>
......@@ -152,11 +153,12 @@ Use the form below to create a new group in the system.
</td>
</tr>
</table>
<br>
* Required fields
</div>
</fieldset>
<p>
* Required fields
</p>
<br><br>
<input type="submit" name="create" value="Create Group">
<input type="submit" name="cancel" value="Cancel">
......@@ -167,7 +169,7 @@ Use the form below to create a new group in the system.
document.f.name.focus();
function checkFields() {
}
</script>
......
This diff is collapsed.
......@@ -76,7 +76,7 @@
throw new UnauthorizedException("Only user 'admin' may login.");
}
}
authToken = AuthFactory.getAuthToken(username, password);
authToken = AuthFactory.authenticate(username, password);
session.setAttribute("jive.admin.authToken", authToken);
response.sendRedirect(go(url));
return;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment