# Slauth web interface package Slauth::User::Web; use strict; #use warnings FATAL => 'all', NONFATAL => 'redefine'; use Slauth::Config; use Slauth::Config::Apache; BEGIN { if ( $Slauth::Config::Apache::MOD_PERL >= 2 ) { require Apache2::Response; require Apache2::RequestRec; require Apache2::RequestUtil; require Apache2::RequestIO; require Apache2::URI; require Apache2::Const; import Apache2::Const qw( HTTP_OK OK REDIRECT ); } else { require Apache2; require Apache::RequestRec; require Apache::RequestIO; require Apache::RequestUtil; require Apache::Const; import Apache::Const qw( HTTP_OK OK REDIRECT ); } require APR::Pool; require APR::Table; } use Slauth::Storage::Session_DB; use Slauth::Storage::User_DB; use CGI qw( :common ); use CGI::Carp qw(fatalsToBrowser); sub debug { $Slauth::Config::debug; } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->initialize(@_); return $self; } sub initialize { my $self = shift; # check for valid parameters while ( @_ ) { my $pname = shift; my $pval = shift; if ( $pname eq "request" ) { $self->{r} = $pval; } } # set up CGI.pm my $cgi = CGI->new(); $self->{cgi} = $cgi; # instantiate the configuration $self->{config} = Slauth::Config->new( ( defined $self->{r}) ? $self->{r} : ()); # set up tags for template my $server_name = $cgi->server_name; $self->{tags} = { "self_url" => $self->self_url, "script_name" => $cgi->script_name, "path_info" => $cgi->path_info, "referer" => $cgi->referer, "remote_addr" => $cgi->remote_addr, "remote_host" => $cgi->remote_host, "remote_user" => $cgi->remote_user, "request_method" => $cgi->request_method, "server_name" => $cgi->server_name, "server_port" => $cgi->server_port, "user_agent" => $cgi->user_agent, "server_port" => $cgi->server_port, "server_protocol" => $cgi->server_protocol, }; # open the template file my $slauth_dir = $self->get( "dir" ); my $template_file = $self->get( "template" ); if ( ! open ( TFILE, "$slauth_dir/$template_file" )) { close TFILE; croak "can't open slauth template file: $!\n"; } # gulp the template $self->{template} = []; @{$self->{template}} = ; close TFILE; # get request path if ( $cgi->path_info eq "" ) { return $self->redirect( $cgi->self_url."/" ); } $self->{path} = []; @{$self->{path}} = split ( '/', $cgi->path_info ); shift @{$self->{path}}; # attempt to get the remote IP address if we're running mod_perl 2.0 #eval { # use Apache::RequestUtil; # my $r = Apache->request; # my $c = $r->connection; # $self->{remote_addr} = $c->remote_addr->ip_get; #} or $self->{remote_addr} = $cgi->remote_addr; $self->{remote_addr} = $cgi->remote_addr; } # get configuration value # configuration values are read-only sub get { my ( $self, $key ) = @_; return $self->{config}->get( $key ); } # read CGI parameters # CGI paramaters are read-only sub param { my ( $self, $name ) = @_; if ( defined $name ) { return $self->{cgi}->param($name); } else { return $self->{cgi}->param; } } # set cookies for response sub cookie { my $self = shift; if ( ! defined $self->{cookies}) { $self->{cookies} = []; } my $cookie = $self->{cgi}->cookie( @_ ); push ( @{$self->{cookies}}, $cookie ); } # get or set template tag value # template tags are read-write sub tag { my ( $self, $name, $value ) = @_; if ( defined $value ) { if ( defined $self->{tags}{$name}) { $self->{tags}{$name} .= "\n".$value; } else { $self->{tags}{$name} = $value; } } else { if ( defined $self->{tags}{$name}) { return $self->{tags}{$name} } return undef; } } # main web interface function sub interface { my $self = shift; my $cgi = $self->{cgi}; my $realm = $self->get( "realm" ); # determine content from request path my @path; if ( defined $self->{path}) { @path = @{$self->{path}}; } debug and print STDERR "Slauth::User::Web::interface: realm=$realm " ."path=".join(' / ', @path)."\n"; if ( ! @path ) { $self->{tags}{subtitle} = "Authentication Menu", $self->{tags}{text} = "\n", } elsif ( $path[0] eq "register" ) { $self->do_register; } elsif ( $path[0] eq "login" ) { $self->do_login; } elsif ( $path[0] eq "error-login" ) { $self->do_login; } elsif ( $path[0] eq "maint" ) { $self->do_maint; } else { $self->{tags}{subtitle} = "Unrecognized Path", $self->{tags}{text} = "The path you requested does not exist.", } # handle redirects if ( defined $self->{tags}{dest}) { return $self->redirect( $self->{tags}{dest} ); } # # send HTTP response # # start with response headers my @header_params; # set content type if ( defined $self->{r}) { $self->{r}->content_type( "text/html" ); $self->{r}->no_cache(1); } else { push ( @header_params, -type => "text/html" ); } # set cookies if ( defined $self->{cookies}) { my $cookie; foreach $cookie ( @{$self->{cookies}}) { debug and print STDERR "Slauth::User::Web::interface: " ."cookie=$cookie\n"; if ( defined $self->{r}) { # mod_perl mode $self->{r}->headers_out->add( 'Set-Cookie' => $cookie ); } else { # CGI mode push ( @header_params, -cookie => $cookie ); } } } # set response status if ( !defined $self->{tags}{status}) { if ( defined $self->{r}) { $self->{tags}{status} = HTTP_OK; } else { $self->{tags}{status} = 200; } } debug and print STDERR "Slauth::User::Web::interface: " ."status=".$self->{tags}{status}."\n"; if ( defined $self->{r}) { # mod_perl mode $self->{r}->status($self->{tags}{status}); } else { # CGI mode push ( @header_params, -status => $self->{tags}{status}); } # send headers out if ( defined $self->{r}) { $self->{r}->rflush; $self->{r}->print( "\n" ); } else { if ( @header_params ) { print $cgi->header(@header_params); } else { print $cgi->header; } } # print text response $self->template_out; if ( defined $self->{r}) { return OK; } else { return; } } sub template_out { my $self = shift; my $template = $self->{template}; my $i; for ( $i = 0; $i < @$template; $i++ ) { print $self->template_line( $template->[$i]); } } sub template_line { my ( $self, $line ) = @_; my $tags = $self->{tags}; my ( $result ); ( defined $line ) or return; if ( $line =~ /^(.*)\%([a-z0-9_]+)\%(.*)/s ) { my ( $before, $tag, $after ) = ( $1, $2, $3 ); if ( defined $tags->{$tag}) { if ( ref $tags->{$tag} eq "CODE" ) { $result = $tags->{$tag}($tags); } elsif ( ref $tags->{$tag} eq "ARRAY" ) { $result = "\n".join( "", (@{$tags->{$tag}})) ."\n"; } else { $result = $tags->{$tag}; } } else { $result = $self->get( $tag ); if ( !defined $result ) { $result = ""; } } return $self->template_line($before) .$self->template_line($result) .$self->template_line($after); } else { return $line; } } sub check_params { my $self = shift; my ( @reqs ) = @_; foreach ( @reqs ) { defined $self->param( $_ ) or return 0; } return 1; } sub new_session { my $self = shift; my $login = shift; my $text; # create login session my $session_db = Slauth::Storage::Session_DB->new($self->{config}); if ( $session_db->error ) { $text = "An error has occurred.\n"; $text .= "The storage subsystem failed.\n"; $text .= "Your user record was stored\n"; $text .= "with the password you used.\n"; $text .= "A login session was not started.\n"; goto DONE; } my $session_hash = $session_db->write_record( $login, $self->{config}); # set the cookie if ( $session_hash ) { my $domain = $self->get( "cookie-domain" ); my $expires = $self->get( "cookie-expires" ); my @cookie_params = ( -name => "slauth_session", -value => $session_hash ); if ( defined $domain ) { push ( @cookie_params, -domain => $domain ); } if ( defined $expires ) { push ( @cookie_params, -expires => $expires ); } $self->cookie( @cookie_params ); if ( $self->param("dest")) { # transfer destination URL to # result tags $self->tag( "dest", $self->param("dest")); return; # skip text tag processing } $text = "Login successful.\n"; $text .= "A cookie has been set in your\n"; $text .= "browser which will allow you\n"; $text .= "into restricted areas of the\n"; $text .= "site appropriate to your\n"; $text .= "mail list memberships.\n"; } else { $text = "An error has occurred.\n"; $text .= "The storage subsystem failed.\n"; $text .= "Your user record was stored\n"; $text .= "with the password you used.\n"; $text .= "A login session was not started.\n"; } DONE: $self->tag("text", $text ); } sub do_register { my $self = shift; my $cgi = $self->{cgi}; my $tags = $self->{tags}; my %register; foreach my $class ( split ( /\s+/, $self->get( "register" ))) { my $reg; eval "require $class" or croak "failed to load $class: $!\n"; $reg = $class->new($self->{config}); if ( my $short_name = $reg->short_name ) { $register{$short_name} = $reg; } } if ( defined $self->{path}[1]) { my $reg = $register{$self->{path}[1]}; if ( defined $reg and ref $reg ne "" ) { if ( defined $self->{path}[2]) { my @subpath = @{$self->{path}}; shift @subpath; shift @subpath; $reg->process_path($self, @subpath ); } elsif ( $self->check_params( $reg->req_params )) { my %vars = $cgi->Vars; $reg->process_form( $self ); } else { $tags->{subtitle} = $reg->long_name; $tags->{text} = $reg->html_form( $self ); } } else { croak "bad registration method '" .$self->{path}[1]."'\n"; } } else { my @keys = keys %register; if ( @keys == 1 ) { # If there's only one registration method, # don't bother with a menu. Just redirect to it. $self->{tags}{dest} = $self->{cgi}->script_name ."/".$self->{path}[0] ."/".$keys[0]; return; } $tags->{subtitle} = "Web Access Registration", $tags->{text} = "Please select a registration method.\n"; $tags->{text} .= "\n"; } } sub do_login { my $self = shift; my $cgi = $self->{cgi}; my $tags = $self->{tags}; my ( $text, $dest ); if ( $self->{path}[0] eq "error-login" ) { $dest = $tags->{self_url}; } # if login and pw are provided, process the login attempt if ( $self->check_params( "login", "pw" )) { my $login = $cgi->param("login"); my $pw = $cgi->param("pw"); debug and print STDERR "Slauth::User::Web::interface: " ."check login=$login\n"; if ( Slauth::Storage::User_DB::check_pw( $login, $pw, $self->{config})) { $self->new_session( $login ); return; } else { $text = "The login information was incorrect.\n"; } # otherwise provide a login form } else { debug and print STDERR "Slauth::User::Web::interface: " ."login form\n"; $text = "
script_name() ."/login/\">\n"; if ( defined $dest ) { $text .= "\n"; } $text .= "
\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "\n"; $text .= "
\n"; $text .= "Please log in\n"; $text .= "
\n"; $text .= "You must enable cookies in your browser\n"; $text .= "to continue\n"; $text .= "
User name:
Password:
\n"; $text .= "If you don't have a login, please \n"; $text .= "script_name."/register/\">register.
\n"; $text .= "
\n"; } $self->tag("text", $text ); } sub do_maint { my $self = shift; # must be logged in to use this function if (( ! defined $self->{remote_user}) or ! $self->{remote_user}) { my $text = "You must be\n"; $text .= "logged in\n"; $text .= "to use this function."; $self->tag("text", $text ); return; } # if new password is provided, process it if ( $self->{path}[1] eq "change-pw" ) { if ( $self->check_params( "pw1", "pw2" )) { } else { } return } # maintenance menu debug and print STDERR "Slauth::User::Web::interface: " ."login form\n"; } # # utility functions # # handle a redirect to a new URL sub redirect { my $self = shift; my $url = shift; if ( defined $self->{r}) { # mod_perl mode my $r = $self->{r}; if ( defined $self->{cookies}) { my $cookie; foreach $cookie ( @{$self->{cookies}}) { $r->err_headers_out->add( 'Set-Cookie' => $cookie); } } $r->headers_out->set( "Location" => $url ); $r->status(REDIRECT); return REDIRECT; } else { # CGI mode my $cgi = $self->{cgi}; if ( defined $self->{cookies}) { my $cookie; foreach $cookie ( @{$self->{cookies}}) { print $cgi->header( -cookie => $cookie ); } } print $cgi->redirect( $cgi->self_url."/" ); return; } } # get the original request URL, even if we're processing an error document sub self_url { my $self = shift; if ( defined $self->{r}) { # mod_perl mode my $r = $self->{r}; # figure out where the request really came from my $req_str = $r->the_request; $req_str =~ s/^[^\s]*\s+//; $req_str =~ s/\s+[^\s]*$//; return $r->construct_url($req_str); } else { # CGI mode - this is ineffective for error documents my $cgi = $self->{cgi}; return $cgi->self_cgi; } } # mod_perl response handler sub handler { my $r = shift; my $web = new Slauth::User::Web ( "request" => $r ); return $web->interface; } 1;