user sessions: use database instead of filesystem
User sessions will be stored in database and not in files anymore. CGI::Session is not used anymore for this purpose. The new module user_sessions.pm provides functions on new database table user_sessions.
This commit is contained in:
@@ -1179,6 +1179,28 @@ CREATE TABLE `calcms_work_schedule` (
|
|||||||
LOCK TABLES `calcms_work_schedule` WRITE;
|
LOCK TABLES `calcms_work_schedule` WRITE;
|
||||||
/*!40000 ALTER TABLE `calcms_work_schedule` DISABLE KEYS */;
|
/*!40000 ALTER TABLE `calcms_work_schedule` DISABLE KEYS */;
|
||||||
/*!40000 ALTER TABLE `calcms_work_schedule` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `calcms_work_schedule` ENABLE KEYS */;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `calcms_user_sessions`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `calcms_user_sessions` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`session_id` varchar(64) NOT NULL,
|
||||||
|
`user` varchar(30) NOT NULL,
|
||||||
|
`start` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`end` timestamp NULL DEFAULT NULL,
|
||||||
|
`expires_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||||
|
`timeout` int(10) unsigned NOT NULL,
|
||||||
|
`pid` int(10) unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `id_UNIQUE` (`id`),
|
||||||
|
UNIQUE KEY `session_id_UNIQUE` (`session_id`),
|
||||||
|
KEY `user` (`user`),
|
||||||
|
KEY `session_id` (`session_id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
@@ -1191,3 +1213,5 @@ UNLOCK TABLES;
|
|||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
-- Dump completed on 2018-01-14 17:23:51
|
-- Dump completed on 2018-01-14 17:23:51
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,22 +5,19 @@ use warnings;
|
|||||||
no warnings 'redefine';
|
no warnings 'redefine';
|
||||||
|
|
||||||
use CGI::Simple();
|
use CGI::Simple();
|
||||||
use CGI::Session qw(-ip-match);
|
|
||||||
use CGI::Cookie();
|
use CGI::Cookie();
|
||||||
|
|
||||||
#$CGI::Session::IP_MATCH=1;
|
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Authen::Passphrase::BlowfishCrypt();
|
use Authen::Passphrase::BlowfishCrypt();
|
||||||
|
|
||||||
use time();
|
use time();
|
||||||
|
use user_sessions ();
|
||||||
|
|
||||||
use base 'Exporter';
|
use base 'Exporter';
|
||||||
our @EXPORT_OK = qw(get_user login logout crypt_password);
|
our @EXPORT_OK = qw(get_user login logout crypt_password);
|
||||||
my $defaultExpiration = 60;
|
my $defaultExpiration = 60;
|
||||||
my $tmp_dir = '/var/tmp/calcms-session';
|
|
||||||
my $debug = 0;
|
|
||||||
|
|
||||||
sub debug;
|
my $debug = 0;
|
||||||
|
sub debug($);
|
||||||
|
|
||||||
#TODO: remove CGI
|
#TODO: remove CGI
|
||||||
sub get_user($$$) {
|
sub get_user($$$) {
|
||||||
@@ -38,7 +35,7 @@ sub get_user($$$) {
|
|||||||
return $user;
|
return $user;
|
||||||
} elsif ( $params->{authAction} eq 'logout' ) {
|
} elsif ( $params->{authAction} eq 'logout' ) {
|
||||||
$cgi = new CGI::Simple() unless defined $cgi;
|
$cgi = new CGI::Simple() unless defined $cgi;
|
||||||
logout($cgi);
|
logout($config, $cgi);
|
||||||
$cgi->delete( 'user', 'password', 'uri', 'authAction' );
|
$cgi->delete( 'user', 'password', 'uri', 'authAction' );
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@@ -51,14 +48,13 @@ sub get_user($$$) {
|
|||||||
return show_login_form( $params->{user}, 'Please login' ) unless defined $session_id;
|
return show_login_form( $params->{user}, 'Please login' ) unless defined $session_id;
|
||||||
|
|
||||||
# read session
|
# read session
|
||||||
my $session = read_session($session_id);
|
my $session = read_session($config, $session_id);
|
||||||
|
|
||||||
# login if user not found
|
# login if user not found
|
||||||
return show_login_form( $params->{user}, 'unknown User' ) unless defined $session;
|
return show_login_form( $params->{user}, 'unknown User' ) unless defined $session;
|
||||||
|
|
||||||
$params->{user} = $session->{user};
|
$params->{user} = $session->{user};
|
||||||
$params->{expires} = $session->{expires};
|
$params->{expires} = $session->{expires};
|
||||||
debug( $params->{expires} );
|
|
||||||
return $session->{user}, $session->{expires};
|
return $session->{user}, $session->{expires};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,29 +78,28 @@ sub login($$$) {
|
|||||||
my $password = shift;
|
my $password = shift;
|
||||||
debug("login") if $debug;
|
debug("login") if $debug;
|
||||||
|
|
||||||
#print STDERR "login $user $password\n";
|
|
||||||
my $result = authenticate( $config, $user, $password );
|
my $result = authenticate( $config, $user, $password );
|
||||||
|
|
||||||
#print STDERR Dumper($result);
|
|
||||||
|
|
||||||
return show_login_form( $user, 'Could not authenticate you' ) unless defined $result;
|
return show_login_form( $user, 'Could not authenticate you' ) unless defined $result;
|
||||||
return unless defined $result->{login} eq '1';
|
return unless defined $result->{login} eq '1';
|
||||||
|
|
||||||
my $timeout = $result->{timeout} || $defaultExpiration;
|
my $timeout = $result->{timeout} || $defaultExpiration;
|
||||||
$timeout = '+' . $timeout . 'm';
|
my $session_id = create_session( $config, $user, $timeout * 60 );
|
||||||
|
|
||||||
my $session_id = create_session( $user, $password, $timeout );
|
# here timeout is in minutes
|
||||||
|
$timeout = '+' . $timeout . 'm';
|
||||||
return $user if create_cookie( $session_id, $timeout );
|
return $user if create_cookie( $session_id, $timeout );
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
#TODO: remove cgi
|
#TODO: remove cgi
|
||||||
sub logout($) {
|
sub logout($$) {
|
||||||
|
my $config = shift;
|
||||||
my $cgi = shift;
|
my $cgi = shift;
|
||||||
|
|
||||||
my $session_id = read_cookie();
|
my $session_id = read_cookie();
|
||||||
debug("logout") if $debug;
|
debug("logout") if $debug;
|
||||||
unless ( delete_session($session_id) ) {
|
unless ( delete_session($config, $session_id) ) {
|
||||||
return show_login_form( 'Cant delete session', 'logged out' );
|
return show_login_form( 'Cant delete session', 'logged out' );
|
||||||
}
|
}
|
||||||
unless ( delete_cookie($cgi) ) {
|
unless ( delete_cookie($cgi) ) {
|
||||||
@@ -127,8 +122,7 @@ sub create_cookie($$) {
|
|||||||
-expires => $timeout,
|
-expires => $timeout,
|
||||||
-secure => 1
|
-secure => 1
|
||||||
);
|
);
|
||||||
print "Set-Cookie: ", $cookie->as_string, "\n";
|
print "Set-Cookie: " . $cookie->as_string . "\n";
|
||||||
print STDERR "#Set-Cookie: ", $cookie->as_string, "\n";
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -160,49 +154,46 @@ sub delete_cookie($) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# read and write server-side session data
|
# read and write server-side session data
|
||||||
# expiration is in seconds
|
# timeout is in seconds
|
||||||
sub create_session ($$$) {
|
sub create_session ($$$) {
|
||||||
|
my $config = shift;
|
||||||
my $user = shift;
|
my $user = shift;
|
||||||
my $password = shift;
|
my $timeout = shift;
|
||||||
my $expiration = shift;
|
|
||||||
|
|
||||||
debug("create_session") if $debug;
|
debug("create_session") if $debug;
|
||||||
mkdir $tmp_dir unless -e $tmp_dir;
|
|
||||||
my $session = CGI::Session->new( undef, undef, { Directory => $tmp_dir } );
|
|
||||||
$session->expire($expiration);
|
|
||||||
$session->param( "user", $user );
|
|
||||||
$session->param( "pid", $$ );
|
|
||||||
|
|
||||||
return $session->id();
|
my $session_id = user_sessions::start(
|
||||||
|
$config, {
|
||||||
|
user => $user,
|
||||||
|
timeout => $timeout,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return $session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub read_session($) {
|
sub read_session($$) {
|
||||||
|
my $config = shift;
|
||||||
my $session_id = shift;
|
my $session_id = shift;
|
||||||
|
|
||||||
debug("read_session") if $debug;
|
|
||||||
return undef unless defined $session_id;
|
return undef unless defined $session_id;
|
||||||
|
|
||||||
debug("read_session2") if $debug;
|
my $session = user_sessions::check( $config, { session_id => $session_id } );
|
||||||
my $session = CGI::Session->new( undef, $session_id, { Directory => $tmp_dir } );
|
|
||||||
return undef unless defined $session;
|
return undef unless defined $session;
|
||||||
|
|
||||||
debug("read_session3") if $debug;
|
|
||||||
my $user = $session->param("user") || undef;
|
|
||||||
return undef unless defined $user;
|
|
||||||
my $expires = time::time_to_datetime( $session->param("_SESSION_ATIME") + $session->param("_SESSION_ETIME") );
|
|
||||||
return {
|
return {
|
||||||
user => $user,
|
user => $session->{user},
|
||||||
expires => $expires
|
expires => $session->{expires_at}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub delete_session($) {
|
sub delete_session($$) {
|
||||||
|
my $config = shift;
|
||||||
my $session_id = shift;
|
my $session_id = shift;
|
||||||
|
|
||||||
debug("delete_session") if $debug;
|
debug("delete_session") if $debug;
|
||||||
return undef unless ( defined $session_id );
|
return undef unless defined $session_id;
|
||||||
my $session = CGI::Session->new( undef, $session_id, { Directory => $tmp_dir } );
|
|
||||||
$session->delete();
|
user_sessions::stop( $config, { session_id => $session_id } );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
231
lib/calcms/user_sessions.pm
Normal file
231
lib/calcms/user_sessions.pm
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
package user_sessions;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
no warnings 'redefine';
|
||||||
|
|
||||||
|
use Digest::MD5();
|
||||||
|
|
||||||
|
use time;
|
||||||
|
|
||||||
|
# access user name by session id
|
||||||
|
|
||||||
|
# table: calcms_user_sessions
|
||||||
|
# columns: id,
|
||||||
|
# user,
|
||||||
|
# timeout,
|
||||||
|
# pid,
|
||||||
|
# start (timestamp),
|
||||||
|
# end (timestamp),
|
||||||
|
|
||||||
|
use base 'Exporter';
|
||||||
|
our @EXPORT_OK = qw(get_columns get insert update delete);
|
||||||
|
|
||||||
|
sub debug;
|
||||||
|
|
||||||
|
sub get_columns($) {
|
||||||
|
my $config = shift;
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
my $cols = db::get_columns( $dbh, 'calcms_user_sessions' );
|
||||||
|
my $columns = {};
|
||||||
|
for my $col (@$cols) {
|
||||||
|
$columns->{$col} = 1;
|
||||||
|
}
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map schedule id to id
|
||||||
|
sub get($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $condition = shift;
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
|
||||||
|
my @conditions = ();
|
||||||
|
my @bind_values = ();
|
||||||
|
|
||||||
|
if ( ( defined $condition->{id} ) && ( $condition->{id} ne '' ) ) {
|
||||||
|
push @conditions, 'id=?';
|
||||||
|
push @bind_values, $condition->{id};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ( defined $condition->{user} ) && ( $condition->{user} ne '' ) ) {
|
||||||
|
push @conditions, 'user=?';
|
||||||
|
push @bind_values, $condition->{user};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ( defined $condition->{session_id} ) && ( $condition->{session_id} ne '' ) ) {
|
||||||
|
push @conditions, 'session_id=?';
|
||||||
|
push @bind_values, $condition->{session_id};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ( defined $condition->{start} ) && ( $condition->{start} ne '' ) ) {
|
||||||
|
push @conditions, 'start>?';
|
||||||
|
push @bind_values, $condition->{start};
|
||||||
|
}
|
||||||
|
|
||||||
|
my $conditions = '';
|
||||||
|
$conditions = " where " . join( " and ", @conditions ) if @conditions;
|
||||||
|
|
||||||
|
my $query = qq{
|
||||||
|
select *
|
||||||
|
from calcms_user_sessions
|
||||||
|
$conditions
|
||||||
|
order by start
|
||||||
|
};
|
||||||
|
|
||||||
|
my $entries = db::get( $dbh, $query, \@bind_values );
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
# insert entry and return database id
|
||||||
|
sub insert ($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry->{user};
|
||||||
|
return undef unless defined $entry->{timeout};
|
||||||
|
|
||||||
|
unless ( defined $entry->{session_id} ) {
|
||||||
|
my $md5 = Digest::MD5->new();
|
||||||
|
$md5->add( $$, time(), rand(time) );
|
||||||
|
$entry->{session_id} = $md5->hexdigest();
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry->{pid} = $$;
|
||||||
|
$entry->{expires_at} = time::time_to_datetime( time() + $entry->{timeout} );
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
return db::insert( $dbh, 'calcms_user_sessions', $entry );
|
||||||
|
}
|
||||||
|
|
||||||
|
# start session and return generated session id
|
||||||
|
sub start($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry->{user};
|
||||||
|
return undef unless defined $entry->{timeout};
|
||||||
|
|
||||||
|
my $id = insert(
|
||||||
|
$config,
|
||||||
|
{
|
||||||
|
user => $entry->{user},
|
||||||
|
timeout => $entry->{timeout},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return undef unless defined $id;
|
||||||
|
|
||||||
|
my $sessions = get( $config, { id => $id } );
|
||||||
|
return undef unless defined $sessions;
|
||||||
|
|
||||||
|
my $session = $sessions->[0];
|
||||||
|
return undef unless defined $session;
|
||||||
|
|
||||||
|
return $session->{session_id};
|
||||||
|
}
|
||||||
|
|
||||||
|
# expand session by timeout
|
||||||
|
sub keep_alive ($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry;
|
||||||
|
|
||||||
|
$entry->{pid} = $$;
|
||||||
|
$entry->{expires_at} = time::time_to_datetime( time() + $entry->{timeout} );
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
return update( $config, $entry );
|
||||||
|
}
|
||||||
|
|
||||||
|
# get session by session id and expand session if valid
|
||||||
|
sub check($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry;
|
||||||
|
my $entries = get( $config, { session_id => $entry->{session_id} } );
|
||||||
|
return undef unless defined $entry;
|
||||||
|
|
||||||
|
$entry = $entries->[0];
|
||||||
|
return undef unless defined $entry;
|
||||||
|
|
||||||
|
my $now = time::time_to_datetime( time() );
|
||||||
|
return undef unless defined $entry->{expires_at};
|
||||||
|
return undef unless defined $entry->{user};
|
||||||
|
|
||||||
|
return undef if $entry->{expires_at} le $now;
|
||||||
|
return undef if $entry->{end} && ( $entry->{end} le $now );
|
||||||
|
|
||||||
|
keep_alive( $config, $entry );
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
# stop session
|
||||||
|
sub stop ($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry;
|
||||||
|
|
||||||
|
my $entries = get( $config, { session_id => $entry->{session_id} } );
|
||||||
|
return undef unless defined $entries;
|
||||||
|
|
||||||
|
$entry = $entries->[0];
|
||||||
|
return undef unless defined $entry;
|
||||||
|
|
||||||
|
$entry->{end} = time::time_to_datetime( time() );
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
return update( $config, $entry );
|
||||||
|
}
|
||||||
|
|
||||||
|
#schedule id to id
|
||||||
|
sub update ($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry->{session_id};
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
my $values = join( ",", map { $_ . '=?' } ( keys %$entry ) );
|
||||||
|
my @bind_values = map { $entry->{$_} } ( keys %$entry );
|
||||||
|
|
||||||
|
push @bind_values, $entry->{session_id};
|
||||||
|
|
||||||
|
my $query = qq{
|
||||||
|
update calcms_user_sessions
|
||||||
|
set $values
|
||||||
|
where session_id=?
|
||||||
|
};
|
||||||
|
return db::put( $dbh, $query, \@bind_values );
|
||||||
|
}
|
||||||
|
|
||||||
|
#map schedule id to id
|
||||||
|
sub delete($$) {
|
||||||
|
my $config = shift;
|
||||||
|
my $entry = shift;
|
||||||
|
|
||||||
|
return undef unless defined $entry->{session_id};
|
||||||
|
|
||||||
|
my $dbh = db::connect($config);
|
||||||
|
|
||||||
|
my $query = qq{
|
||||||
|
delete
|
||||||
|
from calcms_user_sessions
|
||||||
|
where session_id=?
|
||||||
|
};
|
||||||
|
my $bind_values = [ $entry->{session_id} ];
|
||||||
|
|
||||||
|
return db::put( $dbh, $query, $bind_values );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub error($) {
|
||||||
|
my $msg = shift;
|
||||||
|
print "ERROR: $msg<br/>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
#do not delete last line!
|
||||||
|
1;
|
||||||
@@ -176,7 +176,8 @@ sub insert ($$) {
|
|||||||
my $config = shift;
|
my $config = shift;
|
||||||
my $entry = shift;
|
my $entry = shift;
|
||||||
|
|
||||||
return unless ( defined $entry->{user} );
|
return unless defined $entry->{user};
|
||||||
|
|
||||||
my $dbh = db::connect($config);
|
my $dbh = db::connect($config);
|
||||||
return db::insert( $dbh, 'calcms_user_settings', $entry );
|
return db::insert( $dbh, 'calcms_user_settings', $entry );
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user