1 #include "opensrf/osrf_app_session.h"
2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_json.h"
5 #include "opensrf/log.h"
6 #include "openils/oils_utils.h"
7 #include "openils/oils_constants.h"
8 #include "openils/oils_event.h"
10 #define OILS_AUTH_CACHE_PRFX "oils_auth_"
11 #define OILS_AUTH_COUNT_SFFX "_count"
13 #define MODULENAME "open-ils.auth_internal"
15 #define OILS_AUTH_OPAC "opac"
16 #define OILS_AUTH_STAFF "staff"
17 #define OILS_AUTH_TEMP "temp"
18 #define OILS_AUTH_PERSIST "persist"
20 // Default time for extending a persistent session: ten minutes
21 #define DEFAULT_RESET_INTERVAL 10 * 60
23 int osrfAppInitialize();
24 int osrfAppChildInit();
26 static long _oilsAuthOPACTimeout = 0;
27 static long _oilsAuthStaffTimeout = 0;
28 static long _oilsAuthOverrideTimeout = 0;
29 static long _oilsAuthPersistTimeout = 0;
32 @brief Initialize the application by registering functions for method calls.
33 @return Zero on success, 1 on error.
35 int osrfAppInitialize() {
37 osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Internal Server...");
39 /* load and parse the IDL */
40 /* return non-zero to indicate error */
41 if (!oilsInitIDL(NULL)) return 1;
43 osrfAppRegisterMethod(
45 "open-ils.auth_internal.session.create",
46 "oilsAutInternalCreateSession",
47 "Adds a user to the authentication cache to indicate "
48 "the user is authenticated", 1, 0
55 @brief Dummy placeholder for initializing a server drone.
57 There is nothing to do, so do nothing.
59 int osrfAppChildInit() {
65 @brief Determine the login timeout.
66 @param userObj Pointer to an object describing the user.
67 @param type Pointer to one of four possible character strings identifying the login type.
68 @param orgloc Org unit to use for settings lookups (negative or zero means unspecified)
69 @return The length of the timeout, in seconds.
71 The default timeout value comes from the configuration file, and
72 depends on the login type.
74 The default may be overridden by a corresponding org unit setting.
75 The @a orgloc parameter says what org unit to use for the lookup.
76 If @a orgloc <= 0, or if the lookup for @a orgloc yields no result,
77 we look up the setting for the user's home org unit instead (except
78 that if it's the same as @a orgloc we don't bother repeating the
81 Whether defined in the config file or in an org unit setting, a
82 timeout value may be expressed as a raw number (i.e. all digits,
83 possibly with leading and/or trailing white space) or as an interval
84 string to be translated into seconds by PostgreSQL.
86 static long oilsAuthGetTimeout(
87 const jsonObject* userObj, const char* type, int orgloc) {
89 if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */
91 jsonObject* value_obj;
93 value_obj = osrf_settings_host_value_object(
94 "/apps/open-ils.auth/app_settings/default_timeout/opac" );
95 _oilsAuthOPACTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
96 jsonObjectFree(value_obj);
97 if( -1 == _oilsAuthOPACTimeout ) {
98 osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for OPAC logins" );
99 _oilsAuthOPACTimeout = 0;
102 value_obj = osrf_settings_host_value_object(
103 "/apps/open-ils.auth/app_settings/default_timeout/staff" );
104 _oilsAuthStaffTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
105 jsonObjectFree(value_obj);
106 if( -1 == _oilsAuthStaffTimeout ) {
107 osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for staff logins" );
108 _oilsAuthStaffTimeout = 0;
111 value_obj = osrf_settings_host_value_object(
112 "/apps/open-ils.auth/app_settings/default_timeout/temp" );
113 _oilsAuthOverrideTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
114 jsonObjectFree(value_obj);
115 if( -1 == _oilsAuthOverrideTimeout ) {
116 osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for temp logins" );
117 _oilsAuthOverrideTimeout = 0;
120 value_obj = osrf_settings_host_value_object(
121 "/apps/open-ils.auth/app_settings/default_timeout/persist" );
122 _oilsAuthPersistTimeout = oilsUtilsIntervalToSeconds( jsonObjectGetString( value_obj ));
123 jsonObjectFree(value_obj);
124 if( -1 == _oilsAuthPersistTimeout ) {
125 osrfLogWarning( OSRF_LOG_MARK, "Invalid default timeout for persist logins" );
126 _oilsAuthPersistTimeout = 0;
129 osrfLogInfo(OSRF_LOG_MARK, "Set default auth timeouts: "
130 "opac => %ld : staff => %ld : temp => %ld : persist => %ld",
131 _oilsAuthOPACTimeout, _oilsAuthStaffTimeout,
132 _oilsAuthOverrideTimeout, _oilsAuthPersistTimeout );
135 int home_ou = (int) jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ));
139 char* setting = NULL;
140 long default_timeout = 0;
142 if( !strcmp( type, OILS_AUTH_OPAC )) {
143 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
144 default_timeout = _oilsAuthOPACTimeout;
145 } else if( !strcmp( type, OILS_AUTH_STAFF )) {
146 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
147 default_timeout = _oilsAuthStaffTimeout;
148 } else if( !strcmp( type, OILS_AUTH_TEMP )) {
149 setting = OILS_ORG_SETTING_TEMP_TIMEOUT;
150 default_timeout = _oilsAuthOverrideTimeout;
151 } else if( !strcmp( type, OILS_AUTH_PERSIST )) {
152 setting = OILS_ORG_SETTING_PERSIST_TIMEOUT;
153 default_timeout = _oilsAuthPersistTimeout;
156 // Get the org unit setting, if there is one.
157 char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
159 if( orgloc != home_ou ) {
160 osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
161 "trying home_ou %d", orgloc, home_ou );
162 timeout = oilsUtilsFetchOrgSetting( home_ou, setting );
167 return default_timeout; // No override from org unit setting
169 // Translate the org unit setting to a number
172 osrfLogWarning( OSRF_LOG_MARK,
173 "Timeout org unit setting is an empty string for %s login; using default",
177 // Treat timeout string as an interval, and convert it to seconds
178 t = oilsUtilsIntervalToSeconds( timeout );
180 // Unable to convert; possibly an invalid interval string
181 osrfLogError( OSRF_LOG_MARK,
182 "Unable to convert timeout interval \"%s\" for %s login; using default",
193 * Verify workstation exists and stuff it into the user object to be cached
195 static oilsEvent* oilsAuthVerifyWorkstation(
196 const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) {
198 jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws);
200 if(!workstation || workstation->type == JSON_NULL) {
201 jsonObjectFree(workstation);
202 return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND");
205 long wsid = oilsFMGetObjectId(workstation);
206 LONG_TO_STRING(wsid);
207 char* orgid = oilsFMGetString(workstation, "owning_lib");
208 oilsFMSetString(userObj, "wsid", LONGSTR);
209 oilsFMSetString(userObj, "ws_ou", orgid);
211 jsonObjectFree(workstation);
217 @brief Implement the session create method
218 @param ctx The method context.
219 @return -1 upon error; zero if successful, and if a STATUS message has
220 been sent to the client to indicate completion; a positive integer if
221 successful but no such STATUS message has been sent.
224 - a hash with some combination of the following elements:
225 - "user_id" -- actor.usr (au) ID for the user to cache.
226 - "org_unit" -- actor.org_unit (aou) ID representing the physical
227 location / context used for timeout, etc. settings.
228 - "login_type" -- login type (opac, staff, temp, persist)
229 - "workstation" -- workstation name
232 int oilsAutInternalCreateSession(osrfMethodContext* ctx) {
233 OSRF_METHOD_VERIFY_CONTEXT(ctx);
235 const jsonObject* args = jsonObjectGetIndex(ctx->params, 0);
237 const char* user_id = jsonObjectGetString(jsonObjectGetKeyConst(args, "user_id"));
238 const char* org_unit = jsonObjectGetString(jsonObjectGetKeyConst(args, "org_unit"));
239 const char* login_type = jsonObjectGetString(jsonObjectGetKeyConst(args, "login_type"));
240 const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
242 if ( !(user_id && login_type && org_unit) ) {
243 return osrfAppRequestRespondException( ctx->session, ctx->request,
244 "Missing parameters for method: %s", ctx->method->name );
247 oilsEvent* response = NULL;
249 // fetch the user object
250 jsonObject* idParam = jsonNewNumberStringObject(user_id);
251 jsonObject* userObj = oilsUtilsCStoreReq(
252 "open-ils.cstore.direct.actor.user.retrieve", idParam);
253 jsonObjectFree(idParam);
256 return osrfAppRequestRespondException(ctx->session,
257 ctx->request, "No user found with ID %s", user_id);
260 // If a workstation is defined, add the workstation info
262 response = oilsAuthVerifyWorkstation(ctx, userObj, workstation);
264 jsonObjectFree(userObj);
265 osrfAppRespondComplete(ctx, oilsEventToJSON(response));
266 oilsEventFree(response);
271 // Otherwise, use the home org as the workstation org on the user
272 char* orgid = oilsFMGetString(userObj, "home_ou");
273 oilsFMSetString(userObj, "ws_ou", orgid);
277 // determine the auth/cache timeout
278 long timeout = oilsAuthGetTimeout(userObj, login_type, atoi(org_unit));
280 char* string = va_list_to_string("%d.%ld.%ld",
281 (long) getpid(), time(NULL), oilsFMGetObjectId(userObj));
282 char* authToken = md5sum(string);
283 char* authKey = va_list_to_string(
284 "%s%s", OILS_AUTH_CACHE_PRFX, authToken);
286 oilsFMSetString(userObj, "passwd", "");
287 jsonObject* cacheObj = jsonParseFmt("{\"authtime\": %ld}", timeout);
288 jsonObjectSetKey(cacheObj, "userobj", jsonObjectClone(userObj));
290 if( !strcmp(login_type, OILS_AUTH_PERSIST)) {
291 // Add entries for endtime and reset_interval, so that we can gracefully
292 // extend the session a bit if the user is active toward the end of the
293 // timeout originally specified.
294 time_t endtime = time( NULL ) + timeout;
295 jsonObjectSetKey(cacheObj, "endtime",
296 jsonNewNumberObject( (double) endtime ));
298 // Reset interval is hard-coded for now, but if we ever want to make it
299 // configurable, this is the place to do it:
300 jsonObjectSetKey(cacheObj, "reset_interval",
301 jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL));
304 osrfCachePutObject(authKey, cacheObj, (time_t) timeout);
305 jsonObjectFree(cacheObj);
306 jsonObject* payload = jsonParseFmt(
307 "{\"authtoken\": \"%s\", \"authtime\": %ld}", authToken, timeout);
309 response = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload);
310 free(string); free(authToken); free(authKey);
311 jsonObjectFree(payload);
313 jsonObjectFree(userObj);
314 osrfAppRespondComplete(ctx, oilsEventToJSON(response));
315 oilsEventFree(response);