Apply patch for redacting parameters in the log.
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 80ba3f8..21c04d8 100644 (file)
@@ -1,9 +1,5 @@
 package Catalyst;
 
-# we don't need really need this, but if we load it before MRO::Compat gets
-# loaded (via Moose and Class::MOP), we can avoid some nasty warnings
-use Class::C3;
-
 use Moose;
 extends 'Catalyst::Component';
 use bytes;
@@ -25,12 +21,12 @@ use Time::HiRes qw/gettimeofday tv_interval/;
 use URI ();
 use URI::http;
 use URI::https;
-use Scalar::Util qw/weaken blessed/;
+use Scalar::Util qw/weaken/;
 use Tree::Simple qw/use_weak_refs/;
 use Tree::Simple::Visitor::FindByUID;
 use attributes;
 use utf8;
-use Carp qw/croak carp/;
+use Carp qw/croak carp shortmess/;
 
 BEGIN { require 5.008001; }
 
@@ -81,7 +77,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.8000_04';
+our $VERSION = '5.8000_05';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -361,6 +357,13 @@ Almost the same as C<forward>, but does a full dispatch, instead of just
 calling the new C<$action> / C<$class-E<gt>$method>. This means that C<begin>,
 C<auto> and the method you go to are called, just like a new request.
 
+In addition both C<< $c->action >> and C<< $c->namespace >> are localized.
+This means, for example, that $c->action methods such as C<name>, C<class> and
+C<reverse> return information for the visited action when they are invoked
+within the visited action.  This is different from the behavior of C<forward>
+which continues to use the $c->action object from the caller action even when
+invoked from the callee.
+
 C<$c-E<gt>stash> is kept unchanged.
 
 In effect, C<visit> allows you to "wrap" another action, just as it
