views:

153

answers:

4

Using the examples from the CGI::Session::Auth::DBI and CGI::Session::Auth pages, I have attempted to implement the _login function with no success. I'm using Windows 7 and Apache 2.

#!/usr/bin/perl -w
use strict;

use CGI::Carp qw(fatalsToBrowser);

use CGI;
use CGI::Session;
use CGI::Session::Auth::DBI;

my $cgi = new CGI;

# using '.' directory for testing
my $session = new CGI::Session(undef, $cgi, {Directory=>'.'});
my $auth = new CGI::Session::Auth::DBI({
    CGI => $cgi,
    Session => $session,
    DSN => 'dbi:mysql:dbname=foobar:host=localhost',
    DBUser => 'foo',
    DBPasswd => 'bar',
    UserTable => 'cgi_auth_user' # auth_user already in use
});

print "Content-type: text/html\n\n";

if ($auth->_login("admin", "admin")) {
    print "<p>login ok</p>";
} else {
    print "<p>login fail</p>";
}

if ($auth->loggedIn) {
    print "<p>logged in; go to <a href='index.pl'>index</a></p>";
} else {
    print "<p>not logged in</p>";
}

The rendered output from this is:

login ok
not logged in

If I change the values passed to _login to "foo", "bar" (an invalid username/password), then I get this rendered result:

login fail
not logged in

I'm using '.' just for testing as I know it's a dir I can write to. Every time I run the code, a cgisess_ file is created (e.g. cgisess_9fb493cc9155ee9dd2b18fddc38139d8), but this is created regardless of if I use a correct username or not. No errors are being returned, but $auth->loggedIn is always false.

The documentation says that _login is virtual, and it sounds like the DBI module overrides this, but I'm not sure.

What could I be doing wrong?

Update 1:

I've also tried using $auth->authenticate() before the call to $auth->loggedIn but this has no effect. I've also tried using $auth->authenticate() and $auth->loggedIn on another after successful login, but I get the same result. No matter what I do, $auth->loggedI is always false.

Update 2:

I've also tried chaning the directory to "/" and all it does is create the cgisess files in / rather than current dir.

Update 3:

I figured it may be an issue with the database records; I'm using the default example ones from the example page, but with a modified admin password. Here's a phpMyAdmin export:

CREATE TABLE IF NOT EXISTS `cgi_auth_user` (
  `userid` char(32) collate utf8_unicode_ci NOT NULL,
  `username` varchar(30) collate utf8_unicode_ci NOT NULL,
  `passwd` varchar(30) collate utf8_unicode_ci NOT NULL default '',
  PRIMARY KEY  (`userid`),
  UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `cgi_auth_user` (`userid`, `username`, `passwd`) VALUES
('325684ec1b028eaf562dd484c5607a65', 'admin', 'admin'),
('ef19a80d627b5c48728d388c11900f3f', 'guest', 'guest');

Then again, if _login is returning true with a valid username and password, then I would assume that the userid is valid... No?

Update 4:

I've also tested this on our Linux production server, and I get the exact same issue.

+1  A: 

The doc is pretty clear that you have to override the _login() method, unless guest/guest works for you. Plus you aren't calling authenticate(), which actually invokes _login(), you shouldn't be doing it.

_login()

This virtual method performs the actual login attempt by comparing the login form data the visitor sent with some local user database. The _login method of the base class CGI::Session::Auth only knows the user 'guest' with password 'guest'.

To access a real user database, you have to use a subclass that modifies the _login method appropriately. See the modules in the Auth/ subdirectory.

ergosys
The "See the modules in the Auth/ subdirectory." part is a little misleading I think; since `CGI::Session::Auth::DBI` is in the Auth/ subdirectory (isn't it?) so it lead me to assume that this module overrides `_login`...
nbolton
By the way, `_login` returns true when I use a valid username and password, and false when I use false credentials. So I believe that I'm using it correctly. I will update my answer.
nbolton
Yeah after looking at it a bit I see _login() is overridden by DBI. authenticate() seems to use "log_password", "log_username" by default though.
ergosys
+1  A: 

Looking at the source code and documentation, _login() never actually sets the internal loggedin flag. That is only performed in the authenticate module. You should also remember that it is generally accepted perl practice that methods starting with '_' should be treated essentially as private methods, (or in Java terminology perhaps protected) only to be used by subclasses. So you shouldn't be using _login directly.

LoginVarPrefix: By default, CGI::Session::Auth expects the username and password of the visitor to be passed in the form variables 'log_username' and 'log_password'. To avoid conflicts, the prefix 'log_' can be altered by this parameter.

I believe this will work

$session->param('log_username', 'admin');
$session->param('log_password', 'admin');

$auth->authenticate(); # no return value to check

print "Content-type: text/html\n\n";

if ($auth->loggedIn) {
    print "<p>logged in; go to <a href='index.pl'>index</a></p>";
} else {
    print "<p>not logged in</p>";
}
Rob Van Dam
Thanks, I'll give this a shot tomorrow.
nbolton
Hmm still returning "not logged in" -- I copied and pasted your code.
nbolton
Aha! Almost, take a look at Wolf's answer. The code looks at $cgi params and not $session params.
nbolton
A: 

You can turn on logging by putting this above the my $cgi = new CGI; line

use Log::Log4perl qw(:easy);
# Set priority of root logger to DEBUG
Log::Log4perl->easy_init($DEBUG);

This will turn on debugging and allow you to see more of what is going on.

Jeremy Heslop
Thanks, this will be helpful I think.
nbolton
I had to install Log::Log4perl -- the page loads ok, but how can I see the debug output?
nbolton
+2  A: 

Try outputting the results of $session->header() as the first thing you output. This should set your cookie and load the existing session instead of creating a new one each time.

Also, _login() only performs the authentication with the database, but does not modify the $auth object. Using authenticate(), you need to define the username and password using the param() function of your $cgi object. You need to set the fields log_username and log_password for the authenticate() function to work.

#!/usr/bin/perl -w
use strict;

use CGI::Carp qw(fatalsToBrowser);

use CGI;
use CGI::Session;
use CGI::Session::Auth::DBI;

my $cgi = CGI->new;
my $session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});

my $auth = new CGI::Session::Auth::DBI({
    CGI => $cgi,
    Session => $session,
    DSN => 'dbi:mysql:dbname=foobar:host=localhost',
    DBUser => 'foo',
    DBPasswd => 'bar',
    UserTable => 'cgi_auth_user'
});

print $session->header();

$cgi->param('log_username', 'admin');
$cgi->param('log_password', 'admin');

$auth->authenticate();

if ($auth->loggedIn) {
    print "<p>logged in; go to <a href='index.pl'>index</a></p>";
} else {
    print "<p>not logged in</p>";
}

I haven't tested it, but this should be working.

Wolf
Perfect; I wonder if this is a hack way of doing it? The example code doesn't mention that we need to set `$cgi->param` -- I would assume it would use POST values, but I tried this and no luck. Anyway, your answer works, so thanks!
nbolton
Ah! Actually I just re-tried with POST and it turns out you need to use log_ before the field names: `print '<form method="post"><input type="text" name="log_username" /><input type="password" name="log_password" /><input type="submit" /></form>';`
nbolton
I believe you could also use GET, but that would not be too secure. ;)
Wolf