pcrud fleshing, a work in progress
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 25 Oct 2011 21:01:04 +0000 (17:01 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 28 Feb 2012 16:40:09 +0000 (11:40 -0500)
This allows fleshing in pcrud queries if
a) the objects you would flesh have a pcrud controller in the IDL,
b) you have permissions for all the objects you would flesh just as
   you would have to have them if you asked for them directly.

The checking of controller and of permissions as it currently stands
probably leads to a lot of repeated tests and repeated exchanges with
the database.  Caching and cleverness yet to come.  Perhaps
other improvements, too.

Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/c-apps/oils_sql.c

index 99918d5..b002e61 100644 (file)
@@ -119,7 +119,7 @@ static ClassInfo* add_joined_class( const char* alias, const char* classname );
 static void clear_query_stack( void );
 
 static const jsonObject* verifyUserPCRUD( osrfMethodContext* );
-static int verifyObjectPCRUD( osrfMethodContext*, const jsonObject*, const int );
+static int verifyObjectPCRUD( osrfMethodContext*, osrfHash*, const jsonObject*, const int );
 static const char* org_tree_root( osrfMethodContext* ctx );
 static jsonObject* single_hash( const char* key, const char* value );
 
@@ -1200,12 +1200,12 @@ int doSearch( osrfMethodContext* ctx ) {
                return -1;
        }
 
-       // Return each row to the client (except that some may be suppressed by PCRUD)
+       // Return each row to the client
        jsonObject* cur = 0;
        unsigned long res_idx = 0;
        while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, cur, obj->size ))
-                       continue;
+               // We used to discard based on perms here, but now that's
+               // inside doFieldmapperSearch()
                osrfAppRespond( ctx, cur );
        }
        jsonObjectFree( obj );
@@ -1301,11 +1301,10 @@ int doIdList( osrfMethodContext* ctx ) {
        jsonObject* cur;
        unsigned long res_idx = 0;
        while((cur = jsonObjectGetIndex( obj, res_idx++ ) )) {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, cur, obj->size ))
-                       continue;        // Suppress due to lack of permission
-               else
-                       osrfAppRespond( ctx,
-                               oilsFMGetObject( cur, osrfHashGet( class_meta, "primarykey" ) ) );
+               // We used to discard based on perms here, but now that's
+               // inside doFieldmapperSearch()
+               osrfAppRespond( ctx,
+                       oilsFMGetObject( cur, osrfHashGet( class_meta, "primarykey" ) ) );
        }
 
        jsonObjectFree( obj );
@@ -1350,7 +1349,7 @@ static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param )
        }
 
        if( enforce_pcrud )
-               return verifyObjectPCRUD( ctx, param, 1 );
+               return verifyObjectPCRUD( ctx, class, param, 1 );
        else
                return 1;
 }
@@ -1417,18 +1416,18 @@ static const jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
 /**
        @brief For PCRUD: Determine whether the current user may access the current row.
        @param ctx Pointer to the method context.
+       @param class Same as ctx->method->userData's item for key "class" except when called in recursive doFieldmapperSearch
        @param obj Pointer to the row being potentially accessed.
        @return 1 if access is permitted, or 0 if it isn't.
 
        The @a obj parameter points to a JSON_HASH of column values, keyed on column name.
 */
