Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Embedding "user" CGI output into a mod_perl response

by bmcatt (Friar)
on May 09, 2006 at 22:08 UTC ( #548338=perlquestion: print w/replies, xml ) Need Help??

bmcatt has asked for the wisdom of the Perl Monks concerning the following question:

My fellow monks,

My instincts say that I should be able to do what I want, but I'm new enough to mod_perl that I don't see how to do it.

Basic issue: I'm upgrading a website that uses a bunch of external (vendor-ish) CGI scripts (cvsweb.cgi being one example). There are two primary goals for this upgrade:

  1. Remove the umpteen bazillion .htaccess files that are used in multiple places, consolidating all of the authentication in a single place. An adjunct to this is to build a login mechanism from the home page, etc.
  2. Provide consistent headers / footers for all pages, some of which is dynamic-ish content.
I've already converted all of "our" stuff over to mod_perl and am using HTML::Template for all dynamic bits. My trouble comes in when I get to trying to deal with the external CGI scripts because they all want to think they "own" the entire screen.

Simply putting all of these external CGIs under mod_perl isn't sufficient because I then lose the consistent header/footer as well as the ability to (outside of .htaccess) control access to these pages.

My gut says that I should be able to have all of the requests that would (normally) go to a CGI pointed through a portal-ish mod_perl routine. This portal would, in theory at least, take care of the access controls / restrictions as well as then "proxying" for the underlying CGI scripts.

Following this train of thought, I should (somehow) be able to construct the proper internal (to Apache) CGI call, including knitting the authentication info to show that I've got an authenticated user, and then intercept the results of the CGI and insert that into my pre-fabricated display structure.

My problem is... Given that I can grab any arbitrary URL (Yes, I have complete control over this server), how can I inject myself into the CGI process so that I, and not Apache, own the call to the CGI?

Any / all thoughts and suggestions from anyone who's done anything remotely like this (or knows enough mod_perl to point me in the right direction) would be greatly appreciated.

  • Comment on Embedding "user" CGI output into a mod_perl response

Replies are listed 'Best First'.
Re: Embedding "user" CGI output into a mod_perl response
by perrin (Chancellor) on May 10, 2006 at 01:44 UTC
    For access control, write a mod_perl handler that hooks into the Access or Authentication/Authorization phases of apache. The mod_perl docs have lots of information on this.

    If you're using mod_perl 2, you can customize the output using an output filter written in perl. This is also in the mod_perl docs.

      Good ideas, but coming from the wrong side. I've already got the access and authentication pieces. While I might be able to tackle this with an output filter, that doesn't give me a good way to pre-empt the access and authentication aspects.

      I'm still pretty sure that there should be a way to, from a mod_perl2 handler, "pretend" to be Apache starting up a cgi script and then have the output available for further processing.

        I'm not following. Why can't you "pre-empt" the access and authentication? A mod_perl access handler can run for static files, CGI scripts, PHP, etc. It's a standard phase provided by the web server.

        I think you are overestimating how easy it is to do your own CGI. You can try copying some code from one of the perld HTTP daemons on CPAN, but there's a lot to deal with in terms of proper error handling and security. Better to let mod_cgi do it if you can.

      perrin, I am forced to bow to your mastery here... with appreciation.

      I haven't finished all the bits that I need, but it's definitely turning out to be significantly easier to do this as you suggested than I had originally feared. And, yes, this is mod_perl2, so I can do an output filter.

      For anyone else who cares, relevant code (and otherwise) fragments: (Names have been changed to protect the guilty...)


      PerlModule Foo::FooAuth PerlModule Foo::FooFilter <Location /foo/cgi-bin/foo.cgi> PerlOutputFilterHandler Foo::FooFilter PerlAuthenHandler Foo::FooAuth require valid-user </Location>
      package Foo::FooAuth; use strict; use Apache2::Const qw(:common); use Foo::AuthAux; # Provides whoami - uses a cookie set elsewhere sub handler { my $r = shift; my $user = whoami($r); unless ($user) { $r->note_basic_auth_failure; return AUTH_REQUIRED; } $r->user($user); $r->subprocess_env(FOO => "bar+$user"); return OK; } 1;
      package Foo::FooFilter; use strict; use Apache2::Filter; use Apache2::Const qw(:common); use Apache2::Request; use HTML::Template; use Foo::Config; # Provides $TMPL_PATH below use Foo::Auth; use Foo::AuthAux; # Provides whoami below my $BUFF_LEN = 8192; sub handler { my $f = shift; my $r = $f->r; my $fullbuf; while ($f->read(my $buf, $BUFF_LEN)) { $fullbuf .= $buf; } my $tmpl = HTML::Template->new(scalarref => \$fullbuf, path => $TMPL_PATH, die_on_bad_params => 0, ); my $user = whoami($r); my $userinfo = ISPNET::Auth::userinfo($user); $tmpl->param(_user => $user, _logo => $userinfo->{logo}, _logo_alt => $userinfo->{logo_alt}, # Set _home (would be $ENV{SCRIPT_NAME})... ); $f->print($tmpl->output); return OK; } 1;

      I'm still debating where to put the .cgi scripts (in the filesystem) and have to figure out how to reference them from elsewhere (which will be used to adjust the $tmpl->param(_home => ...) bits, but otherwise, it seems to be working nicely.

Re: Embedding "user" CGI output into a mod_perl response
by ioannis (Abbot) on May 10, 2006 at 01:26 UTC
    To customize the response to the browser, you will need to capture the output from the cgi script. For cgi scripts at another machine, your Apache handler will in affect act as a forward proxy to the request: it will send the request with LWP::UserAgent and capture the output with LWP::Response.

    There is no difficulty if you know the Apache API. Just ensure sure that all incoming and outgoing headers are copied, especially the HTTP headers that may appear twice, like Cookie -- you cannot use functions like $r->headers_in that return hash structures, thus suppressing the second Cookie header.

      This is actually going to be for cgi scripts that are deployed (in the current "old" system) on the same system, so there's no need to act as a full proxy. Theoretically, I *should* be able to just use some aspect of the Apache interface(s) to fire off the CGI script "inline" and then extract the body of the response.

      Thanks for the suggestion, though. I'd already thought of proxying it, but that then leaves me needing to have an exposed (in the sense of publicly-visible) cgi interface and I'm explicitly trying to avoid that.

Re: Embedding "user" CGI output into a mod_perl response
by Joost (Canon) on May 10, 2006 at 14:30 UTC
Re: Embedding "user" CGI output into a mod_perl response
by derby (Abbot) on May 10, 2006 at 11:49 UTC

    I've never done it but if I would attempt it, I would start by looking at Apache::PerlRun. Modelling from Apache::PerlRun, I would figure out how to capture the script's STDOUT (localizing STDOUT before the scripts run and re-opening STDOUT out on an IO::Scalar??). Once I have the scripts output in a scalar, then I would filter it. Sounds like a real pain ... I would give serious consideration to just using an iframe.


Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://548338]
Approved by GrandFather
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2022-01-27 06:42 GMT
Find Nodes?
    Voting Booth?
    In 2022, my preferred method to securely store passwords is:

    Results (70 votes). Check out past polls.