Call set_audit_info and clear_audit_info DB funcs
authorThomas Berezansky <tsbere@mvlc.org>
Mon, 21 Nov 2011 21:51:50 +0000 (16:51 -0500)
committerMike Rylander <mrylander@gmail.com>
Tue, 20 Mar 2012 17:43:56 +0000 (13:43 -0400)
Set whenever we can (including automatically via pcrud)
Clear whenever we disconnect (hopefully) from a location we set from.

Signed-off-by: Thomas Berezansky <tsbere@mvlc.org>
Signed-off-by: Jason Stephenson <jstephenson@mvlc.org>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
17 files changed:
Open-ILS/include/openils/oils_sql.h
Open-ILS/src/c-apps/oils_cstore.c
Open-ILS/src/c-apps/oils_pcrud.c
Open-ILS/src/c-apps/oils_rstore.c
Open-ILS/src/c-apps/oils_sql.c
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/StatCat.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Survey.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Transit.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/storage.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/CStoreEditor.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/Editor.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/BadDebt.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/TemplateBatchBibUpdate.pm

index 6674d21..d581c4b 100644 (file)
@@ -56,6 +56,8 @@ int doIdList( osrfMethodContext* ctx );
 int is_identifier( const char* s);
 int is_good_operator( const char* op );
 
+int setAuditInfo( osrfMethodContext* ctx );
+
 #ifdef __cplusplus
 }
 #endif
