X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Fhtml%2Fautohandler;h=823adef459baff5ba2aac033ee2fe395a0b91c03;hp=c854c2b337ec9d598513d97580a50790e30d0a2c;hb=ef20b2b6b1feb47ad02b5ff7525f1a0fd11d0fa4;hpb=a513c0bef534d05f03c1242831b6f3be19b97dae diff --git a/rt/html/autohandler b/rt/html/autohandler index c854c2b33..823adef45 100644 --- a/rt/html/autohandler +++ b/rt/html/autohandler @@ -2,7 +2,7 @@ %# %# COPYRIGHT: %# -%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC +%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC %# %# %# (Except where explicitly superseded by other copyright notices) @@ -22,7 +22,9 @@ %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software -%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +%# 02110-1301 or visit their web page on the internet at +%# http://www.gnu.org/copyleft/gpl.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: @@ -49,30 +51,75 @@ $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth; -local *session unless $m->is_subrequest; # avoid reentrancy, as suggested by masonbook +if ($RT::StatementLog) { + $RT::Handle->ClearSQLStatementLog; + $RT::Handle->LogSQLStatements(1); +} + +local *session + unless $m->is_subrequest; # avoid reentrancy, as suggested by masonbook # Disable AutoFlush using an attribute -if ($m->request_comp->attr_exists('AutoFlush')) { - $m->autoflush($m->request_comp->attr('AutoFlush')); +if ( $m->request_comp->attr_exists('AutoFlush') ) { + $m->autoflush( $m->request_comp->attr('AutoFlush') ); } %ARGS = map { - # if they've passed multiple values, they'll be an array. if they've + + # if they've passed multiple values, they'll be an array. if they've # passed just one, a scalar whatever they are, mark them as utf8 my $type = ref($_); - (!$type) - ? Encode::is_utf8($_) ? $_ : Encode::decode(utf8 => $_, Encode::FB_PERLQQ) : - ($type eq 'ARRAY') - ? [ map { (ref($_) or Encode::is_utf8($_)) ? $_ : Encode::decode(utf8 => $_, Encode::FB_PERLQQ) } @$_ ] : - ($type eq 'HASH') - ? { map { (ref($_) or Encode::is_utf8($_)) ? $_ : Encode::decode(utf8 => $_, Encode::FB_PERLQQ) } %$_ } : $_ - } %ARGS; - -$m->{'rt_base_time'} = [Time::HiRes::gettimeofday()]; - -$m->comp('/Elements/SetupSessionCookie', %ARGS); - -unless ($session{'CurrentUser'} && $session{'CurrentUser'}->Id) { + ( !$type ) + ? Encode::is_utf8($_) + ? $_ + : Encode::decode( utf8 => $_, Encode::FB_PERLQQ ) + : ( $type eq 'ARRAY' ) + ? [ + map { + ( ref($_) or Encode::is_utf8($_) ) + ? $_ + : Encode::decode( utf8 => $_, Encode::FB_PERLQQ ) + } @$_ + ] + : ( $type eq 'HASH' ) + ? { + map { + ( ref($_) or Encode::is_utf8($_) ) + ? $_ + : Encode::decode( utf8 => $_, Encode::FB_PERLQQ ) + } %$_ + } + : $_ +} %ARGS; + +# Latter in the code we use +# $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS ); +# instead of $m->call_next to avoid problems with UTF8 keys in arguments. +# The call_next method pass through original arguments and if you have +# an argument with unicode key then in a next component you'll get two +# records in the args hash: one with key without UTF8 flag and another +# with the flag, which may result into errors. "{ base_comp => $m->request_comp }" +# is copied from mason's source to get the same results as we get from +# call_next method, this feature is not documented, so we just leave it +# here to avoid possible side effects. + +# This code canonicalizes time inputs in hours into minutes +foreach my $field ( keys %ARGS ) { + next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS{$1}; + my $local = $1; + $ARGS{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b} + {($1 || 0) + $3 ? $2 / $3 : 0}xe; + if ( $ARGS{$field} && $ARGS{$field} =~ /hours/i ) { + $ARGS{$local} *= 60; + } + delete $ARGS{$field}; +} + +$m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ]; + +$m->comp( '/Elements/SetupSessionCookie', %ARGS ); + +unless ( $session{'CurrentUser'} && $session{'CurrentUser'}->Id ) { $session{'CurrentUser'} = RT::CurrentUser->new(); } @@ -80,147 +127,201 @@ unless ($session{'CurrentUser'} && $session{'CurrentUser'}->Id) { $r->content_type("text/html; charset=utf-8"); # If it's a noauth file, don't ask for auth. -if ($m->base_comp->path =~ $RT::WebNoAuthRegex ) -{ - $m->call_next(%ARGS); - $m->abort(); +if ( $m->base_comp->path =~ $RT::WebNoAuthRegex ) { + $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS); + $m->abort; } # If RT is configured for external auth, let's go through and get REMOTE_USER -elsif ( $RT::WebExternalAuth ) { +elsif ($RT::WebExternalAuth) { # do we actually have a REMOTE_USER equivlent? if ( RT::Interface::Web::WebCanonicalizeInfo() ) { - my $orig_user = $user; - - $user = RT::Interface::Web::WebCanonicalizeInfo(); - $session{'CurrentUser'} = RT::CurrentUser->new(); - my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load'; - - if ($^O eq 'MSWin32' and $RT::WebExternalGecos) { - my $NodeName = Win32::NodeName(); - $user =~ s/^\Q$NodeName\E\\//i; - } - - $session{'CurrentUser'}->$load_method($user); - - if ($RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) { - # Create users on-the-fly - - my $UserObj = RT::User->new(RT::CurrentUser->new('RT_System')); - - my ($val, $msg) = $UserObj->Create( - %{ref($RT::AutoCreate) ? $RT::AutoCreate : {}}, - Name => $user, - Gecos => $user, - ); - - if ($val) { - - # now get user specific information, to better create our user. - my $new_user_info = RT::Interface::Web::WebExternalAutoInfo($user); - - # set the attributes that have been defined. - # FIXME: this is a horrible kludge. I'm sure there's something cleaner - foreach my $attribute ('Name', 'Comments', 'Signature', 'EmailAddress', - 'PagerEmailAddress', 'FreeformContactInfo', - 'Organization', 'Disabled', 'Privileged', - 'RealName', 'NickName', 'Lang', 'EmailEncoding', - 'WebEncoding', 'ExternalContactInfoId', - 'ContactInfoSystem', 'ExternalAuthId', 'Gecos', - 'HomePhone', 'WorkPhone', 'MobilePhone', - 'PagerPhone', 'Address1', 'Address2', 'City', - 'State', 'Zip', 'Country') { - $m->comp('/Elements/Callback', %ARGS, _CallbackName => 'NewUser'); - - my $method = "Set$attribute"; - $UserObj->$method($new_user_info->{$attribute}) - if( defined $new_user_info->{$attribute} ); - } - $session{'CurrentUser'}->Load($user); - } - else { - # we failed to successfully create the user. abort abort abort. - delete $session{'CurrentUser'}; - $m->abort() unless $RT::WebFallbackToInternalAuth; - $m->comp('/Elements/Login', %ARGS, - Error=> loc('Cannot create user: [_1]', $msg)); - } - } - - unless ( $session{'CurrentUser'}->Id() ) { - delete $session{'CurrentUser'}; - $user = $orig_user; - - if ( $RT::WebExternalOnly ) { - $m->comp('/Elements/Login', %ARGS, - Error=> loc('You are not an authorized user')); - $m->abort(); - } - } + my $orig_user = $user; + + $user = RT::Interface::Web::WebCanonicalizeInfo(); + $session{'CurrentUser'} = RT::CurrentUser->new(); + my $load_method = $RT::WebExternalGecos ? 'LoadByGecos' : 'Load'; + + if ( $^O eq 'MSWin32' and $RT::WebExternalGecos ) { + my $NodeName = Win32::NodeName(); + $user =~ s/^\Q$NodeName\E\\//i; + } + + $session{'CurrentUser'}->$load_method($user); + + if ( $RT::WebExternalAuto and !$session{'CurrentUser'}->Id() ) { + + # Create users on-the-fly + + my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') ); + + my ( $val, $msg ) = $UserObj->Create( + %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} }, + Name => $user, + Gecos => $user, + ); + + if ($val) { + + # now get user specific information, to better create our user. + my $new_user_info + = RT::Interface::Web::WebExternalAutoInfo($user); + + # set the attributes that have been defined. + # FIXME: this is a horrible kludge. I'm sure there's something cleaner + foreach my $attribute ( + 'Name', 'Comments', + 'Signature', 'EmailAddress', + 'PagerEmailAddress', 'FreeformContactInfo', + 'Organization', 'Disabled', + 'Privileged', 'RealName', + 'NickName', 'Lang', + 'EmailEncoding', 'WebEncoding', + 'ExternalContactInfoId', 'ContactInfoSystem', + 'ExternalAuthId', 'Gecos', + 'HomePhone', 'WorkPhone', + 'MobilePhone', 'PagerPhone', + 'Address1', 'Address2', + 'City', 'State', + 'Zip', 'Country' + ) + { + $m->comp( '/Elements/Callback', %ARGS, + _CallbackName => 'NewUser' ); + + my $method = "Set$attribute"; + $UserObj->$method( $new_user_info->{$attribute} ) + if ( defined $new_user_info->{$attribute} ); + } + $session{'CurrentUser'}->Load($user); + } + else { + + # we failed to successfully create the user. abort abort abort. + delete $session{'CurrentUser'}; + $m->abort() unless $RT::WebFallbackToInternalAuth; + $m->comp( '/Elements/Login', %ARGS, + Error => loc( 'Cannot create user: [_1]', $msg ) ); + } + } + + unless ( $session{'CurrentUser'}->Id() ) { + delete $session{'CurrentUser'}; + $user = $orig_user; + + if ($RT::WebExternalOnly) { + $m->comp( '/Elements/Login', %ARGS, + Error => loc('You are not an authorized user') ); + $m->abort(); + } + } } elsif ($RT::WebFallbackToInternalAuth) { - unless (defined($session{'CurrentUser'})) { - $m->comp('/Elements/Login', %ARGS, - Error=> loc('You are not an authorized user')); - $m->abort(); - } - } else { - # WebExternalAuth is set, but we don't have a REMOTE_USER. abort - delete $session{'CurrentUser'} if defined $session{'CurrentUser'}; + unless ( defined( $session{'CurrentUser'} ) ) { + $m->comp( '/Elements/Login', %ARGS, + Error => loc('You are not an authorized user') ); + $m->abort(); + } + } + else { + + # WebExternalAuth is set, but we don't have a REMOTE_USER. abort + delete $session{'CurrentUser'} if defined $session{'CurrentUser'}; } } delete $session{'CurrentUser'} - unless $session{'CurrentUser'} and defined $session{'CurrentUser'}->Id; - + unless $session{'CurrentUser'} + and $session{'CurrentUser'}->Id; # Process per-page authentication callbacks -$m->comp('/Elements/Callback', %ARGS, _CallbackName => 'Auth'); +$m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'Auth' ); # If the user is logging in, let's authenticate -if (!$session{'CurrentUser'} && defined ($user) && defined ($pass) ){ +if ( !$session{'CurrentUser'} && defined $user && defined $pass ) { $session{'CurrentUser'} = RT::CurrentUser->new(); $session{'CurrentUser'}->Load($user); - if (!$session{'CurrentUser'}->id() || - !$session{'CurrentUser'}->IsPassword($pass)) + unless ( $session{'CurrentUser'}->id + && $session{'CurrentUser'}->IsPassword($pass) ) { delete $session{'CurrentUser'}; - $RT::Logger->error("FAILED LOGIN for $user from $ENV{'REMOTE_ADDR'}"); - $m->comp('/Elements/Login', %ARGS, - Error => loc('Your username or password is incorrect')); - $m->abort(); + $RT::Logger->error("FAILED LOGIN for $user from $ENV{'REMOTE_ADDR'}"); + $m->comp( '/Elements/Login', %ARGS, + Error => loc('Your username or password is incorrect') ); + $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'FailedLogin' ); + $m->abort; } else { - $RT::Logger->info("Successful login for $user from $ENV{'REMOTE_ADDR'}"); + $RT::Logger->info( + "Successful login for $user from $ENV{'REMOTE_ADDR'}"); + $m->comp( '/Elements/Callback', %ARGS, _CallbackName => 'SuccessfulLogin' ); } } - + # If we've got credentials, let's serve the file up. -if ( (defined $session{'CurrentUser'}) and - ( $session{'CurrentUser'}->Id) ) { - +if ( ( defined $session{'CurrentUser'} ) + and ( $session{'CurrentUser'}->Id ) ) +{ + # Process per-page global callbacks - $m->comp('/Elements/Callback', %ARGS); + $m->comp( '/Elements/Callback', %ARGS ); # If the user isn't privileged, they can only see SelfService - if ((! $session{'CurrentUser'}->Privileged) and - ($m->base_comp->path !~ '^(/+)SelfService/') ) { - $m->comp('/SelfService/index.html'); - $m->abort(); + if ( not $session{'CurrentUser'}->Privileged ) { + + # if the user is trying to access a ticket, redirect them + if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html' + and $ARGS{'id'} ) + { + RT::Interface::Web::Redirect($RT::WebURL."SelfService/Display.html?id=".$ARGS{'id'}); + } + + # otherwise, drop the user at the SelfService default page + elsif ( $m->base_comp->path !~ '^(/+)SelfService/' ) { + RT::Interface::Web::Redirect($RT::WebURL."SelfService/"); + } + else { + $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS); + } } else { - $m->call_next(%ARGS); + $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS); } } # If we have no credentials else { - $m->comp('/Elements/Login', %ARGS); + $m->comp( '/Elements/Login', %ARGS ); $m->abort(); } + +if ($RT::StatementLog) { + my @log = $RT::Handle->SQLStatementLog; + $RT::Handle->ClearSQLStatementLog; + for my $stmt (@log) { + my ( $time, $sql, $bind, $duration ) = @{$stmt}; + my @bind; + if ( ref $bind ) { + @bind = @{$bind}; + } + else { + + # Older DBIx-SB + $duration = $bind; + } + $RT::Logger->log( + level => $RT::StatementLog, + message => "SQL(" . sprintf( "%.2f", $duration ) . "s): $sql;" + . ( + @bind ? " [ bound values: @{[map{qq|'$_'|} @bind]} ]" : "" + ) + ); + } +} + <& /Elements/Footer, %ARGS &> <%ARGS>