@@ -510,9 +513,24 @@ sub _comp_search_prefixes {
 
     # don't warn if we didn't find any results, it just might not exist
     if( @result ) {
-        $c->log->warn( qq(Found results for "${name}" using regexp fallback.) );
-        $c->log->warn( 'Relying on the regexp fallback behavior for component resolution is unreliable and unsafe.' );
-        $c->log->warn( 'If you really want to search, pass in a regexp as the argument.' );
+        my $msg = "Used regexp fallback for \$c->model('${name}'), which found '" .
+           (join '", "', @result) . "'. Relying on regexp fallback behavior for " .
+           "component resolution is unreliable and unsafe.";
+        my $short = $result[0];
+        $short =~ s/.*?Model:://;
+        my $shortmess = Carp::shortmess('');
+        if ($shortmess =~ m#Catalyst/Plugin#) {
+           $msg .= " You probably need to set '$short' instead of '${name}' in this " .
+              "plugin's config";
+        } elsif ($shortmess =~ m#Catalyst/lib/(View|Controller)#) {
+           $msg .= " You probably need to set '$short' instead of '${name}' in this " .
+              "component's config";
+        } else {
+           $msg .= " You probably meant \$c->model('$short') instead of \$c->model{'${name}'}, " .
+              "but if you really wanted to search, pass in a regexp as the argument " .
+              "like so: \$c->model(qr/${name}/)";
+        }
+        $c->log->warn( "${msg}$shortmess" );
     }
 
     return @result;
@@ -613,7 +631,7 @@ sub model {
     my( $comp, $rest ) = $c->_comp_search_prefixes( undef, qw/Model M/);
 
     if( $rest ) {
-        $c->log->warn( 'Calling $c->model() will return a random model unless you specify one of:' );
+        $c->log->warn( Carp::shortmess('Calling $c->model() will return a random model unless you specify one of:') );
         $c->log->warn( '* $c->config->{default_model} # the name of the default model to use' );
         $c->log->warn( '* $c->stash->{current_model} # the name of the model to use for this request' );
         $c->log->warn( '* $c->stash->{current_model_instance} # the instance of the model to use for this request' );
@@ -752,7 +770,7 @@ sub component {
         return map { $c->_filter_component( $_, @args ) } @result if ref $name;
 
         if( $result[ 0 ] ) {
-            $c->log->warn( qq(Found results for "${name}" using regexp fallback.) );
+            $c->log->warn( Carp::shortmess(qq(Found results for "${name}" using regexp fallback)) );
             $c->log->warn( 'Relying on the regexp fallback behavior for component resolution' );
             $c->log->warn( 'is unreliable and unsafe. You have been warned' );
             return $c->_filter_component( $result[ 0 ], @args );
@@ -971,7 +989,8 @@ EOF
         my @plugins = map { "$_  " . ( $_->VERSION || '' ) } $class->registered_plugins;
 
         if (@plugins) {
-            my $t = Text::SimpleTable->new(74);
+            my $column_width = Catalyst::Utils::term_width() - 6;
+            my $t = Text::SimpleTable->new($column_width);
             $t->row($_) for @plugins;
             $class->log->debug( "Loaded plugins:\n" . $t->draw . "\n" );
         }
@@ -1003,7 +1022,8 @@ EOF
     $class->setup_components;
 
     if ( $class->debug ) {
-        my $t = Text::SimpleTable->new( [ 63, 'Class' ], [ 8, 'Type' ] );
+        my $column_width = Catalyst::Utils::term_width() - 8 - 9;
+        my $t = Text::SimpleTable->new( [ $column_width, 'Class' ], [ 8, 'Type' ] );
         for my $comp ( sort keys %{ $class->components } ) {
             my $type = ref $class->components->{$comp} ? 'instance' : 'class';
             $t->row( $comp, $type );
@@ -1034,8 +1054,33 @@ EOF
     Scope::Upper::reap(sub {
         my $meta = Class::MOP::get_metaclass_by_name($class);
         $meta->make_immutable unless $meta->is_immutable;
-    }, 1);
+    }, Scope::Upper::SCOPE(1));
+
+    $class->setup_finalize;
+}
+
+
+=head2 $app->setup_finalize
+
+A hook to attach modifiers to.
+Using C< after setup => sub{}; > doesn't work, because of quirky things done for plugin setup.
+Also better than C< setup_finished(); >, as that is a getter method.
+
+    sub setup_finalize {
+
+        my $app = shift;
+
+        ## do stuff, i.e., determine a primary key column for sessions stored in a DB
 
+        $app->next::method(@_);
+
+
+    }
+
+=cut
+
+sub setup_finalize {
+    my ($class) = @_;
     $class->setup_finished(1);
 }
 
@@ -1761,15 +1806,10 @@ sub prepare_body {
     $c->prepare_parameters;
     $c->prepare_uploads;
 
-    if ( $c->debug && keys %{ $c->req->body_parameters } ) {
-        my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
-        for my $key ( sort keys %{ $c->req->body_parameters } ) {
-            my $param = $c->req->body_parameters->{$key};
-            my $value = defined($param) ? $param : '';
-            $t->row( $key,
-                ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
-        }
-        $c->log->debug( "Body Parameters are:\n" . $t->draw );
+    if ( $c->debug ) {
+        $c->log_parameters( 
+            'Body Parameters are', $c->request->body_parameters
+        );
     }
 }
 
@@ -1855,15 +1895,65 @@ sub prepare_query_parameters {
 
     $c->engine->prepare_query_parameters( $c, @_ );
 
-    if ( $c->debug && keys %{ $c->request->query_parameters } ) {
-        my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
-        for my $key ( sort keys %{ $c->req->query_parameters } ) {
-            my $param = $c->req->query_parameters->{$key};
+    if ( $c->debug ) {
+        $c->log_parameters( 
+            'Query Parameters are', $c->request->query_parameters 
+        );
+    }
+}
+
+=head2 $c->log_parameters($name, $parameters)
+
+Logs a hash reference of key value pairs, with a caption above the table.
+
+Looks like:
+
+ [debug] Query Parameters are:
+ .-------------------------------------+--------------------------------------.
+ | Parameter                           | Value                                |
+ +-------------------------------------+--------------------------------------+
+ | search                              | Moose                                |
+ | searchtype                          | modules                              |
+ '-------------------------------------+--------------------------------------'
+
+If there are query parameters you don't want to display in this output, such
+as passwords or other sensitive input, you can configure your application to
+redact those parameters:
+
+  C<< MyApp->config->{Debug}->{redact_parameters} = [ 'password' ] >>
+
+In that case, the output will look like:
+
+ [debug] Query Parameters are:
+ .-------------------------------------+--------------------------------------.
+ | Parameter                           | Value                                |
+ +-------------------------------------+--------------------------------------+
+ | password                            | (redacted by config)                 |
+ | username                            | some_user                            |
+ '-------------------------------------+--------------------------------------'
+
+=cut
+
+sub log_parameters {
+    my ( $c, $name, $parameters ) = @_;
+
+    my $skip = $c->config->{Debug}->{redact_parameters};
+    if ( 
+        ( not defined $skip or ref $skip eq 'ARRAY' )
+        && keys %{ $parameters } 
+     ) {
+        my $t = Text::SimpleTable->new( 
+            [ 35, 'Parameter' ], [ 36, 'Value' ] );
+        my %skip_params = map { $_ => $_ } @{ $skip || [] };
+        for my $key ( sort keys %$parameters ) {
+            my $param = $parameters->{$key};
             my $value = defined($param) ? $param : '';
+            $value = '(redacted by config)' if exists $skip_params{$key};
+
             $t->row( $key,
                 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
         }
-        $c->log->debug( "Query Parameters are:\n" . $t->draw );
+        $c->log->debug( "$name:\n" . $t->draw );
     }
 }
 
@@ -2520,6 +2610,8 @@ audreyt: Audrey Tang
 
 bricas: Brian Cassidy <bricas@cpan.org>
 
+Byron Young <Byron.Young@riverbed.com>
+
 Caelum: Rafael Kitover <rkitover@io.com>
 
 chansen: Christian Hansen
@@ -2546,6 +2638,8 @@ ilmari: Dagfinn Ilmari MannsÃ¥ker <ilmari@ilmari.org>
 
 jcamacho: Juan Camacho
 
+jhannah: Jay Hannah <jay@jays.net>
+
 Jody Belka
 
 Johan Lindstrom
@@ -2572,6 +2666,8 @@ obra: Jesse Vincent
 
 omega: Andreas Marienborg
 
+Oleg Kostyuk <cub.uanic@gmail.com>
+
 phaylon: Robert Sedlacek <phaylon@dunkelheit.at>
 
 rafl: Florian Ragwitz <rafl@debian.org>
@@ -2580,6 +2676,8 @@ sky: Arthur Bergman
 
 the_jester: Jesse Sheidlower
 
+t0m: Tomas Doran <bobtfish@bobtfish.net>
+
 Ulf Edvinsson
 
 willert: Sebastian Willert <willert@cpan.org>