1 package Catalyst::Authentication::Credential::Remote;
5 use Try::Tiny qw/ try catch /;
7 use base 'Class::Accessor::Fast';
10 __PACKAGE__->mk_accessors(
11 qw/allow_re deny_re cutname_re source realm username_field/);
15 my ( $class, $config, $app, $realm ) = @_;
20 # we are gonna compile regular expresions defined in config parameters
21 # and explicitly throw an exception saying what parameter was invalid
22 if (defined($config->{allow_regexp}) && ($config->{allow_regexp} ne "")) {
23 try { $self->allow_re( qr/$config->{allow_regexp}/ ) }
25 Catalyst::Exception->throw( "Invalid regular expression in ".
26 "'allow_regexp' configuration parameter");
29 if (defined($config->{deny_regexp}) && ($config->{deny_regexp} ne "")) {
30 try { $self->deny_re( qr/$config->{deny_regexp}/ ) }
32 Catalyst::Exception->throw( "Invalid regular expression in ".
33 "'deny_regexp' configuration parameter");
36 if (defined($config->{cutname_regexp}) && ($config->{cutname_regexp} ne "")) {
37 try { $self->cutname_re( qr/$config->{cutname_regexp}/ ) }
39 Catalyst::Exception->throw( "Invalid regular expression in ".
40 "'cutname_regexp' configuration parameter");
43 $self->source($config->{source} || 'REMOTE_USER');
45 $self->username_field($config->{username_field} || 'username');
50 my ( $self, $c, $realm, $authinfo ) = @_;
53 if ($self->source eq "REMOTE_USER") {
55 if ($c->engine->can('env') && defined($c->engine->env)) {
56 # BEWARE: $c->engine->env was broken prior 5.80005
57 $remuser = $c->engine->env->{REMOTE_USER};
59 elsif ($c->req->can('remote_user')) {
60 # $c->req->remote_users was introduced in 5.80005; if not evailable we are
61 # gonna use $c->req->user that is deprecated but more or less works as well
62 $remuser = $c->req->remote_user;
64 elsif ($c->req->can('user')) {
65 # maybe show warning that we are gonna use DEPRECATED $req->user
66 if (ref($c->req->user)) {
67 # I do not know exactly when this happens but it happens
68 Catalyst::Exception->throw( "Cannot get remote user from ".
69 "\$c->req->user as it seems to be a reference not a string" );
72 $remuser = $c->req->user;
76 elsif ($self->source =~ /^(SSL_CLIENT_.*|CERT_*|AUTH_USER)$/) {
77 # if you are using 'exotic' webserver or if the user is
78 # authenticated e.g via SSL certificate his name could be avaliable
79 # in different variables
80 # BEWARE: $c->engine->env was broken prior 5.80005
81 my $nam=$self->source;
82 if ($c->engine->can('env')) {
83 $remuser = $c->engine->env->{$nam};
86 # this happens on Catalyst 5.80004 and before (when using FastCGI)
87 Catalyst::Exception->throw( "Cannot handle parameter 'source=$nam'".
88 " as runnig Catalyst engine has broken \$c->engine->env" );
92 Catalyst::Exception->throw( "Invalid value of 'source' parameter");
94 return unless defined($remuser);
95 return if ($remuser eq "");
97 # $authinfo hash can contain item username (it is optional) - if it is so
98 # this username has to be equal to remote_user
99 my $authuser = $authinfo->{username};
100 return if (defined($authuser) && ($authuser ne $remuser));
102 # handle deny / allow checks
103 return if (defined($self->deny_re) && ($remuser =~ $self->deny_re));
104 return if (defined($self->allow_re) && ($remuser !~ $self->allow_re));
106 # if param cutname_regexp is specified we try to cut the final usename as a
107 # substring from remote_user
109 if (defined($self->cutname_re)) {
110 if (($remuser =~ $self->cutname_re) && ($1 ne "")) {
115 $authinfo->{ $self->username_field } = $usr;
116 my $user_obj = $realm->find_user( $authinfo, $c );
117 return ref($user_obj) ? $user_obj : undef;
128 Catalyst::Authentication::Credential::Remote - Let the webserver (e.g. Apache)
129 authenticate Catalyst application users
136 'Plugin::Authentication' => {
137 default_realm => 'remoterealm',
142 allow_regexp => '^(user.*|admin|guest)$',
143 deny_regexp => 'test',
147 # if you want to have some additional user attributes
148 # like user roles, user full name etc. you can specify
149 # here the store where you keep this data
157 # in your Controller/Root.pm you can implement "auto-login" in this way
158 sub begin : Private {
159 my ( $self, $c ) = @_;
160 unless ($c->user_exists) {
161 # authenticate() for this module does not need any user info
162 # as the username is taken from $c->req->remote_user and
163 # password is not needed
164 unless ($c->authenticate( {} )) {
165 # return 403 forbidden or kick out the user in other way
170 # or you can implement in any controller an ordinary login action like this
172 my ( $self, $c ) = @_;
173 $c->authenticate( {} );
178 This module allows you to authenticate the users of your Catalyst application
179 on underlaying webserver. The complete list of authentication method available
180 via this module depends just on what your webserver (e.g. Apache, IIS, Lighttpd)
183 Besides the common methods like HTTP Basic and Digest authentication you can
184 also use sophisticated ones like so called "integrated authentication" via
185 NTLM or Kerberos (popular in corporate intranet applications running in Windows
186 Active Directory environment) or even the SSL authentication when users
187 authenticate themself using their client SSL certificates.
189 The main idea of this module is based on a fact that webserver passes the name
190 of authenticated user into Catalyst application as REMOTE_USER variable (or in
191 case of SSL client authentication in other variables like SSL_CLIENT_S_DN on
192 Apache + mod_ssl) - from this point referenced as WEBUSER.
193 This module simply takes this value - perfoms some optional checks (see
194 below) - and if everything is OK the WEBUSER is declared as authenticated on
195 Catalyst level. In fact this module does not perform any check for password or
196 other credential; it simply believes the webserver that user was properly
203 This config item is B<REQUIRED>.
205 B<class> is part of the core L<Catalyst::Plugin::Authentication> module, it
206 contains the class name of the store to be used.
208 The classname used for Credential. This is part of L<Catalyst::Plugin::Authentication>
209 and is the method by which Catalyst::Authentication::Credential::Remote is
210 loaded as the credential validator. For this module to be used, this must be set
215 This config item is B<OPTIONAL> - default is REMOTE_USER.
217 B<source> contains a name of a variable passed from webserver that contains the
220 Supported values: REMOTE_USER, SSL_CLIENT_*, CERT_*, AUTH_USER
222 B<BEWARE:> Support for using different variables than REMOTE_USER does not work
223 properly with Catalyst 5.8004 and before (if you want details see source code).
225 Note1: Apache + mod_ssl uses SSL_CLIENT_S_DN, SSL_CLIENT_S_DN_* etc. (has to be
226 enabled by 'SSLOption +StdEnvVars') or you can also let Apache make a copy of
227 this value into REMOTE_USER (Apache option 'SSLUserName SSL_CLIENT_S_DN').
229 Note2: Microsoft IIS uses CERT_SUBJECT, CERT_SERIALNUMBER etc. for storing info
230 about client authenticated via SSL certificate. AUTH_USER on IIS seems to have
231 the same value as REMOTE_USER (but there might be some differences I am not
236 This config item is B<OPTIONAL> - no default value.
238 B<deny_regexp> contains a regular expression used for check against WEBUSER
243 This config item is B<OPTIONAL> - no default value.
245 B<deny_regexp> contains a regular expression used for check against WEBUSER.
247 Allow/deny checking of WEBUSER values goes in this way:
249 1) If B<deny_regexp> is defined and WEBUSER matches deny_regexp then
250 authentication FAILS otherwise continues with next step. If deny_regexp is not
251 defined or is an empty string we skip this step.
253 2) If B<allow_regexp> is defined and WEBUSER matches allow_regexp then
254 authentication PASSES otherwise FAILS. If allow_regexp is not
255 defined or is an empty string we skip this step.
257 The order deny-allow is fixed.
259 =head2 cutname_regexp
261 This config item is B<OPTIONAL> - no default value.
263 If param B<cutname_regexp> is specified we try to cut the final usename passed to
264 Catalyst application as a substring from WEBUSER. This is useful for
265 example in case of SSL authentication when WEBUSER looks like this
266 'CN=john, OU=Unit Name, O=Company, C=CZ' - from this format we can simply cut
267 pure usename by cutname_regexp set to 'CN=(.*), OU=Unit Name, O=Company, C=CZ'.
269 Substring is always taken as '$1' regexp substring. If WEBUSER does not
270 match cutname_regexp at all or if '$1' regexp substring is empty we pass the
271 original WEBUSER value (without cutting) to Catalyst application.
273 =head2 username_field
275 This config item is B<OPTIONAL> - default is I<username>
277 The key name in the authinfo hash that the user's username is mapped into.
278 This is useful for using a store which requires a specific unusual field name
279 for the username. The username is additionally mapped onto the I<id> key.
283 =head2 new ( $config, $app, $realm )
285 Instantiate a new Catalyst::Authentication::Credential::Remote object using the
286 configuration hash provided in $config. In case of invalid value of any
287 configuration parameter (e.g. invalid regular expression) throws an exception.
291 =head2 authenticate ( $realm, $authinfo )
293 Takes the username form WEBUSER set by webserver, performs additional
294 checks using optional allow_regexp/deny_regexp configuration params, optionaly
295 takes substring from WEBUSER and the sets the resulting value as
302 It is B<strongly recommended> to use this module with Catalyst 5.80005 and above
303 as previous versions have some bugs related to $c->engine->env and do not
304 support $c->req->remote_user.
306 This module tries some workarounds when it detects an older version and should