3 package OpenILS::Application::PermaCrud;
4 use OpenILS::Application;
5 use base qw/OpenILS::Application/;
7 use Unicode::Normalize;
8 use OpenSRF::EX qw/:try/;
10 use OpenSRF::AppSession;
11 use OpenSRF::Utils::SettingsClient;
12 use OpenSRF::Utils::Logger qw/:level/;
14 use OpenILS::Utils::Fieldmapper;
15 use OpenSRF::Utils::JSON;
17 use OpenILS::Utils::CStoreEditor qw/:funcs/;
20 use XML::LibXML::XPathContext;
24 our %namespace_map = (
25 oils_persist=> {ns => 'http://open-ils.org/spec/opensrf/IDL/persistence/v1'},
26 oils_obj => {ns => 'http://open-ils.org/spec/opensrf/IDL/objects/v1'},
27 idl => {ns => 'http://opensrf.org/spec/IDL/base/v1'},
28 reporter => {ns => 'http://open-ils.org/spec/opensrf/IDL/reporter/v1'},
29 perm => {ns => 'http://open-ils.org/spec/opensrf/IDL/permacrud/v1'},
33 my $log = 'OpenSRF::Utils::Logger';
35 my $parser = XML::LibXML->new();
36 my $xslt = XML::LibXSLT->new();
38 my $xpc = XML::LibXML::XPathContext->new();
39 $xpc->registerNs($_, $namespace_map{$_}{ns}) for ( keys %namespace_map );
45 my $conf = OpenSRF::Utils::SettingsClient->new;
46 my $idl_file = $conf->config_value( 'IDL' );
48 $idl = $parser->parse_file( $idl_file );
50 $log->debug( 'IDL XML file loaded' );
57 sub CRUD_action_object_permcheck {
63 my $e = shift || new_editor(authtoken => $auth, xact => 1);
64 return $e->event unless $e->checkauth;
66 if (ref($obj) && $obj->json_hint ne $self->{class_hint}) {
67 throw OpenSRF::DomainObject::oilsException->new(
69 status => "Class missmatch: $self->{class_hint} method called with " . $obj->json_hint,
75 ($class_node) = $xpc->findnodes( "//idl:class[\@id='$self->{class_hint}']", $idl->documentElement );
78 $log->error("Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]");
79 throw OpenSRF::DomainObject::oilsException->new(
81 status => "Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]"
86 $log->error("Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]");
87 throw OpenSRF::DomainObject::oilsException->new(
89 status => "Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]"
95 ($action_node) = $xpc->findnodes( "perm:permacrud/perm:actions/perm:$self->{action}", $class_node );
98 $log->error("Error finding action node: $error [perm:permacrud/perm:actions/perm:$self->{action}]");
99 throw OpenSRF::DomainObject::oilsException->new(
101 status => "Error finding action node: $error [perm:permacrud/perm:actions/perm:$self->{action}]"
106 $log->error("Error finding action node: $error [perm:permacrud/perm:actions/perm:$self->{action}]");
107 throw OpenSRF::DomainObject::oilsException->new(
109 status => "Error finding action node: $error [perm:permacrud/perm:actions/perm:$self->{action}]"
113 my $all_perms = $action_node->getAttribute( 'all_perms' );
115 my $fm_class = $xpc->findvalue( '@oils_obj:fieldmapper', $class_node );
117 my $retrieve_method = 'retrieve_' . $fm_class;
118 $retrieve_method =~ s/::/_/go;
119 $obj = $e->$retrieve_method( $obj );
122 (my $o_type = $fm_class) =~ s/::/./go;
124 my $perm_field_value = $action_node->getAttribute('permission');
126 if ($perm_field_value) {
127 my @perms = split '\|', $perm_field_value;
130 if ($action_node->getAttribute('global_required')) {
131 push @context_ous, $e->search_actor_org_unit( { parent_ou => undef } )->[0]->id;
134 my $context_field_value = $action_node->getAttribute('context_field');
136 if ($context_field_value) {
137 push @context_ous, $obj->$_ for ( split '\|', $context_field_value );
139 for my $context_node ( $xpc->findnodes( "perm:context", $action_node ) ) {
140 my $context_field = $context_node->getAttribute('field');
141 my $link_field = $context_node->getAttribute('link');
145 my ($link_node) = $xpc->findnodes( "idl:links/idl:link[\@field='$link_field']", $class_node );
146 my $link_class_hint = $link_node->getAttribute('class');
147 my $remote_field = $link_node->getAttribute('key');
149 my ($remote_class_node) = $xpc->findnodes( "//idl:class[\@id='$link_class_hint']", $idl->documentElement );
150 my $search_method = 'search_' . $xpc->findvalue( '@oils_obj:fieldmapper', $remote_class_node );
151 $search_method =~ s/::/_/go;
153 for my $remote_object ( @{$e->$search_method( { $remote_field => $obj->$link_field } )} ) {
154 push @context_ous, $remote_object->$context_field;
157 push @context_ous, $obj->$_ for ( split '\|', $context_field );
164 for my $perm (@perms) {
166 for my $c_ou (@context_ous) {
167 if ($e->allowed($perm => $c_ou => $obj)) {
173 $pok++ if ($e->allowed($perm => undef => $obj));
177 if ((lc($all_perms) eq 'true' && @perms != $pok) or !$pok) {
178 return OpenILS::Event->new('PERM_FAILURE',
179 ilsperm => "", # XXX add logic to report which perm failed
181 payload => "Perm failure -- action: $self->{action}, object type: $self->{json_hint}",
186 if ($self->{action} eq 'retrieve') {
191 my $val = $e->session->request("open-ils.cstore.direct.$o_type.$self->{action}" => $obj )->gather(1);
197 sub search_permacrud {
204 delete $args[1]{flesh};
205 delete $args[1]{flesh_fields};
208 my $e = new_editor(authtoken => $auth, xact => 1);
209 return $e->event unless $e->checkauth;
213 ($class_node) = $xpc->findnodes( "//idl:class[\@id='$self->{class_hint}']", $idl->documentElement );
216 $log->error("Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]");
217 throw OpenSRF::DomainObject::oilsException->new(
219 status => "Error finding class node: $error [//idl:class[\@id='$self->{class_hint}']]"
223 my $search_method = 'search_' . $xpc->findvalue( '@oils_obj:fieldmapper', $class_node );
224 $search_method =~ s/::/_/go;
226 $log->debug("Calling CStoreEditor search method: $search_method");
228 my $obj_list = $e->$search_method( \@args );
230 my $retriever = $self->method_lookup( $self->{retriever} );
231 for my $o ( @$obj_list ) {
233 ($o) = $retriever->run( $auth, $o, $e );
234 $client->respond( $o ) if ($o);
241 sub generate_methods {
243 for my $class_node ( $xpc->findnodes( '//idl:class[perm:permacrud]', $idl->documentElement ) ) {
244 my $hint = $class_node->getAttribute('id');
245 $log->debug("permacrud class_node $hint");
247 for my $action_node ( $xpc->findnodes( "perm:permacrud/perm:actions/perm:*", $class_node ) ) {
248 (my $method = $action_node->localname) =~ s/^.+:(.+)$/$1/o;
249 $log->internal("permacrud method = $method");
251 __PACKAGE__->register_method(
252 method => 'CRUD_action_object_permcheck',
253 api_name => 'open-ils.permacrud.' . $method . '.' . $hint,
258 if ($method eq 'retrieve') {
259 __PACKAGE__->register_method(
260 method => 'search_permacrud',
261 api_name => 'open-ils.permacrud.search.' . $hint,
263 retriever => 'open-ils.permacrud.retrieve.' . $hint,
271 $log->error("error generating permacrud methods: $e");