From aa2931cf5e8a69eb06141bb34b86031347d1a67b Mon Sep 17 00:00:00 2001 From: Mike Rylander Date: Thu, 24 Jul 2014 15:03:37 -0400 Subject: [PATCH] LP#1347774 Anonymous PCRUD mode Support for anonymous access to public (field_safe=true) IDL data via PCRUD without requiring an authtoken. To use, pass an authtoken of ANONYMOUS. Includes initial CStoreEditor plugin for anon pcrud. Signed-off-by: Mike Rylander Signed-off-by: Bill Erickson --- Open-ILS/src/c-apps/oils_sql.c | 99 ++++++++++++------- .../lib/OpenILS/Utils/CStoreEditor.pm | 63 ++++++++++-- 2 files changed, 119 insertions(+), 43 deletions(-) diff --git a/Open-ILS/src/c-apps/oils_sql.c b/Open-ILS/src/c-apps/oils_sql.c index 77cb12c44e..d29c6f2583 100644 --- a/Open-ILS/src/c-apps/oils_sql.c +++ b/Open-ILS/src/c-apps/oils_sql.c @@ -119,6 +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 const jsonObject* verifyUserPCRUDfull( osrfMethodContext*, int ); static int verifyObjectPCRUD( osrfMethodContext*, osrfHash*, const jsonObject*, int ); static const char* org_tree_root( osrfMethodContext* ctx ); static jsonObject* single_hash( const char* key, const char* value ); @@ -1406,48 +1407,62 @@ static int verifyObjectClass ( osrfMethodContext* ctx, const jsonObject* param ) server; otherwise NULL. */ static const jsonObject* verifyUserPCRUD( osrfMethodContext* ctx ) { + return verifyUserPCRUDfull( ctx, 0 ); +} + +static const jsonObject* verifyUserPCRUDfull( osrfMethodContext* ctx, int anon_ok ) { // Get the authkey (the first method parameter) const char* auth = jsonObjectGetString( jsonObjectGetIndex( ctx->params, 0 ) ); - // See if we have the same authkey, and a user object, - // locally cached from a previous call - const char* cached_authkey = getAuthkey( ctx ); - if( cached_authkey && !strcmp( cached_authkey, auth ) ) { - const jsonObject* cached_user = getUserLogin( ctx ); - if( cached_user ) - return cached_user; - } - - // We have no matching authentication data in the cache. Authenticate from scratch. - jsonObject* auth_object = jsonNewObject( auth ); - - // Fetch the user object from the authentication server - jsonObject* user = oilsUtilsQuickReq( "open-ils.auth", "open-ils.auth.session.retrieve", - auth_object ); - jsonObjectFree( auth_object ); - - if( !user->classname || strcmp(user->classname, "au" )) { + jsonObject* user = NULL; + + // If we are /not/ in anonymous mode + if( strcmp( "ANONYMOUS", auth ) ) { + // See if we have the same authkey, and a user object, + // locally cached from a previous call + const char* cached_authkey = getAuthkey( ctx ); + if( cached_authkey && !strcmp( cached_authkey, auth ) ) { + const jsonObject* cached_user = getUserLogin( ctx ); + if( cached_user ) + return cached_user; + } - growing_buffer* msg = buffer_init( 128 ); - buffer_fadd( - msg, - "%s: permacrud received a bad auth token: %s", - modulename, - auth - ); + // We have no matching authentication data in the cache. Authenticate from scratch. + jsonObject* auth_object = jsonNewObject( auth ); + + // Fetch the user object from the authentication server + user = oilsUtilsQuickReq( "open-ils.auth", "open-ils.auth.session.retrieve", auth_object ); + jsonObjectFree( auth_object ); + + if( !user->classname || strcmp(user->classname, "au" )) { + + growing_buffer* msg = buffer_init( 128 ); + buffer_fadd( + msg, + "%s: permacrud received a bad auth token: %s", + modulename, + auth + ); + + char* m = buffer_release( msg ); + osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", + ctx->request, m ); + + 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; + } - char* m = buffer_release( msg ); - osrfAppSessionStatus( ctx->session, OSRF_STATUS_UNAUTHORIZED, "osrfMethodException", - ctx->request, m ); - 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; + } else if ( anon_ok ) { // we /are/ (attempting to be) anonymous + user = jsonNewObjectType(JSON_ARRAY); + jsonObjectSetClass( user, "aou" ); + oilsFMSetString(user, "id", "-1"); } setUserLogin( ctx, user ); @@ -1503,6 +1518,12 @@ static int verifyObjectPCRUD ( osrfMethodContext* ctx, osrfHash *class, const js fetch = 1; // MUST go to the db for the object for update and delete } + // In retrieve or search ONLY we allow anon. Later perm checks will fail as they should, + // in the face of a fake user but required permissions. + int anon_ok = 0; + if( *method_type == 'r' ) + anon_ok = 1; + // Get the appropriate permacrud entry from the IDL, depending on method type osrfHash* pcrud = osrfHashGet( osrfHashGet( class, "permacrud" ), method_type ); if( !pcrud ) { @@ -1527,9 +1548,9 @@ static int verifyObjectPCRUD ( osrfMethodContext* ctx, osrfHash *class, const js } // Get the user id, and make sure the user is logged in - const jsonObject* user = verifyUserPCRUD( ctx ); + const jsonObject* user = verifyUserPCRUDfull( ctx, anon_ok ); if( !user ) - return 0; // Not logged in? No access. + return 0; // Not logged in or anon? No access. int userid = atoi( oilsFMGetStringConst( user, "id" ) ); @@ -1544,6 +1565,10 @@ static int verifyObjectPCRUD ( osrfMethodContext* ctx, osrfHash *class, const js return 1; } + // But, if there are perms and the user is anonymous ... FAIL + if ( -1 == userid ) + return 0; + // Build a list of org units that own the row. This is fairly convoluted because there // are several different ways that an org unit may own the row, as defined by the // permacrud entry. diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/CStoreEditor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/CStoreEditor.pm index 4ad858f76f..995461011c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/CStoreEditor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/CStoreEditor.pm @@ -48,6 +48,30 @@ use base qw/Exporter/; push @EXPORT_OK, ( 'new_editor', 'new_rstore_editor' ); %EXPORT_TAGS = ( funcs => [ qw/ new_editor new_rstore_editor / ] ); +our $personality = 'open-ils.cstore'; + +sub personality { + my( $self, $app ) = @_; + $personality = $app if $app; + init() if $app; # rewrite if we changed personalities + return $personality; +} + +sub import { + my $class = shift; + + my @super_args = (); + while ( my $a = shift ) { + if ($a eq 'personality') { + $class->personality( shift ); + } else { + push @super_args, $a; + } + } + + return $class->SUPER::import( @super_args ); +} + sub new_editor { return OpenILS::Utils::CStoreEditor->new(@_); } sub new_rstore_editor { @@ -90,7 +114,7 @@ sub DESTROY { sub app { my( $self, $app ) = @_; $self->{app} = $app if $app; - $self->{app} = 'open-ils.cstore' unless $self->{app}; + $self->{app} = $self->personality unless $self->{app}; return $self->{app}; } @@ -189,6 +213,7 @@ sub died { sub authtoken { my( $self, $auth ) = @_; $self->{authtoken} = $auth if $auth; + return 'ANONYMOUS' if ($self->personality eq 'open-ils.pcrud' and !defined($self->{authtoken})); return $self->{authtoken}; } @@ -418,7 +443,7 @@ sub request { if( ($self->{xact} or $always_xact) and $self->session->state != OpenSRF::AppSession::CONNECTED() ) { #$logger->error("CStoreEditor lost it's connection!!"); - throw OpenSRF::EX::ERROR ("CStore connection timed out - transaction cannot continue"); + throw OpenSRF::EX::ERROR ($self->app." connection timed out - transaction cannot continue"); } @@ -735,7 +760,12 @@ sub runmethod { } my @arg = ( ref($arg) eq 'ARRAY' ) ? @$arg : ($arg); - my $method = $self->app.".direct.$type.$action"; + my $method = ''; + if ($self->personality eq 'open-ils.pcrud') { + $method = $self->app.".$action.$type"; + } else { + $method = $self->app.".direct.$type.$action"; + } if( $action eq 'search' ) { $method .= '.atomic'; @@ -784,7 +814,8 @@ sub runmethod { $self->log_activity($method, $type, $action, $arg); } - if($$options{checkperm}) { + # only check perms this way in non-pcrud mode + if($self->personality ne 'open-ils.pcrud' and $$options{checkperm}) { my $a = ($action eq 'search') ? 'retrieve' : $action; my $e = $self->_checkperm($type, $a, $$options{permorg}); if($e) { @@ -796,6 +827,9 @@ sub runmethod { my $obj; my $err = ''; + # In pcrud mode, sub authtoken returns 'ANONYMOUS' if one is not yet set + unshift(@$arg, $self->authtoken) if ($self->personality eq 'open-ils.pcrud'); + try { $obj = $self->request($method, @arg); } catch Error with { $err = shift; }; @@ -840,7 +874,7 @@ sub runmethod { } if( $action eq 'search' ) { - $self->log(I, "$type.$action : returned ".scalar(@$obj). " result(s)"); + $self->log(I, "$method: returned ".scalar(@$obj). " result(s)"); $self->event(_mk_not_found($type, $arg)) unless @$obj; } @@ -884,7 +918,12 @@ sub init { my $map = $Fieldmapper::fieldmap; for my $object (keys %$map) { my $obj = __fm2meth($object, '_'); - my $type = __fm2meth($object, '.'); + my $type; + if (__PACKAGE__->personality eq 'open-ils.pcrud') { + $type = $object->json_hint; + } else { + $type = __fm2meth($object, '.'); + } foreach my $command (qw/ update retrieve search create delete batch_retrieve retrieve_all /) { eval "sub ${command}_$obj {return shift()->runmethod('$command', '$type', \@_);}\n"; } @@ -896,6 +935,18 @@ init(); # Add very many subs to this namespace sub json_query { my( $self, $arg, $options ) = @_; + + if( $self->personality eq 'open-ils.pcrud' ) { + $self->event( + OpenILS::Event->new( + 'JSON_QUERY_NOT_ALLOWED', + attempted_query => $arg, + debug => "json_query is not allowed when using the open-ils.pcrud personality of CStoreEditor" + ) + ); + return undef; + } + $options ||= {}; my @arg = ( ref($arg) eq 'ARRAY' ) ? @$arg : ($arg); my $method = $self->app.'.json_query.atomic'; -- 2.43.2