]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_auth.c
now comparing login type without regard to string case
[Evergreen.git] / Open-ILS / src / c-apps / oils_auth.c
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"
9
10 #define OILS_AUTH_CACHE_PRFX "oils_auth_"
11
12 #define MODULENAME "open-ils.auth"
13
14 int osrfAppInitialize();
15 int osrfAppChildInit();
16
17 int __oilsAuthOPACTimeout = 0;
18 int __oilsAuthStaffTimeout = 0;
19
20
21 int osrfAppInitialize() {
22
23         osrfAppRegisterMethod( 
24                 MODULENAME, 
25                 "open-ils.auth.authenticate.init", 
26                 "oilsAuthInit", 
27                 "Start the authentication process and returns the intermediate authentication seed"
28                 " PARAMS( username )", 1, 0 );
29
30         osrfAppRegisterMethod( 
31                 MODULENAME, 
32                 "open-ils.auth.authenticate.complete", 
33                 "oilsAuthComplete", 
34                 "Completes the authentication process.  Returns an object like so: "
35                 "{authtoken : <token>, authtime:<time>}, where authtoken is the login "
36                 "tokena and authtime is the number of seconds the session will be active"
37                 "PARAMS(username, md5sum( seed + password ), type, org_id ) "
38                 "type can be one of 'opac' or 'staff' and it defaults to 'staff' "
39                 "org_id is the location at which the login should be considered "
40                 "active for login timeout purposes"     , 2, 0 );
41
42         osrfAppRegisterMethod( 
43                 MODULENAME, 
44                 "open-ils.auth.session.retrieve", 
45                 "oilsAuthSessionRetrieve", 
46                 "Pass in the auth token and an optional second parameter which tells the "
47                 "method to reset the session timeout for the user"
48                 "Returns the user object (password blanked) for the given login session "
49                 "PARAMS( authToken, [reset?] )", 1, 0 );
50
51         osrfAppRegisterMethod( 
52                 MODULENAME, 
53                 "open-ils.auth.session.delete", 
54                 "oilsAuthSessionDelete", 
55                 "Destroys the given login session "
56                 "PARAMS( authToken )",  1, 0 );
57
58         osrfAppRegisterMethod(
59                 MODULENAME,
60                 "open-ils.auth.session.reset_timeout",
61                 "oilsAuthResetTimeout",
62                 "Resets the login timeout for the given session "
63                 "Returns an ILS Event with payload = session_timeout of session "
64                 "is found, otherwise returns the NO_SESSION event"
65                 "PARAMS( authToken )", 1, 0 );
66
67         return 0;
68 }
69
70 int osrfAppChildInit() {
71         return 0;
72 }
73
74 int oilsAuthInit( osrfMethodContext* ctx ) {
75         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
76
77         jsonObject* resp;
78         char* username = NULL;
79         char* seed = NULL;
80         char* md5seed = NULL;
81         char* key = NULL;
82
83         if( (username = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 0))) ) {
84
85                 seed = va_list_to_string( "%d.%d.%s", time(NULL), getpid(), username );
86                 key = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, username );
87
88                 md5seed = md5sum(seed);
89                 osrfCachePutString( key, md5seed, 30 );
90
91                 osrfLogDebug( "oilsAuthInit(): has seed %s and key %s", md5seed, key );
92
93                 resp = jsonNewObject(md5seed);  
94                 osrfAppRespondComplete( ctx, resp );
95
96                 jsonObjectFree(resp);
97                 free(seed);
98                 free(md5seed);
99                 free(key);
100                 return 0;
101         }
102
103         return -1;
104 }
105
106 /** Verifies that the user has permission to login with the 
107  * given type.  If the permission fails, an oilsEvent is returned
108  * to the caller.
109  * @return -1 if the permission check failed, 0 if ther permission
110  * is granted
111  */
112 int oilsAuthCheckLoginPerm( 
113                 osrfMethodContext* ctx, jsonObject* userObj, char* type ) {
114
115         if(!(userObj && type)) return -1;
116         oilsEvent* perm = NULL;
117
118         if(!strcasecmp(type, "opac")) {
119                 char* permissions[] = { "OPAC_LOGIN" };
120                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
121
122         } else if(!strcasecmp(type, "staff")) {
123                 char* permissions[] = { "STAFF_LOGIN" };
124                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
125         }
126
127         if(perm) {
128                 osrfAppRespondComplete( ctx, oilsEventToJSON(perm) ); 
129                 oilsEventFree(perm);
130                 return -1;
131         }
132
133         return 0;
134 }
135
136 /**
137  * Returns 1 if the password provided matches the user's real password
138  * Returns 0 otherwise
139  * Returns -1 on error
140  */
141 int oilsAuthVerifyPassword( 
142                 osrfMethodContext* ctx, jsonObject* userObj, char* uname, char* password ) {
143
144         int ret = 0;
145         char* realPassword = oilsFMGetString( userObj, "passwd" ); /**/
146         char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); /**/
147
148         if(!seed) {
149                 return osrfAppRequestRespondException( ctx->session,
150                         ctx->request, "No authentication seed found. "
151                         "open-ils.auth.authenticate.init must be called first");
152         }
153
154         osrfLogDebug( "oilsAuth retrieved seed from cache: %s", seed );
155         char* maskedPw = md5sum( "%s%s", seed, realPassword );
156         if(!maskedPw) return -1;
157         osrfLogDebug( "oilsAuth generated masked password %s. "
158                         "Testing against provided password %s", maskedPw, password );
159
160         if( !strcmp( maskedPw, password ) ) ret = 1;
161
162         free(realPassword);
163         free(seed);
164         free(maskedPw);
165
166         return ret;
167 }
168
169 /**
170  * Calculates the login timeout
171  * 1. If orgloc is 1 or greater and has a timeout specified as an 
172  * org unit setting, it is used
173  * 2. If orgloc is not valid, we check the org unit auth timeout 
174  * setting for the home org unit of the user logging in
175  * 3. If that setting is not defined, we use the configured defaults
176  */
177 double oilsAuthGetTimeout( jsonObject* userObj, char* type, double orgloc ) {
178
179         if(!__oilsAuthOPACTimeout) { /* Load the default timeouts */
180
181                 __oilsAuthOPACTimeout = 
182                         jsonObjectGetNumber( 
183                                 osrf_settings_host_value_object( 
184                                         "/apps/open-ils.auth/app_settings/default_timeout/opac"));
185
186                 __oilsAuthStaffTimeout = 
187                         jsonObjectGetNumber( 
188                                 osrf_settings_host_value_object( 
189                                         "/apps/open-ils.auth/app_settings/default_timeout/staff" ));
190
191                 osrfLogInfo("Set default auth timetouts: opac => %d : staff => %d",
192                                 __oilsAuthOPACTimeout, __oilsAuthStaffTimeout );
193         }
194
195         char* setting = NULL;
196
197         double home_ou = jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ) );
198         if(orgloc < 1) orgloc = (int) home_ou;
199
200         if(!strcmp(type, "opac")) 
201                 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
202         else if(!strcmp(type, "staff")) 
203                 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
204
205         char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
206
207         if(!timeout) {
208                 if( orgloc != home_ou ) {
209                         osrfLogDebug("Auth timeout not defined for org %d, "
210                                                                 "trying home_ou %d", orgloc, home_ou );
211                         timeout = oilsUtilsFetchOrgSetting( (int) home_ou, setting );
212                 }
213                 if(!timeout) {
214                         if(!strcmp(type, "staff")) return __oilsAuthStaffTimeout;
215                         return __oilsAuthOPACTimeout;
216                 }
217         }
218         double t = atof(timeout);
219         free(timeout);
220         return t ;
221 }
222
223 /* Adds the authentication token to the user cache.  The timeout for the 
224  * auth token is based on the type of login as well as (if type=='opac') 
225  * the org location id.
226  * Returns the event that should be returned to the user.  
227  * Event must be freed
228  */
229 oilsEvent* oilsAuthHandleLoginOK( 
230                 jsonObject* userObj, char* uname, char* type, double orgloc ) { 
231                 
232         oilsEvent* response;
233         osrfLogActivity( "User %s successfully logged in", uname );
234
235         double timeout = oilsAuthGetTimeout( userObj, type, orgloc );
236         osrfLogDebug("Auth session timeout for %s: %lf", uname, timeout );
237
238         char* string = va_list_to_string( 
239                         "%d.%d.%s", getpid(), time(NULL), uname ); 
240         char* authToken = md5sum(string); 
241         char* authKey = va_list_to_string( 
242                         "%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
243
244         oilsFMSetString( userObj, "passwd", "" );
245         jsonObject* cacheObj = jsonParseString("{\"authtime\": %lf}", timeout);
246         jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));
247
248         //osrfCachePutObject( authKey, userObj, timeout ); 
249         osrfCachePutObject( authKey, cacheObj, timeout ); 
250         jsonObjectFree(cacheObj);
251         osrfLogInternal("oilsAuthComplete(): Placed user object into cache");
252         jsonObject* payload = jsonParseString(
253                 "{ \"authtoken\": \"%s\", \"authtime\": %lf }", authToken, timeout );
254
255         response = oilsNewEvent2( OILS_EVENT_SUCCESS, payload );
256         free(string); free(authToken); free(authKey);
257         jsonObjectFree(payload);
258         return response;
259 }
260
261
262
263 int oilsAuthComplete( osrfMethodContext* ctx ) {
264         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
265
266         char* uname             = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 0));
267         char* password = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 1));
268         char* type              = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 2));
269         double orgloc   = jsonObjectGetNumber(jsonObjectGetIndex(ctx->params, 3));
270
271         if(!type) type = "staff";
272
273         if( !(uname && password) ) {
274                 return osrfAppRequestRespondException( ctx->session, ctx->request, 
275                         "username and password required for method: %s", ctx->method->name );
276         }
277
278         oilsEvent* response = NULL;
279         jsonObject* userObj = oilsUtilsFetchUserByUsername( uname ); 
280         
281         if(!userObj) { 
282                 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
283                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
284                 oilsEventFree(response);
285                 return 0;
286         }
287
288         /* check to see if the user is allowed to login */
289         if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
290                 jsonObjectFree(userObj);
291                 return 0;
292         }
293
294         int passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
295         if( passOK < 0 ) return passOK;
296
297         if( passOK ) {
298                 response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc );
299
300         } else {
301                 response = oilsNewEvent( OILS_EVENT_AUTH_FAILED );
302                 osrfLogInfo( "Login failed for for %s", uname );
303         }
304
305         jsonObjectFree(userObj);
306         osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
307         oilsEventFree(response);
308
309         return 0;
310 }
311
312
313
314 int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
315         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
316
317         char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
318         jsonObject* resp = NULL;
319
320         if( authToken ) {
321                 osrfLogDebug("Removing auth session: %s", authToken );
322                 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
323                 osrfCacheRemove(key);
324                 resp = jsonNewObject(authToken); /**/
325                 free(key);
326         }
327
328         osrfAppRespondComplete( ctx, resp );
329         jsonObjectFree(resp);
330         return 0;
331 }
332
333 /** Resets the auth login timeout
334  * @return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
335  */
336 oilsEvent*  _oilsAuthResetTimeout( char* authToken ) {
337         if(!authToken) return NULL;
338
339         oilsEvent* evt = NULL;
340         double timeout;
341
342         osrfLogDebug("Resetting auth timeout for session %s", authToken);
343         char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
344         jsonObject* cacheObj = osrfCacheGetObject( key ); 
345
346         if(!cacheObj) {
347                 evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
348
349         } else {
350
351                 timeout = jsonObjectGetNumber( jsonObjectGetKey( cacheObj, "authtime"));
352                 osrfCacheSetExpire( timeout, key );
353                 jsonObject* payload = jsonNewNumberObject(timeout);
354                 evt = oilsNewEvent2(OILS_EVENT_SUCCESS, payload);
355                 jsonObjectFree(payload);
356                 jsonObjectFree(cacheObj);
357         }
358
359         free(key);
360         return evt;
361 }
362
363 int oilsAuthResetTimeout( osrfMethodContext* ctx ) {
364         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
365         char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
366         oilsEvent* evt = _oilsAuthResetTimeout(authToken);
367         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
368         oilsEventFree(evt);
369         return 0;
370 }
371
372
373 int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
374         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
375
376         char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
377         char* reset = jsonObjectToSimpleString( jsonObjectGetIndex(ctx->params, 1));
378         jsonObject* cacheObj = NULL;
379
380         if( authToken ){
381
382                 if(reset) {
383                         oilsEvent* evt = _oilsAuthResetTimeout(authToken);
384                         if( evt && strcmp(evt->event,OILS_EVENT_SUCCESS) ) {
385                                 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
386                                 oilsEventFree(evt);
387                         }
388                         free(reset);
389                 }
390
391                 osrfLogDebug("Retrieving auth session: %s", authToken);
392                 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
393                 cacheObj = osrfCacheGetObject( key ); 
394                 if(cacheObj) {
395                         osrfAppRespondComplete( ctx, jsonObjectGetKey( cacheObj, "userobj"));
396                         jsonObjectFree(cacheObj);
397                 } else {
398                         oilsEvent* evt = oilsNewEvent(OILS_EVENT_NO_SESSION);
399                         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) ); /* should be event.. */
400                         oilsEventFree(evt);
401                 }
402                 free(key);
403         }
404
405         return 0;
406 }
407
408
409