Using Password Complexity Policies
A password complexity policy enforces requirements that make user passwords more resistant to brute-force attacks. You can create a password complexity class and add it to My webMethods Server for use with the system directory service. You cannot use this function for external directory services.
You include a password complexity policy Java class in a Composite Application Framework (CAF) application. When you deploy the application to My webMethods Server, the server registers the Java class. The password complexity policy component is then available for use during subsequent requests to the server. Implementing the password complexity policy does not require a restart of My webMethods Server.
Before creating the Java class, you must create a CAF application in Designer. For more information about creating, working with, and deploying CAF applications, see webMethods CAF and OpenUI Development Help.
To implement a custom password complexity as a Java class
1. In an existing CAF application in Designer, create a Java class that implements the ISystemPasswordComplexityPolicy interface. The component is registered as an OSGi service via the @Component annotation.
The following example shows how to implement the custom password complexity policy.
package caf.war.cafapp1.dir;
import java.util.Map;
import java.util.regex.Pattern;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webmethods.portal.resources.Ui;
import com.webmethods.portal.service.dir.IDirPrincipal;
import com.webmethods.portal.service.dir.ISystemPasswordComplexityPolicy;
import com.webmethods.portal.service.global.IGlobalProvider;
import com.webmethods.portal.service.global.IMessageInfo;
import com.webmethods.portal.system.init.InitializationException;
/**
* Custom implementation of password complexity policy
*/
@Component (
service = {
ISystemPasswordComplexityPolicy.class
},
property = {
"name:String=CustomSystemPasswordComplexityPolicy1"
},
immediate = true
)
public class CustomSystemPasswordComplexityPolicy1 implements
ISystemPasswordComplexityPolicy {
private Logger logger = LoggerFactory.getLogger(getClass());
//references to other services
private IGlobalProvider gp = null;
/**
* Reference injection of the other OSGi service
*/
@Reference
protected void bindGlobalProvider(IGlobalProvider globalProvider) {
if (logger.isTraceEnabled()) {
logger.trace("Bound IGlobalProvider");
}
this.gp = globalProvider;
}
/**
* Activation of OSGi declaritive service
* @param config the configuration properties of the OSGi service
* @throws InitializationException if activation fails
*/
@Activate
protected void activate(Map<String,Object> config) throws
InitializationException {
if (logger.isInfoEnabled()) {
logger.info("Activating service");
}
}
/**
* Deactivation of OSGi declaritive service
*/
@Deactivate
protected void deactivate() {
if (logger.isInfoEnabled()) {
logger.info("Deactivating service");
}
}
/**
* Check the candidate password to make sure the value satisfies the
complexity
* requirements. If the password is not complex enough, this method
should throw
* an {@link InvalidPasswordException} with the reason. If no exception
is thrown
* the password is valid.
*
* @param candidatePassword the password to check
* @throws InvalidPasswordException if the password is not valid
*/
@Override
public void checkPasswordForNewUser(String candidatePassword)
throws InvalidPasswordException {
if (candidatePassword == null || candidatePassword.length() < 6) {
IMessageInfo messageInfo = gp.getMessageInfo(Ui.class,
"password.too.short", null);
String msg = messageInfo.getLocalizedMessage(false);
throw new InvalidPasswordException(msg);
}
boolean hasUpper = !candidatePassword.toLowerCase().equals(candidatePassword);
boolean hasLower = !candidatePassword.toUpperCase().equals(candidatePassword);
if (!(hasUpper && hasLower)) {
IMessageInfo messageInfo = gp.getMessageInfo(Ui.class,
"password.mix.case", null);
String msg = messageInfo.getLocalizedMessage(false);
throw new InvalidPasswordException(msg);
}
boolean hasNumberOrSpecialChar = Pattern.matches(".*[\\W\\d]+.*$",
candidatePassword);
if (!hasNumberOrSpecialChar) {
IMessageInfo messageInfo = gp.getMessageInfo(Ui.class,
"password.special.char", null);
String msg = messageInfo.getLocalizedMessage(false);
throw new InvalidPasswordException(msg);
}
}
/**
* Check the candidate password to make sure the value satisfies the
complexity
* requirements. If the password is not complex enough, this method
should throw
* an {@link InvalidPasswordException} with the reason. If no exception
is thrown
* the password is valid.
*
* @param user the user whose password is being checked.
* @param candidatePassword the password to check
* @throws InvalidPasswordException if the password is not valid
*/
@Override
public void checkPasswordForExistingUser(IDirPrincipal user,
String candidatePassword) throws InvalidPasswordException {
checkPasswordForNewUser(candidatePassword);
}
/**
* Return how long a password is valid (in milliseconds) before it
expires. The user
* will not be able to login after this time duration has expired
and the user must be
* reset by an administrator or a custom reset password page.
*
* @param user the user to get the expiration value for.
* @return return -1 for no password expiration, or the duration (in ms)
*/
@Override
public long getPasswordExpirationDuration(IDirPrincipal user) {
try {
if ("sysadmin".equals(user.getName())) {
return -1; //don't expire the sysadmin password.
}
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
return 1 * 60 * 60 * 1000; //1 hour
}
/**
* Returns a description of the expected pattern a password must have.
This text
* is displayed on the create user and update user pages of the UI.
*
* @return password descriptive text.
*/
@Override
public String getPasswordPatternText() {
IMessageInfo messageInfo = gp.getMessageInfo(Ui.class,
"password.complexity.description", null);
return messageInfo.getLocalizedMessage(false);
}
}
2. Deploy the application to My webMethods Server.
3. To open the My webMethods Server Directory Services page, go to http://hostname:port/webm.apps.config.directory.service.admin, where hostname is the hostname of My webMethods Server and port is the port number of My webMethods Server.
4. On the List Directory Services tab, click the system directory service.
5. In the Password Complexity Class field, select the name of the Java class that you created in the CAF application, and then click Apply.
6. Debug and test the Java class.
7. If required, make further changes to the Java class and repeat steps 2 and 6 until the class works as expected.