From: Bill Erickson Date: Thu, 19 Nov 2015 20:00:20 +0000 (-0500) Subject: LP#1468422 New open-ils.auth_internal service X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=722338616034d1be24603ef193cc775fbbe08307;p=evergreen%2Fmasslnc.git LP#1468422 New open-ils.auth_internal service Service is responsible for adding user data to the authentication cache. Cache times are determined from opensrf.xml/AOUS settings. No authentication checks are performed. Signed-off-by: Bill Erickson Signed-off-by: Dan Wells --- diff --git a/Open-ILS/examples/opensrf.xml.example b/Open-ILS/examples/opensrf.xml.example index 3b47481f86..59f737a921 100644 --- a/Open-ILS/examples/opensrf.xml.example +++ b/Open-ILS/examples/opensrf.xml.example @@ -424,6 +424,29 @@ vim:et:ts=4:sw=4: + + 30 + 90 + 10 + + + + + + + 5 + 1 + c + oils_auth_internal.so + + 1000 + 1 + 15 + 1 + 5 + + + 420 @@ -431,13 +454,10 @@ vim:et:ts=4:sw=4: 300 2 weeks - - 30 - 90 - 10 - - + + + @@ -1177,6 +1197,7 @@ vim:et:ts=4:sw=4: open-ils.circ open-ils.actor open-ils.auth + open-ils.auth_internal open-ils.auth_proxy open-ils.storage open-ils.justintime diff --git a/Open-ILS/src/c-apps/Makefile.am b/Open-ILS/src/c-apps/Makefile.am index 06fdf7cf6c..0672a8ef1b 100644 --- a/Open-ILS/src/c-apps/Makefile.am +++ b/Open-ILS/src/c-apps/Makefile.am @@ -33,7 +33,7 @@ test_qstore_CFLAGS = $(AM_CFLAGS) test_qstore_LDFLAGS = $(AM_LDFLAGS) -loils_idl -loils_utils test_qstore_DEPENDENCIES = liboils_idl.la liboils_utils.la -lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_qstore.la oils_rstore.la oils_pcrud.la oils_auth.la +lib_LTLIBRARIES = liboils_idl.la liboils_utils.la oils_cstore.la oils_qstore.la oils_rstore.la oils_pcrud.la oils_auth.la oils_auth_internal.la liboils_idl_la_SOURCES = oils_idl-core.c liboils_idl_la_LDFLAGS = -version-info 2:0:0 @@ -61,4 +61,9 @@ oils_auth_la_SOURCES = oils_auth.c oils_auth_la_LDFLAGS = -module -loils_utils -lpcre -version-info 2:0:0 oils_auth_la_DEPENDENCIES = liboils_utils.la +oils_auth_internal_la_SOURCES = oils_auth_internal.c +oils_auth_internal_la_LDFLAGS = -module -loils_utils -version-info 2:0:0 +oils_auth_internal_la_DEPENDENCIES = liboils_utils.la + + diff --git a/Open-ILS/src/c-apps/oils_auth.c b/Open-ILS/src/c-apps/oils_auth.c index 587fbb46ca..b9998b52ff 100644 --- a/Open-ILS/src/c-apps/oils_auth.c +++ b/Open-ILS/src/c-apps/oils_auth.c @@ -24,10 +24,6 @@ int osrfAppInitialize(); int osrfAppChildInit(); -static long _oilsAuthOPACTimeout = 0; -static long _oilsAuthStaffTimeout = 0; -static long _oilsAuthOverrideTimeout = 0; -static long _oilsAuthPersistTimeout = 0; static long _oilsAuthSeedTimeout = 0; static long _oilsAuthBlockTimeout = 0; static long _oilsAuthBlockCount = 0; @@ -535,130 +531,6 @@ static int oilsAuthVerifyPassword( const osrfMethodContext* ctx, int user_id, return verified; } -/** - @brief Determine the login timeout. - @param userObj Pointer to an object describing the user. - @param type Pointer to one of four possible character strings identifying the login type. - @param orgloc Org unit to use for settings lookups (negative or zero means unspecified) - @return The length of the timeout, in seconds. - - The default timeout value comes from the configuration file, and depends on the - login type. - - The default may be overridden by a corresponding org unit setting. The @a orgloc - parameter says what org unit to use for the lookup. If @a orgloc <= 0, or if the - lookup for @a orgloc yields no result, we look up the setting for the user's home org unit - instead (except that if it's the same as @a orgloc we don't bother repeating the lookup). - - Whether defined in the config file or in an org unit setting, a timeout value may be - expressed as a raw number (i.e. all digits, possibly with leading and/or trailing white - space) or as an interval string to be translated into seconds by PostgreSQL. -*/ -static long oilsAuthGetTimeout( const jsonObject* userObj, const char* type, int orgloc ) { - - if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */ - - jsonObject* value_obj; - - value_obj = osrf_settings_host_value_object( - "/apps/open-ils.auth/app_settings/default_timeout/opac" ); - _oilsAuthOPACTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); - jsonObjectFree(value_obj); - if( -1 == _oilsAuthOPACTimeout ) { - osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for OPAC logins" ); - _oilsAuthOPACTimeout = 0; - } - - value_obj = osrf_settings_host_value_object( - "/apps/open-ils.auth/app_settings/default_timeout/staff" ); - _oilsAuthStaffTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); - jsonObjectFree(value_obj); - if( -1 == _oilsAuthStaffTimeout ) { - osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for staff logins" ); - _oilsAuthStaffTimeout = 0; - } - - value_obj = osrf_settings_host_value_object( - "/apps/open-ils.auth/app_settings/default_timeout/temp" ); - _oilsAuthOverrideTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); - jsonObjectFree(value_obj); - if( -1 == _oilsAuthOverrideTimeout ) { - osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for temp logins" ); - _oilsAuthOverrideTimeout = 0; - } - - value_obj = osrf_settings_host_value_object( - "/apps/open-ils.auth/app_settings/default_timeout/persist" ); - _oilsAuthPersistTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); - jsonObjectFree(value_obj); - if( -1 == _oilsAuthPersistTimeout ) { - osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for persist logins" ); - _oilsAuthPersistTimeout = 0; - } - - osrfLogInfo(OSRF_LOG_MARK, "Set default auth timeouts: " - "opac => %ld : staff => %ld : temp => %ld : persist => %ld", - _oilsAuthOPACTimeout, _oilsAuthStaffTimeout, - _oilsAuthOverrideTimeout, _oilsAuthPersistTimeout ); - } - - int home_ou = (int) jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" )); - if(orgloc < 1) - orgloc = home_ou; - - char* setting = NULL; - long default_timeout = 0; - - if( !strcmp( type, OILS_AUTH_OPAC )) { - setting = OILS_ORG_SETTING_OPAC_TIMEOUT; - default_timeout = _oilsAuthOPACTimeout; - } else if( !strcmp( type, OILS_AUTH_STAFF )) { - setting = OILS_ORG_SETTING_STAFF_TIMEOUT; - default_timeout = _oilsAuthStaffTimeout; - } else if( !strcmp( type, OILS_AUTH_TEMP )) { - setting = OILS_ORG_SETTING_TEMP_TIMEOUT; - default_timeout = _oilsAuthOverrideTimeout; - } else if( !strcmp( type, OILS_AUTH_PERSIST )) { - setting = OILS_ORG_SETTING_PERSIST_TIMEOUT; - default_timeout = _oilsAuthPersistTimeout; - } - - // Get the org unit setting, if there is one. - char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting ); - if(!timeout) { - if( orgloc != home_ou ) { - osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, " - "trying home_ou %d", orgloc, home_ou ); - timeout = oilsUtilsFetchOrgSetting( home_ou, setting ); - } - } - - if(!timeout) - return default_timeout; // No override from org unit setting - - // Translate the org unit setting to a number - long t; - if( !*timeout ) { - osrfLogWarning( OSRF_LOG_MARK, - "Timeout org unit setting is an empty string for %s login; using default", - timeout, type ); - t = default_timeout; - } else { - // Treat timeout string as an interval, and convert it to seconds - t = oilsUtilsIntervalToSeconds( timeout ); - if( -1 == t ) { - // Unable to convert; possibly an invalid interval string - osrfLogError( OSRF_LOG_MARK, - "Unable to convert timeout interval \"%s\" for %s login; using default", - timeout, type ); - t = default_timeout; - } - } - - free(timeout); - return t; -} - /* Adds the authentication token to the user cache. The timeout for the auth token is based on the type of login as well as (if type=='opac') @@ -669,80 +541,38 @@ static long oilsAuthGetTimeout( const jsonObject* userObj, const char* type, int static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname, const char* type, int orgloc, const char* workstation ) { - oilsEvent* response; + oilsEvent* response = NULL; - long timeout; - char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou")); - if(wsorg) { /* if there is a workstation, use it for the timeout */ - osrfLogDebug( OSRF_LOG_MARK, - "Auth session trying workstation id %d for auth timeout", atoi(wsorg)); - timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) ); - free(wsorg); - } else { - osrfLogDebug( OSRF_LOG_MARK, - "Auth session trying org from param [%d] for auth timeout", orgloc ); - timeout = oilsAuthGetTimeout( userObj, type, orgloc ); - } - osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %ld", uname, timeout ); - - char* string = va_list_to_string( - "%d.%ld.%s", (long) getpid(), time(NULL), uname ); - char* authToken = md5sum(string); - char* authKey = va_list_to_string( - "%s%s", OILS_AUTH_CACHE_PRFX, authToken ); - - const char* ws = (workstation) ? workstation : ""; - osrfLogActivity(OSRF_LOG_MARK, - "successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws ); - - oilsFMSetString( userObj, "passwd", "" ); - jsonObject* cacheObj = jsonParseFmt( "{\"authtime\": %ld}", timeout ); - jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj)); - - if( !strcmp( type, OILS_AUTH_PERSIST )) { - // Add entries for endtime and reset_interval, so that we can gracefully - // extend the session a bit if the user is active toward the end of the - // timeout originally specified. - time_t endtime = time( NULL ) + timeout; - jsonObjectSetKey( cacheObj, "endtime", jsonNewNumberObject( (double) endtime ) ); - - // Reset interval is hard-coded for now, but if we ever want to make it - // configurable, this is the place to do it: - jsonObjectSetKey( cacheObj, "reset_interval", - jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL )); - } + jsonObject* params = jsonNewObject(NULL); + jsonObjectSetKey(params, "user_id", + jsonNewNumberObject(oilsFMGetObjectId(userObj))); + jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc)); + jsonObjectSetKey(params, "login_type", jsonNewObject(type)); + if (workstation) + jsonObjectSetKey(params, "workstation", jsonNewObject(workstation)); + + jsonObject* authEvt = oilsUtilsQuickReq( + "open-ils.auth_internal", + "open-ils.auth_internal.session.create", params); + jsonObjectFree(params); - osrfCachePutObject( authKey, cacheObj, (time_t) timeout ); - jsonObjectFree(cacheObj); - osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache"); - jsonObject* payload = jsonParseFmt( - "{ \"authtoken\": \"%s\", \"authtime\": %ld }", authToken, timeout ); + if (authEvt) { - response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload ); - free(string); free(authToken); free(authKey); - jsonObjectFree(payload); + response = oilsNewEvent2( + OSRF_LOG_MARK, + jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode")), + jsonObjectGetKey(authEvt, "payload") // cloned within Event + ); - return response; -} + jsonObjectFree(authEvt); -static oilsEvent* oilsAuthVerifyWorkstation( - const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) { - osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %s", ws); - jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws); - if(!workstation || workstation->type == JSON_NULL) { - jsonObjectFree(workstation); - return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND"); - } - long wsid = oilsFMGetObjectId(workstation); - LONG_TO_STRING(wsid); - char* orgid = oilsFMGetString(workstation, "owning_lib"); - oilsFMSetString(userObj, "wsid", LONGSTR); - oilsFMSetString(userObj, "ws_ou", orgid); - free(orgid); - jsonObjectFree(workstation); - return NULL; -} + } else { + osrfLogError(OSRF_LOG_MARK, + "Error caching auth session in open-ils.auth_internal"); + } + return response; +} /** @@ -928,24 +758,6 @@ int oilsAuthComplete( osrfMethodContext* ctx ) { return 0; } - // If a workstation is defined, add the workstation info - if( workstation != NULL ) { - osrfLogDebug(OSRF_LOG_MARK, "Workstation is %s", workstation); - response = oilsAuthVerifyWorkstation( ctx, userObj, workstation ); - if(response) { - jsonObjectFree(userObj); - osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); - oilsEventFree(response); - return 0; - } - - } else { - // Otherwise, use the home org as the workstation org on the user - char* orgid = oilsFMGetString(userObj, "home_ou"); - oilsFMSetString(userObj, "ws_ou", orgid); - free(orgid); - } - char* freeable_uname = NULL; if(!uname) { uname = freeable_uname = oilsFMGetString( userObj, "usrname" ); diff --git a/Open-ILS/src/c-apps/oils_auth_internal.c b/Open-ILS/src/c-apps/oils_auth_internal.c new file mode 100644 index 0000000000..38c5166e06 --- /dev/null +++ b/Open-ILS/src/c-apps/oils_auth_internal.c @@ -0,0 +1,319 @@ +#include "opensrf/osrf_app_session.h" +#include "opensrf/osrf_application.h" +#include "opensrf/osrf_settings.h" +#include "opensrf/osrf_json.h" +#include "opensrf/log.h" +#include "openils/oils_utils.h" +#include "openils/oils_constants.h" +#include "openils/oils_event.h" + +#define OILS_AUTH_CACHE_PRFX "oils_auth_" +#define OILS_AUTH_COUNT_SFFX "_count" + +#define MODULENAME "open-ils.auth_internal" + +#define OILS_AUTH_OPAC "opac" +#define OILS_AUTH_STAFF "staff" +#define OILS_AUTH_TEMP "temp" +#define OILS_AUTH_PERSIST "persist" + +// Default time for extending a persistent session: ten minutes +#define DEFAULT_RESET_INTERVAL 10 * 60 + +int osrfAppInitialize(); +int osrfAppChildInit(); + +static long _oilsAuthOPACTimeout = 0; +static long _oilsAuthStaffTimeout = 0; +static long _oilsAuthOverrideTimeout = 0; +static long _oilsAuthPersistTimeout = 0; + +/** + @brief Initialize the application by registering functions for method calls. + @return Zero on success, 1 on error. +*/ +int osrfAppInitialize() { + + osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Internal Server..."); + + /* load and parse the IDL */ + /* return non-zero to indicate error */ + if (!oilsInitIDL(NULL)) return 1; + + osrfAppRegisterMethod( + MODULENAME, + "open-ils.auth_internal.session.create", + "oilsAutInternalCreateSession", + "Adds a user to the authentication cache to indicate " + "the user is authenticated", 1, 0 + ); + + return 0; +} + +/** + @brief Dummy placeholder for initializing a server drone. + + There is nothing to do, so do nothing. +*/ +int osrfAppChildInit() { + return 0; +} + + +/** + @brief Determine the login timeout. + @param userObj Pointer to an object describing the user. + @param type Pointer to one of four possible character strings identifying the login type. + @param orgloc Org unit to use for settings lookups (negative or zero means unspecified) + @return The length of the timeout, in seconds. + + The default timeout value comes from the configuration file, and + depends on the login type. + + The default may be overridden by a corresponding org unit setting. + The @a orgloc parameter says what org unit to use for the lookup. + If @a orgloc <= 0, or if the lookup for @a orgloc yields no result, + we look up the setting for the user's home org unit instead (except + that if it's the same as @a orgloc we don't bother repeating the + lookup). + + Whether defined in the config file or in an org unit setting, a + timeout value may be expressed as a raw number (i.e. all digits, + possibly with leading and/or trailing white space) or as an interval + string to be translated into seconds by PostgreSQL. +*/ +static long oilsAuthGetTimeout( + const jsonObject* userObj, const char* type, int orgloc) { + + if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */ + + jsonObject* value_obj; + + value_obj = osrf_settings_host_value_object( + "/apps/open-ils.auth/app_settings/default_timeout/opac" ); + _oilsAuthOPACTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); + jsonObjectFree(value_obj); + if( -1 == _oilsAuthOPACTimeout ) { + osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for OPAC logins" ); + _oilsAuthOPACTimeout = 0; + } + + value_obj = osrf_settings_host_value_object( + "/apps/open-ils.auth/app_settings/default_timeout/staff" ); + _oilsAuthStaffTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); + jsonObjectFree(value_obj); + if( -1 == _oilsAuthStaffTimeout ) { + osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for staff logins" ); + _oilsAuthStaffTimeout = 0; + } + + value_obj = osrf_settings_host_value_object( + "/apps/open-ils.auth/app_settings/default_timeout/temp" ); + _oilsAuthOverrideTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); + jsonObjectFree(value_obj); + if( -1 == _oilsAuthOverrideTimeout ) { + osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for temp logins" ); + _oilsAuthOverrideTimeout = 0; + } + + value_obj = osrf_settings_host_value_object( + "/apps/open-ils.auth/app_settings/default_timeout/persist" ); + _oilsAuthPersistTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj )); + jsonObjectFree(value_obj); + if( -1 == _oilsAuthPersistTimeout ) { + osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for persist logins" ); + _oilsAuthPersistTimeout = 0; + } + + osrfLogInfo(OSRF_LOG_MARK, "Set default auth timeouts: " + "opac => %ld : staff => %ld : temp => %ld : persist => %ld", + _oilsAuthOPACTimeout, _oilsAuthStaffTimeout, + _oilsAuthOverrideTimeout, _oilsAuthPersistTimeout ); + } + + int home_ou = (int) jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" )); + if(orgloc < 1) + orgloc = home_ou; + + char* setting = NULL; + long default_timeout = 0; + + if( !strcmp( type, OILS_AUTH_OPAC )) { + setting = OILS_ORG_SETTING_OPAC_TIMEOUT; + default_timeout = _oilsAuthOPACTimeout; + } else if( !strcmp( type, OILS_AUTH_STAFF )) { + setting = OILS_ORG_SETTING_STAFF_TIMEOUT; + default_timeout = _oilsAuthStaffTimeout; + } else if( !strcmp( type, OILS_AUTH_TEMP )) { + setting = OILS_ORG_SETTING_TEMP_TIMEOUT; + default_timeout = _oilsAuthOverrideTimeout; + } else if( !strcmp( type, OILS_AUTH_PERSIST )) { + setting = OILS_ORG_SETTING_PERSIST_TIMEOUT; + default_timeout = _oilsAuthPersistTimeout; + } + + // Get the org unit setting, if there is one. + char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting ); + if(!timeout) { + if( orgloc != home_ou ) { + osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, " + "trying home_ou %d", orgloc, home_ou ); + timeout = oilsUtilsFetchOrgSetting( home_ou, setting ); + } + } + + if(!timeout) + return default_timeout; // No override from org unit setting + + // Translate the org unit setting to a number + long t; + if( !*timeout ) { + osrfLogWarning( OSRF_LOG_MARK, + "Timeout org unit setting is an empty string for %s login; using default", + timeout, type ); + t = default_timeout; + } else { + // Treat timeout string as an interval, and convert it to seconds + t = oilsUtilsIntervalToSeconds( timeout ); + if( -1 == t ) { + // Unable to convert; possibly an invalid interval string + osrfLogError( OSRF_LOG_MARK, + "Unable to convert timeout interval \"%s\" for %s login; using default", + timeout, type ); + t = default_timeout; + } + } + + free(timeout); + return t; +} + +/** + * Verify workstation exists and stuff it into the user object to be cached + */ +static oilsEvent* oilsAuthVerifyWorkstation( + const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) { + + jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws); + + if(!workstation || workstation->type == JSON_NULL) { + jsonObjectFree(workstation); + return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND"); + } + + long wsid = oilsFMGetObjectId(workstation); + LONG_TO_STRING(wsid); + char* orgid = oilsFMGetString(workstation, "owning_lib"); + oilsFMSetString(userObj, "wsid", LONGSTR); + oilsFMSetString(userObj, "ws_ou", orgid); + free(orgid); + jsonObjectFree(workstation); + return NULL; +} + + +/** + @brief Implement the session create method + @param ctx The method context. + @return -1 upon error; zero if successful, and if a STATUS message has + been sent to the client to indicate completion; a positive integer if + successful but no such STATUS message has been sent. + + Method parameters: + - a hash with some combination of the following elements: + - "user_id" -- actor.usr (au) ID for the user to cache. + - "org_unit" -- actor.org_unit (aou) ID representing the physical + location / context used for timeout, etc. settings. + - "login_type" -- login type (opac, staff, temp, persist) + - "workstation" -- workstation name + +*/ +int oilsAutInternalCreateSession(osrfMethodContext* ctx) { + OSRF_METHOD_VERIFY_CONTEXT(ctx); + + const jsonObject* args = jsonObjectGetIndex(ctx->params, 0); + + const char* user_id = jsonObjectGetString(jsonObjectGetKeyConst(args, "user_id")); + const char* org_unit = jsonObjectGetString(jsonObjectGetKeyConst(args, "org_unit")); + const char* login_type = jsonObjectGetString(jsonObjectGetKeyConst(args, "login_type")); + const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation")); + + if ( !(user_id && login_type && org_unit) ) { + return osrfAppRequestRespondException( ctx->session, ctx->request, + "Missing parameters for method: %s", ctx->method->name ); + } + + oilsEvent* response = NULL; + + // fetch the user object + jsonObject* idParam = jsonNewNumberStringObject(user_id); + jsonObject* userObj = oilsUtilsCStoreReq( + "open-ils.cstore.direct.actor.user.retrieve", idParam); + jsonObjectFree(idParam); + + if (!userObj) { + return osrfAppRequestRespondException(ctx->session, + ctx->request, "No user found with ID %s", user_id); + } + + // If a workstation is defined, add the workstation info + if (workstation) { + response = oilsAuthVerifyWorkstation(ctx, userObj, workstation); + if (response) { + jsonObjectFree(userObj); + osrfAppRespondComplete(ctx, oilsEventToJSON(response)); + oilsEventFree(response); + return 0; + } + + } else { + // Otherwise, use the home org as the workstation org on the user + char* orgid = oilsFMGetString(userObj, "home_ou"); + oilsFMSetString(userObj, "ws_ou", orgid); + free(orgid); + } + + // determine the auth/cache timeout + long timeout = oilsAuthGetTimeout(userObj, login_type, atoi(org_unit)); + + char* string = va_list_to_string("%d.%ld.%ld", + (long) getpid(), time(NULL), oilsFMGetObjectId(userObj)); + char* authToken = md5sum(string); + char* authKey = va_list_to_string( + "%s%s", OILS_AUTH_CACHE_PRFX, authToken); + + oilsFMSetString(userObj, "passwd", ""); + jsonObject* cacheObj = jsonParseFmt("{\"authtime\": %ld}", timeout); + jsonObjectSetKey(cacheObj, "userobj", jsonObjectClone(userObj)); + + if( !strcmp(login_type, OILS_AUTH_PERSIST)) { + // Add entries for endtime and reset_interval, so that we can gracefully + // extend the session a bit if the user is active toward the end of the + // timeout originally specified. + time_t endtime = time( NULL ) + timeout; + jsonObjectSetKey(cacheObj, "endtime", + jsonNewNumberObject( (double) endtime )); + + // Reset interval is hard-coded for now, but if we ever want to make it + // configurable, this is the place to do it: + jsonObjectSetKey(cacheObj, "reset_interval", + jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL)); + } + + osrfCachePutObject(authKey, cacheObj, (time_t) timeout); + jsonObjectFree(cacheObj); + jsonObject* payload = jsonParseFmt( + "{\"authtoken\": \"%s\", \"authtime\": %ld}", authToken, timeout); + + response = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload); + free(string); free(authToken); free(authKey); + jsonObjectFree(payload); + + jsonObjectFree(userObj); + osrfAppRespondComplete(ctx, oilsEventToJSON(response)); + oilsEventFree(response); + + return 0; +} +