index a2aa3fa..5014605 100644 (file)
@@ -69,6 +69,7 @@ void osrfAppChildExit( void ) {
        - savepoint.set
        - savepoint.release
        - savepoint.rollback
+       - set_audit_info
 
        For each non-virtual class, create up to eight class-specific methods:
 
@@ -165,6 +166,12 @@ int osrfAppInitialize( void ) {
        osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
                        "rollbackSavepoint", "", 1, 0 );
 
+       buffer_reset(method_name);
+       OSRF_BUFFER_ADD(method_name, modulename );
+       OSRF_BUFFER_ADD(method_name, ".set_audit_info");
+       osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
+                       "setAuditInfo", "", 3, 0 );
+
        static const char* global_method[] = {
                "create",
                "retrieve",
index b95bdae..18fadfc 100644 (file)
@@ -72,6 +72,7 @@ void osrfAppChildExit( void ) {
        - savepoint.set
        - savepoint.release
        - savepoint.rollback
+       - set_audit_info
 
        For each non-virtual class, create up to eight class-specific methods:
 
@@ -160,6 +161,12 @@ int osrfAppInitialize( void ) {
        osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
                        "rollbackSavepoint", "", 1, 0 );
 
+       buffer_reset(method_name);
+       OSRF_BUFFER_ADD(method_name, modulename );
+       OSRF_BUFFER_ADD(method_name, ".set_audit_info");
+       osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
+                       "setAuditInfo", "", 3, 0 );
+
        static const char* global_method[] = {
                "create",
                "retrieve",
index ae76a76..d503ce1 100644 (file)
@@ -69,6 +69,7 @@ void osrfAppChildExit( void ) {
        - savepoint.set
        - savepoint.release
        - savepoint.rollback
+       - set_audit_info
 
        For each non-virtual class, create up to eight class-specific methods:
 
@@ -164,6 +165,12 @@ int osrfAppInitialize( void ) {
        osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
                        "rollbackSavepoint", "", 1, 0 );
 
+       buffer_reset(method_name);
+       OSRF_BUFFER_ADD(method_name, modulename );
+       OSRF_BUFFER_ADD(method_name, ".set_audit_info");
+       osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
+                       "setAuditInfo", "", 3, 0 );
+
        static const char* global_method[] = {
                "create",
                "retrieve",
index 29d80d5..887a2df 100644 (file)
@@ -143,6 +143,8 @@ static int perm_at_threshold = 5;
 static int enforce_pcrud = 0;     // Boolean
 static char* modulename = NULL;
 
+int writeAuditInfo( osrfMethodContext* ctx, const char* user_id, const char* ws_id);
+
 /**
        @brief Connect to the database.
        @return A database connection if successful, or NULL if not.
@@ -473,6 +475,14 @@ void userDataFree( void* blob ) {
                                errnum, msg ? msg : "(No description available)" );
                };
        }
+       if( writehandle ) {
+               if( !dbi_conn_query( writehandle, "SELECT auditor.clear_audit_info();" ) ) {
+                       const char* msg;
+                       int errnum = dbi_conn_error( writehandle, &msg );
+                       osrfLogWarning( OSRF_LOG_MARK, "Unable to perform audit info clearing: %d %s",
+                               errnum, msg ? msg : "(No description available)" );
+               }
+       }
 
        osrfHashFree( hash );
 }
@@ -1408,6 +1418,10 @@ static const jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) {
                free( m );
                jsonObjectFree( user );
                user = NULL;
+       } else if( writeAuditInfo( ctx, oilsFMGetStringConst( user, "id" ), oilsFMGetStringConst( user, "wsid" ) ) ) {
+               // Failed to set audit information - But note that write_audit_info already set error information.
+               jsonObjectFree( user );
+               user = NULL;
        }
 
        setUserLogin( ctx, user );
@@ -7140,4 +7154,87 @@ static void clear_query_stack( void ) {
                pop_query_frame();
 }
 
+/**
+       @brief Implement the set_audit_info method.
+       @param ctx Pointer to the method context.
+       @return Zero if successful, or -1 if not.
+
+       Issue a SAVEPOINT to the database server.
+
+       Method parameters:
+       - authkey
+       - user id (int)
+       - workstation id (int)
+
+       If user id is not provided the authkey will be used.
+       For PCRUD the authkey is always used, even if a user is provided.
+*/
+int setAuditInfo( osrfMethodContext* ctx ) {
+       if(osrfMethodVerifyContext( ctx )) {
+               osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
+               return -1;
+       }
+
+       // Get the user id from the parameters
+       const char* user_id = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 1) );
+
+       if( enforce_pcrud || !user_id ) {
+               timeout_needs_resetting = 1;
+               const jsonObject* user = verifyUserPCRUD( ctx );
+               if( !user )
+                       return -1;
+               osrfAppRespondComplete( ctx, NULL );
+               return 0;
+       }
+
+       // Not PCRUD and have a user_id?
+       int result = writeAuditInfo( ctx, user_id, jsonObjectGetString( jsonObjectGetIndex(ctx->params, 2) ) );
+       osrfAppRespondComplete( ctx, NULL );
+       return result;
+}
+
+/**
+       @brief Save a audit info
+       @param ctx Pointer to the method context.
+       @param user_id User ID to write as a string
+       @param ws_id Workstation ID to write as a string
+*/
+int writeAuditInfo( osrfMethodContext* ctx, const char* user_id, const char* ws_id) {
+       if( ctx && ctx->session ) {
+               osrfAppSession* session = ctx->session;
+
+               osrfHash* cache = session->userData;
+
+               // If the session doesn't already have a hash, create one.  Make sure
+               // that the application session frees the hash when it terminates.
+               if( NULL == cache ) {
+                       session->userData = cache = osrfNewHash();
+                       osrfHashSetCallback( cache, &sessionDataFree );
+                       ctx->session->userDataFree = &userDataFree;
+               }
+
+               dbi_result result = dbi_conn_queryf( writehandle, "SELECT auditor.set_audit_info( %s, %s );", user_id, ws_id ? ws_id : "NULL" );
+               if( !result ) {
+                       osrfLogWarning( OSRF_LOG_MARK, "BAD RESULT" );
+                       const char* msg;
+                       int errnum = dbi_conn_error( writehandle, &msg );
+                       osrfLogError(
+                               OSRF_LOG_MARK,
+                               "%s: Error setting auditor information: %d %s",
+                               modulename,
+                               errnum,
+                               msg ? msg : "(No description available)"
+                       );
+                       osrfAppSessionStatus( ctx->session, OSRF_STATUS_INTERNALSERVERERROR,
+                               "osrfMethodException", ctx->request, "Error setting auditor info" );
+                       if( !oilsIsDBConnected( writehandle ))
+                               osrfAppSessionPanic( ctx->session );
+                       return -1;
+               } else {
+                       dbi_result_free( result );
+                       return 0;
+               }
+       }
+}
+
 /*@}*/
index 1599d30..4ba9ad5 100644 (file)
@@ -351,6 +351,7 @@ sub update_patron {
        $evt = check_group_perm($session, $user_obj, $patron);
        return $evt if $evt;
 
+       $apputils->set_audit_info($session, $user_session, $user_obj->id, $user_obj->wsid);
 
        # $new_patron is the patron in progress.  $patron is the original patron
        # passed in with the method.  new_patron will change as the components
@@ -1037,6 +1038,7 @@ sub set_user_work_ous {
        return $evt if $evt;
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($session, $ses, $requestor->id, $requestor->wsid);
 
        for my $map (@$maps) {
 
@@ -1077,6 +1079,7 @@ sub set_user_perms {
 
        my( $user_obj, $evt ) = $U->checkses($ses);
        return $evt if $evt;
+       $apputils->set_audit_info($session, $ses, $user_obj->id, $user_obj->wsid);
 
        my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
 
index e8369ea..07bd151 100644 (file)
@@ -45,6 +45,18 @@ sub start_db_session {
        return $session;
 }
 
+sub set_audit_info {
+       my $self = shift;
+       my $session = shift;
+       my $authtoken = shift;
+       my $user_id = shift;
+       my $ws_id = shift;
+       
+       my $audit_req = $session->request( "open-ils.storage.set_audit_info", $authtoken, $user_id, $ws_id );
+       my $audit_resp = $audit_req->recv();
+       $audit_req->finish();
+}
+
 my $PERM_QUERY = {
     select => {
         au => [ {
index 1058e8c..d400853 100644 (file)
@@ -586,6 +586,7 @@ sub create_grocery_bill {
 
     $transaction->clear_id;
     my $session = $apputils->start_db_session;
+    $apputils->set_audit_info($login, $staff->id, $staff->wsid);
     my $transid = $session->request(
         'open-ils.storage.direct.money.grocery.create', $transaction)->gather(1);
 
index 8ca8325..aadb8c7 100644 (file)
@@ -177,6 +177,7 @@ sub stat_cat_create {
 
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $newid = _create_stat_cat($session, $stat_cat, $method);
 
        if( ref($stat_cat->entries) ) {
@@ -296,6 +297,7 @@ sub update_stat_entry {
        return $evt if $evt;
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $req = $session->request($method, $entry); 
        my $status = $req->gather(1);
        $apputils->commit_db_session($session);
@@ -328,6 +330,7 @@ sub update_stat {
        return $evt if $evt;
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $req = $session->request($method, $cat); 
        my $status = $req->gather(1);
        $apputils->commit_db_session($session);
@@ -361,6 +364,7 @@ sub create_stat_entry {
 
        $entry->clear_id();
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $req = $session->request($method, $entry); 
        my $status = $req->gather(1);
        $apputils->commit_db_session($session);
@@ -416,6 +420,7 @@ sub create_stat_map {
        $map->clear_id();
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $req = $session->request($method, $map); 
        my $newid = $req->gather(1);
        warn "Created new stat cat map with id $newid\n";
@@ -466,6 +471,7 @@ sub update_stat_map {
 
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $req = $session->request($method, $map); 
        my $newid = $req->gather(1);
        warn "Updated new stat cat map with id $newid\n";
index 9f75be3..98f046f 100644 (file)
@@ -38,6 +38,7 @@ sub add_survey {
     return $evt if $evt;
 
        my $session = $apputils->start_db_session();
+       $apputils->set_audit_info($user_session, $user_obj->id, $user_obj->wsid);
        my $err = undef; my $id;
 
 
index ff4796e..e0900dc 100644 (file)
@@ -44,6 +44,7 @@ sub copy_transit_receive {
        ($copy, $evt) = $U->fetch_copy_by_barcode($params{barcode}) unless $copy;
        return $evt if $evt;
        my $session = $U->start_db_session();
+       $U->set_audit_info($authtoken, $requestor->id, $requestor->wsid);
        $evt = transit_receive( $self, $copy, $requestor, $session );
        $U->commit_db_session($session) if $U->event_equals($evt,'SUCCESS');
        return $evt;
@@ -133,6 +134,7 @@ sub copy_transit_create {
        my $source              = $requestor->home_ou;
        my $dest                        = $params{destination} || $copy->circ_lib;
        my $transit             = Fieldmapper::action::transit_copy->new;
+       $U->set_audit_info($authtoken, $requestor->id, $requestor->wsid);
 
        $logger->activity("User ". $requestor->id ." creating a ".
                " new copy transit for copy ".$copy->id." to org $dest");
index 8fd8515..95d9db4 100644 (file)
 #      }
 
        my $_xact_session;
+       my $_audit_session;
 
        sub current_xact_session {
                my $self = shift;
                return undef;
        }
 
+       sub current_audit_session {
+               my $self = shift;
+               if (defined($_audit_session)) {
+                       return $_audit_session;
+               }
+               return undef;
+       }
+
        sub current_xact_is_auto {
                my $self = shift;
                my $auto = shift;
                return $_xact_session;
        }
 
+       sub set_audit_session {
+               my $self = shift;
+               my $ses = shift;
+               if (!defined($ses)) {
+                       return undef;
+               }
+               $_audit_session = $ses;
+               return $_audit_session;
+       }
+
        sub unset_xact_session {
                my $self = shift;
                my $ses = $_xact_session;
                return $ses;
        }
 
+       sub unset_audit_session {
+               my $self = shift;
+               my $ses = $_audit_session;
+               undef $_audit_session;
+               return $ses;
+       }
+
 }
 
 1;
index d7623d0..989d84c 100644 (file)
                argc            => 1,
        );
 
+       sub pg_set_audit_info {
+               my $self = shift;
+               my $client = shift;
+               my $authtoken = shift;
+               my $user_id = shift;
+               my $ws_id = shift;
+
+               local $OpenILS::Application::Storage::WRITE = 1;
+
+               $log->debug("Setting auditor information", INFO);
+
+               if($pg->current_audit_session) {
+                       $log->debug("Already sent audit data.", INFO);
+                       return 1;
+               }
+
+               my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
+               
+               try {
+                       if(!$user_id) {
+                               my $ses = OpenSRF::AppSession->create('open-ils.auth');
+                               my $content = $ses->request('open-ils.auth.session.retrieve', $authtoken, 1)->gather(1);
+                               if(!$content or !$content->{userObj}) {
+                                       return 0;
+                               }
+                               $user_id = $content->{userObj}->id;
+                               $ws_id = $content->{userObj}->wsid;
+                       }
+                       $ws_id = 'NULL' unless $ws_id;
+                       $dbh->do("SELECT auditor.set_audit_info($user_id, $ws_id);");
+               } catch Error with {
+                       my $e = shift;
+                       $log->debug("Failed to set auditor information: ".$e, INFO);
+                       throw $e;
+               };
+
+               $pg->set_audit_session( $client->session );
+
+               my $death_cb = $client->session->register_callback(
+                       death => sub {
+                               __PACKAGE__->pg_clear_audit_info;
+                       }
+               );
+
+               $log->debug("Registered 'death' callback [$death_cb] for clearing audit information", DEBUG);
+
+               $client->session->session_data( death_cb_ai => $death_cb );
+
+               return 1;
+
+       }
+       __PACKAGE__->register_method(
+               method          => 'pg_set_audit_info',
+               api_name        => 'open-ils.storage.set_audit_info',
+               api_level       => 1,
+               argc            => 3,
+       );
+
+       sub pg_clear_audit_info {
+               my $self = shift;
+
+               try {
+                       my $dbh = OpenILS::Application::Storage::CDBI->db_Main;
+                       $log->debug("Clearing Audit Information", INFO);
+                       $dbh->do("SELECT auditor.clear_audit_info();");
+               } catch Error with {
+                       my $e = shift;
+                       $log->debug("Failed to clear audit information: ".$e, INFO);
+               };
+
+               $pg->current_audit_session->unregister_callback( death => 
+                       $pg->current_audit_session->session_data( 'death_cb_ai' )
+               ) if ($pg->current_audit_session);
+
+               $pg->unset_audit_session;
+       }
+
+
 
        sub copy_create_start {
                my $self = shift;
index 18e1775..d41e435 100644 (file)
@@ -237,6 +237,18 @@ sub xact_begin {
            my $stat = $self->request($self->app . '.transaction.begin');
            $self->log(E, "error starting database transaction") unless $stat;
         $self->{xact_id} = $stat;
+        if($self->authtoken) {
+            if(!$self->requestor) {
+                $self->checkauth;
+            }
+            my $user_id = undef;
+            my $ws_id = undef;
+            if($self->requestor) {
+                $user_id = $self->requestor->id;
+                $ws_id = $self->requestor->wsid;
+            }
+            $self->request($self->app . '.set_audit_info', $self->authtoken, $user_id, $ws_id);
+        }
     }
     $self->{xact} = 1;
     return $self->{xact_id};
index 801faff..e31e29d 100644 (file)
@@ -135,6 +135,18 @@ sub xact_start {
        $self->log(D, "starting new db session");
        my $stat = $self->request('open-ils.storage.transaction.begin');
        $self->log(E, "error starting database transaction") unless $stat;
+    if($self->authtoken) {
+        if(!$self->requestor) {
+            $self->checkauth;
+        }
+        my $user_id = undef;
+        my $ws_id = undef;
+        if($self->requestor) {
+            $user_id = $self->requestor->id;
+            $ws_id = $self->requestor->wsid;
+        }
+        $self->request('open-ils.storage.set_audit_info', $self->authtoken, $user_id, $ws_id);
+    }
        return $stat;
 }
 
index 72cce27..007623d 100644 (file)
@@ -89,6 +89,7 @@ sub handler {
 
     $cstore->connect();
     $cstore->request('open-ils.cstore.transaction.begin')->gather(1);
+    $cstore->request('open-ils.cstore.set_audit_info', $auth_ses, $user->id, $user->wsid)->gather(1);
 
     for my $xact ( @xacts ) {
         try {
index 56af440..f72ac17 100644 (file)
@@ -89,6 +89,7 @@ sub handler {
 
     my $e = OpenSRF::AppSession->connect('open-ils.cstore');
     $e->request('open-ils.cstore.transaction.begin')->gather(1);
+    $e->request('open-ils.cstore.set_audit_info', $authid, $usr->id, $usr->wsid)->gather(1);
 
     # still no records ...
     my $container = $cgi->param('containerid');