-static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, const int rs_size ) {
+static int verifyObjectPCRUD ( osrfMethodContext* ctx, osrfHash *class, const jsonObject* obj, const int rs_size ) {
 
        dbhandle = writehandle;
 
        // Figure out what class and method are involved
        osrfHash* method_metadata = (osrfHash*) ctx->method->userData;
-       osrfHash* class = osrfHashGet( method_metadata, "class" );
        const char* method_type = osrfHashGet( method_metadata, "methodtype" );
 
        // Set fetch to 1 in all cases except for inserts, meaning that for local or foreign
@@ -1480,7 +1479,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
        // Get a list of permissions from the permacrud entry.
        osrfStringArray* permission = osrfHashGet( pcrud, "permission" );
        if( permission->size == 0 ) {
-               osrfLogDebug( OSRF_LOG_MARK, "No permissions required for this action, passing through" );
+               osrfLogDebug(
+                       OSRF_LOG_MARK,
+                       "No permissions required for this action (class %s), passing through",
+                       osrfHashGet(class, "classname")
+               );
                return 1;
        }
 
@@ -1555,9 +1558,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
 
                if( fetch ) {
                        // Fetch the row so that we can look at the foreign key(s)
+                       osrfHashSet((osrfHash*) ctx->method->userData, "1", "inside_verify");
                        jsonObject* _tmp_params = single_hash( pkey, pkey_value );
                        jsonObject* _list = doFieldmapperSearch( ctx, class, _tmp_params, NULL, &err );
                        jsonObjectFree( _tmp_params );
+                       osrfHashSet((osrfHash*) ctx->method->userData, "0", "inside_verify");
 
                        param = jsonObjectExtractIndex( _list, 0 );
                        jsonObjectFree( _list );
@@ -1647,8 +1652,11 @@ static int verifyObjectPCRUD (  osrfMethodContext* ctx, const jsonObject* obj, c
 
                                        // Look up the row to which the foreign key points
                                        jsonObject* _tmp_params = single_hash( foreign_pkey, foreign_pkey_value );
+
+                                       osrfHashSet((osrfHash*) ctx->method->userData, "1", "inside_verify");
                                        jsonObject* _list = doFieldmapperSearch(
                                                ctx, osrfHashGet( oilsIDL(), class_name ), _tmp_params, NULL, &err );
+                                       osrfHashSet((osrfHash*) ctx->method->userData, "0", "inside_verify");
 
                                        jsonObject* _fparam = NULL;
                                        if( _list && JSON_ARRAY == _list->type && _list->size > 0 )
@@ -2359,7 +2367,7 @@ int doRetrieve( osrfMethodContext* ctx ) {
 
        if( enforce_pcrud ) {
                // no result, skip this entirely
-               if(NULL != obj && !verifyObjectPCRUD( ctx, obj, 1 )) {
+               if(NULL != obj && !verifyObjectPCRUD( ctx, class_def, obj, 1 )) {
                        jsonObjectFree( obj );
 
                        growing_buffer* msg = buffer_init( 128 );
@@ -5529,10 +5537,31 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
        dbhandle = writehandle;
 
        char* core_class = osrfHashGet( class_meta, "classname" );
+       osrfLogDebug( OSRF_LOG_MARK, "entering doFieldmapperSearch() with core_class %s", core_class );
+
        char* pkey = osrfHashGet( class_meta, "primarykey" );
 
+       char *inside_verify = osrfHashGet( (osrfHash*) ctx->method->userData, "inside_verify" );
+       int need_to_verify = 1;
+
+       if (inside_verify)
+               need_to_verify = !atoi(inside_verify);
+
        const jsonObject* _tmp;
 
+       // XXX This can be redundant with another instance of the same test that happens
+       // within the functions that call doFieldmapperSearch(), but we have it here to
+       // prevent any non-pcrud-controlled classes from being fleshed on.
+       //
+       // TODO To avoid redundancy, move this block to right before we recurse,
+       // and change the class we're checking to the one we're /about/ to search for,
+       // not the one we're currently searching for.
+       if (!osrfStringArrayContains(osrfHashGet(class_meta, "controller"), modulename)) {
+               osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
+                       modulename, core_class);
+               return jsonNewObjectType( JSON_ARRAY ); /* empty */
+       }
+
        char* sql = buildSELECT( where_hash, query_hash, class_meta, ctx );
        if( !sql ) {
                osrfLogDebug( OSRF_LOG_MARK, "Problem building query, returning NULL" );
@@ -5583,8 +5612,11 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
                                jsonObjectFree( row_obj );
                                free( pkey_val );
                        } else {
-                               osrfHashSet( dedup, pkey_val, pkey_val );
-                               jsonObjectPush( res_list, row_obj );
+                               if( !enforce_pcrud || !need_to_verify ||
+                                               verifyObjectPCRUD( ctx, class_meta, row_obj, 1 /* not at all sure about this 1 */ )) {
+                                       osrfHashSet( dedup, pkey_val, pkey_val );
+                                       jsonObjectPush( res_list, row_obj );
+                               }
                        }
                } while( dbi_result_next_row( result ));
                osrfHashFree( dedup );
@@ -5599,9 +5631,9 @@ static jsonObject* doFieldmapperSearch( osrfMethodContext* ctx, osrfHash* class_
        free( sql );
 
        // If we're asked to flesh, and there's anything to flesh, then flesh it
-       // (but not for PCRUD, lest the user to bypass permissions by fleshing
-       // something that he has no permission to look at).
-       if( res_list->size && query_hash && ! enforce_pcrud ) {
+       // (formerly we would skip fleshing if in pcrud mode, but now we support
+       // fleshing even in PCRUD).
+       if( res_list->size && query_hash ) {
                _tmp = jsonObjectGetKeyConst( query_hash, "flesh" );
                if( _tmp ) {
                        // Get the flesh depth
@@ -6136,7 +6168,7 @@ int doDelete( osrfMethodContext* ctx ) {
 
                id = oilsFMGetString( jsonObjectGetIndex(ctx->params, _obj_pos), pkey );
        } else {
-               if( enforce_pcrud && !verifyObjectPCRUD( ctx, NULL, 1 )) {
+               if( enforce_pcrud && !verifyObjectPCRUD( ctx, meta, NULL, 1 )) {
                        osrfAppRespondComplete( ctx, NULL );
                        return -1;
                }