I'm using HTML::Mason with Apache2 mod_perl2 for a project and am unsure what's a good way to manage session state easily.
Please don't say use Catalyst. I normally do, but not on this occasion.
After struggling with this for a long time I finally have a working solution:
This is using mysql to store session data so that no matter which front-end server you hit, you will get the same session data.
You will need a db with a table called sessions made with this:
CREATE TABLE sessions (id char(32), length int, a_session text);
This is in MySession.pm in my INC path.
package MySession;
use DBI();
use Apache::Session::MySQL;
use Apache2::Cookie;
sub start_session($){
my ($r) = #_;
my $cookie_name = 'mysite-session';
my $cookie_domain = '.mysite.com';
my $dsn = "DBI:mysql:database=db;host=host.com";
my $dbuser = 'admin';
my $dbpass = 'password';
my $dbh = DBI->connect($dsn, $dbuser, $dbpass, {'RaiseError' => 1});
my $session_cookie = Apache2::Cookie->fetch($r)->{$cookie_name};
my %cookie_hash;
if(defined($session_cookie)){
%cookie_hash = $session_cookie->value();
}
tie my %session, 'Apache::Session::MySQL', $cookie_hash{SessionID}, {
Handle => $dbh,
LockHandle => $dbh
};
my $cookie = Apache2::Cookie->new($r,
-name => $cookie_name,
-domain => $cookie_domain,
-value => {SessionID => $session{_session_id}}
);
$cookie->bake($r);
return \%session;
}
1;
Then on any page you wish to use/modify session data:
% use MySession;
% my $session = MySession::start_session($r);
% $session->{variable} = "Wow, I have a cookie";
Looks like I've found the answer in
MasonX::Request::WithApacheSession
Related
We are writing a Perl code (to be run from Unix) which will reset the password of a Windows AD User. (We are not using powershell as we have been asked not to use Windows scripts).
With the following Perl code, we are able to connect to the AD User directory and query the correct user.
#!/usr/bin/perl -w
#########################
#This script resets the password in active user directory
#########################
use strict;
use warnings;
use DBI;
use Net::LDAP;
use Net::LDAPS;
use Authen::SASL qw(Perl);
use Net::LDAP::Control::Paged;
use Time::Local;
my $CERTDIR = "<cert path>";
my $AD_PASS = "$CERTDIR/.VDIAD_pass";
my $sAN = "vahmed";
### Generate Random Password ###
my $randompass = askPasswd();
my $uninewpass;
my $mail;
my $fullname;
my $name;
my $distName;
my $finalresult;
my #AD_passwords = get_domain_pass();
my $result = reset_AD_Password();
#Reset AD user password
sub reset_AD_Password {
my $ad = Net::LDAP->new($AD_passwords[0]);
my $msg = $ad->bind(dn => "cn=$AD_passwords[2],$AD_passwords[1]",
password => $AD_passwords[3],
version => 3);
if ($msg->code)
{
print "Error :" . $msg->error() . "\n";
exit 2;
}
my $acc_name = 'sAMAccountName';
my $acc_fullname = 'displayName';
my $acc_base = 'manager';
my $acc_distName = 'distinguishedName';
my $acc_mail = 'mail';
my $act = $ad->search(
base => "$AD_passwords[1]",
filter => "(&(objectCategory=person)(sAMAccountName=$sAN))",
attrs => [$acc_name, $acc_fullname, $acc_distName, $acc_mail]);
die 1 if ($act->count() !=1 );
my $samdn = $act->entry(0)->dn;
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
}
}
However we get an error on the line:
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
The error states "Can't locate object method "get_value" via package (distinguished Name) (perhaps you forgot to load (distinguished Name))"
However the code works correctly when we replace $samdn with the following code:
foreach my $entry ($act->entries){
$name = $entry->get_value($acc_name);
$fullname = $entry->get_value($acc_fullname);
$distName = $entry->get_value($acc_distName);
$mail = $entry->get_value($acc_mail);
}
It would appear that the code is unable to identify $samdn as a Net::LDAP::Entry record.
We have tried typecasting $samdn but got the same error.
Could someone help in resolving this issue as we would not prefer to use the for loop just in case more that one record is returned by the search? Thanks in advance.
You are not assigning a Net::LDAP::Entry to $samdn. You are assigning the dn of the first entry.
# VVVV
my $samdn = $act->entry(0)->dn;
Get rid of that ->dn and it should work, if $act->entry(0) returns a Net::LDAP::Entry.
I'm creating an info board app with data taken from firebird 1.5 database.
I would like not to use polling but events.
I've created sample trigger which fires up an event in firebird database.
Now I need a client to listen for and handle the event.
I've decided to go with perl.
Here is the doc DBD::Firebird.
I'm trying to go with async events.
Here's the code, I give the skeleton which won't work anyway.
The event won't fire up in perl. What am I doing wrong. Please help, thanks!
#!/usr/bin/perl
use DBI;
$dsn =<< "DSN";
dbi:Firebird:dbname=/home/firebird/dev/db.gdb;
host=localhost;
port=3050;
ib_dialect=3;
DSN
$dbh = DBI->connect($dsn, "user", "password");
# events of interest
#event_names = ("schedule_event");
$evh = $dbh->func(#event_names, 'ib_init_event');
my $cb = sub {.
print "got event";
};
$dbh->func($evh, $cb, 'ib_register_callback');
while (1) {
}
UPDATE:
ok here's the php script I'm going with right now, just leave it here maybe it helps someone:
<?php
declare(ticks = 1);
$username = "user";
$password = "password";
$host = 'localhost:/home/firebird/base/base.gdb';
$sockets = array();
$dbh = ibase_connect($host, $username, $password);
function event_handler($event_name, $link) {
echo $event_name . "\n";
send_message($event_name);
}
# we need to unregister event in firebird before exit
function terminate($signal) {
global $event;
if (is_null($event)) {
die ( "no event has been registered yet, exiting" );
} else {
ibase_free_event_handler($event);
die ( "unregistered evented and exiting\n" );
}
}
function send_message($msg)
{
$localsocket = 'tcp://127.0.0.1:8010';
$instance = stream_socket_client ($localsocket, $errno, $errstr);
fwrite($instance, json_encode(['message' => $msg, 'userId' => 824]) . \n");
}
$event = ibase_set_event_handler($dbh, "event_handler", "schedule_update");
pcntl_signal(SIGTERM, "terminate");
while (true) {
sleep(1);
}
?>
Now my app.psgi contains (simplified):
builder {
enable 'Session', store => 'File'; #default uses Plack::Session::State::Cookie
$app;
};
Later, in the $app I'm using:
my $req = Plack::Request->new($env);
my $session = $req->session(); #returns env->{'psgix.session'}
$session->{user} = "name";
It works ok, e.g.:
when the user logged in, I store his name in the server-side stored session-file, and the Plack::Middleware::Session sets an simple session-state-cookie,
and when the user closing the browser, the cookie is automatically cleared (because the Plack::Session::State::Cookie by default didn't set any expiration for the cookie).
Now, I want implement the "Remember me" feature in my login-panel. In this case, the sesion-state-cookie should-not be removed automatically from the browser. This can be done, by using the expires method from the Plack::Session::State::Cookie.
The question:
How I can change the cookie expiration (managed by the Session middleware) from my $app. With other words, how to call the expire method somewhat here:
my $req = Plack::Request->new($env);
my $session = $req->session(); #returns env->{'psgix.session'}
$session->{user} = "name";
my $cookie_state = WHAT_TO_DO_HERE_TO_GET; #the current Plack::Session::State::Cookie object
$cookie_state->expire(86400*14); #expire in two weeks
If someone needs, here is an working example.
use strict;
use warnings;
use Plack::Request;
use Plack::Response;
use Plack::Builder;
use Data::Dumper;
my $app = sub {
my $env = shift;
my $req = Plack::Request->new($env);
my $session = $req->session;
my $res = Plack::Response->new(200);
$res->content_type('text/html');
my $link = $session->{user}
? q{ logout}
: q{ login}
;
$res->body(["Session user:", $session->{user}, "<br>$link"]);
return $res->finalize;
};
my $login = sub {
my $env = shift;
my $req = Plack::Request->new($env);
my $session = $req->session;
$session->{user} = "some";
#how to set here the session-state-cookie expiration?
my $res = Plack::Response->new();
$res->redirect("/", 302);
return $res->finalize;
};
my $logout = sub {
my $env = shift;
my $req = Plack::Request->new($env);
my $session = $req->session;
delete $session->{user};
my $res = Plack::Response->new();
$res->redirect("/", 302);
return $res->finalize;
};
builder {
enable 'Session', store => 'File';
mount "/login" => $login;
mount "/logout" => $logout;
mount "/favicon.ico" => sub { return [ 404, ['Content-Type' => 'text/html'], [ '404 Not Found' ] ] };
mount "/" => $app;
};
You can't change the expiration date directly, but you can force the session middleware to create a new session with a new expiration date like this:
$env->{'psgix.session.options'}{change_id} = 1;
$env->{'psgix.session.options'}{expires} = $my_expires;
If a user logs in, you should change the ID anyway to prevent session fixation attacks. See Cookie::Baker for supported expiration date formats.
EDIT: If you want to set the default expiration timeout globally, you can build the state object manually and pass the expires parameter to the constructor:
builder {
enable 'Session',
state => Plack::Session::State->new(
expires => $timeout_in_seconds,
);
$app;
};
I'm getting an error No form defined at cqSubmitter.pl at line 33 which is the second set_fields method. Other times I get an Error POSTing http://micron.com Internal Server Error at line 39 , which corresponds to the last click_button line.
I'm not really sure what's going on, and why it's saying no form defined? The first half of the code which includes the first click_button method works fine and saves the correct page, but when I try set_fields for the second time, it errors out.
Anyone familiar with the Mechanize package realize what's going on here?
use Data::Dumper;
use HTTP::Request::Common qw(GET);
use WWW::Mechanize;
#Prepopulated information
my $types_ = "";
my $dept_ = "";
my $group_ = "";
#Create new WWW::Mechanize object
my $mech = WWW::Mechanize->new( 'ssl_opts' => { 'verify_hostname' => 0 } );
my $url = "http://f2prbrequest";
#Fetch URL or Die Tryin'
$mech ->get($url);
$fname = "user";
$pswd = "password";
#Login to ClearQuest form using credentials
$mech->set_fields(
USER => $fname
,PASSWORD => $pswd
);
$mech->click_button(
name => 'Submit'
);
#Set fields and actually fill out ClearQuest Form
$mech->set_fields(
types => $types_
,dept => $dept_
,group => $group_
);
$mech->click_button(
name => 'submit1'
);
$mech->save_content("clearQuestFilled.html");
There's Net::Msmgr module on CPAN. It's written clean and the code looks trustworthy at the first glance. However this module seems to be beta and there is little documentation and no tests :-/
Has anyone used this module in production? I haven't managed to make it run by now, because it requires all event loop processing to be done in the application and as I've already said there is little documentation and no working examples to study.
That's where I've gone so far:
#!/usr/bin/perl
use strict;
use warnings;
use Event;
use Net::Msmgr::Object;
use Net::Msmgr::Session;
use Net::Msmgr::User;
use constant DEBUG => 511;
use constant EVENT_TIMEOUT => 5; # seconds
my ($username, $password) = qw/my.username#live.com my.password/;
my $buddy = 'your.username#live.com';
my $user = Net::Msmgr::User->new(user => $username, password => $password);
my $session = Net::Msmgr::Session->new;
$session->debug(DEBUG);
$session->login_handler(\&login_handler);
$session->user($user);
my $conv;
sub login_handler {
my $self = shift;
print "LOGIN\n";
$self->ui_state_nln;
$conv = $session->ui_new_conversation;
$conv->invite($buddy);
}
our %watcher;
sub ConnectHandler {
my ($connection) = #_;
warn "CONNECT\n";
my $socket = $connection->socket;
$watcher{$connection} = Event->io(fd => $socket,
cb => [ $connection, '_recv_message' ],
poll => 're',
desc => 'recv_watcher',
repeat => 1);
}
sub DisconnectHandler {
my $connection = shift;
print "DISCONNECT\n";
$watcher{$connection}->cancel;
}
$session->connect_handler(\&ConnectHandler);
$session->disconnect_handler(\&DisconnectHandler);
$session->Login;
Event::loop();
That's what it outputs:
Dispatch Server connecting to: messenger.hotmail.com:1863
Dispatch Server connected
CONNECT
Dispatch Server >>>VER 1 MSNP2 CVR0
--> VER 1 MSNP2 CVR0
Dispatch Server >>>USR 2 MD5 I my.username#live.com
--> USR 2 MD5 I my.username#live.com
Dispatch Server <<<VER 1 CVR0
<-- VER 1 CVR0
And that's all, here it hangs. The handler on login is not being triggered. What am I doing wrong?
Hope these documents will help you out
1) Net::Msmgr documentation
2) Net::Msmgr::Session