This file is indexed.

/usr/share/perl5/Catalyst/Authentication/Credential/Remote.pm is in libcatalyst-plugin-authentication-perl 0.10023-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package Catalyst::Authentication::Credential::Remote;
use Moose;
use namespace::autoclean;

with 'MooseX::Emulate::Class::Accessor::Fast';

use Try::Tiny qw/ try catch /;

__PACKAGE__->mk_accessors(
    qw/allow_re deny_re cutname_re source realm username_field/);

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 "")) {
        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 "")) {
        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 "")) {
        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);
    $self->username_field($config->{username_field} || 'username');
    return $self;
}

sub authenticate {
    my ( $self, $c, $realm, $authinfo ) = @_;

    my $remuser;
    if ($self->source eq "REMOTE_USER") {    
        # compatibility hack:
        if ($c->engine->can('env') && defined($c->engine->env)) {
            # BEWARE: $c->engine->env was broken prior 5.80005
            $remuser = $c->engine->env->{REMOTE_USER};
        }
        elsif ($c->req->can('remote_user')) {
            # $c->req->remote_users was introduced in 5.80005; if not evailable we are
            # gonna use $c->req->user that is deprecated but more or less works as well 
            $remuser = $c->req->remote_user;
        }
        elsif ($c->req->can('user')) {
            # 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;
        }
        }
    }    
    elsif ($self->source =~ /^(SSL_CLIENT_.*|CERT_*|AUTH_USER)$/) {
        # if you are using 'exotic' webserver or if the user is 
        # authenticated e.g via SSL certificate his name could be avaliable
        # in different variables
        # BEWARE: $c->engine->env was broken prior 5.80005
        my $nam=$self->source;
        if ($c->engine->can('env')) {
            $remuser = $c->engine->env->{$nam};
        }
        else {
            # this happens on Catalyst 5.80004 and before (when using FastCGI)
            Catalyst::Exception->throw( "Cannot handle parameter 'source=$nam'".
                " as runnig Catalyst engine has broken \$c->engine->env" );
        }
    }
    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->{ $self->username_field } = $usr;
    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 ) = @_;        
        unless ($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     
            unless ($c->authenticate( {} )) {
              # return 403 forbidden or kick out the user in other way
            };
        }   
    }

    # 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 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
of authenticated user into Catalyst application as REMOTE_USER variable (or in 
case of SSL client authentication in other variables like SSL_CLIENT_S_DN on
Apache + mod_ssl) - 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_*, CERT_*, AUTH_USER

B<BEWARE:> Support for using different variables than REMOTE_USER does not work 
properly with Catalyst 5.8004 and before (if you want details see source code). 

Note1: Apache + mod_ssl uses SSL_CLIENT_S_DN, SSL_CLIENT_S_DN_* etc. (has to be 
enabled by 'SSLOption +StdEnvVars') or you can also let Apache make a copy of 
this value into REMOTE_USER (Apache option 'SSLUserName SSL_CLIENT_S_DN'). 

Note2: Microsoft IIS uses CERT_SUBJECT, CERT_SERIALNUMBER etc. for storing info
about client authenticated via SSL certificate. AUTH_USER on IIS seems to have
the same value as REMOTE_USER (but there might be some differences I am not
aware of).

=head2 deny_regexp

This config item is B<OPTIONAL> - no default value.

B<deny_regexp> contains a regular expression used for check against WEBUSER 
(see details 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 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'.

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.

=head2 username_field

This config item is B<OPTIONAL> - default is I<username>

The key name in the authinfo hash that the user's username is mapped into.
This is useful for using a store which requires a specific unusual field name
for the username.  The username is additionally mapped onto the I<id> key.

=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

=head1 COMPATIBILITY

It is B<strongly recommended> to use this module with Catalyst 5.80005 and above
as previous versions have some bugs related to $c->engine->env and do not 
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