Newer
Older
/*
* org.nrg.xnat.security.DisableInactiveUsersJob
* XNAT http://www.xnat.org
* Copyright (c) 2014, Washington University School of Medicine
* All Rights Reserved
*
* Released under the Simplified BSD.
*
* Last modified 11/4/13 9:51 AM
*/
package org.nrg.xnat.security;
import org.apache.commons.lang3.time.DateUtils;
import org.nrg.xdat.security.helpers.Users;
import org.nrg.xdat.turbine.utils.AdminUtils;
import org.nrg.xdat.turbine.utils.TurbineUtils;
import org.nrg.xft.XFTTable;
import org.nrg.xft.event.EventUtils;
import org.nrg.xft.exception.DBPoolException;
import org.nrg.xft.exception.InvalidPermissionException;
import org.nrg.xft.security.UserI;
import org.nrg.xnat.utils.XnatUserProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class DisableInactiveUsers implements Runnable {
public DisableInactiveUsers(final XnatUserProvider userProvider, final int inactivityBeforeLockout, final int lockoutDuration) {
_userProvider = userProvider;
_inactivityBeforeLockout = inactivityBeforeLockout;
Rick Herrick
committed
_lockoutDuration = lockoutDuration;
}
/**
* Finds user accounts that have not been validated or authenticated within the indicated time frame
* and disables them.
*/
@Override
public void run() {
final UserI adminUser = _userProvider.get();
final XFTTable table = XFTTable.Execute("SELECT xdat_user.login FROM xdat_user INNER JOIN " +
"(" +
"SELECT y.login, last_login, activation_date FROM xdat_user_meta_data INNER JOIN " +
"(" +
"SELECT xdat_user.login, xdat_user.xdat_user_id, MAX(xdat_user_login.login_date) AS last_login FROM xdat_user_login RIGHT JOIN xdat_user ON xdat_user_login.user_xdat_user_id=xdat_user.xdat_user_id GROUP BY xdat_user.login,xdat_user.xdat_user_id" +
") y " + //get last login times for each user
"ON y.xdat_user_id=xdat_user_meta_data.meta_data_id AND y.login NOT IN (SELECT username FROM xhbm_user_role WHERE role='Administrator') AND y.xdat_user_id NOT IN (SELECT xdat_user_xdat_user_id FROM xdat_r_xdat_role_type_assign_xdat_user WHERE xdat_r_xdat_role_type_assign_xdat_user.xdat_role_type_role_name = 'Administrator')" +
") x " + //get dates that each non-admin user was created
"ON x.login=xdat_user.login AND ((x.activation_date<(now()- INTERVAL '" + _inactivityBeforeLockout + " seconds')) AND ((x.last_login IS NULL) OR x.last_login<(now()- INTERVAL '" + _inactivityBeforeLockout + " seconds'))) AND xdat_user.enabled=1", null, null);
table.resetRowCursor();
while (table.hasMoreRows()) {
final Object[] row = table.nextRow();
final String username = (String) row[0];
final UserI u = Users.getUser(username);
// Fixes XNAT-2407. Only disable user if they have not been recently modified (enabled).
Mike McKay
committed
// Also do not disable the guest user.
if (!hasUserBeenModified(u, _inactivityBeforeLockout) && !username.equals("guest")) {
u.setEnabled("0");
u.setVerified("0");
Users.save(u, u, false, EventUtils.newEventInstance(EventUtils.CATEGORY.SIDE_ADMIN, EventUtils.TYPE.PROCESS, "Disabled due to inactivity"));
Rick Herrick
committed
String expiration = TurbineUtils.getDateTimeFormatter().format(DateUtils.addMilliseconds(GregorianCalendar.getInstance().getTime(), _lockoutDuration));
System.out.println("Locked out " + u.getLogin() + " user account until " + expiration);
AdminUtils.sendAdminEmail(u.getLogin() + " account disabled due to inactivity.", "User " + u.getLogin() + " has been automatically disabled due to inactivity.");
}
} catch (InvalidPermissionException e) {
if (e.getMessage().contains("wrk:workflowData")) {
logger.warn("An invalid permission exception was encountered while attempting to disable the user {} with provided user {}. This probably indicates that the system is still initializing. If it occurs frequently after the system has started, there may be an issue with your database schema or data.");
} else {
logger.error("An unexpected invalid permission exception occurred", e);
}
} catch (Exception e) {
logger.error("", e);
}
}
} catch (SQLException e) {
logger.error("An error occurred querying the database: [" + e.getErrorCode() + "] " + e.getSQLState(), e);
} catch (DBPoolException e) {
logger.error("A database connection or pooling error occurred.", e);
}
}
/**
* Function determines if the user has been modified in the past amount of seconds.
* Fixes XNAT-2407. This keeps the job from disabling a user if the admin has just enabled (modified) them.
*
* @param u - the user we are interested in.
* @param seconds - Has the user been modified in the past amount of seconds.
* @return true if the user has been modified / otherwise false.
*/
private boolean hasUserBeenModified(final UserI u, final int seconds) {
// Subtract seconds from today's date.
final Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, -seconds);
// If the time is before the last modified date, the user has been modified.
final Date lastModified = u.getLastModified();
return lastModified != null && (c.getTime().before(lastModified));
}
private static final Logger logger = LoggerFactory.getLogger(DisableInactiveUsers.class);
private final XnatUserProvider _userProvider;
private final int _inactivityBeforeLockout;
private final int _lockoutDuration;