]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/AccessHandler.pm
LP2045292 Color contrast for AngularJS patron bills
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / AccessHandler.pm
1 package OpenILS::WWW::AccessHandler;
2 use strict; use warnings;
3
4 # Apache Requirements
5 use Apache2::Const -compile => qw(:common HTTP_OK HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY HTTP_MOVED_PERMANENTLY);
6 use Apache2::RequestRec;
7 use Apache2::URI;
8
9 # OpenSRF requirements
10 use OpenSRF::System;
11
12 # Other requirements
13 use URI::Escape;
14
15 # Auth Handler
16 sub handler {
17     my ($r) = @_;
18
19     # Configuration options
20
21     # URL to redirect to for login
22     my $lurl = $r->dir_config('OILSAccessHandlerLoginURL') || '/eg/opac/login';
23     # Redirection variable to come "back" from the previous URL
24     my $lurlvar = $r->dir_config('OILSAccessHandlerLoginURLRedirectVar') || 'redirect_to';
25     # No access page? If not set, just return forbidden.
26     my $failurl = $r->dir_config('OILSAccessHandlerFailURL');
27     # Required permission (if set)
28     my $userperm = $r->dir_config('OILSAccessHandlerPermission');
29     # Need to be in good standing?
30     my $userstanding = $r->dir_config('OILSAccessHandlerGoodStanding') || 0;
31     # Required org unit (if set)
32     my $userou = $r->dir_config('OILSAccessHandlerHomeOU');
33     # OU for permission/standing checks, if not set use home OU
34     my $checkou = $r->dir_config('OILSAccessHandlerCheckOU');
35     # Set referrer based on OU Setting?
36     my $referrersetting = $r->dir_config('OILSAccessHandlerReferrerSetting');
37
38     my $url = $r->construct_url();
39
40     # push everyone to the secure site
41     if ($url =~ /^http:/o) {
42         my $target = $r->construct_url($r->unparsed_uri);
43         $target =~ s/^http:/https:/o;
44         $r->headers_out->set(Location => $target);
45         return Apache2::Const::HTTP_MOVED_PERMANENTLY;
46     }
47
48     # We could use CGI....but that creates issues if post data may be submitted
49     my $auth_ses = ($r->headers_in->get('Cookie') =~ /(?:^|\s)ses=([^;]*)/)[0];
50     $auth_ses = ($r->headers_in->get('Cookie') =~ /(?:^|\s)eg.auth.token=%22([^;]*)%22/)[0] unless $auth_ses;
51     my $user = _verify_login($auth_ses);
52
53     if (!defined($user)) {
54         my $redirect = $r->construct_url($r->unparsed_uri);
55         my $target = $r->construct_url($lurl) . '?' . $lurlvar . '=' . uri_escape($redirect);
56         $target =~ s/^http:/https:/o; # This should never be needed due to the redirect above, but better safe than sorry
57         $r->headers_out->set(Location => $target);
58         # Lets not cache this either, just in case.
59         $r->headers_out->set('Cache-Control' => 'no-cache, no-store, must-revalidate');
60         $r->headers_out->set(Pragma => 'no-cache');
61         $r->headers_out->set(Expires => 0);
62         return Apache2::Const::HTTP_MOVED_TEMPORARILY;
63     }
64
65     # Convert check OU from shortname, if needed
66     $checkou = _get_org_id($checkou);
67
68     # If we have no check OU at this point, use the user's home OU
69     $checkou ||= $user->home_ou;
70
71     my $failed = 0;
72
73     if ($userperm) {
74         my @permissions = split(/\s*[ ,]\s*/, $userperm);
75         $failed++ unless _verify_permission($auth_ses, $user, $checkou, \@permissions);
76     }
77     if (!$failed && $userstanding) {
78         $failed++ unless _verify_standing($user);
79     }
80     if (!$failed && $userou) {
81         $failed++ unless _verify_home_ou($user, $userou);
82     }
83
84     # If we failed one of the above checks they aren't allowed in
85     if ($failed > 0) {
86         if ($failurl) {
87             my $target = $r->construct_url($failurl);
88             $r->headers_out->set(Location => $target);
89             return Apache2::Const::HTTP_MOVED_TEMPORARILY;
90         } else {
91             return Apache2::Const::HTTP_FORBIDDEN;
92         }
93     }
94
95     # Forced referrer for some referrer auth services?
96     if ($referrersetting) {
97         my $referrervalue = _get_ou_setting($referrersetting, $checkou);
98         if ($referrervalue && $referrervalue->{value}) {
99             $r->headers_in->set('Referer', $referrervalue->{value});
100         }
101     }
102
103     # If we haven't thrown them out yet, let them through
104     return Apache2::Const::OK;
105 }
106
107 # "Private" functions
108
109 # Verify our login
110 sub _verify_login {
111     my ($token) = @_;
112     return undef unless $token;
113
114     my $user = OpenSRF::AppSession
115         ->create('open-ils.auth')
116         ->request('open-ils.auth.session.retrieve', $token)
117         ->gather(1);
118
119     if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
120         return undef;
121     }
122
123     return $user if ref($user);
124     return undef;
125 }
126
127 # OU Shortname to ID
128 sub _get_org_id {
129     my ($org_identifier) = @_;
130     # Does this look like a number?
131     if ($org_identifier !~ '^[0-9]+$') {
132         # Not a database id
133         if ($org_identifier) { 
134             # Look up org unit by shortname
135             my $org_unit = OpenSRF::AppSession
136                 ->create('open-ils.actor')
137                 ->request('open-ils.actor.org_unit.retrieve_by_shortname', $org_identifier)
138                 ->gather(1);
139             if ($org_unit && ref($org_unit) ne 'HASH') {
140                 # We appear to have an org unit! So return the ID.
141                 return $org_unit->id;
142             }
143         }
144     } else {
145         # Looks like a database ID, so leave it alone.
146         return $org_identifier;
147     }
148     # If we have reached this point, assume that we found no useful ID.
149     return undef;
150 }
151
152 # Verify home OU
153 sub _verify_home_ou {
154     my ($user, $home_ou) = @_;
155     my $org_tree = OpenSRF::AppSession
156         ->create('open-ils.actor')
157         ->request('open-ils.actor.org_tree.ancestors.retrieve', $user->home_ou)
158         ->gather(1);
159     if ($org_tree && ref($org_tree) ne 'HASH') {
160         my %user_orgs;
161         do {
162             $user_orgs{$org_tree->id} = 1;
163             if ($org_tree->children) {
164                 $org_tree = @{$org_tree->children}[0];
165             } else {
166                 $org_tree = undef;
167             }
168         } while ($org_tree);
169
170         my @home_ous = split(/\s*[ ,]\s*/,$home_ou);
171         for my $cur_ou (@home_ous) {
172             $cur_ou = _get_org_id($cur_ou);
173             if ($user_orgs{$cur_ou}) {
174                 return 1;
175             }
176         }
177     }
178     return 0;
179 }
180
181 # Verify permission
182 sub _verify_permission {
183     my ($token, $user, $org_unit, $permissions) = @_;
184
185     my $failures = OpenSRF::AppSession
186         ->create('open-ils.actor')
187         ->request('open-ils.actor.user.perm.check', $token, $user->id, $org_unit, $permissions)
188         ->gather(1);
189
190     return !scalar(@$failures);
191 }
192
193 # Verify standing
194 sub _verify_standing {
195     my ($user) = @_;
196
197     # If barred you are not in good standing
198     return 0 if $user->barred;
199     # If inactive you are also not in good standing
200     return 0 unless $user->active;
201
202     # Possible addition: Standing Penalty Checks?
203
204     return 1;
205 }
206
207 sub _get_ou_setting {
208     my ($setting, $org_unit) = @_;
209
210     my $value = OpenSRF::AppSession->create('open-ils.actor')
211         ->request('open-ils.actor.ou_setting.ancestor_default', $org_unit, $setting)
212         ->gather(1);
213
214     return $value;
215 }
216
217 1;