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;
|
||||
/*!40000 ALTER TABLE `calcms_work_schedule` DISABLE 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;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
@@ -1191,3 +1213,5 @@ UNLOCK TABLES;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2018-01-14 17:23:51
|
||||
|
||||
|
||||
|
||||
@@ -5,22 +5,19 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
|
||||
use CGI::Simple();
|
||||
use CGI::Session qw(-ip-match);
|
||||
use CGI::Cookie();
|
||||
|
||||
#$CGI::Session::IP_MATCH=1;
|
||||
use Data::Dumper;
|
||||
use Authen::Passphrase::BlowfishCrypt();
|
||||
|
||||
use time();
|
||||
use user_sessions ();
|
||||
|
||||
use base 'Exporter';
|
||||
our @EXPORT_OK = qw(get_user login logout crypt_password);
|
||||
my $defaultExpiration = 60;
|
||||
my $tmp_dir = '/var/tmp/calcms-session';
|
||||
my $debug = 0;
|
||||
|
||||
sub debug;
|
||||
my $debug = 0;
|
||||
sub debug($);
|
||||
|
||||
#TODO: remove CGI
|
||||
sub get_user($$$) {
|
||||
@@ -38,7 +35,7 @@ sub get_user($$$) {
|
||||
return $user;
|
||||
} elsif ( $params->{authAction} eq 'logout' ) {
|
||||
$cgi = new CGI::Simple() unless defined $cgi;
|
||||
logout($cgi);
|
||||
logout($config, $cgi);
|
||||
$cgi->delete( 'user', 'password', 'uri', 'authAction' );
|
||||
return undef;
|
||||
}
|
||||
@@ -51,14 +48,13 @@ sub get_user($$$) {
|
||||
return show_login_form( $params->{user}, 'Please login' ) unless defined $session_id;
|
||||
|
||||
# read session
|
||||
my $session = read_session($session_id);
|
||||
my $session = read_session($config, $session_id);
|
||||
|
||||
# login if user not found
|
||||
return show_login_form( $params->{user}, 'unknown User' ) unless defined $session;
|
||||
|
||||
$params->{user} = $session->{user};
|
||||
$params->{expires} = $session->{expires};
|
||||
debug( $params->{expires} );
|
||||
return $session->{user}, $session->{expires};
|
||||
}
|
||||
|
||||
@@ -82,29 +78,28 @@ sub login($$$) {
|
||||
my $password = shift;
|
||||
debug("login") if $debug;
|
||||
|
||||
#print STDERR "login $user $password\n";
|
||||
my $result = authenticate( $config, $user, $password );
|
||||
|
||||
#print STDERR Dumper($result);
|
||||
|
||||
return show_login_form( $user, 'Could not authenticate you' ) unless defined $result;
|
||||
return unless defined $result->{login} eq '1';
|
||||
|
||||
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 undef;
|
||||
}
|
||||
|
||||
#TODO: remove cgi
|
||||
sub logout($) {
|
||||
sub logout($$) {
|
||||
my $config = shift;
|
||||
my $cgi = shift;
|
||||
|
||||
my $session_id = read_cookie();
|
||||
debug("logout") if $debug;
|
||||
unless ( delete_session($session_id) ) {
|
||||
unless ( delete_session($config, $session_id) ) {
|
||||
return show_login_form( 'Cant delete session', 'logged out' );
|
||||
}
|
||||
unless ( delete_cookie($cgi) ) {
|
||||
@@ -127,8 +122,7 @@ sub create_cookie($$) {
|
||||
-expires => $timeout,
|
||||
-secure => 1
|
||||
);
|
||||
print "Set-Cookie: ", $cookie->as_string, "\n";
|
||||
print STDERR "#Set-Cookie: ", $cookie->as_string, "\n";
|
||||
print "Set-Cookie: " . $cookie->as_string . "\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -160,49 +154,46 @@ sub delete_cookie($) {
|
||||
}
|
||||
|
||||
# read and write server-side session data
|
||||
# expiration is in seconds
|
||||
# timeout is in seconds
|
||||
sub create_session ($$$) {
|
||||
my $config = shift;
|
||||
my $user = shift;
|
||||
my $password = shift;
|
||||
my $expiration = shift;
|
||||
my $timeout = shift;
|
||||
|
||||
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;
|
||||
|
||||
debug("read_session") if $debug;
|
||||
return undef unless defined $session_id;
|
||||
|
||||
debug("read_session2") if $debug;
|
||||
my $session = CGI::Session->new( undef, $session_id, { Directory => $tmp_dir } );
|
||||
my $session = user_sessions::check( $config, { session_id => $session_id } );
|
||||
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 {
|
||||
user => $user,
|
||||
expires => $expires
|
||||
user => $session->{user},
|
||||
expires => $session->{expires_at}
|
||||
};
|
||||
}
|
||||
|
||||
sub delete_session($) {
|
||||
sub delete_session($$) {
|
||||
my $config = shift;
|
||||
my $session_id = shift;
|
||||
|
||||
debug("delete_session") if $debug;
|
||||
return undef unless ( defined $session_id );
|
||||
my $session = CGI::Session->new( undef, $session_id, { Directory => $tmp_dir } );
|
||||
$session->delete();
|
||||
return undef unless defined $session_id;
|
||||
|
||||
user_sessions::stop( $config, { session_id => $session_id } );
|
||||
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 $entry = shift;
|
||||
|
||||
return unless ( defined $entry->{user} );
|
||||
return unless defined $entry->{user};
|
||||
|
||||
my $dbh = db::connect($config);
|
||||
return db::insert( $dbh, 'calcms_user_settings', $entry );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user