Managing logins, automatic disabling of userIds

We are trying to disable system and user accounts that have not been logged in to after 35 days. We are trying to determine what MWS/webMethods tables to query in order to find a value for userID or username. We cannot query on email because the field is sometimes left blank. We use local users managed within MWS, and would like to disable those users who have not logged in to the system within 35 days.

Does anyone else do anything like this, or has anyone discovered the webMethods table and schema that we need to query? This is an urgent request, so any assistance would be great.

Daryl

I would advise against directly querying or manipulating the database. It is safer to use the provided java APIs. How are you tracking when they last logged in?

Also, if you are only concerned about stale passwords on system user accounts, then there is an existing mechanism to expire passwords via the “Password Complexity Policies” configured on the system directory service.

The system user password complexity policy feature was added for:
MWS_8.2_SP1_Fix8
MWS_8.0_SP2_Fix15
Or any version of later than MWS 9.0

For example, you could create your own custom jar file that contains a custom java class that implements the
com.webMethods.portal.service.dir.ISystemPasswordComplexityPolicy interface. Specifically, the getPasswordExpirationDuration method would determine how long a password is valid.

For example, something like this:


import java.util.regex.Pattern;
import com.webmethods.portal.service.dir.IDirPrincipal;
import com.webmethods.portal.service.dir.ISystemPasswordComplexityPolicy;
import com.webmethods.rtl.util.Debug;

/**
 * Sample implementation of password complexity policy
 */
public class SampleSystemPasswordComplexityPolicy implements
        ISystemPasswordComplexityPolicy {

    /**
     * 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
     */
    public void checkPasswordForNewUser(String candidatePassword)
            throws InvalidPasswordException {
        if (candidatePassword == null || candidatePassword.length() < 6) {
            throw new InvalidPasswordException("Your password must contain at least 6 characters");
        }

        boolean hasUpper = !candidatePassword.toLowerCase().equals(candidatePassword);
        boolean hasLower = !candidatePassword.toUpperCase().equals(candidatePassword);
        if (!(hasUpper && hasLower)) {
            throw new InvalidPasswordException("Your password must contain upper and lower case characters");
        }

        boolean hasNumberOrSpecialChar = Pattern.matches(".*[\\W\\d]+.*$", candidatePassword);
        if (!hasNumberOrSpecialChar) {
            throw new InvalidPasswordException("Your password must contain at least one number or special character");
        }        
    }

    /**
     * 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
     */
    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)
     */
    public long getPasswordExpirationDuration(IDirPrincipal user) {
        try {
            if ("sysadmin".equals(user.getName())) {
                return -1; //don't expire the sysadmin password.
            }
        } catch (Exception e) {
            Debug.warn(e);
        }
	int oneday = 86400000; //milliseconds in 1 day
        return oneday * 35; //35 days
    }


    /**
     * 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.
     */
    public String getPasswordPatternText() {
	return "Password must contain at least 6 characters, contain upper and lower case characters and at least one number or special character.";
    }
    
}

Below is how this feature is documented in the latest version:

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 feature for external directory services.

To install a password complexity class for the system directory service

  1. Stop My webMethods Server.
  2. Copy your custom jar file that contains your custom password complexity class to the SoftwareAG\MWS\lib directory.
  3. Create a [your_jar_file_name_without_jar_extension].bnd file in the SoftwareAG\MWS\lib directory whose file content looks like the following:
# attach as fragment to the caf.server bundle
Fragment-Host: com.webmethods.caf.server
#expand (inline) the contents of the jar containing the classes
#into the bundle.
# TODO: make sure this filename matches the name of your jar file
Include-Resource: @jar_file_name.jar
#import any external java packages that are required
Import-Package: *;resolution:=optional
#export the java packages that should be visible to external consumers
Export-Package: *
-nouses: true
  1. To re-apply the custom JAR file to the My webMethods Server profile with the new information, run this command from the command line: ./mws.[bat|sh] -s default update-osgi-profile
  2. Restart My webMethods Server.
  3. To navigate to the correct page, do one of the following:
    a) In My webMethods: Navigate > Applications > Administration > My webMethods >Directory Services > List Directory Services.
    b) As system administrator: Administration > User Management > Directory Services Administration > List Directory Services.
  4. Click the system directory service.
  5. In the Password Complexity Class field, type the name of the Password Complexity Class created in the Composite Application Framework and click Apply.

If you need to update your custom JAR file, do the following:

  1. Stop My webMethods Server.
  2. Update or copy the new custom JAR file to the Software AG_directory\MWS\lib directory.
  3. Run this command from the command line: ./mws.[bat|sh] -s default update-osgi-profile
  4. Restart My webMethods Server.
1 Like

Seeing how to do that is really interesting (we need an Eric Norman blog) but this doesn’t seem to apply to the OP’s situation. He doesn’t want this:

Day 1: user logs in, changes password
Day 1-29: user logs in and does work
Day 30: user goes on holiday for 10 days
Day 40: back with sunburn. Tries to log in. Oops! Password expired

He wants it to expire after 35 days without logging in. So in the scenario, because he went on holiday on day 30, his account won’t be disabled until day 65 in this timeline.

Eric, et al,

We are OK with the changing of the passwords. We have a requirement to disable any accounts that have not been logged in to after 35 days. We delete them at another later interval. We are not looking to set up a complex password policy or anything like that. We simply want to set the flag from 0 to 1 and allow the user account to be set to disabled/inactive because of a defined policy.

We are trying to query the DB to get this information, but have not yet found it. Thank you for the complex password solution, but we are only looking to disable inactive logins, not change passwords.

Daryl

Ok, well that is mainly why I was asking how you are tracking when the user has last logged in. I don’t believe the system keeps track of that information out of the box. So if you have some custom way of tracking that information in your own custom database table (or somewhere else) that is populated by listening for login events then you could query your own custom table to get the usernames that are expired.

Once you know the usernames that have not logged in for 35 days then you can use the java APIs to mark the user as disabled.

I know this is a crazy way to do it, but rather than modifying your login portlet etc (unless you already have, in which case maybe extending it to capture last login time in an external db is better than this) why don’t you do this:

$backlog = 35

Do this nightly, maybe in IS:

  1. Load into memory the last $backlog days’ worth of MWS full logs.
  2. Parse the logs for this line: 2014-01-31 05:36:18 EST (Framework:DEBUG) [RID:13692] - Login succeeded for user: rgrant
  3. Build a list (map?) of users and their last logged in times
  4. Remove users whose time is less than $backlog days
  5. Hometime! Sorry, I was digging through docs and ran out of time. Hope this is a start.

Notes:
a) For 1), the audit log may be sufficient. You’ll have to see how logging has been configured.
b) I know this is crazy. It’s just that if you haven’t done a lot of MWS customisation, this might be a lighter touch way of doing things.