+#define _XOPEN_SOURCE
+#include <time.h>
+#include <string.h>
+#include <strings.h>
#include "opensrf/osrf_app_session.h"
#include "opensrf/osrf_application.h"
#include "opensrf/osrf_settings.h"
#define OILS_AUTH_TEMP "temp"
#define OILS_AUTH_PERSIST "persist"
+#define BLOCK_EXPIRED_STAFF_LOGIN_FLAG "auth.block_expired_staff_login"
+
// Default time for extending a persistent session: ten minutes
#define DEFAULT_RESET_INTERVAL 10 * 60
return 0;
}
+int _checkIfExpiryDatePassed(const char *expire_date) {
+
+ struct tm expire_tm;
+ memset(&expire_tm, 0, sizeof(expire_tm));
+ strptime(expire_date, "%FT%T%z", &expire_tm);
+ time_t now = time(NULL);
+ time_t expire_time_t = mktime(&expire_tm);
+ if (now > expire_time_t) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int _blockExpiredStaffLogin(osrfMethodContext* ctx, int user_id) {
+ // check global flag whether we're supposed to block or not
+ jsonObject *cgfObj = NULL, *params = NULL;
+ params = jsonNewObject(BLOCK_EXPIRED_STAFF_LOGIN_FLAG);
+ cgfObj = oilsUtilsCStoreReqCtx(
+ ctx, "open-ils.cstore.direct.config.global_flag.retrieve", params);
+ jsonObjectFree(params);
+
+ int may_block_login = 0;
+ char* tmp_str = NULL;
+ if (cgfObj && cgfObj->type != JSON_NULL) {
+ tmp_str = oilsFMGetString(cgfObj, "enabled");
+ if (oilsUtilsIsDBTrue(tmp_str)) {
+ may_block_login = 1;
+ }
+ free(tmp_str);
+ }
+ jsonObjectFree(cgfObj);
+
+ if (!may_block_login) {
+ return 0;
+ }
+
+ // OK, we're supposed to block logins by expired staff accounts,
+ // so let's see if the account is one. We'll do so by seeing
+ // if the account has the STAFF_LOGIN permission anywhere. We
+ // are _not_ checking the login_type, as blocking 'staff' and
+ // 'temp' logins still leaves open the possibility of constructing
+ // an 'opac'-type login that _also_ sets a workstation, which
+ // in turn could be used to set an authtoken cookie that works
+ // in the staff interface. This means, that unlike ordinary patrons,
+ // a staff account that expires will not be able to log into
+ // the public catalog... but then, staff members really ought
+ // to be using a separate account when acting as a library patron
+ // anyway.
+
+ int block_login = 0;
+
+ // using the root org unit as the context org unit.
+ int org_id = -1;
+ char* perms[1];
+ perms[0] = "STAFF_LOGIN";
+ oilsEvent* response = oilsUtilsCheckPerms(user_id, org_id, perms, 1);
+
+ if (!response) {
+ // user has STAFF_LOGIN, so should be blocked
+ block_login = 1;
+ } else {
+ oilsEventFree(response);
+ }
+
+ return block_login;
+}
int oilsAuthInternalValidate(osrfMethodContext* ctx) {
OSRF_METHOD_VERIFY_CONTEXT(ctx);
jsonObject *userObj = NULL, *params = NULL;
char* tmp_str = NULL;
int user_exists = 0, user_active = 0,
- user_barred = 0, user_deleted = 0;
+ user_barred = 0, user_deleted = 0,
+ expired = 0;
// Confirm user exists, active=true, barred=false, deleted=false
params = jsonNewNumberStringObject(user_id);
tmp_str = oilsFMGetString(userObj, "deleted");
user_deleted = oilsUtilsIsDBTrue(tmp_str);
free(tmp_str);
+
+ tmp_str = oilsFMGetString(userObj, "expire_date");
+ expired = _checkIfExpiryDatePassed(tmp_str);
+ free(tmp_str);
}
if (!user_exists || user_barred || user_deleted) {
response = oilsNewEvent(OILS_LOG_MARK_SAFE, OILS_EVENT_AUTH_FAILED);
}
+ if (!response && expired) {
+ if (_blockExpiredStaffLogin(ctx, atoi(user_id))) {
+ tmp_str = oilsFMGetString(userObj, "usrname");
+ osrfLogWarning( OSRF_LOG_MARK, "Blocked login for expired staff user %s", tmp_str );
+ free(tmp_str);
+ response = oilsNewEvent(OILS_LOG_MARK_SAFE, OILS_EVENT_AUTH_FAILED);
+ }
+ }
+
if (!response && !user_active) {
// In some cases, it's useful for the caller to know if the
// patron was unable to login becuase the account is inactive.
--- /dev/null
+Block Login of Expired Staff Accounts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Evergreen now has the ability to prevent staff users whose
+accounts have expired from logging in. This is controlled
+by the new global flag "auth.block_expired_staff_login", which
+is not enabled by default. If that flag is turned on, accounts
+that have the `STAFF_LOGIN` permission and whose expiration date
+is in the past are prevented from logging into any Evergreen
+interface, including the staff client, the public catalog, and SIP2.
+
+It should be noted that ordinary patrons are allowed to log into
+the public catalog if their circulation privileges have expired. This
+feature prevents expired staff users from logging into the public catalog
+(and all other Evergreen interfaces and APIs) outright in order to
+prevent them from getting into the staff interface anyway by
+creative use of Evergreen's authentication APIs.
+
+Evergreen admins are advised to check the expiration status of staff
+accounts before turning on the global flag, as otherwise it is
+possible to lock staff users out unexpectedly. The following SQL
+query will identify expired but otherwise un-deleted users that
+would be blocked by turning on the flag:
+
+[source,sql]
+----
+SELECT DISTINCT usrname, expire_date
+FROM actor.usr au, permission.usr_has_perm_at_all(id, 'STAFF_LOGIN')
+WHERE active
+AND NOT deleted
+AND NOT barred
+AND expire_date < NOW()
+----
+
+Note that this query can take a long time to run in large databases
+given the general way that it checks for users that have the
+`STAFF_LOGIN` permission. Replacing the use of
+`permission.usr_has_perm_at_all()` with a query on expired users
+with profiles known to have the `STAFF_LOGIN` permission will
+be much faster.