Add howto
[catagits/Catalyst-Plugin-Authentication.git] / lib / Catalyst / Authentication / Credential / Remote.pm
index b4266c6..5e20adc 100644 (file)
@@ -1,14 +1,13 @@
 package Catalyst::Authentication::Credential::Remote;
+use Moose;
+use namespace::autoclean;
 
-use strict;
-use warnings;
+with 'MooseX::Emulate::Class::Accessor::Fast';
 
-use base 'Class::Accessor::Fast';
+use Try::Tiny qw/ try catch /;
 
-BEGIN {
-    __PACKAGE__->mk_accessors(
-        qw/allow_re deny_re cutname_re source realm username_field/);
-}
+__PACKAGE__->mk_accessors(
+    qw/allow_re deny_re cutname_re source realm username_field/);
 
 sub new {
     my ( $class, $config, $app, $realm ) = @_;
@@ -17,21 +16,27 @@ sub new {
     bless $self, $class;
 
     # we are gonna compile regular expresions defined in config parameters
-    # and explicitly throw an exception saying what parameter was invalid 
-    if (defined($config->{allow_regexp}) && ($config->{allow_regexp} ne "")) { 
-        eval { $self->allow_re( qr/$config->{allow_regexp}/ ) };
-        Catalyst::Exception->throw( "Invalid regular expression in ".
-        "'allow_regexp' configuration parameter") if $@;
+    # and explicitly throw an exception saying what parameter was invalid
+    if (defined($config->{allow_regexp}) && ($config->{allow_regexp} ne "")) {
+        try { $self->allow_re( qr/$config->{allow_regexp}/ ) }
+        catch {
+            Catalyst::Exception->throw( "Invalid regular expression in ".
+                "'allow_regexp' configuration parameter");
+        };
     }
-    if (defined($config->{deny_regexp}) && ($config->{deny_regexp} ne "")) { 
-        eval { $self->deny_re( qr/$config->{deny_regexp}/ ) };     
-        Catalyst::Exception->throw( "Invalid regular expression in ".
-             "'deny_regexp' configuration parameter") if $@;
+    if (defined($config->{deny_regexp}) && ($config->{deny_regexp} ne "")) {
+        try { $self->deny_re( qr/$config->{deny_regexp}/ ) }
+        catch {
+            Catalyst::Exception->throw( "Invalid regular expression in ".
+                 "'deny_regexp' configuration parameter");
+        };
     }
-    if (defined($config->{cutname_regexp}) && ($config->{cutname_regexp} ne "")) { 
-        eval { $self->cutname_re( qr/$config->{cutname_regexp}/ ) };
-        Catalyst::Exception->throw( "Invalid regular expression in ".
-             "'cutname_regexp' configuration parameter") if $@;
+    if (defined($config->{cutname_regexp}) && ($config->{cutname_regexp} ne "")) {
+        try { $self->cutname_re( qr/$config->{cutname_regexp}/ ) }
+        catch {
+            Catalyst::Exception->throw( "Invalid regular expression in ".
+                "'cutname_regexp' configuration parameter");
+        };
     }
     $self->source($config->{source} || 'REMOTE_USER');
     $self->realm($realm);
@@ -45,7 +50,7 @@ sub authenticate {
     my $remuser;
     if ($self->source eq "REMOTE_USER") {    
         # compatibility hack:
-        if (defined($c->engine->env)) {
+        if ($c->engine->can('env') && defined($c->engine->env)) {
             # BEWARE: $c->engine->env was broken prior 5.80005
             $remuser = $c->engine->env->{REMOTE_USER};
         }
@@ -58,12 +63,12 @@ sub authenticate {
             # maybe show warning that we are gonna use DEPRECATED $req->user            
             if (ref($c->req->user)) {
                 # I do not know exactly when this happens but it happens
-               Catalyst::Exception->throw( "Cannot get remote user from ".
-               "\$c->req->user as it seems to be a reference not a string" );
-           }
-           else {
-               $remuser = $c->req->user;
-           }
+            Catalyst::Exception->throw( "Cannot get remote user from ".
+        "\$c->req->user as it seems to be a reference not a string" );
+        }
+        else {
+            $remuser = $c->req->user;
+        }
         }
     }    
     elsif ($self->source =~ /^(SSL_CLIENT_.*|CERT_*|AUTH_USER)$/) {
@@ -104,9 +109,8 @@ sub authenticate {
             $usr = $1;
         }
     }
-    
-    $authinfo->{id} = $authinfo->{ $self->username_field } = $usr;
-    $authinfo->{remote_user} = $remuser; # just to keep the original value
+
+    $authinfo->{ $self->username_field } = $usr;
     my $user_obj = $realm->find_user( $authinfo, $c );
     return ref($user_obj) ? $user_obj : undef;
 }
@@ -177,7 +181,7 @@ is able to handle.
 Besides the common methods like HTTP Basic and Digest authentication you can
 also use sophisticated ones like so called "integrated authentication" via
 NTLM or Kerberos (popular in corporate intranet applications running in Windows
-Active Directory enviroment) or even the SSL authentication when users 
+Active Directory environment) or even the SSL authentication when users 
 authenticate themself using their client SSL certificates.   
 
 The main idea of this module is based on a fact that webserver passes the name
@@ -255,7 +259,7 @@ The order deny-allow is fixed.
 This config item is B<OPTIONAL> - no default value.
 
 If param B<cutname_regexp> is specified we try to cut the final usename passed to
-Catalyst application as a substring from WEBUSER. This is usefull for 
+Catalyst application as a substring from WEBUSER. This is useful for 
 example in case of SSL authentication when WEBUSER looks like this 
 'CN=john, OU=Unit Name, O=Company, C=CZ' - from this format we can simply cut
 pure usename by cutname_regexp set to 'CN=(.*), OU=Unit Name, O=Company, C=CZ'.
@@ -300,4 +304,61 @@ support $c->req->remote_user.
 This module tries some workarounds when it detects an older version and should
 work as well.
 
+=head1 USING WITH A REVERSE PROXY
+
+If you are using a reverse proxy, then the WEBUSER will not be
+directly accessible by the Catalyst server.  To use remote
+authentication, you will have to modify the web server to set a header
+containing the WEBUSER.  You would then need to modify the PSGI
+configuration to map the header back to the WEBUSER variable.
+
+For example, in Apache you would add the configuration
+
+  RequestHeader unset X-Forwarded-User
+  RewriteEngine On
+  RewriteCond %{LA-U:REMOTE_USER} (.+)
+  RewriteRule . - [E=RU:%1]
+  RequestHeader set X-Forwarded-User %{RU}e
+
+You then need to create a Plack::Middleware module to map the
+header back to the WEBUSER:
+
+  package Plack::Middleware::MyRemote;
+
+  use parent qw( Plack::Middleware );
+
+  use Plack::Util;
+
+  sub call {
+      my ($self, $env) = @_;
+
+      my $user = $env->{HTTP_X_FORWARDED_USER} // "";
+
+      $env->{REMOTE_USER} = $user
+        if ($user && ($user ne '(null)'));
+
+      my $res = $self->app->($env);
+
+      return $res;
+  }
+
+  1;
+
+Finally, you need to modify F<myapp.psgi> to use the custom middleware:
+
+  use strict;
+  use warnings;
+
+  use MyApp;
+
+  use Plack::Builder;
+
+  my $app = Drain->apply_default_middlewares(Drain->psgi_app);
+
+  builder {
+     enable "Plack::Middleware::MyRemote";
+     $app;
+  };
+
+
 =cut