1 #include "opensrf/osrf_app_session.h"
2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
10 #define OILS_AUTH_CACHE_PRFX "oils_auth_"
12 #define MODULENAME "open-ils.auth"
14 #define OILS_AUTH_OPAC "opac"
15 #define OILS_AUTH_STAFF "staff"
16 #define OILS_AUTH_TEMP "temp"
18 int osrfAppInitialize();
19 int osrfAppChildInit();
21 int __oilsAuthOPACTimeout = 0;
22 int __oilsAuthStaffTimeout = 0;
23 int __oilsAuthOverrideTimeout = 0;
26 int osrfAppInitialize() {
28 osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Server...");
30 osrfAppRegisterMethod(
32 "open-ils.auth.authenticate.init",
34 "Start the authentication process and returns the intermediate authentication seed"
35 " PARAMS( username )", 1, 0 );
37 osrfAppRegisterMethod(
39 "open-ils.auth.authenticate.complete",
41 "Completes the authentication process. Returns an object like so: "
42 "{authtoken : <token>, authtime:<time>}, where authtoken is the login "
43 "tokena and authtime is the number of seconds the session will be active"
44 "PARAMS(username, md5sum( seed + password ), type, org_id ) "
45 "type can be one of 'opac','staff', or 'temp' and it defaults to 'staff' "
46 "org_id is the location at which the login should be considered "
47 "active for login timeout purposes" , 1, 0 );
49 osrfAppRegisterMethod(
51 "open-ils.auth.session.retrieve",
52 "oilsAuthSessionRetrieve",
53 "Pass in the auth token and this retrieves the user object. The auth "
54 "timeout is reset when this call is made "
55 "Returns the user object (password blanked) for the given login session "
56 "PARAMS( authToken )", 1, 0 );
58 osrfAppRegisterMethod(
60 "open-ils.auth.session.delete",
61 "oilsAuthSessionDelete",
62 "Destroys the given login session "
63 "PARAMS( authToken )", 1, 0 );
65 osrfAppRegisterMethod(
67 "open-ils.auth.session.reset_timeout",
68 "oilsAuthResetTimeout",
69 "Resets the login timeout for the given session "
70 "Returns an ILS Event with payload = session_timeout of session "
71 "is found, otherwise returns the NO_SESSION event"
72 "PARAMS( authToken )", 1, 0 );
77 int osrfAppChildInit() {
81 int oilsAuthInit( osrfMethodContext* ctx ) {
82 OSRF_METHOD_VERIFY_CONTEXT(ctx);
86 char* username = NULL;
91 if( (username = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0))) ) {
93 seed = va_list_to_string( "%d.%d.%s", time(NULL), getpid(), username );
94 key = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, username );
96 md5seed = md5sum(seed);
97 osrfCachePutString( key, md5seed, 30 );
99 osrfLogDebug( OSRF_LOG_MARK, "oilsAuthInit(): has seed %s and key %s", md5seed, key );
101 resp = jsonNewObject(md5seed);
102 osrfAppRespondComplete( ctx, resp );
104 jsonObjectFree(resp);
115 /** Verifies that the user has permission to login with the
116 * given type. If the permission fails, an oilsEvent is returned
118 * @return -1 if the permission check failed, 0 if ther permission
121 int oilsAuthCheckLoginPerm(
122 osrfMethodContext* ctx, jsonObject* userObj, char* type ) {
124 if(!(userObj && type)) return -1;
125 oilsEvent* perm = NULL;
127 if(!strcasecmp(type, OILS_AUTH_OPAC)) {
128 char* permissions[] = { "OPAC_LOGIN" };
129 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
131 } else if(!strcasecmp(type, OILS_AUTH_STAFF)) {
132 char* permissions[] = { "STAFF_LOGIN" };
133 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
135 } else if(!strcasecmp(type, OILS_AUTH_TEMP)) {
136 char* permissions[] = { "STAFF_LOGIN" };
137 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
141 osrfAppRespondComplete( ctx, oilsEventToJSON(perm) );
150 * Returns 1 if the password provided matches the user's real password
151 * Returns 0 otherwise
152 * Returns -1 on error
154 int oilsAuthVerifyPassword(
155 osrfMethodContext* ctx, jsonObject* userObj, char* uname, char* password ) {
158 char* realPassword = oilsFMGetString( userObj, "passwd" ); /**/
159 char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); /**/
162 return osrfAppRequestRespondException( ctx->session,
163 ctx->request, "No authentication seed found. "
164 "open-ils.auth.authenticate.init must be called first");
167 osrfLogDebug(OSRF_LOG_MARK, "oilsAuth retrieved seed from cache: %s", seed );
168 char* maskedPw = md5sum( "%s%s", seed, realPassword );
169 if(!maskedPw) return -1;
170 osrfLogDebug(OSRF_LOG_MARK, "oilsAuth generated masked password %s. "
171 "Testing against provided password %s", maskedPw, password );
173 if( !strcmp( maskedPw, password ) ) ret = 1;
183 * Calculates the login timeout
184 * 1. If orgloc is 1 or greater and has a timeout specified as an
185 * org unit setting, it is used
186 * 2. If orgloc is not valid, we check the org unit auth timeout
187 * setting for the home org unit of the user logging in
188 * 3. If that setting is not defined, we use the configured defaults
190 double oilsAuthGetTimeout( jsonObject* userObj, char* type, double orgloc ) {
192 if(!__oilsAuthOPACTimeout) { /* Load the default timeouts */
194 __oilsAuthOPACTimeout =
196 osrf_settings_host_value_object(
197 "/apps/open-ils.auth/app_settings/default_timeout/opac"));
199 __oilsAuthStaffTimeout =
201 osrf_settings_host_value_object(
202 "/apps/open-ils.auth/app_settings/default_timeout/staff" ));
204 __oilsAuthOverrideTimeout =
206 osrf_settings_host_value_object(
207 "/apps/open-ils.auth/app_settings/default_timeout/temp" ));
210 osrfLogInfo(OSRF_LOG_MARK, "Set default auth timetouts: opac => %d : staff => %d : temp => %d",
211 __oilsAuthOPACTimeout, __oilsAuthStaffTimeout, __oilsAuthOverrideTimeout );
214 char* setting = NULL;
216 double home_ou = jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ) );
217 if(orgloc < 1) orgloc = (int) home_ou;
219 if(!strcmp(type, OILS_AUTH_OPAC))
220 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
221 else if(!strcmp(type, OILS_AUTH_STAFF))
222 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
223 else if(!strcmp(type, OILS_AUTH_TEMP))
224 setting = OILS_ORG_SETTING_TEMP_TIMEOUT;
226 char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
229 if( orgloc != home_ou ) {
230 osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
231 "trying home_ou %d", orgloc, home_ou );
232 timeout = oilsUtilsFetchOrgSetting( (int) home_ou, setting );
235 if(!strcmp(type, OILS_AUTH_STAFF)) return __oilsAuthStaffTimeout;
236 if(!strcmp(type, OILS_AUTH_TEMP)) return __oilsAuthOverrideTimeout;
237 return __oilsAuthOPACTimeout;
241 double t = atof(timeout);
246 /* Adds the authentication token to the user cache. The timeout for the
247 * auth token is based on the type of login as well as (if type=='opac')
248 * the org location id.
249 * Returns the event that should be returned to the user.
250 * Event must be freed
252 oilsEvent* oilsAuthHandleLoginOK(
253 jsonObject* userObj, char* uname, char* type, double orgloc ) {
256 osrfLogActivity(OSRF_LOG_MARK, "User %s successfully logged in", uname );
259 char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou"));
260 if(wsorg) { /* if there is a workstation, use it for the timeout */
261 osrfLogDebug( OSRF_LOG_MARK,
262 "Auth session trying workstation id %d for auth timeout", atoi(wsorg));
263 timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) );
266 osrfLogDebug( OSRF_LOG_MARK,
267 "Auth session trying org from param [%d] for auth timeout", orgloc );
268 timeout = oilsAuthGetTimeout( userObj, type, orgloc );
270 osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %lf", uname, timeout );
272 char* string = va_list_to_string(
273 "%d.%d.%s", getpid(), time(NULL), uname );
274 char* authToken = md5sum(string);
275 char* authKey = va_list_to_string(
276 "%s%s", OILS_AUTH_CACHE_PRFX, authToken );
278 oilsFMSetString( userObj, "passwd", "" );
279 jsonObject* cacheObj = jsonParseString("{\"authtime\": %lf}", timeout);
280 jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));
282 osrfCachePutObject( authKey, cacheObj, timeout );
283 jsonObjectFree(cacheObj);
284 osrfLogInternal(OSRF_LOG_MARK, "oilsAuthComplete(): Placed user object into cache");
285 jsonObject* payload = jsonParseString(
286 "{ \"authtoken\": \"%s\", \"authtime\": %lf }", authToken, timeout );
288 response = oilsNewEvent2( OILS_EVENT_SUCCESS, payload );
289 free(string); free(authToken); free(authKey);
290 jsonObjectFree(payload);
295 oilsEvent* oilsAuthVerifyWorkstation(
296 osrfMethodContext* ctx, jsonObject* userObj, char* ws ) {
297 osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %s", ws);
298 jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws);
299 if(!workstation) return oilsNewEvent("WORKSTATION_NOT_FOUND");
300 long wsid = oilsFMGetObjectId(workstation);
301 LONG_TO_STRING(wsid);
302 char* orgid = oilsFMGetString(workstation, "owning_lib");
303 oilsFMSetString(userObj, "wsid", LONGSTR);
304 oilsFMSetString(userObj, "ws_ou", orgid);
311 int oilsAuthComplete( osrfMethodContext* ctx ) {
312 OSRF_METHOD_VERIFY_CONTEXT(ctx);
314 jsonObject* args = jsonObjectGetIndex(ctx->params, 0);
316 char* uname = jsonObjectGetString(jsonObjectGetKey(args, "username"));
317 char* password = jsonObjectGetString(jsonObjectGetKey(args, "password"));
318 char* type = jsonObjectGetString(jsonObjectGetKey(args, "type"));
319 double orgloc = jsonObjectGetNumber(jsonObjectGetKey(args, "org"));
320 char* workstation = jsonObjectGetString(jsonObjectGetKey(args, "workstation"));
321 char* barcode = jsonObjectToSimpleString(jsonObjectGetKey(args, "barcode"));
324 if(!type) type = OILS_AUTH_STAFF;
326 if( !( (uname || barcode) && password) ) {
328 return osrfAppRequestRespondException( ctx->session, ctx->request,
329 "username/barocode and password required for method: %s", ctx->method->name );
332 oilsEvent* response = NULL;
333 jsonObject* userObj = NULL;
335 if(uname) userObj = oilsUtilsFetchUserByUsername( uname );
336 else if(barcode) userObj = oilsUtilsFetchUserByBarcode( barcode );
339 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
340 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
341 oilsEventFree(response);
346 /* check to see if the user is allowed to login */
347 if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
348 jsonObjectFree(userObj);
355 if(uname) passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
357 passOK = oilsAuthVerifyPassword( ctx, userObj, barcode, password );
364 /* if a workstation is defined, flesh the user with the workstation info */
365 if( workstation != NULL ) {
366 osrfLogDebug(OSRF_LOG_MARK, "Workstation is %s", workstation);
367 response = oilsAuthVerifyWorkstation( ctx, userObj, workstation );
369 jsonObjectFree(userObj);
370 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
371 oilsEventFree(response);
377 /* otherwise, use the home org as the workstation org on the user */
378 char* orgid = oilsFMGetString(userObj, "home_ou");
379 oilsFMSetString(userObj, "ws_ou", orgid);
384 response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc );
387 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
388 osrfLogInfo(OSRF_LOG_MARK, "Login failed for for %s", uname );
391 jsonObjectFree(userObj);
392 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
393 oilsEventFree(response);
401 int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
402 OSRF_METHOD_VERIFY_CONTEXT(ctx);
404 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
405 jsonObject* resp = NULL;
408 osrfLogDebug(OSRF_LOG_MARK, "Removing auth session: %s", authToken );
409 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
410 osrfCacheRemove(key);
411 resp = jsonNewObject(authToken); /**/
415 osrfAppRespondComplete( ctx, resp );
416 jsonObjectFree(resp);
420 /** Resets the auth login timeout
421 * @return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
423 oilsEvent* _oilsAuthResetTimeout( char* authToken ) {
424 if(!authToken) return NULL;
426 oilsEvent* evt = NULL;
429 osrfLogDebug(OSRF_LOG_MARK, "Resetting auth timeout for session %s", authToken);
430 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
431 jsonObject* cacheObj = osrfCacheGetObject( key );
434 osrfLogError(OSRF_LOG_MARK, "No user in the cache exists with key %s", key);
435 evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
439 timeout = jsonObjectGetNumber( jsonObjectGetKey( cacheObj, "authtime"));
440 osrfCacheSetExpire( timeout, key );
441 jsonObject* payload = jsonNewNumberObject(timeout);
442 evt = oilsNewEvent2(OILS_EVENT_SUCCESS, payload);
443 jsonObjectFree(payload);
444 jsonObjectFree(cacheObj);
451 int oilsAuthResetTimeout( osrfMethodContext* ctx ) {
452 OSRF_METHOD_VERIFY_CONTEXT(ctx);
453 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
454 oilsEvent* evt = _oilsAuthResetTimeout(authToken);
455 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
461 int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
462 OSRF_METHOD_VERIFY_CONTEXT(ctx);
464 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
465 jsonObject* cacheObj = NULL;
466 oilsEvent* evt = NULL;
470 evt = _oilsAuthResetTimeout(authToken);
472 if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) {
473 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
478 osrfLogDebug(OSRF_LOG_MARK, "Retrieving auth session: %s", authToken);
479 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
480 cacheObj = osrfCacheGetObject( key );
482 osrfAppRespondComplete( ctx, jsonObjectGetKey( cacheObj, "userobj"));
483 jsonObjectFree(cacheObj);
485 oilsEvent* evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
486 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) ); /* should be event.. */
494 evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
495 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );