[Catalyst-commits] r10301 - branches/Catalyst-Plugin-Authentication/credential_remote/lib/Catalyst/Authentication/Credential

kmx at dev.catalyst.perl.org kmx at dev.catalyst.perl.org
Tue May 26 21:36:24 GMT 2009


Author: kmx
Date: 2009-05-26 21:36:24 +0000 (Tue, 26 May 2009)
New Revision: 10301

Added:
   branches/Catalyst-Plugin-Authentication/credential_remote/lib/Catalyst/Authentication/Credential/Remote.pm
Log:
Catalyst::Authentication::Credential::Remote - first try, more or less works, needs some testing

Added: branches/Catalyst-Plugin-Authentication/credential_remote/lib/Catalyst/Authentication/Credential/Remote.pm
===================================================================
--- branches/Catalyst-Plugin-Authentication/credential_remote/lib/Catalyst/Authentication/Credential/Remote.pm	                        (rev 0)
+++ branches/Catalyst-Plugin-Authentication/credential_remote/lib/Catalyst/Authentication/Credential/Remote.pm	2009-05-26 21:36:24 UTC (rev 10301)
@@ -0,0 +1,237 @@
+package Catalyst::Authentication::Credential::Remote;
+
+use strict;
+use warnings;
+
+use base 'Class::Accessor::Fast';
+
+BEGIN {
+    __PACKAGE__->mk_accessors(qw/allow_re deny_re cutname_re source realm/);
+}
+
+sub new {
+    my ( $class, $config, $app, $realm ) = @_;
+
+    my $self = { };
+    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 $@;
+    }
+    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->{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 $@;
+    }
+    $self->source($config->{source} || 'REMOTE_USER');
+    $self->realm($realm);
+    return $self;
+}
+
+sub authenticate {
+    my ( $self, $c, $realm, $authinfo ) = @_;
+
+    my $remuser;
+    if ($self->source eq "REMOTE_USER") {
+	# BEWARE: $c->engine->env was broken prior 5.80005
+	$remuser = $c->engine->env->{REMOTE_USER};
+        # the original idea was to use $c->req->remote_user but ...
+    }
+    elsif ($self->source eq "SSL_CLIENT_DN") {
+        # if user is authenticated via SSL certificate his distinguished name
+        # is available in SSL_CLIENT_DN variable
+	# BEWARE: $c->engine->env was broken prior 5.80005    
+        $remuser = $c->engine->env->{SSL_CLIENT_DN};    
+    }
+    else {
+        Catalyst::Exception->throw( "Invalid value of 'source' parameter");
+    }
+    return unless defined($remuser);
+    return if ($remuser eq "");
+
+    # $authinfo hash can contain item username (it is optional) - if it is so
+    # this username has to be equal to remote_user 
+    my $authuser = $authinfo->{username};             
+    return if (defined($authuser) && ($authuser ne $remuser));
+
+    # handle deny / allow checks 
+    return if (defined($self->deny_re)  && ($remuser =~ $self->deny_re));
+    return if (defined($self->allow_re) && ($remuser !~ $self->allow_re));
+
+    # if param cutname_regexp is specified we try to cut the final usename as a
+    # substring from remote_user 
+    my $usr = $remuser;
+    if (defined($self->cutname_re)) {
+        if (($remuser =~ $self->cutname_re) && ($1 ne "")) {
+            $usr = $1;
+    }
+    }
+    
+    $authinfo->{id} = $authinfo->{username} = $usr; 
+    $authinfo->{remote_user} = $remuser; # just to keep the original value
+    my $user_obj = $realm->find_user( $authinfo, $c );
+    return ref($user_obj) ? $user_obj : undef;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Authentication::Credential::Remote - Let the webserver (e.g. Apache)
+authenticate Catalyst application users
+
+=head1 SYNOPSIS
+
+    # in your MyApp.pm
+    __PACKAGE__->config(
+
+        'Plugin::Authentication' => {
+            default_realm => 'remoterealm',
+            realms => {
+                remoterealm => {
+                    credential => {
+                        class        => 'Remote',
+                        allow_regexp => '^(user.*|admin|guest)$',
+                        deny_regexp  => 'test',
+                    },
+                    store => {
+                        class => 'Null',
+                        # if you want to have some additional user attributes
+			# like user roles, user full name etc. you can specify
+			# here the store where you keep this data
+                    }
+                },
+            },
+        },
+        
+    );
+    
+    # in your Controller/Root.pm you can implement "auto-login" in this way
+    sub begin : Private {
+        my ( $self, $c ) = @_;        
+        if(defined($c->req->remote_user) and !$c->user_exists) {
+            # authenticate() for this module does not need any user info
+            # as the username is taken from $c->req->remote_user and
+	    # password is not needed	     
+	    $c->authenticate( {} );
+	}   
+    }
+
+    # or you can implement in any controller an ordinary login action like this
+    sub login : Global {
+        my ( $self, $c ) = @_;
+        $c->authenticate( {} );
+    }
+
+=head1 DESCRIPTION
+
+This module allows you to authenticate the users of your Catalyst application
+on underlaying webserver. The complete list of authentication method available 
+via this module depends just on what your webserver (e.g. Apache, IIS, Lighttpd)
+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 
+authenticate themself using their client SSL certificates.   
+
+The main idea of this module is based on a fact that webserver passes the name
+of authenticated user into Catalyst application as REMOTE_USER variable (or in 
+case of SSL client authentication SSL_CLIENT_DN) - from this point referenced as 
+WEBUSER. This module simply takes this value - perfoms some optional checks (see
+below) - and if everything is OK the WEBUSER is declared as authenticated on 
+Catalyst level. In fact this module does not perform any check for password or 
+other credential; it simply believes the webserver that user was properly 
+authenticated.
+
+=head1 CONFIG
+
+=head2 class
+
+This config item is B<REQUIRED>. 
+
+B<class> is part of the core L<Catalyst::Plugin::Authentication> module, it 
+contains the class name of the store to be used.
+
+The classname used for Credential. This is part of L<Catalyst::Plugin::Authentication>
+and is the method by which Catalyst::Authentication::Credential::Remote is
+loaded as the credential validator. For this module to be used, this must be set
+to 'Remote'.
+
+=head2 source
+
+This config item is B<OPTIONAL> - default is REMOTE_USER.
+
+B<source> contains a name of a variable passed from webserver that contains the 
+user identification - supported values: REMOTE_USER, SSL_CLIENT_DN
+
+=head2 deny_regexp
+
+This config item is B<OPTIONAL> - no default value.
+
+B<deny_regexp> contains a regular expression used for check against WEBUSER (details see below)
+
+=head2 allow_regexp
+
+This config item is B<OPTIONAL> - no default value.
+
+B<deny_regexp> contains a regular expression used for check against WEBUSER.
+
+Allow/deny checking of WEBUSER values goes in this way:
+
+1) If B<deny_regexp> is defined and WEBUSER matches deny_regexp then 
+authentication FAILS otherwise continues with next step. If deny_regexp is not 
+defined or is an empty string we skip this step.  
+
+2) If B<allow_regexp> is defined and WEBUSER matches allow_regexp then 
+authentication PASSES otherwise FAILS. If allow_regexp is not 
+defined or is an empty string we skip this step.
+
+The order deny-allow is fixed.
+
+=head2 cutname_regexp
+
+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 
+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'.
+
+Substring is always taken as '$1' regexp substring. If WEBUSER does not
+match cutname_regexp at all or if '$1' regexp substring is empty we pass the
+original WEBUSER value (without cutting) to Catalyst application.
+
+=head1 METHODS
+
+=head2 new ( $config, $app, $realm )
+
+Instantiate a new Catalyst::Authentication::Credential::Remote object using the
+configuration hash provided in $config. In case of invalid value of any 
+configuration parameter (e.g. invalid regular expression) throws an exception.
+
+=cut
+
+=head2 authenticate ( $realm, $authinfo )
+
+Takes the username form WEBUSER set by webserver, performs additional 
+checks using optional allow_regexp/deny_regexp configuration params, optionaly 
+takes substring from WEBUSER and the sets the resulting value as
+a Catalyst username.
+
+=cut




More information about the Catalyst-commits mailing list