1 %# BEGIN BPS TAGGED BLOCK {{{
5 %# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
6 %# <jesse@bestpractical.com>
8 %# (Except where explicitly superseded by other copyright notices)
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
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.
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/copyleft/gpl.html.
30 %# CONTRIBUTION SUBMISSION POLICY:
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.)
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.
47 %# END BPS TAGGED BLOCK }}}
50 # Roll back any dangling transactions from a previous failed connection
51 $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;
54 if ($RT::StatementLog) {
55 $RT::Handle->ClearSQLStatementLog;
56 $RT::Handle->LogSQLStatements(1);
60 unless $m->is_subrequest; # avoid reentrancy, as suggested by masonbook
62 # Disable AutoFlush using an attribute
63 if ( $m->request_comp->attr_exists('AutoFlush') ) {
64 $m->autoflush( $m->request_comp->attr('AutoFlush') );
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
75 : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
76 : ( $type eq 'ARRAY' )
79 ( ref($_) or Encode::is_utf8($_) )
81 : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
87 ( ref($_) or Encode::is_utf8($_) )
89 : Encode::decode( utf8 => $_, Encode::FB_PERLQQ )
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.
106 # This code canonicalizes time inputs in hours into minutes
107 foreach my $field ( keys %ARGS ) {
108 next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS{$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 ) {
115 delete $ARGS{$field};
118 $m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
120 $m->comp( '/Elements/SetupSessionCookie', %ARGS );
122 unless ( $session{'CurrentUser'} && $session{'CurrentUser'}->Id ) {
123 $session{'CurrentUser'} = RT::CurrentUser->new();
126 # Set the proper encoding for the current language handle
127 $r->content_type("text/html; charset=utf-8");
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);
135 # If RT is configured for external auth, let's go through and get REMOTE_USER
136 elsif ($RT::WebExternalAuth) {
138 # do we actually have a REMOTE_USER equivlent?
139 if ( RT::Interface::Web::WebCanonicalizeInfo() ) {
141 my $orig_user = $user;
143 $user = RT::Interface::Web::WebCanonicalizeInfo();
144 $session{'CurrentUser'} = RT::CurrentUser->new();
145 my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load';
147 if ( $^O eq 'MSWin32' and $RT::WebExternalGecos ) {
148 my $NodeName = Win32::NodeName();
149 $user =~ s/^\Q$NodeName\E\\//i;
152 $session{'CurrentUser'}->$load_method($user);
154 if ( $RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) {
156 # Create users on-the-fly
158 my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );
160 my ( $val, $msg ) = $UserObj->Create(
161 %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
168 # now get user specific information, to better create our user.
170 = RT::Interface::Web::WebExternalAutoInfo($user);
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 (
176 'Signature', 'EmailAddress',
177 'PagerEmailAddress', 'FreeformContactInfo',
178 'Organization', 'Disabled',
179 'Privileged', 'RealName',
181 'EmailEncoding', 'WebEncoding',
182 'ExternalContactInfoId', 'ContactInfoSystem',
183 'ExternalAuthId', 'Gecos',
184 'HomePhone', 'WorkPhone',
185 'MobilePhone', 'PagerPhone',
186 'Address1', 'Address2',
191 $m->comp( '/Elements/Callback', %ARGS,
192 _CallbackName => 'NewUser' );
194 my $method = "Set$attribute";
195 $UserObj->$method( $new_user_info->{$attribute} )
196 if ( defined $new_user_info->{$attribute} );
198 $session{'CurrentUser'}->Load($user);
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 ) );
210 unless ( $session{'CurrentUser'}->Id() ) {
211 delete $session{'CurrentUser'};
214 if ($RT::WebExternalOnly) {
215 $m->comp( '/Elements/Login', %ARGS,
216 Error => loc('You are not an authorized user') );
221 elsif ($RT::WebFallbackToInternalAuth) {
222 unless ( defined( $session{'CurrentUser'} ) ) {
223 $m->comp( '/Elements/Login', %ARGS,
224 Error => loc('You are not an authorized user') );
230 # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
231 delete $session{'CurrentUser'} if defined $session{'CurrentUser'};
235 delete $session{'CurrentUser'}
236 unless $session{'CurrentUser'}
237 and $session{'CurrentUser'}->Id;
239 # Process per-page authentication callbacks
240 $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'Auth' );
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);
247 unless ( $session{'CurrentUser'}->id
248 && $session{'CurrentUser'}->IsPassword($pass) )
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' );
259 "Successful login for $user from $ENV{'REMOTE_ADDR'}");
260 $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'SuccessfulLogin' );
264 # If we've got credentials, let's serve the file up.
265 if ( ( defined $session{'CurrentUser'} )
266 and ( $session{'CurrentUser'}->Id ) )
269 # Process per-page global callbacks
270 $m->comp( '/Elements/Callback', %ARGS );
272 # If the user isn't privileged, they can only see SelfService
273 if ( not $session{'CurrentUser'}->Privileged ) {
275 # if the user is trying to access a ticket, redirect them
276 if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html'
279 RT::Interface::Web::Redirect($RT::WebURL."SelfService/Display.html?id=".$ARGS{'id'});
282 # otherwise, drop the user at the SelfService default page
283 elsif ( $m->base_comp->path !~ '^(/+)SelfService/' ) {
284 RT::Interface::Web::Redirect($RT::WebURL."SelfService/");
287 $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
291 $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);
295 # If we have no credentials
297 $m->comp( '/Elements/Login', %ARGS );
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};
316 level => $RT::StatementLog,
317 message => "SQL(" . sprintf( "%.2f", $duration ) . "s): $sql;"
319 @bind ? " [ bound values: @{[map{qq|'$_'|} @bind]} ]" : ""
326 <& /Elements/Footer, %ARGS &>