import rt 3.8.11
[freeside.git] / rt / html / autohandler
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %# 
3 %# COPYRIGHT:
4 %#  
5 %# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC 
6 %#                                          <jesse@bestpractical.com>
7 %# 
8 %# (Except where explicitly superseded by other copyright notices)
9 %# 
10 %# 
11 %# LICENSE:
12 %# 
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
16 %# from www.gnu.org.
17 %# 
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 %# General Public License for more details.
22 %# 
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 %# 
29 %# 
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %# 
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
37 %# 
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
46 %# 
47 %# END BPS TAGGED BLOCK }}}
48 <%INIT>
49
50 # Roll back any dangling transactions from a previous failed connection
51 $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;
52
53
54 if ($RT::StatementLog) {
55     $RT::Handle->ClearSQLStatementLog;
56     $RT::Handle->LogSQLStatements(1);
57 }
58
59 local *session
60     unless $m->is_subrequest;    # avoid reentrancy, as suggested by masonbook
61
62 # Disable AutoFlush using an attribute
63 if ( $m->request_comp->attr_exists('AutoFlush') ) {
64     $m->autoflush( $m->request_comp->attr('AutoFlush') );
65 }
66
67 %ARGS = map {
68
69     # if they've passed multiple values, they'll be an array. if they've
70     # passed just one, a scalar whatever they are, mark them as utf8
71     my $type = ref($_);
72     ( !$type )
73         ? Encode::is_utf8($_)
74         ? $_
75         : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ )
76         : ( $type eq 'ARRAY' )
77         ? [
78         map {
79             ( ref($_) or Encode::is_utf8($_) )
80                 ? $_
81                 : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ )
82             } @$_
83         ]
84         : ( $type eq 'HASH' )
85         ? {
86         map {
87             ( ref($_) or Encode::is_utf8($_) )
88                 ? $_
89                 : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ )
90             } %$_
91         }
92         : $_
93 } %ARGS;
94
95 # Latter in the code we use
96 # $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
97 # instead of $m->call_next to avoid problems with UTF8 keys in arguments.
98 # The call_next method pass through original arguments and if you have
99 # an argument with unicode key then in a next component you'll get two
100 # records in the args hash: one with key without UTF8 flag and another
101 # with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
102 # is copied from mason's source to get the same results as we get from
103 # call_next method, this feature is not documented, so we just leave it
104 # here to avoid possible side effects.
105
106 # This code canonicalizes time inputs in hours into minutes
107 foreach my $field ( keys %ARGS ) {
108     next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS{$1};
109     my $local = $1;
110     $ARGS{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
111                       {($1 || 0) + $3 ? $2 / $3 : 0}xe;
112     if ( $ARGS{$field} && $ARGS{$field} =~ /hours/i ) {
113         $ARGS{$local} *= 60;
114     }
115     delete $ARGS{$field};
116 }
117
118 $m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
119
120 $m->comp( '/Elements/SetupSessionCookie', %ARGS );
121
122 unless ( $session{'CurrentUser'} && $session{'CurrentUser'}->Id ) {
123     $session{'CurrentUser'} = RT::CurrentUser->new();
124 }
125
126 # Set the proper encoding for the current language handle
127 $r->content_type("text/html; charset=utf-8");
128
129 # If it's a noauth file, don't ask for auth.
130 if ( $m->base_comp->path =~ $RT::WebNoAuthRegex ) {
131     $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
132     $m->abort;
133 }
134
135 # If RT is configured for external auth, let's go through and get REMOTE_USER
136 elsif ($RT::WebExternalAuth) {
137
138     # do we actually have a REMOTE_USER equivlent?
139     if ( RT::Interface::Web::WebCanonicalizeInfo() ) {
140
141         my $orig_user = $user;
142
143         $user = RT::Interface::Web::WebCanonicalizeInfo();
144         $session{'CurrentUser'} = RT::CurrentUser->new();
145         my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load';
146
147         if ( $^O eq 'MSWin32' and $RT::WebExternalGecos ) {
148             my $NodeName = Win32::NodeName();
149             $user =~ s/^\Q$NodeName\E\\//i;
150         }
151
152         $session{'CurrentUser'}->$load_method($user);
153
154         if ( $RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) {
155
156             # Create users on-the-fly
157
158             my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );
159
160             my ( $val, $msg ) = $UserObj->Create(
161                 %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
162                 Name  => $user,
163                 Gecos => $user,
164             );
165
166             if ($val) {
167
168                 # now get user specific information, to better create our user.
169                 my $new_user_info
170                     = RT::Interface::Web::WebExternalAutoInfo($user);
171
172                 # set the attributes that have been defined.
173                 # FIXME: this is a horrible kludge. I'm sure there's something cleaner
174                 foreach my $attribute (
175                     'Name',                  'Comments',
176                     'Signature',             'EmailAddress',
177                     'PagerEmailAddress',     'FreeformContactInfo',
178                     'Organization',          'Disabled',
179                     'Privileged',            'RealName',
180                     'NickName',              'Lang',
181                     'EmailEncoding',         'WebEncoding',
182                     'ExternalContactInfoId', 'ContactInfoSystem',
183                     'ExternalAuthId',        'Gecos',
184                     'HomePhone',             'WorkPhone',
185                     'MobilePhone',           'PagerPhone',
186                     'Address1',              'Address2',
187                     'City',                  'State',
188                     'Zip',                   'Country'
189                     )
190                 {
191                     $m->comp( '/Elements/Callback', %ARGS,
192                         _CallbackName => 'NewUser' );
193
194                     my $method = "Set$attribute";
195                     $UserObj->$method( $new_user_info->{$attribute} )
196                         if ( defined $new_user_info->{$attribute} );
197                 }
198                 $session{'CurrentUser'}->Load($user);
199             }
200             else {
201
202                # we failed to successfully create the user. abort abort abort.
203                 delete $session{'CurrentUser'};
204                 $m->abort() unless $RT::WebFallbackToInternalAuth;
205                 $m->comp( '/Elements/Login', %ARGS,
206                     Error => loc( 'Cannot create user: [_1]', $msg ) );
207             }
208         }
209
210         unless ( $session{'CurrentUser'}->Id() ) {
211             delete $session{'CurrentUser'};
212             $user = $orig_user;
213
214             if ($RT::WebExternalOnly) {
215                 $m->comp( '/Elements/Login', %ARGS,
216                     Error => loc('You are not an authorized user') );
217                 $m->abort();
218             }
219         }
220     }
221     elsif ($RT::WebFallbackToInternalAuth) {
222         unless ( defined( $session{'CurrentUser'} ) ) {
223             $m->comp( '/Elements/Login', %ARGS,
224                 Error => loc('You are not an authorized user') );
225             $m->abort();
226         }
227     }
228     else {
229
230         # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
231         delete $session{'CurrentUser'} if defined $session{'CurrentUser'};
232     }
233 }
234
235 delete $session{'CurrentUser'}
236     unless $session{'CurrentUser'}
237     and $session{'CurrentUser'}->Id;
238
239 # Process per-page authentication callbacks
240 $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'Auth' );
241
242 # If the user is logging in, let's authenticate
243 if ( !$session{'CurrentUser'} && defined $user && defined $pass ) {
244     $session{'CurrentUser'} = RT::CurrentUser->new();
245     $session{'CurrentUser'}->Load($user);
246
247     unless ( $session{'CurrentUser'}->id
248         && $session{'CurrentUser'}->IsPassword($pass) )
249     {
250         delete $session{'CurrentUser'};
251         $RT::Logger->error("FAILED LOGIN for $user from $ENV{'REMOTE_ADDR'}");
252         $m->comp( '/Elements/Login', %ARGS,
253             Error => loc('Your username or password is incorrect') );
254         $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'FailedLogin' );
255         $m->abort;
256     }
257     else {
258         $RT::Logger->info(
259             "Successful login for $user from $ENV{'REMOTE_ADDR'}");
260         $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'SuccessfulLogin' );
261     }
262 }
263
264 # If we've got credentials, let's serve the file up.
265 if (    ( defined $session{'CurrentUser'} )
266     and ( $session{'CurrentUser'}->Id ) )
267 {
268
269     # Process per-page global callbacks
270     $m->comp( '/Elements/Callback', %ARGS );
271
272     # If the user isn't privileged, they can only see SelfService
273     if ( not $session{'CurrentUser'}->Privileged ) {
274
275         # if the user is trying to access a ticket, redirect them
276         if (    $m->request_comp->path =~ '^(/+)Ticket/Display.html'
277             and $ARGS{'id'} )
278         {
279             RT::Interface::Web::Redirect($RT::WebURL."SelfService/Display.html?id=".$ARGS{'id'});
280         }
281
282         # otherwise, drop the user at the SelfService default page
283         elsif ( $m->base_comp->path !~ $RT::SelfServiceRegex ) {
284             RT::Interface::Web::Redirect($RT::WebURL."SelfService/");
285         }
286         else {
287             $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
288         }
289     }
290     else {
291         $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
292     }
293 }
294
295 # If we have no credentials
296 else {
297     $m->comp( '/Elements/Login', %ARGS );
298     $m->abort();
299 }
300
301 if ($RT::StatementLog) {
302     my @log = $RT::Handle->SQLStatementLog;
303     $RT::Handle->ClearSQLStatementLog;
304     for my $stmt (@log) {
305         my ( $time, $sql, $bind, $duration ) = @{$stmt};
306         my @bind;
307         if ( ref $bind ) {
308             @bind = @{$bind};
309         }
310         else {
311
312             # Older DBIx-SB
313             $duration = $bind;
314         }
315         $RT::Logger->log(
316             level   => $RT::StatementLog,
317             message => "SQL(" . sprintf( "%.2f", $duration ) . "s): $sql;"
318                 . (
319                 @bind ? "  [ bound values: @{[map{qq|'$_'|} @bind]} ]" : ""
320                 )
321         );
322     }
323 }
324
325 </%INIT>
326 <& /Elements/Footer, %ARGS &>
327 <%ARGS>
328 $user => undef
329 $pass => undef
330 $menu => undef
331 </%ARGS>