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