Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Structuring multiple CGI::Application modules

by Anonymous Monk
on Jun 25, 2004 at 20:41 UTC ( [id://369746] : perlquestion . print w/replies, xml ) Need Help??

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

Having nearly finished my first C::A module (it performs authentication), I'm not sure what the best way is to chain to the next C::A module. I want multiple/separate C::A modules to be able to reuse the authentication module. I haven't been able to find many examples of this type and would appreciate suggestions from other C::A users. My current thought is to create the initial script to look like the following untested code:
#!/usr/local/bin/perl use MyAuth; ## has strict, warnings, etc. my $webapp = MyAuth->new( PARAMS => { 'Next_CGI' => "some_url_without_query_string" } ); $webapp->run();
Then teardown() within MyAuth.pm would look like the following:
sub teardown() my $self = shift; my $output = ''; ## probably best to store the following in a file my %Allowed_Scripts = ( 'https://site/pathto/ca.script1.cgi' => 1, 'https://site/pathto/ca.script2.cgi' => 1, ); ## usual teardown stuff my $url = $self->param('Next_CGI'); if ( exists($Allowed_Scripts{$url}) ) { if (authorized) { my $query_string = "?whatever"; $self->header_type('redirect'); $self->header_props( -url => "$url$query_string" ); } else { ## use template for unauthorized user message } } else { ## use template for unauthorized script message } return $output; }
Is this a reasonable way to handle an application using multiple CAs? Will it cause memory problems (bloat)? Thanks.

Replies are listed 'Best First'.
Re: Structuring multiple CGI::Application modules
by valdez (Monsignor) on Jun 25, 2004 at 21:49 UTC

    When I need to share authorization code between different C::A applications, I put the necessary code in the cgiapp_prerun() method of a base module; then every application use that module and inherits everything needed, including some common run modes (i.e. login, login_failed, etc). BTW, this is also the suggested solution in CGI::Application documentation.
    The following is a stripped down example from my code:

    package MyApp::Base; use base 'CGI::Application'; sub cgiapp_init { my $self = shift; # init your application: session, database, whatever is needed ... # set the name of the run mode CGI param $self->mode_param('rm'); # shared run modes $self->run_modes('login' => 'login', 'AUTOLOAD' => 'autoload_exception'); return; } ... sub session { return shift->param('SESSION'); } ... sub cgiapp_prerun { my $self = shift; my $runmode = shift; my $user = $self->session->param('user'); unless ($user) { $self->prerun_mode( 'login' ); } }
    Then in your application you only need to say:
    package MyApp::Application1; use base 'MyApp::Base'; ...
    and your application will be accessible only to authorized users.

    HTH, Valerio

Re: Structuring multiple CGI::Application modules
by MrCromeDome (Deacon) on Jun 25, 2004 at 20:55 UTC

    I haven't been using C::A for too terribly long, but so far, it's been a blessing to my web development efforts ;) Your approach to this is not too dissimilar to my own:

    My applications have a lot in common, from configuration files to session parameters to application parameters. Typically, when one of my apps needs to handle a login, they simply redirect to my user module as follows:

    # Are we logged in? If not, redirect someplace sane! unless(user_is_logged_in()) { $self->param("session")->param("login_redirect", $request->url + . "?mode=modify"); # Redirect! $self->header_type("redirect"); $self->header_props(-url => "/cgi-bin/users.cgi?rm=login"); return "Please wait. . ."; }
    Notice the login_redirect param. Login uses this to decide where to send the user upon a successful login.

    My login handling code then looks like this:

    # Check for username and password my $username = untaint_string($request->param("username")) || ""; my $password = untaint_string($request->param("password")) || ""; # Perform the login? my $page; if($username ne "") { # Log them in! my $check_user = user_login($username, $password); # Check login status if($username eq $check_user) { # Set session parameters $self->param("session")->param("is_logged_in", "Y"); # Where do we redirect upon success? my $redirect = $self->param("session")->param("login_redir +ect"); $redirect = $config{URL_BASE} unless($redirect ne ""); # Redirect! $self->header_type("redirect"); $self->header_props(-url => $redirect); return "Please wait. . ."; } elsif($username eq "INACTIVE") { $self->param("error", "This account is currently inactive. +"); $page = $self->login(); } else { $self->param("error", "Invalid user/password combination. Please enter a ne +w username and password."); $page = $self->login(); } } else { $self->param("error", "Invalid user/password combination. Ple +ase enter a new username and password."); $page = $self->login(); } return $page;
    Some of these functions are from my own application code, but you should get the gist of what is going on.

    Sorry that I am unable to answer your question with regards to bloat. I'm not the best at profiling an application :(

    Hope this helps you somewhat and answers your questions. Feel free to bombard me with your questions.

    Good luck!
    MrCromeDome

Re: Structuring multiple CGI::Application modules
by Arunbear (Prior) on Jun 25, 2004 at 21:21 UTC
    The cgiapp_prerun() method is the natural place to do authentication (teardown is for cleanup e.g. closing database connections).
    The following is from CGI::Application's documentation (see prerun_mode()):
    # In WebApp.pm: package WebApp; use base 'CGI::Application'; sub cgiapp_prerun { my $self = shift; # Get the web user name, if any my $q = $self->query(); my $user = $q->remote_user(); # Redirect to login, if necessary unless ($user) { $self->prerun_mode('login'); } }
    Once you've done something like the above in your MyAuth module, your other modules can subclass MyAuth to reuse the authentication code.
    Some useful nodes: Re: Re: Re: Why CGI::Application?, Re: Breaking up Large Modules..

      Your example is wrong: if you are going to use remote_user information from CGI.pm, then you will never reach that prerun mode without a valid user and the code unless ($user) ... is useless.

      Ciao, Valerio

Re: Structuring multiple CGI::Application modules
by dragonchild (Archbishop) on Jun 28, 2004 at 12:18 UTC
    Re: Why CGI::Application? has my thoughts on the matter.

    Update: Fixed broken link.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested