Commit 8c3ff5fb authored by Xavier Guimard's avatar Xavier Guimard

Minimize Apache::Session tie/untie (closes: #1173)

parent ffd769e7
......@@ -72,12 +72,10 @@ sub saveSession {
id => $self->md5,
force => 1,
kind => "Captcha",
info =>
{ _utime => time, code => $self->code, image => $self->image }
}
);
$session->update(
{ _utime => time, code => $self->code, image => $self->image } );
}
sub getSession {
......
......@@ -64,8 +64,10 @@ has 'error' => (
isa => 'Str|Undef',
);
has info => ( is => 'rw' );
sub BUILD {
my $self = shift;
my ($self) = @_;
# Load Apache::Session module
unless ( $self->storageModule->can('populate') ) {
......@@ -107,6 +109,14 @@ sub BUILD {
$data->{_session_kind} = $self->kind;
}
if ( $self->{info} ) {
foreach ( keys %{ $self->{info} } ) {
$data->{$_} = $self->{info}->{$_}
if ( defined $self->{info}->{$_} );
}
delete $self->{info};
}
# Load session data into object
if ($data) {
$self->_save_data($data);
......@@ -118,8 +128,8 @@ sub BUILD {
}
sub _tie_session {
my $self = shift;
my $options = shift || {};
my $self = $_[0];
my $options = $_[1] || {};
my %h;
......@@ -154,9 +164,7 @@ sub _save_data {
}
sub update {
my $self = shift;
my $infos = shift;
my $tieOptions = shift;
my ( $self, $infos, $tieOptions ) = @_;
unless ( ref $infos eq "HASH" ) {
$self->error("You need to provide a HASHREF");
......@@ -186,8 +194,7 @@ sub update {
}
sub remove {
my $self = shift;
my $tieOptions = shift;
my ( $self, $tieOptions ) = @_;
my $data = $self->_tie_session($tieOptions);
......
......@@ -84,7 +84,7 @@ sub session {
}
sub getApacheSession {
my ( $self, $mod, $id ) = @_;
my ( $self, $mod, $id, $info ) = @_;
my $apacheSession = Lemonldap::NG::Common::Session->new(
{
storageModule => $mod->{module},
......@@ -95,6 +95,7 @@ sub getApacheSession {
Lemonldap::NG::Handler::PSGI::Main->tsv->{sessionCacheOptions},
id => $id,
kind => $mod->{kind},
( $info ? ( info => $info ) : () ),
}
);
if ( $apacheSession->error ) {
......
......@@ -979,10 +979,6 @@ sub extractFormInfo {
# Keep initial SAML request data in memory in case of proxing
if ( $req->datas->{_proxiedSamlRequest} ) {
my $samlSessionInfo = $self->getSamlSession();
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
my $infos;
$infos->{type} = 'proxy';
......@@ -993,7 +989,9 @@ sub extractFormInfo {
$infos->{Artifact} = $req->datas->{_proxiedArtifact};
$infos->{ProxyID} = $samlID;
$samlSessionInfo->update($infos);
my $samlSessionInfo = $self->getSamlSession(undef, $infos);
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
$self->logger->debug(
"Keep initial SAML request data in memory for ID $samlID");
......@@ -1182,10 +1180,6 @@ sub authFinish {
. $nameid->dump
. " and SessionIndex $session_index for session $id" );
# Save SAML session
my $samlSessionInfo = $self->getSamlSession();
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
my $infos;
$infos->{type} = 'saml'; # Session type
......@@ -1194,7 +1188,10 @@ sub authFinish {
$infos->{_nameID} = $nameid->dump; # SAML NameID
$infos->{_sessionIndex} = $session_index; # SAML SessionIndex
$samlSessionInfo->update($infos);
# Save SAML session
my $samlSessionInfo = $self->getSamlSession(undef, $infos);
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
my $session_id = $samlSessionInfo->id;
......
......@@ -157,13 +157,6 @@ sub run {
$self->logger->debug(
"Create a CAS service ticket for service $service");
my $casServiceSession = $self->getCasSession();
unless ($casServiceSession) {
$self->logger->error("Unable to create CAS session");
return PE_ERROR;
}
my $Sinfos;
$Sinfos->{type} = 'casService';
$Sinfos->{service} = $service;
......@@ -171,7 +164,12 @@ sub run {
$Sinfos->{_cas_id} = $session_id;
$Sinfos->{_utime} = $time;
$casServiceSession->update($Sinfos);
my $casServiceSession = $self->getCasSession( undef, $Sinfos );
unless ($casServiceSession) {
$self->logger->error("Unable to create CAS session");
return PE_ERROR;
}
my $casServiceSessionID = $casServiceSession->id;
$casServiceTicket = "ST-" . $casServiceSessionID;
......@@ -524,30 +522,28 @@ sub _validate2 {
$self->logger->debug(
"Create a CAS proxy granting ticket for service $service");
my $casProxyGrantingSession = $self->getCasSession();
my $PGinfos;
if ($casProxyGrantingSession) {
# PGT session
$PGinfos->{type} = 'casProxyGranting';
$PGinfos->{service} = $service;
$PGinfos->{_cas_id} = $casServiceSession->data->{_cas_id};
$PGinfos->{_utime} = $casServiceSession->data->{_utime};
my $PGinfos;
# Trace proxies
$PGinfos->{proxies} = (
$proxies
? $proxies . $self->{multiValuesSeparator} . $pgtUrl
: $pgtUrl
);
# PGT session
$PGinfos->{type} = 'casProxyGranting';
$PGinfos->{service} = $service;
$PGinfos->{_cas_id} = $casServiceSession->data->{_cas_id};
$PGinfos->{_utime} = $casServiceSession->data->{_utime};
my $casProxyGrantingSession = $self->getCasSession( undef, $PGinfos );
# Trace proxies
$PGinfos->{proxies} = (
$proxies
? $proxies . $self->{multiValuesSeparator} . $pgtUrl
: $pgtUrl
);
if ($casProxyGrantingSession) {
my $casProxyGrantingSessionID = $casProxyGrantingSession->id;
my $casProxyGrantingTicket = "PGT-" . $casProxyGrantingSessionID;
$casProxyGrantingSession->update($PGinfos);
$self->logger->debug(
"CAS proxy granting session $casProxyGrantingSessionID created"
);
......
......@@ -504,14 +504,8 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
# Authorization Code Flow
if ( $flow eq "authorizationcode" ) {
# Generate code
my $codeSession = $self->getOpenIDConnectSession();
my $code = $codeSession->id();
$self->logger->debug("Generated code: $code");
# Store data in session
$codeSession->update(
my $codeSession = $self->getOpenIDConnectSession(undef,
{
redirect_uri => $oidc_request->{'redirect_uri'},
scope => $oidc_request->{'scope'},
......@@ -521,6 +515,11 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
}
);
# Generate code
my $code = $codeSession->id();
$self->logger->debug("Generated code: $code");
# Build Response
my $response_url = $self->buildAuthorizationCodeAuthnResponse(
$oidc_request->{'redirect_uri'},
......@@ -542,8 +541,16 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
if ( $response_type =~ /\btoken\b/ ) {
# Store data in access token
# Generate access_token
my $accessTokenSession = $self->getOpenIDConnectSession;
my $accessTokenSession = $self->getOpenIDConnectSession(undef,
{
scope => $oidc_request->{'scope'},
rp => $rp,
user_session_id => $req->id,
_utime => time,
}
);
unless ($accessTokenSession) {
$self->logger->error(
......@@ -554,16 +561,6 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
$oidc_request->{'state'}, 1 );
}
# Store data in access token
$accessTokenSession->update(
{
scope => $oidc_request->{'scope'},
rp => $rp,
user_session_id => $req->id,
_utime => time,
}
);
$access_token = $accessTokenSession->id;
$self->logger->debug(
......@@ -656,14 +653,8 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
->{oidcRPMetaDataOptionsIDTokenSignAlg};
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
# Generate code
my $codeSession = $self->getOpenIDConnectSession();
my $code = $codeSession->id();
$self->logger->debug("Generated code: $code");
# Store data in session
$codeSession->update(
my $codeSession = $self->getOpenIDConnectSession(undef,
{
redirect_uri => $oidc_request->{'redirect_uri'},
scope => $oidc_request->{'scope'},
......@@ -673,13 +664,25 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
}
);
# Generate code
my $code = $codeSession->id();
$self->logger->debug("Generated code: $code");
# Compute hash to store in c_hash
$c_hash = $self->createHash( $code, $hash_level );
if ( $response_type =~ /\btoken\b/ ) {
# Generate access_token
my $accessTokenSession = $self->getOpenIDConnectSession;
my $accessTokenSession = $self->getOpenIDConnectSession(undef,
{
scope => $oidc_request->{'scope'},
rp => $rp,
user_session_id => $req->id,
_utime => time,
}
);
unless ($accessTokenSession) {
$self->logger->error(
......@@ -690,16 +693,6 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
$oidc_request->{'state'}, 1 );
}
# Store data in access token
$accessTokenSession->update(
{
scope => $oidc_request->{'scope'},
rp => $rp,
user_session_id => $req->id,
_utime => time,
}
);
$access_token = $accessTokenSession->id;
$self->logger->debug(
......@@ -902,17 +895,7 @@ sub token {
$self->logger->debug("Found corresponding user: $user_id");
# Generate access_token
my $accessTokenSession = $self->getOpenIDConnectSession;
unless ($accessTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for access_token");
$codeSession->remove();
$self->p->sendError( $req, "invalid_request", 400 );
}
# Store data in access token
$accessTokenSession->update(
my $accessTokenSession = $self->getOpenIDConnectSession(undef,
{
scope => $codeSession->data->{scope},
rp => $rp,
......@@ -921,6 +904,13 @@ sub token {
}
);
unless ($accessTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for access_token");
$codeSession->remove();
$self->p->sendError( $req, "invalid_request", 400 );
}
my $access_token = $accessTokenSession->id;
$self->logger->debug("Generated access token: $access_token");
......
......@@ -415,23 +415,23 @@ sub run {
# Force authentication if flag is on, or previous flag still active
if ($force_authn) {
# Store flag for further requests
$forceAuthnSessionInfo =
$self->getSamlSession($forceAuthn_session);
$forceAuthnSessionInfo->update( { $spConfKey => 1 } );
my $info = { $spConfKey => 1 };
unless ($forceAuthn_session) {
my $forceInfos;
$forceInfos->{'_type'} = "forceAuthn";
$forceInfos->{'_saml_id'} = $session_id;
$forceInfos->{'_utime'} = $time;
$forceAuthnSessionInfo->update($forceInfos);
$forceAuthn_session = $forceAuthnSessionInfo->id;
$info->{'_type'} = "forceAuthn";
$info->{'_saml_id'} = $session_id;
$info->{'_utime'} = $time;
$self->logger->debug(
"Create ForceAuthn session $forceAuthn_session");
}
# Store flag for further requests
$forceAuthnSessionInfo =
$self->getSamlSession( $forceAuthn_session, $info );
$forceAuthn_session = $forceAuthnSessionInfo->id
unless ($forceAuthn_session);
$self->logger->debug(
"Set ForceAuthn flag for SP $spConfKey in ForceAuthn session $forceAuthn_session"
);
......@@ -454,8 +454,8 @@ sub run {
# Else remove flag
$forceAuthnSessionInfo =
$self->getSamlSession($forceAuthn_session);
$forceAuthnSessionInfo->update( { $spConfKey => 0 } );
$self->getSamlSession( $forceAuthn_session,
{ $spConfKey => 0 } );
$self->logger->debug(
"Unset ForceAuthn flag for SP $spConfKey in ForceAuthn session $forceAuthn_session"
......@@ -849,10 +849,6 @@ sub run {
. $nameid->dump
. " and SessionIndex $sessionIndex for session $session_id" );
my $samlSessionInfo = $self->getSamlSession();
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
my $infos;
$infos->{type} = 'saml'; # Session type
......@@ -861,7 +857,9 @@ sub run {
$infos->{_nameID} = $nameid->dump; # SAML NameID
$infos->{_sessionIndex} = $sessionIndex; # SAML SessionIndex
$samlSessionInfo->update($infos);
my $samlSessionInfo = $self->getSamlSession( undef, $infos );
return PE_SAML_SESSION_ERROR unless $samlSessionInfo;
my $saml_session_id = $samlSessionInfo->id;
......@@ -1333,10 +1331,10 @@ sub sloRelaySoap {
}
# Store success status for this SLO request
my $sloStatusSessionInfos = $self->getSamlSession($relayState);
my $sloStatusSessionInfos =
$self->getSamlSession( $relayState, { $spConfKey => 1 } );
if ($sloStatusSessionInfos) {
$sloStatusSessionInfos->update( { $spConfKey => 1 } );
$self->logger->debug(
"Store SLO status for $spConfKey in session $relayState");
}
......@@ -1530,9 +1528,6 @@ sub sloServer {
$self->logger->debug("Set $relaystate in RelayState");
}
# Create SLO status session and get ID
my $sloStatusSessionInfo = $self->getSamlSession();
my $sloInfos;
$sloInfos->{type} = 'sloStatus';
$sloInfos->{_utime} = time;
......@@ -1540,7 +1535,9 @@ sub sloServer {
$sloInfos->{_session} =
$logout->get_session() ? $logout->get_session()->dump : "";
$sloInfos->{_method} = $method;
$sloStatusSessionInfo->update($sloInfos);
# Create SLO status session and get ID
my $sloStatusSessionInfo = $self->getSamlSession( undef, $sloInfos );
my $relayID = $sloStatusSessionInfo->id;
# Prepare logout on all others SP
......
......@@ -31,7 +31,7 @@ sub sendSoapResponse {
# Try to recover the CAS session corresponding to id and return session datas
# If id is set to undef, return a new session
sub getCasSession {
my ( $self, $id ) = @_;
my ( $self, $id, $info ) = @_;
my $casSession = Lemonldap::NG::Common::Session->new(
{
......@@ -41,6 +41,7 @@ sub getCasSession {
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
id => $id,
kind => "CAS",
( $info ? ( info => $info ) : () ),
}
);
......
......@@ -15,9 +15,6 @@ has timeout => (
sub createToken {
my ( $self, $infos ) = @_;
# Create a new session
my $tsession = $self->p->getApacheSession();
# Set _utime for session autoremove
# Use default session timeout and register session timeout to compute it
my $time = time();
......@@ -34,8 +31,8 @@ sub createToken {
# Store type
$infos->{_type} ||= "token";
# Update session
$tsession->update($infos);
# Create a new session
my $tsession = $self->p->getApacheSession( undef, info => $infos );
return $tsession->id;
}
......
......@@ -258,8 +258,7 @@ sub buildAuthorizationCodeAuthnRequest {
my $nonce;
if ($use_nonce) {
my $nonceSession = $self->getOpenIDConnectSession();
$nonceSession->update( { '_utime' => time } );
my $nonceSession = $self->getOpenIDConnectSession( undef, { '_utime' => time });
$nonce = $nonceSession->id;
}
......@@ -631,7 +630,7 @@ sub decodeJSON {
# If id is set to undef, return a new session
# @return Lemonldap::NG::Common::Session object
sub getOpenIDConnectSession {
my ( $self, $id ) = @_;
my ( $self, $id, $info ) = @_;
my $oidcSession = Lemonldap::NG::Common::Session->new(
{
......@@ -641,6 +640,7 @@ sub getOpenIDConnectSession {
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
id => $id,
kind => "OpenIDConnect",
($info ? (info => $info):()),
}
);
......@@ -673,10 +673,6 @@ sub storeState {
}
return unless ($infos);
# Create state session
my $stateSession = $self->getOpenIDConnectSession();
return unless $stateSession;
# Session type
$infos->{_type} = "state";
......@@ -689,8 +685,9 @@ sub storeState {
$infos->{_utime} = $time + ( $stateTimeout - $timeout );
# Store infos in state session
$stateSession->update($infos);
# Create state session and store infos
my $stateSession = $self->getOpenIDConnectSession( undef, $infos);
return unless $stateSession;
# Return session ID
return $stateSession->id;
......
......@@ -1101,11 +1101,6 @@ sub storeRelayState {
}
return unless ($infos);
# Create relaystate session
my $samlSessionInfo = $self->getSamlSession();
return unless $samlSessionInfo;
# Session type
$infos->{_type} = "relaystate";
......@@ -1118,8 +1113,9 @@ sub storeRelayState {
$infos->{_utime} = $time + ( $samlRelayStateTimeout - $timeout );
# Store infos in relaystate session
$samlSessionInfo->update($infos);
# Create relaystate session and store infos in relaystate session
my $samlSessionInfo = $self->getSamlSession( undef, $infos )
or return undef;
# Session ID
my $relaystate_id = $samlSessionInfo->id;
......@@ -1567,22 +1563,18 @@ sub buildLogoutResponseMsg {
# @return result
sub storeReplayProtection {
my ( $self, $samlID, $samlData ) = @_;
my $samlSessionInfo = $self->getSamlSession();
return 0 unless $samlSessionInfo;
my $infos;
$infos->{type} = 'assertion'; # Session type
$infos->{_utime} = time(); # Creation time
$infos->{_assert_id} = $samlID;
my $infos = {
type => 'assertion', # Session type
_utime => time(), # Creation time
_assert_id => $samlID,
};
if ( defined $samlData && $samlData ) {
$infos->{data} = $samlData;
}
$samlSessionInfo->update($infos);
my $samlSessionInfo = $self->getSamlSession( undef, $infos );
return 0 unless $samlSessionInfo;
my $session_id = $samlSessionInfo->id;
......@@ -1700,19 +1692,16 @@ sub resolveArtifact {
sub storeArtifact {
my ( $self, $id, $message, $session_id ) = @_;
my $samlSessionInfo = $self->getSamlSession();
return 0 unless $samlSessionInfo;
my $infos;
$infos->{type} = 'artifact'; # Session type
$infos->{_utime} = time(); # Creation time
$infos->{_art_id} = $id;
$infos->{message} = $message;
my $infos = {
type => 'artifact', # Session type
_utime => time(), # Creation time
_art_id => $id,
message => $message,
};
$infos->{_saml_id} = $session_id if $session_id;
$samlSessionInfo->update($infos);
my $samlSessionInfo = $self->getSamlSession( undef, $infos ) or return 0;
return 0 unless $samlSessionInfo;
my $art_session_id = $samlSessionInfo->id;
......@@ -2491,9 +2480,6 @@ sub sendLogoutRequestToProvider {
$self->logger->debug("Build POST relay logout request to $providerID");
# Create a new relay session
my $relayInfos = $self->getSamlSession();
my $infos;
# Store infos
......@@ -2503,7 +2489,8 @@ sub sendLogoutRequestToProvider {
$infos->{body} = $logout->msg_body;
$infos->{relayState} = $logout->msg_relayState;
$relayInfos->update($infos);
# Create a new relay session
my $relayInfos = $self->getSamlSession( undef, $infos );
my $relayID = $relayInfos->id;
......@@ -2528,9 +2515,6 @@ sub sendLogoutRequestToProvider {
$self->logger->debug(
"Build SOAP relay logout request for $providerID");
# Create a new relay session
my $relayInfos = $self->getSamlSession();
my $infos;
$infos->{type} = 'relay';
$infos->{_utime} = time;
......@@ -2541,7 +2525,8 @@ sub sendLogoutRequestToProvider {
$infos->{_providerID} = $providerID;
$infos->{_relayState} = $logout->msg_relayState;
$relayInfos->update($infos);
# Create a new relay session
my $relayInfos = $self->getSamlSession( undef, $infos );
my $relayID = $relayInfos->id;
......@@ -2756,7 +2741,7 @@ sub checkDestination {
# @param id session reference
# @return Lemonldap::NG::Common::Session object
sub getSamlSession {
my ( $self, $id ) = @_;
my ( $self, $id, $info ) = @_;
my $samlSession = Lemonldap::NG::Common::Session->new(
{
......@@ -2766,6 +2751,7 @@ sub getSamlSession {
cacheModuleOptions => $self->conf->{localSessionStorageOptions},
id => $id,
kind => "SAML",
( $info ? ( info => $info ) : () ),
}
);
......
......@@ -398,22 +398,15 @@ sub store {
# Create second session for unsecure cookie
if ( $self->conf->{securedCookie} == 2 ) {
my $session2 = $self->getApacheSession(undef);
my %infos = %{ $req->{sessionInfo} };
$infos{_httpSessionType} = 1;
$session2->update( \%infos );
my $session2 = $self->getApacheSession( undef, info => \%infos );
$req->{sessionInfo}->{_httpSession} = $session2->id;
}
# Main session
my $session =
$self->getApacheSession( $req->{id}, force => $req->{force} );
return PE_APACHESESSIONERROR unless ($session);
$req->id( $session->{id} );