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_OVERRIDE "override"
18 int osrfAppInitialize();
19 int osrfAppChildInit();
21 int __oilsAuthOPACTimeout = 0;
22 int __oilsAuthStaffTimeout = 0;
23 int __oilsAuthOverrideTimeout = 0;
26 int osrfAppInitialize() {
28 osrfAppRegisterMethod(
30 "open-ils.auth.authenticate.init",
32 "Start the authentication process and returns the intermediate authentication seed"
33 " PARAMS( username )", 1, 0 );
35 osrfAppRegisterMethod(
37 "open-ils.auth.authenticate.complete",
39 "Completes the authentication process. Returns an object like so: "
40 "{authtoken : <token>, authtime:<time>}, where authtoken is the login "
41 "tokena and authtime is the number of seconds the session will be active"
42 "PARAMS(username, md5sum( seed + password ), type, org_id ) "
43 "type can be one of 'opac','staff', or 'override' and it defaults to 'staff' "
44 "org_id is the location at which the login should be considered "
45 "active for login timeout purposes" , 2, 0 );
47 osrfAppRegisterMethod(
49 "open-ils.auth.session.retrieve",
50 "oilsAuthSessionRetrieve",
51 "Pass in the auth token and this retrieves the user object. The auth "
52 "timeout is reset when this call is made "
53 "Returns the user object (password blanked) for the given login session "
54 "PARAMS( authToken )", 1, 0 );
56 osrfAppRegisterMethod(
58 "open-ils.auth.session.delete",
59 "oilsAuthSessionDelete",
60 "Destroys the given login session "
61 "PARAMS( authToken )", 1, 0 );
63 osrfAppRegisterMethod(
65 "open-ils.auth.session.reset_timeout",
66 "oilsAuthResetTimeout",
67 "Resets the login timeout for the given session "
68 "Returns an ILS Event with payload = session_timeout of session "
69 "is found, otherwise returns the NO_SESSION event"
70 "PARAMS( authToken )", 1, 0 );
75 int osrfAppChildInit() {
79 int oilsAuthInit( osrfMethodContext* ctx ) {
80 OSRF_METHOD_VERIFY_CONTEXT(ctx);
83 char* username = NULL;
88 if( (username = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 0))) ) {
90 seed = va_list_to_string( "%d.%d.%s", time(NULL), getpid(), username );
91 key = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, username );
93 md5seed = md5sum(seed);
94 osrfCachePutString( key, md5seed, 30 );
96 osrfLogDebug( OSRF_LOG_MARK, "oilsAuthInit(): has seed %s and key %s", md5seed, key );
98 resp = jsonNewObject(md5seed);
99 osrfAppRespondComplete( ctx, resp );
101 jsonObjectFree(resp);
111 /** Verifies that the user has permission to login with the
112 * given type. If the permission fails, an oilsEvent is returned
114 * @return -1 if the permission check failed, 0 if ther permission
117 int oilsAuthCheckLoginPerm(
118 osrfMethodContext* ctx, jsonObject* userObj, char* type ) {
120 if(!(userObj && type)) return -1;
121 oilsEvent* perm = NULL;
123 if(!strcasecmp(type, OILS_AUTH_OPAC)) {
124 char* permissions[] = { "OPAC_LOGIN" };
125 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
127 } else if(!strcasecmp(type, OILS_AUTH_STAFF)) {
128 char* permissions[] = { "STAFF_LOGIN" };
129 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
131 } else if(!strcasecmp(type, OILS_AUTH_OVERRIDE)) {
132 char* permissions[] = { "STAFF_LOGIN" };
133 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
137 osrfAppRespondComplete( ctx, oilsEventToJSON(perm) );
146 * Returns 1 if the password provided matches the user's real password
147 * Returns 0 otherwise
148 * Returns -1 on error
150 int oilsAuthVerifyPassword(
151 osrfMethodContext* ctx, jsonObject* userObj, char* uname, char* password ) {
154 char* realPassword = oilsFMGetString( userObj, "passwd" ); /**/
155 char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); /**/
158 return osrfAppRequestRespondException( ctx->session,
159 ctx->request, "No authentication seed found. "
160 "open-ils.auth.authenticate.init must be called first");
163 osrfLogDebug(OSRF_LOG_MARK, "oilsAuth retrieved seed from cache: %s", seed );
164 char* maskedPw = md5sum( "%s%s", seed, realPassword );
165 if(!maskedPw) return -1;
166 osrfLogDebug(OSRF_LOG_MARK, "oilsAuth generated masked password %s. "
167 "Testing against provided password %s", maskedPw, password );
169 if( !strcmp( maskedPw, password ) ) ret = 1;
179 * Calculates the login timeout
180 * 1. If orgloc is 1 or greater and has a timeout specified as an
181 * org unit setting, it is used
182 * 2. If orgloc is not valid, we check the org unit auth timeout
183 * setting for the home org unit of the user logging in
184 * 3. If that setting is not defined, we use the configured defaults
186 double oilsAuthGetTimeout( jsonObject* userObj, char* type, double orgloc ) {
188 if(!__oilsAuthOPACTimeout) { /* Load the default timeouts */
190 __oilsAuthOPACTimeout =
192 osrf_settings_host_value_object(
193 "/apps/open-ils.auth/app_settings/default_timeout/opac"));
195 __oilsAuthStaffTimeout =
197 osrf_settings_host_value_object(
198 "/apps/open-ils.auth/app_settings/default_timeout/staff" ));
200 __oilsAuthOverrideTimeout =
202 osrf_settings_host_value_object(
203 "/apps/open-ils.auth/app_settings/default_timeout/override" ));
206 osrfLogInfo(OSRF_LOG_MARK, "Set default auth timetouts: opac => %d : staff => %d : override => %d",
207 __oilsAuthOPACTimeout, __oilsAuthStaffTimeout, __oilsAuthOverrideTimeout );
210 char* setting = NULL;
212 double home_ou = jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ) );
213 if(orgloc < 1) orgloc = (int) home_ou;
215 if(!strcmp(type, OILS_AUTH_OPAC))
216 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
217 else if(!strcmp(type, OILS_AUTH_STAFF))
218 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
219 else if(!strcmp(type, OILS_AUTH_OVERRIDE))
220 setting = OILS_ORG_SETTING_OVERRIDE_TIMEOUT;
222 char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
225 if( orgloc != home_ou ) {
226 osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
227 "trying home_ou %d", orgloc, home_ou );
228 timeout = oilsUtilsFetchOrgSetting( (int) home_ou, setting );
231 if(!strcmp(type, OILS_AUTH_STAFF)) return __oilsAuthStaffTimeout;
232 if(!strcmp(type, OILS_AUTH_OVERRIDE)) return __oilsAuthOverrideTimeout;
233 return __oilsAuthOPACTimeout;
237 double t = atof(timeout);
242 /* Adds the authentication token to the user cache. The timeout for the
243 * auth token is based on the type of login as well as (if type=='opac')
244 * the org location id.
245 * Returns the event that should be returned to the user.
246 * Event must be freed
248 oilsEvent* oilsAuthHandleLoginOK(
249 jsonObject* userObj, char* uname, char* type, double orgloc ) {
252 osrfLogActivity(OSRF_LOG_MARK, "User %s successfully logged in", uname );
255 char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou"));
256 if(wsorg) { /* if there is a workstation, use it for the timeout */
257 osrfLogDebug( OSRF_LOG_MARK,
258 "Auth session trying workstation id %d for auth timeout", atoi(wsorg));
259 timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) );
262 osrfLogDebug( OSRF_LOG_MARK,
263 "Auth session trying org from param [%d] for auth timeout", orgloc );
264 timeout = oilsAuthGetTimeout( userObj, type, orgloc );
266 osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %lf", uname, timeout );
268 char* string = va_list_to_string(
269 "%d.%d.%s", getpid(), time(NULL), uname );
270 char* authToken = md5sum(string);
271 char* authKey = va_list_to_string(
272 "%s%s", OILS_AUTH_CACHE_PRFX, authToken );
274 oilsFMSetString( userObj, "passwd", "" );
275 jsonObject* cacheObj = jsonParseString("{\"authtime\": %lf}", timeout);
276 jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));
278 osrfCachePutObject( authKey, cacheObj, timeout );
279 jsonObjectFree(cacheObj);
280 osrfLogInternal(OSRF_LOG_MARK, "oilsAuthComplete(): Placed user object into cache");
281 jsonObject* payload = jsonParseString(
282 "{ \"authtoken\": \"%s\", \"authtime\": %lf }", authToken, timeout );
284 response = oilsNewEvent2( OILS_EVENT_SUCCESS, payload );
285 free(string); free(authToken); free(authKey);
286 jsonObjectFree(payload);
291 oilsEvent* oilsAuthVerifyWorkstation( osrfMethodContext* ctx, jsonObject* userObj, double wsid ) {
292 osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %lf", wsid);
293 jsonObject* workstation = oilsUtilsFetchWorkstation(wsid);
294 if(!workstation) return oilsNewEvent("WORKSTATION_NOT_FOUND");
295 DOUBLE_TO_STRING(wsid);
296 char* orgid = oilsFMGetString(workstation, "owning_lib");
297 oilsFMSetString(userObj, "wsid", DOUBLESTR);
298 oilsFMSetString(userObj, "ws_ou", orgid);
305 int oilsAuthComplete( osrfMethodContext* ctx ) {
306 OSRF_METHOD_VERIFY_CONTEXT(ctx);
308 char* uname = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 0));
309 char* password = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 1));
310 char* type = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 2));
311 double orgloc = jsonObjectGetNumber(jsonObjectGetIndex(ctx->params, 3));
312 double wsid = jsonObjectGetNumber(jsonObjectGetIndex(ctx->params, 4));
314 if(!type) type = OILS_AUTH_STAFF;
316 if( !(uname && password) ) {
317 return osrfAppRequestRespondException( ctx->session, ctx->request,
318 "username and password required for method: %s", ctx->method->name );
321 oilsEvent* response = NULL;
322 jsonObject* userObj = oilsUtilsFetchUserByUsername( uname );
325 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
326 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
327 oilsEventFree(response);
331 /* check to see if the user is allowed to login */
332 if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
333 jsonObjectFree(userObj);
337 int passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
338 if( passOK < 0 ) return passOK;
340 if( wsid > 0 && (response = oilsAuthVerifyWorkstation( ctx, userObj, wsid )) ) {
341 jsonObjectFree(userObj);
342 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
343 oilsEventFree(response);
348 response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc );
351 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
352 osrfLogInfo(OSRF_LOG_MARK, "Login failed for for %s", uname );
355 jsonObjectFree(userObj);
356 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
357 oilsEventFree(response);
364 int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
365 OSRF_METHOD_VERIFY_CONTEXT(ctx);
367 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
368 jsonObject* resp = NULL;
371 osrfLogDebug(OSRF_LOG_MARK, "Removing auth session: %s", authToken );
372 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
373 osrfCacheRemove(key);
374 resp = jsonNewObject(authToken); /**/
378 osrfAppRespondComplete( ctx, resp );
379 jsonObjectFree(resp);
383 /** Resets the auth login timeout
384 * @return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
386 oilsEvent* _oilsAuthResetTimeout( char* authToken ) {
387 if(!authToken) return NULL;
389 oilsEvent* evt = NULL;
392 osrfLogDebug(OSRF_LOG_MARK, "Resetting auth timeout for session %s", authToken);
393 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
394 jsonObject* cacheObj = osrfCacheGetObject( key );
397 evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
401 timeout = jsonObjectGetNumber( jsonObjectGetKey( cacheObj, "authtime"));
402 osrfCacheSetExpire( timeout, key );
403 jsonObject* payload = jsonNewNumberObject(timeout);
404 evt = oilsNewEvent2(OILS_EVENT_SUCCESS, payload);
405 jsonObjectFree(payload);
406 jsonObjectFree(cacheObj);
413 int oilsAuthResetTimeout( osrfMethodContext* ctx ) {
414 OSRF_METHOD_VERIFY_CONTEXT(ctx);
415 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
416 oilsEvent* evt = _oilsAuthResetTimeout(authToken);
417 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
423 int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
424 OSRF_METHOD_VERIFY_CONTEXT(ctx);
426 char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
427 jsonObject* cacheObj = NULL;
432 oilsEvent* evt = _oilsAuthResetTimeout(authToken);
433 if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) {
434 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
439 osrfLogDebug(OSRF_LOG_MARK, "Retrieving auth session: %s", authToken);
440 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
441 cacheObj = osrfCacheGetObject( key );
443 osrfAppRespondComplete( ctx, jsonObjectGetKey( cacheObj, "userobj"));
444 jsonObjectFree(cacheObj);
446 oilsEvent* evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
447 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) ); /* should be event.. */