From bd14977ee5087c59ba720dba809d2c3bbb71b049 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 8 Jan 2016 15:06:14 -0500 Subject: [PATCH] LP#1468422 auth-internal validate API Adds a new open-ils.auth_internal API open-ils.auth_internal.user.validate for checking whether a user should be allowed to login. It tests user existence, active=true, barred=false, deleted=false. If a barcode is also provided, it confirms the barcode exists and is active. Modifies open-ils.auth.authenticate.complete to use the new API instead of implementing the logic directly. Signed-off-by: Bill Erickson Signed-off-by: Dan Wells --- Open-ILS/src/c-apps/oils_auth.c | 156 ++++++++++--------------------- Open-ILS/src/c-apps/oils_auth_internal.c | 139 ++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 109 deletions(-) diff --git a/Open-ILS/src/c-apps/oils_auth.c b/Open-ILS/src/c-apps/oils_auth.c index b9998b52ff..5486f3e53a 100644 --- a/Open-ILS/src/c-apps/oils_auth.c +++ b/Open-ILS/src/c-apps/oils_auth.c @@ -417,44 +417,6 @@ int oilsAuthInit(osrfMethodContext* ctx) { } /** - Verifies that the user has permission to login with the - given type. If the permission fails, an oilsEvent is returned - to the caller. - @return -1 if the permission check failed, 0 if the permission - is granted -*/ -static int oilsAuthCheckLoginPerm( - osrfMethodContext* ctx, const jsonObject* userObj, const char* type ) { - - if(!(userObj && type)) return -1; - oilsEvent* perm = NULL; - - if(!strcasecmp(type, OILS_AUTH_OPAC)) { - char* permissions[] = { "OPAC_LOGIN" }; - perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 ); - - } else if(!strcasecmp(type, OILS_AUTH_STAFF)) { - char* permissions[] = { "STAFF_LOGIN" }; - perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 ); - - } else if(!strcasecmp(type, OILS_AUTH_TEMP)) { - char* permissions[] = { "STAFF_LOGIN" }; - perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 ); - } else if(!strcasecmp(type, OILS_AUTH_PERSIST)) { - char* permissions[] = { "PERSISTENT_LOGIN" }; - perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 ); - } - - if(perm) { - osrfAppRespondComplete( ctx, oilsEventToJSON(perm) ); - oilsEventFree(perm); - return -1; - } - - return 0; -} - -/** Returns 1 if the password provided matches the user's real password Returns 0 otherwise Returns -1 on error @@ -652,7 +614,6 @@ int oilsAuthComplete( osrfMethodContext* ctx ) { oilsEvent* response = NULL; // free jsonObject* userObj = NULL; // free int card_active = 1; // boolean; assume active until proven otherwise - int using_card = 0; // true if this is a barcode login char* cache_key = va_list_to_string( "%s%s%s", OILS_AUTH_CACHE_PRFX, identifier, nonce); @@ -674,47 +635,49 @@ int oilsAuthComplete( osrfMethodContext* ctx ) { "open-ils.cstore.direct.actor.user.retrieve", param); jsonObjectFree(param); - using_card = (jsonObjectGetKeyConst(cacheObj, "barcode") != NULL); + char* freeable_uname = NULL; + if (!uname) { + uname = freeable_uname = oilsFMGetString(userObj, "usrname"); + } - if (using_card) { - // see if the card is inactive + // See if the user is allowed to login. - jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", identifier); - jsonObject* card = oilsUtilsCStoreReq( - "open-ils.cstore.direct.actor.card.search", params); - jsonObjectFree(params); + jsonObject* params = jsonNewObject(NULL); + jsonObjectSetKey(params, "user_id", + jsonNewNumberObject(oilsFMGetObjectId(userObj))); + jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc)); + jsonObjectSetKey(params, "login_type", jsonNewObject(type)); - if (card) { - if (card->type != JSON_NULL) { - char* card_active_str = oilsFMGetString(card, "active"); - card_active = oilsUtilsIsDBTrue(card_active_str); - free(card_active_str); - } - jsonObjectFree(card); - } - } + jsonObject* authEvt = oilsUtilsQuickReq( // freed after password test + "open-ils.auth_internal", + "open-ils.auth_internal.user.validate", params); + jsonObjectFree(params); - int barred = 0, deleted = 0; - char *barred_str, *deleted_str; + if (!authEvt) { + // Something went seriously wrong. Get outta here before + // we start segfaulting. + jsonObjectFree(userObj); + if(freeable_uname) free(freeable_uname); + return -1; + } - if (userObj) { - barred_str = oilsFMGetString(userObj, "barred"); - barred = oilsUtilsIsDBTrue(barred_str); - free(barred_str); + const char* evtCode = + jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode")); - deleted_str = oilsFMGetString(userObj, "deleted"); - deleted = oilsUtilsIsDBTrue(deleted_str); - free(deleted_str); - } + // For security/privacy sake, only report that a patron is + // inactive if the correct password is provided below. + int user_inactive = !strcmp(evtCode, "PATRON_INACTIVE"); - if(!userObj || barred || deleted) { - response = oilsNewEvent( __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED ); - osrfLogInfo(OSRF_LOG_MARK, "failed login: username=%s, barcode=%s, workstation=%s", - uname, (barcode ? barcode : "(none)"), ws ); - osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); - oilsEventFree(response); - return 0; // No such user - } + if (strcmp(evtCode, "SUCCESS") && !user_inactive) { // validate failed + osrfLogInfo(OSRF_LOG_MARK, + "failed login: username=%s, barcode=%s, workstation=%s", + uname, (barcode ? barcode : "(none)"), ws); + osrfAppRespondComplete(ctx, authEvt); + jsonObjectFree(authEvt); + jsonObjectFree(userObj); + if(freeable_uname) free(freeable_uname); + return 0; // No such user + } // Such a user exists and isn't barred or deleted. // Now see if he or she has the right credentials. @@ -722,46 +685,23 @@ int oilsAuthComplete( osrfMethodContext* ctx ) { ctx, user_id, identifier, password, nonce); if( passOK < 0 ) { + jsonObjectFree(authEvt); jsonObjectFree(userObj); + if(freeable_uname) free(freeable_uname); return passOK; } - // See if the account is active - char* active = oilsFMGetString(userObj, "active"); - if( !oilsUtilsIsDBTrue(active) ) { - if( passOK ) - response = oilsNewEvent( OSRF_LOG_MARK, "PATRON_INACTIVE" ); - else - response = oilsNewEvent( __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED ); - - osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); - oilsEventFree(response); - jsonObjectFree(userObj); - free(active); - return 0; - } - free(active); - - if( !card_active ) { - osrfLogInfo( OSRF_LOG_MARK, "barcode %s is not active, returning event", barcode ); - response = oilsNewEvent( OSRF_LOG_MARK, "PATRON_CARD_INACTIVE" ); - osrfAppRespondComplete( ctx, oilsEventToJSON( response ) ); - oilsEventFree( response ); - jsonObjectFree( userObj ); - return 0; - } - - - // See if the user is even allowed to log in - if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) { - jsonObjectFree(userObj); - return 0; - } + if (passOK && user_inactive) { + // Patron is inactive but provided the correct password. + // Return the original PATRON_INACTIVE event. + osrfAppRespondComplete(ctx, authEvt); + jsonObjectFree(authEvt); + jsonObjectFree(userObj); + if(freeable_uname) free(freeable_uname); + return 0; + } - char* freeable_uname = NULL; - if(!uname) { - uname = freeable_uname = oilsFMGetString( userObj, "usrname" ); - } + jsonObjectFree(authEvt); // we're all done with this now. if( passOK ) { // login successful diff --git a/Open-ILS/src/c-apps/oils_auth_internal.c b/Open-ILS/src/c-apps/oils_auth_internal.c index 38c5166e06..a6dba099cd 100644 --- a/Open-ILS/src/c-apps/oils_auth_internal.c +++ b/Open-ILS/src/c-apps/oils_auth_internal.c @@ -20,6 +20,9 @@ // Default time for extending a persistent session: ten minutes #define DEFAULT_RESET_INTERVAL 10 * 60 +int safe_line = __LINE__; +#define OILS_LOG_MARK_SAFE __FILE__,safe_line + int osrfAppInitialize(); int osrfAppChildInit(); @@ -48,6 +51,15 @@ int osrfAppInitialize() { "the user is authenticated", 1, 0 ); + osrfAppRegisterMethod( + MODULENAME, + "open-ils.auth_internal.user.validate", + "oilsAutInternalValidate", + "Determines whether a user should be allowed to login. " + "Returns SUCCESS oilsEvent when the user is valid, otherwise " + "returns a non-SUCCESS oilsEvent object", 1, 0 + ); + return 0; } @@ -212,6 +224,33 @@ static oilsEvent* oilsAuthVerifyWorkstation( return NULL; } +/** + Verifies that the user has permission to login with the given type. + Caller is responsible for freeing returned oilsEvent. + @return oilsEvent* if the permission check failed, NULL otherwise. +*/ +static oilsEvent* oilsAuthCheckLoginPerm(osrfMethodContext* ctx, + int user_id, int org_id, const char* type ) { + + char* perms[1]; + + if (!strcasecmp(type, OILS_AUTH_OPAC)) { + perms[0] = "OPAC_LOGIN"; + + } else if (!strcasecmp(type, OILS_AUTH_STAFF)) { + perms[0] = "STAFF_LOGIN"; + + } else if (!strcasecmp(type, OILS_AUTH_TEMP)) { + perms[0] = "STAFF_LOGIN"; + + } else if (!strcasecmp(type, OILS_AUTH_PERSIST)) { + perms[0] = "PERSISTENT_LOGIN"; + } + + return oilsUtilsCheckPerms(user_id, org_id, perms, 1); +} + + /** @brief Implement the session create method @@ -244,7 +283,7 @@ int oilsAutInternalCreateSession(osrfMethodContext* ctx) { "Missing parameters for method: %s", ctx->method->name ); } - oilsEvent* response = NULL; + oilsEvent* response = NULL; // fetch the user object jsonObject* idParam = jsonNewNumberStringObject(user_id); @@ -317,3 +356,101 @@ int oilsAutInternalCreateSession(osrfMethodContext* ctx) { return 0; } + +int oilsAutInternalValidate(osrfMethodContext* ctx) { + OSRF_METHOD_VERIFY_CONTEXT(ctx); + + const jsonObject* args = jsonObjectGetIndex(ctx->params, 0); + + const char* user_id = jsonObjectGetString(jsonObjectGetKeyConst(args, "user_id")); + const char* barcode = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode")); + const char* org_unit = jsonObjectGetString(jsonObjectGetKeyConst(args, "org_unit")); + const char* login_type = jsonObjectGetString(jsonObjectGetKeyConst(args, "login_type")); + + if ( !(user_id && login_type && org_unit) ) { + return osrfAppRequestRespondException( ctx->session, ctx->request, + "Missing parameters for method: %s", ctx->method->name ); + } + + oilsEvent* response = NULL; + jsonObject *userObj = NULL, *params = NULL; + char* tmp_str = NULL; + int user_exists = 0, user_active = 0, + user_barred = 0, user_deleted = 0; + + // Confirm user exists, active=true, barred=false, deleted=false + params = jsonNewNumberStringObject(user_id); + userObj = oilsUtilsCStoreReq( + "open-ils.cstore.direct.actor.user.retrieve", params); + jsonObjectFree(params); + + if (userObj && userObj->type != JSON_NULL) { + user_exists = 1; + + tmp_str = oilsFMGetString(userObj, "active"); + user_active = oilsUtilsIsDBTrue(tmp_str); + free(tmp_str); + + tmp_str = oilsFMGetString(userObj, "barred"); + user_barred = oilsUtilsIsDBTrue(tmp_str); + free(tmp_str); + + tmp_str = oilsFMGetString(userObj, "deleted"); + user_deleted = oilsUtilsIsDBTrue(tmp_str); + free(tmp_str); + } + + if (!user_exists || user_barred || user_deleted) { + response = oilsNewEvent(OILS_LOG_MARK_SAFE, OILS_EVENT_AUTH_FAILED); + } + + if (!response && !user_active) { + // In some cases, it's useful for the caller to know if the + // patron was unable to login becuase the account is inactive. + // Return a specific event for this. + response = oilsNewEvent(OILS_LOG_MARK_SAFE, "PATRON_INACTIVE"); + } + + if (!response && barcode) { + // Caller provided a barcode. Ensure it exists and is active. + + int card_ok = 0; + params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode); + jsonObject* card = oilsUtilsCStoreReq( + "open-ils.cstore.direct.actor.card.search", params); + jsonObjectFree(params); + + if (card && card->type != JSON_NULL) { + tmp_str = oilsFMGetString(card, "active"); + card_ok = oilsUtilsIsDBTrue(tmp_str); + free(tmp_str); + } + + jsonObjectFree(card); // card=NULL OK here. + + if (!card_ok) { + response = oilsNewEvent( + OILS_LOG_MARK_SAFE, "PATRON_CARD_INACTIVE"); + } + } + + if (!response) { // Still OK + // Confirm user has permission to login w/ the requested type. + response = oilsAuthCheckLoginPerm( + ctx, atoi(user_id), atoi(org_unit), login_type); + } + + + if (!response) { + // No tests failed. Return SUCCESS. + response = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_SUCCESS); + } + + + jsonObjectFree(userObj); // userObj=NULL OK here. + osrfAppRespondComplete(ctx, oilsEventToJSON(response)); + oilsEventFree(response); + + return 0; +} + -- 2.11.0