LP#1468422 auth-internal validate API
authorBill Erickson <berickxx@gmail.com>
Fri, 8 Jan 2016 20:06:14 +0000 (15:06 -0500)
committerBill Erickson <berickxx@gmail.com>
Fri, 26 Feb 2016 15:07:42 +0000 (10:07 -0500)
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 <berickxx@gmail.com>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Open-ILS/src/c-apps/oils_auth.c
Open-ILS/src/c-apps/oils_auth_internal.c

index b9998b5..5486f3e 100644 (file)
@@ -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  
         
index 38c5166..a6dba09 100644 (file)
@@ -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;
+}
+