added ability to pass an object ID and hint to allowed() for object perm checking
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / PermaCrud.pm
1 package OpenILS::Application::PermaCrud;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4
5 use Unicode::Normalize;
6 use OpenSRF::EX qw/:try/;
7
8 use OpenSRF::AppSession;
9 use OpenSRF::Utils::SettingsClient;
10 use OpenSRF::Utils::Logger qw/:level/;
11
12 use OpenILS::Utils::Fieldmapper;
13 use OpenSRF::Utils::JSON;
14
15 use OpenILS::Utils::CStoreEditor qw/:funcs/;
16
17 use XML::LibXML;
18 use XML::LibXML::XPathContext;
19 use XML::LibXSLT;
20
21 our %namespace_map = (
22     oils_persist=> {ns => 'http://open-ils.org/spec/opensrf/IDL/persistence/v1'},
23     oils_obj    => {ns => 'http://open-ils.org/spec/opensrf/IDL/objects/v1'},
24     idl         => {ns => 'http://opensrf.org/spec/IDL/base/v1'},
25     reporter    => {ns => 'http://open-ils.org/spec/opensrf/IDL/reporter/v1'},
26 );
27
28
29 my $log = 'OpenSRF::Utils::Logger';
30
31 my $parser = XML::LibXML->new();
32 my $xslt = XML::LibXSLT->new();
33
34 my $xpc = XML::LibXML::XPathContext->new();
35 $xpc->registerNs($_, $namespace_map{$_}{ns}) for ( keys %namespace_map );
36
37 my $idl;
38
39 sub initialize {
40
41     my $conf = OpenSRF::Utils::SettingsClient->new;
42     my $idl_file = $conf->config_value( 'IDL' );
43
44     $idl = $parser->parse_file( $idl_file );
45
46     $log->debug( 'IDL XML file loaded' );
47 }
48 sub child_init {}
49
50 sub CRUD_action_object_permcheck {
51     my $self = shift;
52     my $client = shift;
53     my $auth = shift;
54     my $obj = shift;
55
56     my $e = new_editor(authtoken => $auth, xact => 1);
57     return $e->event unless $e->checkauth;
58
59     unless ($obj->json_hint eq $self->{class_hint}) {
60         throw OpenSRF::DomainObject::oilsException->new(
61             statusCode => 500,
62             status => "Class missmatch: $self->{class_hint} method called with " . $obj->json_hint,
63         );
64     }
65
66     my $action = $self->api_name =~ s/^open-ils\.admin\.([^\.])\..+$/$1/o;
67     my $o_type = $obj->cdbi =~ s/::/./go;
68
69     my ($class_node) = $xpc->findnodes( "//idl:class[\@id='$self->{class_hint}']", $idl->documentElement );
70     my ($action_node) = $xpc->findnodes( "perm:permacrud/perm:actions/perm:$action", $class_node );
71     my $all_perms = $xpc->getAttribute( 'all_perms', $action_node );
72
73     my $perm_field_value = $aciton_node->getAttribute('permission');
74
75     if (defined($perm_field_value)) {
76         my @perms = split '|', $aciton_node->getAttribute('permission');
77
78         my @context_ous;
79         if ($aciton_node->getAttribute('global_required')) {
80             push @context_ous, $e->search_actor_org_unit( { parent_ou => undef } )->[0]->id;
81
82         } else {
83             my $context_field_value = $aciton_node->getAttribute('context_field');
84
85             if (defined($context_field_value)) {
86                 push @context_ous, $obj->$_ for ( split '|', $context_field_value );
87             } else {  
88                 for my $context_node ( $xpc->findnodes( "perm:context", $action_node ) ) {
89                     my $context_field = $context_node->getAttribute('field');
90                     my $link_field = $context_node->getAttribute('link');
91
92                     if ($link_field) {
93
94                         my ($link_node) = $xpc->findnodes( "idl:links/idl:link[\@field='$link_field']", $class_node );
95                         my $link_class_hint = $link_node->getAttribute('class');
96                         my $remote_field = $link_node->getAttribute('key');
97
98                         my ($remote_class_node) = $xpc->findnodes( "//idl:class[\@id='$self->{class_hint}']", $idl->documentElement );
99                         my $search_method = 'search_' . $xpc->findvalue( '@oils_obj:fieldmapper', $remote_class_node );
100                         $search_method =~ s/::/_/go;
101
102                         for my $remote_object ( @{$e->$search_method( { $key => $obj->$link_field } )} ) {
103                             push @context_ous, $remote_object->$context_field;
104                         }
105                     } else {
106                         push @context_ous, $obj->$_ for ( split '|', $context_field );
107                     }
108                 }
109             }
110         }
111
112         my $pok = 0;
113         for my $perm (@perms) {
114             if (@context_ous) {
115                 for my $c_ou (@context_ous) {
116                     if ($e->allowed($perm => $c_ou => $obj)) {
117                         $pok++;
118                         last;
119                     }
120                 }
121             } else {
122                 $pok++ if ($e->allowed($perm => undef => $obj));
123                 next;
124             }
125         }
126
127         if ((lc($all_perms) eq 'true' && @perms != $pok) or !$pok) {
128             throw OpenSRF::DomainObject::oilsException->new(
129                 statusCode => 500,
130                 status => "Perm failure -- action: $action, object type: $self->{json_hint}",
131             );
132         }
133     }
134 }
135
136 for my $class_node ( $xpc->findnodes( "perm:permacrud/perm:actions/perm:$action", $class_node ) ) {
137     my $hint = $class_node->getAttribute('id');
138
139     for my $action_node ( $xpc->findnodes( "perm:permacrud/perm:actions/perm:*", $class_node ) ) {
140         my $method = $action_node->localname =~ s/^.+:(.+)$/$1/o;
141
142         __PACKAGE__->register_method(
143             method      => 'CRUD_action_object_permcheck',
144             api_name    => 'open-ils.permacrud.' . $method . '.' . $hint,
145             class_hint  => $hint,
146         );
147
148     }
149 }
150     
151
152
153 1;
154