[Catalyst] recommended/best practice way of serving static files after authentication?

Peter Karman peter at peknet.com
Wed Jul 29 03:25:46 GMT 2009


Tomas Doran wrote on 7/28/09 7:25 PM:
> 
> On 28 Jul 2009, at 14:55, Ash Berlin wrote:
> 
>>
>> On 28 Jul 2009, at 14:42, John SJ Anderson wrote:
>>
>>>
>>>  $WORK-mate is working on a Cat app that (among other things) allows
>>>  authenticated users to download from a set of static files. He's
>>>  wondering what the best way is to set things up so that the Cat app
>>>  can handle the authentication/authorization parts of things but then
>>>  hand the static file serving part off to Apache.
>>>
>>>  Thanks for any help/hints.
>>>
>>> john.
>>
>> Largely depends on what webserver you are using. Lighttpd has
>> X-LIGHTTPD-Sendfile (or something similarly named) nginx has something
>> like X-Accel-Redirect which does the same (and can do more.) Both of
>> these tell the webserver to serve static content from a file. Hit
>> google for these env vars to find out how they work
>>
>> There's an apache module which will do the same as the lighttpd one
>> http://tn123.ath.cx/mod_xsendfile/ Never used it mind.
> 
> Whilst this is all sage advice, remember - DO THE SIMPLEST THING WHICH
> COULD POSSIBLY WORK.
> 
> Ergo, $c->serve_static_file($c->path_to(qw/ sekrit_files file1.avi /));
> is probably a good bet (From Catalyst::Plugin::Static::Simple).
> 
> Unless you have dozens of concurrent users, all downloading large files
> (where large is 10s of Mbs), then just do that - it's easy, it works,
> you don't have to think about production vs development, etc.

This is also sage advice. It will work well in development, and can also work in
production if your cat app proves to scale to your demand.

With a little more code you can scale far more than C::P::S::S will allow. I
like to use mod_auth_tkt with Apache for general authn/authz tasks, and it works
just as well for one-time authn for serving static files.

Something like this (in MyApp.pm):

 use Apache::AuthTkt;

 __PACKAGE__->config(
     auth_tkt_conf => '/etc/httpd/conf.d/auth_tkt.conf',
     static_uri    => 'http://mystatic.domain/authn/',
     static_user   => 'mystaticuser',
     static_tokens => 'catalyst',
     env           => 'prod',  # or 'dev' or 'test' or ...
 );

 sub serve_my_static_file {
     my ($c, $file) = @_;
     my $conf = $c->config;
     my $env  = $conf->{env} || 'dev';
     if ($env eq 'dev') {

         # do as t0m suggests
         $c->serve_static_file($c->path_to('sekrit', $file));
     }
     else {

         # if it doesn't exist (assuming same filesystem as MyApp)
         # then return 404
         if (! -s $c->path_to('sekrit', $file)) {
             $c->res->status(404);
             $c->res->body('not found');
         }
         else {

             # let apache serve it
             my $at = Apache::AuthTkt->new(
                        conf => $conf->{auth_tkt_conf}, # or hardcode secret
                        ignore_ip => 1, # in case req is proxied
                  );

             my $ticket = $at->ticket(
                        uid => $conf->{static_user},
                        tokens => $conf->{static_tokens},
                      ) or die $at->errstr;

             my $uri = join('',
                      $conf->{static_uri},
                      $file,
                      '?auth_tkt=',
                      $ticket
                   );
             $c->res->redirect($uri);
         }
     }
 }


and then a mod_auth_tkt.conf something like:

TKTAuthSecret foobarbing123
TKTAuthDigestType MD5
<Location /authn>

    # TKTAuthLoginURL must be present
    # but in this case we assume all requests
    # are pre-authenticated. Send someplace that
    # returns a custom 401 or whatever.
    TKTAuthLoginURL http://nosuchurl/

    # really short. i.e., as long as it takes for
    # the browser to receive the 302 and request the
    # new uri. 2 seconds should be *ample*
    TKTAuthTimeout 2

    # *IMPORTANT* lest requests use the same token over
    # and over again, negating its temporary intent.
    TKTAuthTimeoutRefresh 0

    # optional, really. just for example.
    TKTAuthToken catalyst

    # must match config in $at call above in perl
    TKTAuthIgnoreIP on

    # turn on to debug
    TKTAuthDebug 3

    # tell apache to protect this space
    AuthType None
    require valid-user mystaticuser

</Location>


Many variations on the theme are possible of course, including putting the
auth_tkt value in the uri path instead of as a param and then using mod_rewrite
in apache to untangle it.


-- 
Peter Karman  .  http://peknet.com/  .  peter at peknet.com
gpg key: 37D2 DAA6 3A13 D415 4295  3A69 448F E556 374A 34D9



More information about the Catalyst mailing list