Commit 1315769c authored by Daniel Henninger's avatar Daniel Henninger Committed by dhenninger

[JM-1276] Added ability for a user provider to require a name and/or email...

[JM-1276] Added ability for a user provider to require a name and/or email address.  (only used by ClearspaceUserProvider at this time)

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/branches@9980 b35dd754-fafc-0310-a699-88a17e54d16e
parent b65dbe6c
...@@ -155,6 +155,10 @@ public class AuthorizationManager { ...@@ -155,6 +155,10 @@ public class AuthorizationManager {
if (UserManager.getUserProvider().isReadOnly()) { if (UserManager.getUserProvider().isReadOnly()) {
return false; return false;
} }
if (UserManager.getUserProvider().isNameRequired() || UserManager.getUserProvider().isEmailRequired()) {
// If these are required, there's no way we can arbitrarily auto-create this account.
return false;
}
try { try {
UserManager.getUserProvider().createUser(username, StringUtils.randomString(8), null, null); UserManager.getUserProvider().createUser(username, StringUtils.randomString(8), null, null);
if (Log.isDebugEnabled()) { if (Log.isDebugEnabled()) {
......
...@@ -82,7 +82,6 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -82,7 +82,6 @@ public class ClearspaceUserProvider implements UserProvider {
passwordE.addText(password); passwordE.addText(password);
// adds the the email // adds the the email
//todo in CS the email is mandatory
Element emailE = userE.addElement("email"); Element emailE = userE.addElement("email");
emailE.addText(email); emailE.addText(email);
...@@ -332,6 +331,14 @@ public class ClearspaceUserProvider implements UserProvider { ...@@ -332,6 +331,14 @@ public class ClearspaceUserProvider implements UserProvider {
return (readOnly == null ? false : readOnly); return (readOnly == null ? false : readOnly);
} }
public boolean isNameRequired() {
return false;
}
public boolean isEmailRequired() {
return true;
}
private void loadReadOnly() { private void loadReadOnly() {
try { try {
// See if the is read only // See if the is read only
......
...@@ -34,6 +34,8 @@ import org.jivesoftware.openfire.user.UserManager; ...@@ -34,6 +34,8 @@ import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.stringprep.Stringprep;
import org.jivesoftware.stringprep.StringprepException;
import org.xmpp.packet.IQ; import org.xmpp.packet.IQ;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError;
...@@ -123,11 +125,17 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid ...@@ -123,11 +125,17 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
field = new XFormFieldImpl("name"); field = new XFormFieldImpl("name");
field.setType(FormField.TYPE_TEXT_SINGLE); field.setType(FormField.TYPE_TEXT_SINGLE);
field.setLabel("Full name"); field.setLabel("Full name");
if (UserManager.getUserProvider().isNameRequired()) {
field.setRequired(true);
}
registrationForm.addField(field); registrationForm.addField(field);
field = new XFormFieldImpl("email"); field = new XFormFieldImpl("email");
field.setType(FormField.TYPE_TEXT_SINGLE); field.setType(FormField.TYPE_TEXT_SINGLE);
field.setLabel("Email"); field.setLabel("Email");
if (UserManager.getUserProvider().isEmailRequired()) {
field.setRequired(true);
}
registrationForm.addField(field); registrationForm.addField(field);
field = new XFormFieldImpl("password"); field = new XFormFieldImpl("password");
...@@ -298,6 +306,11 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid ...@@ -298,6 +306,11 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
if (email == null || "".equals(email)) { if (email == null || "".equals(email)) {
email = " "; email = " ";
} }
// So that we can set a more informative error message back, lets test this for
// stringprep validity now.
if (username != null) {
Stringprep.nodeprep(username);
}
if (session.getStatus() == Session.STATUS_AUTHENTICATED) { if (session.getStatus() == Session.STATUS_AUTHENTICATED) {
// Flag that indicates if the user is *only* changing his password // Flag that indicates if the user is *only* changing his password
...@@ -384,12 +397,18 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid ...@@ -384,12 +397,18 @@ public class IQRegisterHandler extends IQHandler implements ServerFeaturesProvid
reply.setChildElement(packet.getChildElement().createCopy()); reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.bad_request); reply.setError(PacketError.Condition.bad_request);
} }
catch (IllegalArgumentException e) { catch (StringprepException e) {
// The specified username is not correct according to the stringprep specs // The specified username is not correct according to the stringprep specs
reply = IQ.createResultIQ(packet); reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy()); reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.jid_malformed); reply.setError(PacketError.Condition.jid_malformed);
} }
catch (IllegalArgumentException e) {
// At least one of the fields passed in is not valid
reply = IQ.createResultIQ(packet);
reply.setChildElement(packet.getChildElement().createCopy());
reply.setError(PacketError.Condition.not_acceptable);
}
catch (UnsupportedOperationException e) { catch (UnsupportedOperationException e) {
// The User provider is read-only so this operation is not allowed // The User provider is read-only so this operation is not allowed
reply = IQ.createResultIQ(packet); reply = IQ.createResultIQ(packet);
......
...@@ -711,6 +711,14 @@ public class LdapUserProvider implements UserProvider { ...@@ -711,6 +711,14 @@ public class LdapUserProvider implements UserProvider {
return true; return true;
} }
public boolean isNameRequired() {
return false;
}
public boolean isEmailRequired() {
return false;
}
/** /**
* Parses dates/time stamps stored in LDAP. Some possible values: * Parses dates/time stamps stored in LDAP. Some possible values:
* *
......
...@@ -499,4 +499,12 @@ public class DefaultUserProvider implements UserProvider { ...@@ -499,4 +499,12 @@ public class DefaultUserProvider implements UserProvider {
public boolean isReadOnly() { public boolean isReadOnly() {
return false; return false;
} }
public boolean isNameRequired() {
return false;
}
public boolean isEmailRequired() {
return false;
}
} }
...@@ -402,4 +402,12 @@ public class JDBCUserProvider implements UserProvider { ...@@ -402,4 +402,12 @@ public class JDBCUserProvider implements UserProvider {
public boolean isReadOnly() { public boolean isReadOnly() {
return true; return true;
} }
public boolean isNameRequired() {
return false;
}
public boolean isEmailRequired() {
return false;
}
} }
...@@ -18,6 +18,7 @@ import org.jivesoftware.openfire.event.UserEventDispatcher; ...@@ -18,6 +18,7 @@ import org.jivesoftware.openfire.event.UserEventDispatcher;
import org.jivesoftware.openfire.resultsetmanager.Result; import org.jivesoftware.openfire.resultsetmanager.Result;
import org.jivesoftware.openfire.roster.Roster; import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
...@@ -106,7 +107,9 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -106,7 +107,9 @@ public class User implements Cacheable, Externalizable, Result {
} }
/** /**
* Constructs a new user. All arguments can be <tt>null</tt> except the username. * Constructs a new user. Normally, all arguments can be <tt>null</tt> except the username.
* However, a UserProvider -may- require a name or email address. In those cases, the
* isNameRequired or isEmailRequired UserProvider tests indicate whether <tt>null</tt> is allowed.
* Typically, User objects should not be constructed by end-users of the API. * Typically, User objects should not be constructed by end-users of the API.
* Instead, user objects should be retrieved using {@link UserManager#getUser(String)}. * Instead, user objects should be retrieved using {@link UserManager#getUser(String)}.
* *
...@@ -123,7 +126,13 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -123,7 +126,13 @@ public class User implements Cacheable, Externalizable, Result {
throw new NullPointerException("Username cannot be null"); throw new NullPointerException("Username cannot be null");
} }
this.username = username; this.username = username;
if (UserManager.getUserProvider().isNameRequired() && (name == null || name.equals(""))) {
throw new IllegalArgumentException("Invalid or empty name specified with provider that requires name");
}
this.name = name; this.name = name;
if (UserManager.getUserProvider().isEmailRequired() && !StringUtils.isValidEmailAddress(email)) {
throw new IllegalArgumentException("Invalid or empty email address specified with provider that requires email address");
}
this.email = email; this.email = email;
this.creationDate = creationDate; this.creationDate = creationDate;
this.modificationDate = modificationDate; this.modificationDate = modificationDate;
...@@ -174,6 +183,10 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -174,6 +183,10 @@ public class User implements Cacheable, Externalizable, Result {
throw new UnsupportedOperationException("User provider is read-only."); throw new UnsupportedOperationException("User provider is read-only.");
} }
if ((name == null || name.equals("")) && UserManager.getUserProvider().isNameRequired()) {
throw new IllegalArgumentException("User provider requires name.");
}
try { try {
String originalName = this.name; String originalName = this.name;
UserManager.getUserProvider().setName(username, name); UserManager.getUserProvider().setName(username, name);
...@@ -205,6 +218,10 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -205,6 +218,10 @@ public class User implements Cacheable, Externalizable, Result {
throw new UnsupportedOperationException("User provider is read-only."); throw new UnsupportedOperationException("User provider is read-only.");
} }
if (UserManager.getUserProvider().isEmailRequired() && !StringUtils.isValidEmailAddress(email)) {
throw new IllegalArgumentException("User provider requires email address.");
}
try { try {
String originalEmail= this.email; String originalEmail= this.email;
UserManager.getUserProvider().setEmail(username, email); UserManager.getUserProvider().setEmail(username, email);
......
...@@ -116,13 +116,15 @@ public class UserManager implements IQResultListener { ...@@ -116,13 +116,15 @@ public class UserManager implements IQResultListener {
/** /**
* Creates a new User. Required values are username and password. The email address * Creates a new User. Required values are username and password. The email address
* can optionally be <tt>null</tt>. * and name can optionally be <tt>null</tt>, unless the UserProvider deems that
* either of them are required.
* *
* @param username the new and unique username for the account. * @param username the new and unique username for the account.
* @param password the password for the account (plain text). * @param password the password for the account (plain text).
* @param name the name of the user. * @param name the name of the user, which can be <tt>null</tt> unless the UserProvider
* deems that it's required.
* @param email the email address to associate with the new account, which can * @param email the email address to associate with the new account, which can
* be <tt>null</tt>. * be <tt>null</tt>, unless the UserProvider deems that it's required.
* @return a new User. * @return a new User.
* @throws UserAlreadyExistsException if the username already exists in the system. * @throws UserAlreadyExistsException if the username already exists in the system.
* @throws UnsupportedOperationException if the provider does not support the * @throws UnsupportedOperationException if the provider does not support the
...@@ -141,6 +143,12 @@ public class UserManager implements IQResultListener { ...@@ -141,6 +143,12 @@ public class UserManager implements IQResultListener {
catch (StringprepException se) { catch (StringprepException se) {
throw new IllegalArgumentException("Invalid username: " + username, se); throw new IllegalArgumentException("Invalid username: " + username, se);
} }
if (provider.isNameRequired() && (name == null || name.equals(""))) {
throw new IllegalArgumentException("Invalid or empty name specified with provider that requires name");
}
if (provider.isEmailRequired() && !StringUtils.isValidEmailAddress(email)) {
throw new IllegalArgumentException("Invalid or empty email address specified with provider that requires email address");
}
User user = provider.createUser(username, password, name, email); User user = provider.createUser(username, password, name, email);
userCache.put(username, user); userCache.put(username, user);
......
...@@ -38,8 +38,8 @@ public interface UserProvider { ...@@ -38,8 +38,8 @@ public interface UserProvider {
* *
* @param username the username. * @param username the username.
* @param password the plain-text password. * @param password the plain-text password.
* @param name the user's name, which can be <tt>null</tt>. * @param name the user's name, which can be <tt>null</tt>, unless isNameRequired is set to true.
* @param email the user's email address, which can be <tt>null</tt>. * @param email the user's email address, which can be <tt>null</tt>, unless isEmailRequired is set to true.
* @return a new User. * @return a new User.
* @throws UserAlreadyExistsException if the username is already in use. * @throws UserAlreadyExistsException if the username is already in use.
*/ */
...@@ -208,4 +208,19 @@ public interface UserProvider { ...@@ -208,4 +208,19 @@ public interface UserProvider {
* @return true if the user provider is read-only. * @return true if the user provider is read-only.
*/ */
public boolean isReadOnly(); public boolean isReadOnly();
/**
* Returns true if this UserProvider requires a name to be set on User objects.
*
* @return true if an name is required with this provider.
*/
public boolean isNameRequired();
/**
* Returns true if this UserProvider requires an email address to be set on User objects.
*
* @return true if an email address is required with this provider.
*/
public boolean isEmailRequired();
} }
\ No newline at end of file
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
package org.jivesoftware.util; package org.jivesoftware.util;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.AddressException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
...@@ -1007,4 +1009,28 @@ public class StringUtils { ...@@ -1007,4 +1009,28 @@ public class StringUtils {
return str.substring(0, maxWidth) + "..."; return str.substring(0, maxWidth) + "...";
} }
/**
* Returns true if the string passed in is a valid Email address.
*
* @param address Email address to test for validity.
* @return true if the string passed in is a valid email address.
*/
public static boolean isValidEmailAddress(String address) {
if (address == null) {
return false;
}
if (!address.contains("@")) {
return false;
}
try {
InternetAddress.parse(address);
return true;
}
catch (AddressException e) {
return false;
}
}
} }
\ No newline at end of file
...@@ -71,6 +71,18 @@ ...@@ -71,6 +71,18 @@
if (password != null && passwordConfirm != null && !password.equals(passwordConfirm)) { if (password != null && passwordConfirm != null && !password.equals(passwordConfirm)) {
errors.put("passwordMatch",""); errors.put("passwordMatch","");
} }
// If provider requires email, validate
if (UserManager.getUserProvider().isEmailRequired()) {
if (StringUtils.isValidEmailAddress(email)) {
errors.put("email","");
}
}
// If provider requires name, validate
if (UserManager.getUserProvider().isNameRequired()) {
if (name == null || name.equals("")) {
errors.put("name","");
}
}
// do a create if there were no errors // do a create if there were no errors
if (errors.size() == 0) { if (errors.size() == 0) {
...@@ -185,9 +197,7 @@ ...@@ -185,9 +197,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td width="1%" nowrap> <td width="1%" nowrap><label for="nametf"><fmt:message key="user.create.name" />:</label> <%= UserManager.getUserProvider().isNameRequired() ? "*" : "" %></td>
<label for="nametf"><fmt:message key="user.create.name" />:</label>
</td>
<td width="99%"> <td width="99%">
<input type="text" name="name" size="30" maxlength="75" value="<%= ((name!=null) ? name : "") %>" <input type="text" name="name" size="30" maxlength="75" value="<%= ((name!=null) ? name : "") %>"
id="nametf"> id="nametf">
...@@ -195,7 +205,7 @@ ...@@ -195,7 +205,7 @@
</tr> </tr>
<tr> <tr>
<td width="1%" nowrap> <td width="1%" nowrap>
<label for="emailtf"><fmt:message key="user.create.email" />:</label></td> <label for="emailtf"><fmt:message key="user.create.email" />:</label> <%= UserManager.getUserProvider().isEmailRequired() ? "*" : "" %></td>
<td width="99%"> <td width="99%">
<input type="text" name="email" size="30" maxlength="75" value="<%= ((email!=null) ? email : "") %>" <input type="text" name="email" size="30" maxlength="75" value="<%= ((email!=null) ? email : "") %>"
id="emailtf"> id="emailtf">
......
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
errorPage="error.jsp" errorPage="error.jsp"
%><%@ page import="org.xmpp.packet.JID"%> %><%@ page import="org.xmpp.packet.JID"%>
<%@ page import="org.jivesoftware.openfire.security.SecurityAuditManager" %> <%@ page import="org.jivesoftware.openfire.security.SecurityAuditManager" %>
<%@ page import="org.jivesoftware.util.StringUtils" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %> <%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
...@@ -26,6 +29,7 @@ ...@@ -26,6 +29,7 @@
String username = ParamUtils.getParameter(request,"username"); String username = ParamUtils.getParameter(request,"username");
String name = ParamUtils.getParameter(request,"name"); String name = ParamUtils.getParameter(request,"name");
String email = ParamUtils.getParameter(request,"email"); String email = ParamUtils.getParameter(request,"email");
Map<String, String> errors = new HashMap<String, String>();
// Handle a cancel // Handle a cancel
if (request.getParameter("cancel") != null) { if (request.getParameter("cancel") != null) {
...@@ -38,6 +42,19 @@ ...@@ -38,6 +42,19 @@
// Handle a save // Handle a save
if (save) { if (save) {
// If provider requires email, validate
if (UserManager.getUserProvider().isEmailRequired()) {
if (StringUtils.isValidEmailAddress(email)) {
errors.put("email","");
}
}
// If provider requires name, validate
if (UserManager.getUserProvider().isNameRequired()) {
if (name == null || name.equals("")) {
errors.put("name","");
}
}
user.setEmail(email); user.setEmail(email);
user.setName(name); user.setName(name);
...@@ -59,8 +76,28 @@ ...@@ -59,8 +76,28 @@
<meta name="extraParams" content="<%= "username="+URLEncoder.encode(username, "UTF-8") %>"/> <meta name="extraParams" content="<%= "username="+URLEncoder.encode(username, "UTF-8") %>"/>
</head> </head>
<body> <body>
<% if (!errors.isEmpty()) { %>
<% if (success) { %> <div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td class="jive-icon"><img src="images/error-16x16.gif" width="16" height="16" border="0" alt=""/></td>
<td class="jive-icon-label">
<% if (errors.get("name") != null) { %>
<fmt:message key="user.create.invalid_name" />
<% } else if (errors.get("email") != null) { %>
<fmt:message key="user.create.invalid_email" />
<% } %>
</td>
</tr>
</tbody>
</table>
</div>
<br>
<% } else if (success) { %>
<div class="jive-success"> <div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0"> <table cellpadding="0" cellspacing="0" border="0">
...@@ -99,7 +136,7 @@ ...@@ -99,7 +136,7 @@
</tr> </tr>
<tr> <tr>
<td class="c1"> <td class="c1">
<fmt:message key="user.create.name" />: <fmt:message key="user.create.name" />: <%= UserManager.getUserProvider().isNameRequired() ? "*" : "" %>
</td> </td>
<td> <td>
<input type="text" size="30" maxlength="150" name="name" <input type="text" size="30" maxlength="150" name="name"
...@@ -108,7 +145,7 @@ ...@@ -108,7 +145,7 @@
</tr> </tr>
<tr> <tr>
<td class="c1"> <td class="c1">
<fmt:message key="user.create.email" />: <fmt:message key="user.create.email" />: <%= UserManager.getUserProvider().isEmailRequired() ? "*" : "" %>
</td> </td>
<td> <td>
<input type="text" size="30" maxlength="150" name="email" <input type="text" size="30" maxlength="150" name="email"
...@@ -118,6 +155,7 @@ ...@@ -118,6 +155,7 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</fieldset> </fieldset>
<br><br> <br><br>
...@@ -127,5 +165,11 @@ ...@@ -127,5 +165,11 @@
</form> </form>
<br/>
<span class="jive-description">
* <fmt:message key="user.create.requied" />
</span>
</body> </body>
</html> </html>
\ No newline at end of file
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