2009-04-07 22:38:24 +02:00
|
|
|
## @file
|
|
|
|
# Common SAML functions
|
|
|
|
|
|
|
|
## @class
|
|
|
|
# Common SAML functions
|
|
|
|
package Lemonldap::NG::Portal::_SAML;
|
|
|
|
|
|
|
|
use strict;
|
2010-02-05 18:18:09 +01:00
|
|
|
use XML::Simple;
|
2010-02-12 11:53:43 +01:00
|
|
|
use MIME::Base64;
|
2010-02-24 16:24:54 +01:00
|
|
|
use LWP::UserAgent; # SOAP call
|
|
|
|
use HTTP::Request; # SOAP call
|
2010-01-29 11:44:56 +01:00
|
|
|
|
2009-04-07 22:38:24 +02:00
|
|
|
our $VERSION = '0.01';
|
2010-02-08 18:24:45 +01:00
|
|
|
our $_samlCache;
|
2009-04-07 22:38:24 +02:00
|
|
|
|
2010-02-08 11:06:21 +01:00
|
|
|
BEGIN {
|
|
|
|
|
|
|
|
# Load Glib if available
|
|
|
|
eval 'use Glib;';
|
|
|
|
if ($@) {
|
|
|
|
print STDERR
|
|
|
|
"Glib Lasso messages will not be catched (require Glib module)\n";
|
|
|
|
eval "use constant GLIB => 0";
|
|
|
|
}
|
|
|
|
else {
|
2010-02-08 11:16:28 +01:00
|
|
|
eval "use constant GLIB => 1";
|
2010-02-08 11:06:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# Load Lasso.pm
|
|
|
|
eval 'use Lasso;';
|
|
|
|
if ($@) {
|
|
|
|
print STDERR "Lasso.pm not loaded :$@";
|
|
|
|
eval 'use constant LASSO => 0;use constant BADLASSO => 0;';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
no strict 'subs';
|
|
|
|
eval 'use constant LASSO => 1';
|
2010-02-08 18:24:45 +01:00
|
|
|
|
|
|
|
# Check Lasso version >= 2.2.91
|
2010-02-08 11:06:21 +01:00
|
|
|
my $lasso_check_version_mode = Lasso::Constants::CHECK_VERSION_NUMERIC;
|
|
|
|
my $check_version =
|
|
|
|
Lasso::check_version( 2, 2, 91, $lasso_check_version_mode );
|
|
|
|
unless ($check_version) {
|
|
|
|
eval 'use constant BADLASSO => 1';
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
eval 'use constant BADLASSO => 0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
## @method boolean loadLasso()
|
|
|
|
# Load Lasso module
|
|
|
|
# @return boolean result
|
|
|
|
sub loadLasso {
|
|
|
|
my $self = shift;
|
|
|
|
|
2010-02-01 16:24:56 +01:00
|
|
|
# Catch GLib Lasso messages (require Glib)
|
2010-02-08 11:06:21 +01:00
|
|
|
if (GLIB) {
|
2010-02-01 16:24:56 +01:00
|
|
|
Glib::Log->set_handler(
|
|
|
|
"Lasso",
|
|
|
|
[qw/ error critical warning message info debug /],
|
|
|
|
sub {
|
|
|
|
$self->lmLog( $_[0] . " error " . $_[1] . ": " . $_[2],
|
|
|
|
'debug' );
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2010-02-08 18:24:45 +01:00
|
|
|
|
2010-02-08 11:06:21 +01:00
|
|
|
unless (LASSO) {
|
|
|
|
$self->lmLog( "Module Lasso not loaded (see bellow)", 'error' );
|
2010-01-29 11:44:56 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-08 11:06:21 +01:00
|
|
|
if (BADLASSO) {
|
2010-02-01 15:01:28 +01:00
|
|
|
$self->lmLog( 'Lasso version >= 2.2.91 required', 'error' );
|
2010-01-29 18:33:35 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2010-02-08 18:24:45 +01:00
|
|
|
|
2010-01-29 18:33:35 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean checkLassoError(Lasso::Error error, string level)
|
|
|
|
# Log Lasso error code and message if this is actually a Lasso::Error with code > 0
|
|
|
|
# @param Lasso::Error Lasso error object
|
|
|
|
# @param string optional log level (debug by default)
|
|
|
|
# @return 1 if no error
|
|
|
|
sub checkLassoError {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $error, $level ) = splice @_;
|
2010-02-12 15:26:45 +01:00
|
|
|
$level ||= 'debug';
|
2010-01-29 18:33:35 +01:00
|
|
|
|
2010-02-03 11:59:53 +01:00
|
|
|
# If $error is not a Lasso::Error object, display error string
|
|
|
|
unless ( ref($error) and $error->isa("Lasso::Error") ) {
|
|
|
|
return 1 unless $error;
|
|
|
|
$self->lmLog( "Lasso error: $error", $level );
|
|
|
|
return 0;
|
|
|
|
}
|
2010-01-29 18:33:35 +01:00
|
|
|
|
2010-02-03 11:59:53 +01:00
|
|
|
# Else check error code and error message
|
2010-01-29 18:33:35 +01:00
|
|
|
if ( $error->{code} ) {
|
|
|
|
$self->lmLog(
|
|
|
|
"Lasso error code " . $error->{code} . ": " . $error->{message},
|
|
|
|
$level );
|
|
|
|
return 0;
|
|
|
|
}
|
2010-01-29 11:44:56 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-02-01 18:07:40 +01:00
|
|
|
## @method Lasso::Server createServer(string metadata, string private_key, string private_key_password, string certificate)
|
2010-01-29 18:33:35 +01:00
|
|
|
# Load service metadata and create Lasso::Server object
|
2010-02-01 15:01:28 +01:00
|
|
|
# @param string metadata
|
2010-02-03 11:59:53 +01:00
|
|
|
# @param string private key
|
2010-02-01 15:01:28 +01:00
|
|
|
# @param string optional private key password
|
|
|
|
# @param string optional certificate
|
2010-01-29 18:33:35 +01:00
|
|
|
# @return Lasso::Server object
|
|
|
|
sub createServer {
|
2010-02-08 18:24:45 +01:00
|
|
|
my ( $self, $metadata, $private_key, $private_key_password, $certificate ) =
|
|
|
|
splice @_;
|
2010-02-08 11:16:28 +01:00
|
|
|
my $server = $_samlCache->{$metadata};
|
2010-02-08 18:24:45 +01:00
|
|
|
return $server if ($server);
|
2010-01-29 18:33:35 +01:00
|
|
|
|
2010-02-03 11:59:53 +01:00
|
|
|
eval {
|
|
|
|
$server = Lasso::Server::new_from_buffers( $metadata, $private_key,
|
2010-02-08 11:06:21 +01:00
|
|
|
$private_key_password, $certificate );
|
2010-02-03 11:59:53 +01:00
|
|
|
};
|
2010-01-29 18:33:35 +01:00
|
|
|
|
2010-02-08 18:24:45 +01:00
|
|
|
if ($@) {
|
2010-02-08 11:16:28 +01:00
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
2010-01-29 18:33:35 +01:00
|
|
|
|
|
|
|
return $server;
|
|
|
|
}
|
|
|
|
|
2010-02-01 18:07:40 +01:00
|
|
|
## @method boolean addIDP(Lasso::Server server, string metadata, string public_key, string ca_cert_chain)
|
|
|
|
# Add IDP to an existing Lasso::Server
|
|
|
|
# @param Lasso::Server Lasso::Server object
|
|
|
|
# @param string metadata IDP metadata
|
|
|
|
# @param string optional public key
|
|
|
|
# @param string optional ca cert chain
|
|
|
|
# @return boolean result
|
|
|
|
sub addIDP {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $server, $metadata, $public_key, $ca_cert_chain ) = splice @_;
|
2010-02-01 18:07:40 +01:00
|
|
|
|
|
|
|
return 0 unless ( $server->isa("Lasso::Server") and defined $metadata );
|
|
|
|
|
|
|
|
return $self->addProvider( $server, Lasso::Constants::PROVIDER_ROLE_IDP,
|
|
|
|
$metadata, $public_key, $ca_cert_chain );
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean addProvider(Lasso::Server server, int role, string metadata, string public_key, string ca_cert_chain)
|
|
|
|
# Add provider to an existing Lasso::Server
|
|
|
|
# @param Lasso::Server Lasso::Server object
|
|
|
|
# @param int role (IDP, SP or Both)
|
|
|
|
# @param string metadata IDP metadata
|
|
|
|
# @param string optional public key
|
|
|
|
# @param string optional ca cert chain
|
|
|
|
# @return boolean result
|
|
|
|
sub addProvider {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $server, $role, $metadata, $public_key, $ca_cert_chain ) =
|
|
|
|
splice @_;
|
2010-02-01 18:07:40 +01:00
|
|
|
|
|
|
|
return 0
|
|
|
|
unless ( $server->isa("Lasso::Server")
|
|
|
|
and defined $role
|
|
|
|
and defined $metadata );
|
|
|
|
|
|
|
|
eval {
|
2010-02-02 22:55:25 +01:00
|
|
|
Lasso::Server::add_provider_from_buffer( $server, $role, $metadata,
|
2010-02-01 18:07:40 +01:00
|
|
|
$public_key, $ca_cert_chain );
|
|
|
|
};
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
|
|
|
|
}
|
2010-02-05 18:18:09 +01:00
|
|
|
|
|
|
|
## @method string getOrganizationName(Lasso::Server server, string idp)
|
|
|
|
# Return name of organization picked up from metadata
|
|
|
|
#@param server Lasso::Server object
|
|
|
|
#@param string entityID
|
|
|
|
#@return string organization name
|
|
|
|
sub getOrganizationName {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $server, $idp ) = splice @_;
|
|
|
|
my ( $provider, $node );
|
2010-02-05 18:18:09 +01:00
|
|
|
|
|
|
|
# Get provider from server
|
|
|
|
eval { $provider = Lasso::Server::get_provider( $server, $idp ); };
|
2010-02-09 21:49:23 +01:00
|
|
|
|
2010-02-08 18:24:45 +01:00
|
|
|
if ($@) {
|
2010-02-08 11:16:28 +01:00
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
2010-02-05 18:18:09 +01:00
|
|
|
|
|
|
|
# Get organization node
|
|
|
|
eval { $node = Lasso::Provider::get_organization($provider); };
|
2010-02-09 21:49:23 +01:00
|
|
|
|
2010-02-08 18:24:45 +01:00
|
|
|
if ($@) {
|
2010-02-08 11:16:28 +01:00
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
2010-02-05 18:18:09 +01:00
|
|
|
|
2010-02-12 11:53:43 +01:00
|
|
|
# Return if node is empty
|
|
|
|
return unless $node;
|
|
|
|
|
2010-02-05 18:18:09 +01:00
|
|
|
# Extract organization name
|
|
|
|
my $xs = XML::Simple->new();
|
|
|
|
my $data = $xs->XMLin($node);
|
|
|
|
return $data->{OrganizationName}->{content};
|
|
|
|
}
|
|
|
|
|
2010-03-05 10:28:28 +01:00
|
|
|
## @method Lasso::Login createAuthnRequest(Lasso::Server server, string idp, int method, boolean forceAuthn, string nameIDFormat)
|
2010-02-04 17:02:02 +01:00
|
|
|
# Create authentication request for selected IDP
|
2010-02-26 10:12:18 +01:00
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @param entityID IDP entityID
|
|
|
|
# @param method HTTP method
|
2010-03-05 09:54:01 +01:00
|
|
|
# @param forceAuthn force authentication on IDP
|
2010-03-05 10:28:28 +01:00
|
|
|
# @param nameIDFormat SAML2 NameIDFormat
|
2010-02-04 17:02:02 +01:00
|
|
|
# @return Lasso::Login object
|
|
|
|
sub createAuthnRequest {
|
2010-03-05 10:28:28 +01:00
|
|
|
my ( $self, $server, $idp, $method, $forceAuthn, $nameIDFormat ) =
|
|
|
|
splice @_;
|
2010-02-04 17:02:02 +01:00
|
|
|
|
|
|
|
# Create Lasso Login
|
|
|
|
my $login = $self->createLogin($server);
|
|
|
|
|
|
|
|
unless ($login) {
|
|
|
|
$self->lmLog( 'Unable to create Lasso login', 'error' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Init authentication request
|
|
|
|
unless ( $self->initAuthnRequest( $login, $idp, $method ) ) {
|
|
|
|
$self->lmLog( "Could not initiate authentication request on $idp",
|
|
|
|
'error' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-02-25 12:39:55 +01:00
|
|
|
# Set RelayState
|
|
|
|
my $infos;
|
2010-02-12 11:53:43 +01:00
|
|
|
foreach (qw /_idp urldc/) {
|
2010-02-25 12:39:55 +01:00
|
|
|
$infos->{$_} = $self->{$_} if $self->{$_};
|
2010-02-11 13:39:42 +01:00
|
|
|
}
|
2010-02-25 12:39:55 +01:00
|
|
|
my $relaystate = $self->storeRelayState($infos);
|
2010-02-12 11:53:43 +01:00
|
|
|
$login->msg_relayState($relaystate);
|
|
|
|
$self->lmLog( "Set $relaystate in RelayState", 'debug' );
|
2010-02-11 13:39:42 +01:00
|
|
|
|
2010-02-10 18:18:46 +01:00
|
|
|
# Customize request
|
|
|
|
my $request = $login->request();
|
2010-03-05 10:28:28 +01:00
|
|
|
|
|
|
|
# NameIDFormat
|
|
|
|
if ($nameIDFormat) {
|
|
|
|
$self->lmLog( "Use NameIDFormat $nameIDFormat", 'debug' );
|
|
|
|
$request->NameIDPolicy()->Format($nameIDFormat);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Always allow NameID creation
|
2010-02-11 13:39:42 +01:00
|
|
|
$request->NameIDPolicy()->AllowCreate(1);
|
2010-03-05 10:28:28 +01:00
|
|
|
|
|
|
|
# Force authentication
|
2010-03-05 09:54:01 +01:00
|
|
|
if ($forceAuthn) {
|
|
|
|
$self->lmLog( "Force authentication on IDP", 'debug' );
|
|
|
|
$request->ForceAuthn(1);
|
|
|
|
}
|
2010-02-10 18:18:46 +01:00
|
|
|
|
2010-02-04 17:02:02 +01:00
|
|
|
# Build authentication request
|
|
|
|
unless ( $self->buildAuthnRequestMsg($login) ) {
|
|
|
|
$self->lmLog( "Could not build authentication request on $idp",
|
|
|
|
'error' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $login;
|
|
|
|
}
|
|
|
|
|
2010-02-17 18:37:38 +01:00
|
|
|
## @method Lasso::Login createLogin(Lasso::Server server, string dump)
|
2010-02-04 17:02:02 +01:00
|
|
|
# Create Lasso::Login object
|
2010-02-17 18:37:38 +01:00
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @param dump optional XML dump
|
2010-02-04 17:02:02 +01:00
|
|
|
# @return Lasso::Login object
|
|
|
|
sub createLogin {
|
2010-02-17 18:37:38 +01:00
|
|
|
my ( $self, $server, $dump ) = splice @_;
|
2010-02-04 17:02:02 +01:00
|
|
|
my $login;
|
|
|
|
|
2010-02-17 18:37:38 +01:00
|
|
|
if ($dump) {
|
|
|
|
eval { $login = Lasso::Login::new_from_dump( $server, $dump ); };
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
eval { $login = Lasso::Login->new($server); };
|
|
|
|
}
|
2010-02-04 17:02:02 +01:00
|
|
|
|
2010-02-09 21:49:23 +01:00
|
|
|
if ($@) {
|
2010-02-12 11:53:43 +01:00
|
|
|
$self->checkLassoError($@);
|
2010-02-09 21:49:23 +01:00
|
|
|
return;
|
|
|
|
}
|
2010-02-04 17:02:02 +01:00
|
|
|
|
|
|
|
return $login;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean initAuthnRequest(Lasso::Login login, string idp, int method)
|
|
|
|
# Init authentication request
|
|
|
|
# @param Lasso::Login login
|
|
|
|
# @param string entityID
|
|
|
|
# @param int HTTP method
|
|
|
|
# @return boolean result
|
|
|
|
sub initAuthnRequest {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $login, $idp, $method ) = splice @_;
|
2010-02-04 17:02:02 +01:00
|
|
|
|
|
|
|
eval { Lasso::Login::init_authn_request( $login, $idp, $method ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean buildAuthnRequestMsg(Lasso::Login login)
|
|
|
|
# Build authentication request message
|
|
|
|
# @param Lasso::Login login
|
|
|
|
# @return boolean result
|
|
|
|
sub buildAuthnRequestMsg {
|
2010-02-08 11:06:21 +01:00
|
|
|
my ( $self, $login ) = splice @_;
|
2010-02-04 17:02:02 +01:00
|
|
|
|
|
|
|
eval { Lasso::Login::build_authn_request_msg($login); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
2010-02-01 18:07:40 +01:00
|
|
|
|
2010-02-08 18:24:45 +01:00
|
|
|
## @method boolean processAuthnResponseMsg(Lasso::Login login, string response)
|
|
|
|
# Process authentication response message
|
|
|
|
# @param login Lasso::Login object
|
|
|
|
# @param response SAML response
|
|
|
|
# @return result
|
|
|
|
sub processAuthnResponseMsg {
|
|
|
|
my ( $self, $login, $response ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Login::process_authn_response_msg( $login, $response ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-09 10:02:39 +01:00
|
|
|
## @method Lasso::Saml2NameID getNameIdentifer(Lasso::Profile profile)
|
|
|
|
# Get NameID from Lasso Profile
|
|
|
|
# @param profile Lasso::Profile object
|
|
|
|
# @return result or NULL if error
|
|
|
|
sub getNameIdentifier {
|
|
|
|
my ( $self, $profile ) = splice @_;
|
|
|
|
my $nameid;
|
|
|
|
|
|
|
|
eval { $nameid = Lasso::Profile::get_nameIdentifier($profile); };
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $nameid;
|
|
|
|
}
|
|
|
|
|
2010-02-11 13:39:42 +01:00
|
|
|
## @method Lasso::Identity createIdentity(string dump)
|
|
|
|
# Create Lasso::Identity object
|
|
|
|
# @param dump optional Identity dump
|
|
|
|
# @return Lasso::Identity object
|
|
|
|
sub createIdentity {
|
|
|
|
my ( $self, $dump ) = splice @_;
|
|
|
|
my $identity;
|
|
|
|
|
|
|
|
if ($dump) {
|
|
|
|
eval { $identity = Lasso::Identity::new_from_dump($dump); };
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
eval { $identity = Lasso::Identity->new(); };
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $identity;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method Lasso::Session createSession(string dump)
|
|
|
|
# Create Lasso::Session object
|
|
|
|
# @param dump optional Session dump
|
|
|
|
# @return Lasso::Session object
|
|
|
|
sub createSession {
|
|
|
|
my ( $self, $dump ) = splice @_;
|
|
|
|
my $session;
|
|
|
|
|
|
|
|
if ($dump) {
|
|
|
|
eval { $session = Lasso::Session::new_from_dump($dump); };
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
eval { $session = Lasso::Session->new(); };
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $session;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean acceptSSO(Lasso::Login login)
|
|
|
|
# Accept SSO from IDP
|
|
|
|
# @param login Lasso::Login object
|
|
|
|
# @return result
|
|
|
|
sub acceptSSO {
|
|
|
|
my ( $self, $login ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Login::accept_sso($login); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-25 12:39:55 +01:00
|
|
|
## @method string storeRelayState(hashref infos)
|
|
|
|
# Store information in relayState database and return
|
|
|
|
# corresponding session_id
|
|
|
|
# @param infos HASH reference of information
|
|
|
|
sub storeRelayState {
|
|
|
|
my ( $self, $infos ) = splice @_;
|
|
|
|
my %h;
|
|
|
|
|
|
|
|
# Create relaystate session
|
|
|
|
eval {
|
|
|
|
tie %h, $self->{globalStorage}, undef, $self->{globalStorageOptions};
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->lmLog( "Unable to create relaystate session", 'error' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Session type
|
|
|
|
$h{_type} = "relaystate";
|
|
|
|
|
2010-02-28 20:07:02 +01:00
|
|
|
# UNIX time
|
|
|
|
$h{_utime} = time();
|
|
|
|
|
2010-02-25 12:39:55 +01:00
|
|
|
# Store infos in relaystate session
|
|
|
|
foreach ( keys %$infos ) {
|
|
|
|
$h{$_} = $infos->{$_};
|
|
|
|
}
|
|
|
|
|
|
|
|
# Session ID
|
|
|
|
my $relaystate_id = $h{_session_id};
|
|
|
|
|
|
|
|
# Close session
|
|
|
|
untie %h;
|
|
|
|
|
|
|
|
# Return session ID
|
|
|
|
return $relaystate_id;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean extractRelayState(string relaystate)
|
2010-02-12 11:53:43 +01:00
|
|
|
# Extract RelayState information into $self
|
2010-02-25 12:39:55 +01:00
|
|
|
# @param relayState relayState value
|
|
|
|
# @return result
|
2010-02-12 11:53:43 +01:00
|
|
|
sub extractRelayState {
|
2010-02-25 12:39:55 +01:00
|
|
|
my ( $self, $relaystate ) = splice @_;
|
|
|
|
my %h;
|
2010-02-12 11:53:43 +01:00
|
|
|
|
|
|
|
return 0 unless $relaystate;
|
|
|
|
|
2010-02-25 12:39:55 +01:00
|
|
|
# Open relaystate session
|
|
|
|
eval {
|
|
|
|
tie %h, $self->{globalStorage}, $relaystate,
|
|
|
|
$self->{globalStorageOptions};
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->lmLog( "Unable to open relaystate session", 'error' );
|
|
|
|
return 0;
|
|
|
|
}
|
2010-02-12 11:53:43 +01:00
|
|
|
|
|
|
|
# Push values in $self
|
2010-02-25 12:39:55 +01:00
|
|
|
foreach ( keys %h ) {
|
2010-02-28 20:07:02 +01:00
|
|
|
next if $_ =~ /(type|_session_id|_utime)/;
|
2010-02-25 12:39:55 +01:00
|
|
|
$self->{$_} = $h{$_};
|
2010-02-12 11:53:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-02-12 15:26:45 +01:00
|
|
|
## @method Lasso::Node getAssertion(Lasso::Login login)
|
|
|
|
# Get assertion in Lasso::Login object
|
|
|
|
# @param login Lasso::Login object
|
|
|
|
# @return assertion Lasso::Node object
|
|
|
|
sub getAssertion {
|
|
|
|
my ( $self, $login ) = splice @_;
|
|
|
|
my $assertion;
|
|
|
|
|
|
|
|
eval { $assertion = Lasso::Login::get_assertion($login); };
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $assertion;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method string getAttributeValue(string name, string format, string friendly_name, array_ref attributes)
|
|
|
|
# Get SAML attribute value corresponding to name, format and friendly_name
|
|
|
|
# Multivaluated values are separated by ';'
|
|
|
|
# @param name SAML attribute name
|
|
|
|
# @param format optional SAML attribute format
|
|
|
|
# @param friendly_name optional SAML attribute friendly name
|
|
|
|
# @return attribute value
|
|
|
|
sub getAttributeValue {
|
|
|
|
my ( $self, $name, $format, $friendly_name, $attributes ) = splice @_;
|
|
|
|
my $value;
|
|
|
|
|
|
|
|
# Loop on attributes
|
|
|
|
foreach (@$attributes) {
|
|
|
|
my $attr_name = $_->Name();
|
|
|
|
my $attr_format = $_->NameFormat();
|
|
|
|
my $attr_fname = $_->FriendlyName();
|
|
|
|
|
|
|
|
# Skip if name does not correspond to attribute name
|
|
|
|
next if ( $name ne $attr_name );
|
|
|
|
|
|
|
|
# Verify format and friendly name if given
|
|
|
|
next if ( $format and $format ne $attr_format );
|
|
|
|
next if ( $friendly_name and $friendly_name ne $attr_fname );
|
|
|
|
|
|
|
|
# Attribute is found, return its content
|
|
|
|
my @attr_values = $_->AttributeValue();
|
|
|
|
|
|
|
|
foreach (@attr_values) {
|
|
|
|
my $xs = XML::Simple->new();
|
|
|
|
my $data = $xs->XMLin( $_->dump() );
|
|
|
|
my $content = $data->{content};
|
|
|
|
$value .= $content . ";" if $content;
|
|
|
|
}
|
|
|
|
$value =~ s/;$//;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
|
2010-02-15 18:03:07 +01:00
|
|
|
## @method boolean validateConditions(Lasso::Saml2::Assertion assertion, string entityID)
|
|
|
|
# Validate conditions
|
|
|
|
# @param assertion SAML2 assertion
|
|
|
|
# @param entityID relaying party entity ID
|
|
|
|
# @return result
|
|
|
|
sub validateConditions {
|
|
|
|
my ( $self, $assertion, $entityID ) = splice @_;
|
2010-03-01 10:42:25 +01:00
|
|
|
my $tolerance = 10;
|
2010-02-15 18:03:07 +01:00
|
|
|
my $status;
|
|
|
|
|
2010-03-01 10:42:25 +01:00
|
|
|
# Time
|
2010-02-15 18:03:07 +01:00
|
|
|
eval {
|
|
|
|
$status =
|
2010-03-01 10:42:25 +01:00
|
|
|
Lasso::Saml2Assertion::validate_time_checks( $assertion, $tolerance );
|
2010-02-15 18:03:07 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unless ( $status eq Lasso::Constants::SAML2_ASSERTION_VALID ) {
|
2010-03-01 10:42:25 +01:00
|
|
|
$self->lmLog( "Time conditions validations result: $status", 'error' );
|
2010-02-15 18:03:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-01 10:42:25 +01:00
|
|
|
$self->lmLog( "Time conditions validated", 'debug' );
|
|
|
|
|
|
|
|
# Audience
|
|
|
|
eval {
|
|
|
|
$status =
|
|
|
|
Lasso::Saml2Assertion::validate_audience( $assertion, $entityID );
|
|
|
|
};
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unless ( $status eq Lasso::Constants::SAML2_ASSERTION_VALID ) {
|
|
|
|
$self->lmLog( "Audience conditions validations result: $status",
|
|
|
|
'error' );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Audience conditions validated", 'debug' );
|
|
|
|
|
2010-02-15 18:03:07 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-02-26 10:12:18 +01:00
|
|
|
## @method Lasso::Logout createLogoutRequest(Lasso::Server server, string session_dump, int method)
|
2010-02-17 16:13:00 +01:00
|
|
|
# Create logout request for selected entity
|
|
|
|
# @param server Lasso::Server object
|
2010-02-17 18:37:38 +01:00
|
|
|
# @param session_dump Lasso::Session dump
|
2010-02-26 10:12:18 +01:00
|
|
|
# @param method HTTP method
|
2010-02-17 16:13:00 +01:00
|
|
|
# @return Lasso::Login object
|
|
|
|
sub createLogoutRequest {
|
2010-02-26 10:12:18 +01:00
|
|
|
my ( $self, $server, $session_dump, $method ) = splice @_;
|
2010-02-17 18:37:38 +01:00
|
|
|
my $session;
|
2010-02-17 16:13:00 +01:00
|
|
|
|
|
|
|
# Create Lasso Logout
|
|
|
|
my $logout = $self->createLogout($server);
|
|
|
|
|
2010-02-17 18:37:38 +01:00
|
|
|
unless ( $self->setSessionFromDump( $logout, $session_dump ) ) {
|
2010-02-28 20:07:02 +01:00
|
|
|
$self->lmLog( "Could not fill Lasso::Logout with session dump",
|
|
|
|
'error' );
|
2010-02-17 18:37:38 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-02-17 16:13:00 +01:00
|
|
|
# Init logout request
|
2010-02-18 10:58:59 +01:00
|
|
|
unless ( $self->initLogoutRequest( $logout, undef, $method ) ) {
|
2010-02-17 18:37:38 +01:00
|
|
|
$self->lmLog( "Could not initiate logout request", 'error' );
|
2010-02-17 16:13:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-01 10:19:28 +01:00
|
|
|
# Set RelayState
|
|
|
|
my $infos;
|
|
|
|
foreach (qw /urldc/) {
|
|
|
|
$infos->{$_} = $self->{$_} if $self->{$_};
|
|
|
|
}
|
|
|
|
my $relaystate = $self->storeRelayState($infos);
|
|
|
|
$logout->msg_relayState($relaystate);
|
|
|
|
$self->lmLog( "Set $relaystate in RelayState", 'debug' );
|
|
|
|
|
2010-02-17 16:13:00 +01:00
|
|
|
# Build logout request
|
|
|
|
unless ( $self->buildLogoutRequestMsg($logout) ) {
|
2010-02-17 18:37:38 +01:00
|
|
|
$self->lmLog( "Could not build logout request", 'error' );
|
2010-02-17 16:13:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $logout;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method Lasso::Logout createLogout(Lasso::Server server)
|
|
|
|
# Create Lasso::Logout object
|
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @return Lasso::Logout object
|
|
|
|
sub createLogout {
|
|
|
|
my ( $self, $server ) = splice @_;
|
|
|
|
my $logout;
|
|
|
|
|
|
|
|
eval { $logout = Lasso::Logout->new($server); };
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $logout;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean initLogoutRequest(Lasso::Logout logout, string entityID, int method)
|
|
|
|
# Init logout request
|
|
|
|
# @param logout Lasso::Logout object
|
|
|
|
# @param entityID
|
|
|
|
# @param HTTP method
|
|
|
|
# @return result
|
|
|
|
sub initLogoutRequest {
|
|
|
|
my ( $self, $logout, $entityID, $method ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::init_request( $logout, $entityID, $method ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean buildLogoutRequestMsg(Lasso::Logout logout)
|
|
|
|
# Build logout request message
|
|
|
|
# @param logout Lasso::Logout object
|
|
|
|
# @return result
|
|
|
|
sub buildLogoutRequestMsg {
|
|
|
|
my ( $self, $logout ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::build_request_msg($logout); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-17 18:37:38 +01:00
|
|
|
## @method boolean setSessionFromDump(Lasso::Profile profile)
|
|
|
|
# Set session from dump in Lasso::Profile object
|
|
|
|
# @param profile Lasso::Profile object
|
|
|
|
# @param dump Lasso::Session XML dump
|
|
|
|
# @return result
|
|
|
|
sub setSessionFromDump {
|
|
|
|
my ( $self, $profile, $dump ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Profile::set_session_from_dump( $profile, $dump ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-18 18:22:04 +01:00
|
|
|
## @method string getMetaDataURL(string key, int index)
|
2010-02-18 10:58:59 +01:00
|
|
|
# Get URL stored in a service metadata configuration key
|
|
|
|
# @param key Metadata configuration key
|
2010-02-18 18:22:04 +01:00
|
|
|
# @param index field index containing URL
|
2010-02-18 10:58:59 +01:00
|
|
|
# @return url
|
|
|
|
sub getMetaDataURL {
|
2010-02-18 18:22:04 +01:00
|
|
|
my ( $self, $key, $index ) = splice @_;
|
|
|
|
$index = 3 unless defined $index;
|
2010-02-18 10:58:59 +01:00
|
|
|
|
|
|
|
return unless defined $self->{$key};
|
|
|
|
|
2010-02-18 18:22:04 +01:00
|
|
|
return ( split( /;/, $self->{$key} ) )[$index];
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean processLogoutResponseMsg(Lasso::Logout logout, string response)
|
|
|
|
# Process logout response message
|
|
|
|
# @param logout Lasso::Logout object
|
|
|
|
# @param response SAML response
|
|
|
|
# @return result
|
|
|
|
sub processLogoutResponseMsg {
|
|
|
|
my ( $self, $logout, $response ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::process_response_msg( $logout, $response ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean processLogoutRequestMsg(Lasso::Logout logout, string request)
|
|
|
|
# Process logout request message
|
|
|
|
# @param logout Lasso::Logout object
|
|
|
|
# @param request SAML request
|
|
|
|
# @return result
|
|
|
|
sub processLogoutRequestMsg {
|
|
|
|
my ( $self, $logout, $request ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::process_request_msg( $logout, $request ); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
2010-02-18 10:58:59 +01:00
|
|
|
}
|
|
|
|
|
2010-02-18 18:42:31 +01:00
|
|
|
## @method boolean validateLogoutRequest(Lasso::Logout logout)
|
|
|
|
# Validate logout request
|
|
|
|
# @param logout Lasso::Logout object
|
|
|
|
# @return result
|
|
|
|
sub validateLogoutRequest {
|
|
|
|
my ( $self, $logout ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::validate_request($logout); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-19 12:33:34 +01:00
|
|
|
## @method boolean buildLogoutResponseMsg(Lasso::Logout logout)
|
|
|
|
# Build logout response message
|
|
|
|
# @param Lasso::Logout logout
|
|
|
|
# @return boolean result
|
|
|
|
sub buildLogoutResponseMsg {
|
|
|
|
my ( $self, $logout ) = splice @_;
|
|
|
|
|
|
|
|
eval { Lasso::Logout::build_response_msg($logout); };
|
|
|
|
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
}
|
|
|
|
|
2010-02-28 20:07:02 +01:00
|
|
|
## @method boolean storeReplayProtection(string samlID)
|
|
|
|
# Store ID of an SAML message in Replay Protection base
|
|
|
|
# @param samlID ID of SAML message
|
|
|
|
# @return result
|
|
|
|
sub storeReplayProtection {
|
|
|
|
my ( $self, $samlID ) = splice @_;
|
|
|
|
my %h;
|
|
|
|
|
|
|
|
eval {
|
|
|
|
tie %h, $self->{globalStorage}, undef, $self->{globalStorageOptions};
|
|
|
|
};
|
|
|
|
if ( $@ or !$samlID ) {
|
|
|
|
$self->lmLog( "Unable to create replay protection session", 'error' );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
$h{type} = 'assertion'; # Session type
|
|
|
|
$h{_utime} = time(); # Creation time
|
|
|
|
$h{ID} = $samlID;
|
|
|
|
|
|
|
|
my $session_id = $h{_session_id};
|
|
|
|
|
|
|
|
untie %h;
|
|
|
|
|
|
|
|
$self->lmLog(
|
|
|
|
"Keep Logout request ID $samlID in assertion session $session_id",
|
|
|
|
'debug' );
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-02-22 18:12:16 +01:00
|
|
|
## @method boolean replayProtection(string samlID)
|
|
|
|
# Check if SAML message do not correspond to a previously responded message
|
|
|
|
# @param samlID ID of initial SAML message
|
|
|
|
# @return result
|
|
|
|
sub replayProtection {
|
|
|
|
my ( $self, $samlID ) = splice @_;
|
|
|
|
my %h;
|
|
|
|
|
|
|
|
unless ($samlID) {
|
|
|
|
$self->lmLog( "Cannot verify replay because no SAML ID given",
|
|
|
|
'error' );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $sessions =
|
|
|
|
$self->{globalStorage}
|
|
|
|
->searchOn( $self->{globalStorageOptions}, "ID", $samlID );
|
|
|
|
|
|
|
|
if ( my @keys = keys %$sessions ) {
|
|
|
|
|
|
|
|
# A session was found
|
|
|
|
foreach (@keys) {
|
|
|
|
my $session = $_;
|
|
|
|
|
|
|
|
# Delete it
|
|
|
|
eval {
|
|
|
|
tie %h, $self->{globalStorage}, $_,
|
|
|
|
$self->{globalStorageOptions};
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->lmLog(
|
|
|
|
"Unable to recover assertion session $session (Message ID $samlID)",
|
|
|
|
'error'
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
eval { tied(%h)->delete(); };
|
|
|
|
if ($@) {
|
|
|
|
$self->lmLog(
|
|
|
|
"Unable to delete assertion session $session (Message ID $samlID)",
|
|
|
|
'error'
|
|
|
|
);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
$self->lmLog(
|
|
|
|
"Assertion session $session (Message ID $samlID) was deleted",
|
|
|
|
'debug' );
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-24 16:24:54 +01:00
|
|
|
## @method string resolveArtifact(Lasso::Profile profile, string artifact, int method)
|
|
|
|
# Resolve artifact to get real SAML message
|
|
|
|
# @param profile Lasso::Profile object
|
|
|
|
# @param artifact Artifact message
|
|
|
|
# @param method HTTP method
|
|
|
|
# @return SAML message
|
|
|
|
sub resolveArtifact {
|
|
|
|
my ( $self, $profile, $artifact, $method ) = splice @_;
|
|
|
|
my $message;
|
|
|
|
|
|
|
|
# LWP User Agent
|
|
|
|
my $ua = new LWP::UserAgent;
|
|
|
|
push @{ $ua->requests_redirectable }, 'POST';
|
|
|
|
|
|
|
|
# Login profile
|
|
|
|
if ( $profile->isa("Lasso::Login") ) {
|
|
|
|
|
|
|
|
# Init request message
|
|
|
|
eval { Lasso::Login::init_request( $profile, $artifact, $method ); };
|
|
|
|
return unless $self->checkLassoError($@);
|
|
|
|
|
|
|
|
# Build request message
|
|
|
|
eval { Lasso::Login::build_request_msg($profile); };
|
|
|
|
return unless $self->checkLassoError($@);
|
|
|
|
|
|
|
|
my $request = HTTP::Request->new( 'POST' => $profile->msg_url );
|
|
|
|
$request->content_type('text/xml');
|
|
|
|
$request->content( $profile->msg_body );
|
|
|
|
|
|
|
|
$self->lmLog(
|
|
|
|
"Send message " . $profile->msg_body . " to " . $profile->msg_url,
|
|
|
|
'debug' );
|
|
|
|
|
|
|
|
# SOAP call
|
|
|
|
my $soap_answer = $ua->request($request);
|
|
|
|
if ( $soap_answer->code() == "200" ) {
|
|
|
|
$message = $soap_answer->content();
|
|
|
|
$self->lmLog( "Get message $message", 'debug' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method boolean processArtResponseMsg(Lasso::Profile profile, string response)
|
|
|
|
# Process artifact response message
|
|
|
|
# @param profile Lasso::Profile object
|
|
|
|
# @param response SAML response
|
|
|
|
# @return result
|
|
|
|
sub processArtResponseMsg {
|
|
|
|
my ( $self, $profile, $response ) = splice @_;
|
|
|
|
|
|
|
|
# Login profile
|
|
|
|
if ( $profile->isa("Lasso::Login") ) {
|
|
|
|
|
|
|
|
eval { Lasso::Login::process_response_msg( $profile, $response ); };
|
|
|
|
return $self->checkLassoError($@);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-26 10:12:18 +01:00
|
|
|
## @method string sendSOAPMessage(string endpoint, string message)
|
|
|
|
# Send SOAP message and get response
|
|
|
|
# @param endpoint SOAP End Point
|
|
|
|
# @param message SOAP message
|
|
|
|
# @return SOAP response
|
|
|
|
sub sendSOAPMessage {
|
|
|
|
my ( $self, $endpoint, $message ) = splice @_;
|
|
|
|
my $response;
|
|
|
|
|
|
|
|
# LWP User Agent
|
|
|
|
my $ua = new LWP::UserAgent;
|
|
|
|
push @{ $ua->requests_redirectable }, 'POST';
|
|
|
|
|
|
|
|
my $request = HTTP::Request->new( 'POST' => $endpoint );
|
|
|
|
$request->content_type('text/xml');
|
|
|
|
$request->content($message);
|
|
|
|
|
|
|
|
$self->lmLog( "Send SOAP message $message to $endpoint", 'debug' );
|
|
|
|
|
|
|
|
# SOAP call
|
|
|
|
my $soap_answer = $ua->request($request);
|
|
|
|
if ( $soap_answer->code() == "200" ) {
|
|
|
|
$response = $soap_answer->content();
|
|
|
|
$self->lmLog( "Get response $response", 'debug' );
|
|
|
|
}
|
2010-03-03 17:54:23 +01:00
|
|
|
else {
|
|
|
|
$self->lmLog( "No response to SOAP request", 'debug' );
|
|
|
|
return;
|
|
|
|
}
|
2010-02-26 10:12:18 +01:00
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2010-03-03 17:54:23 +01:00
|
|
|
## @method Lasso::AssertionQuery createAttributeRequest(Lasso::Server server, Lasso::Login login, string idp, hashref attributes)
|
|
|
|
# Create an attribute request
|
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @param login Lasso::Login object
|
|
|
|
# @param idp IDP entityID
|
|
|
|
# @param attributes List of requested attributes
|
|
|
|
# @return assertion request
|
|
|
|
sub createAttributeRequest {
|
|
|
|
my ( $self, $server, $login, $idp, $attributes ) = splice @_;
|
|
|
|
my $query;
|
|
|
|
|
|
|
|
# Create assertion query
|
|
|
|
eval { $query = Lasso::AssertionQuery->new($server); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Assertion query created", 'debug' );
|
|
|
|
|
|
|
|
# Set identity
|
|
|
|
eval {
|
|
|
|
Lasso::Profile::set_identity_from_dump( $query,
|
|
|
|
$login->get_identity()->dump );
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Identity set in query", 'debug' );
|
|
|
|
|
|
|
|
# Init request
|
|
|
|
my $method = Lasso::Constants::HTTP_METHOD_SOAP;
|
|
|
|
my $type = Lasso::Constants::ASSERTION_QUERY_REQUEST_TYPE_ATTRIBUTE;
|
|
|
|
eval {
|
|
|
|
Lasso::AssertionQuery::init_request( $query, $idp, $method, $type );
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Assertion query request initiated", 'debug' );
|
|
|
|
|
|
|
|
# Store attributes in request
|
|
|
|
my @requested_attributes;
|
|
|
|
foreach ( keys %$attributes ) {
|
|
|
|
|
|
|
|
# Create SAML2 Attribute
|
|
|
|
my $attribute;
|
|
|
|
|
|
|
|
eval { $attribute = Lasso::Saml2Attribute->new(); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set attribute properties
|
|
|
|
my ( $mandatory, $name, $format, $friendly_name ) =
|
|
|
|
split( /;/, $attributes->{$_} );
|
|
|
|
|
|
|
|
$attribute->Name($name) if defined $name;
|
|
|
|
$attribute->NameFormat($format) if defined $format;
|
|
|
|
$attribute->FriendlyName($friendly_name) if defined $friendly_name;
|
|
|
|
|
|
|
|
# Store attribute
|
|
|
|
push @requested_attributes, $attribute;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Set attributes in request
|
|
|
|
eval { $query->request()->Attribute(@requested_attributes); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Build message
|
|
|
|
eval { Lasso::AssertionQuery::build_request_msg($query); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Return query
|
|
|
|
return $query;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method Lasso::AssertionQuery processAttributeResponse(Lasso::Server server, string response)
|
|
|
|
# Process an attribute response
|
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @param response Response content
|
|
|
|
# @return assertion query
|
|
|
|
sub processAttributeResponse {
|
|
|
|
my ( $self, $server, $response ) = splice @_;
|
|
|
|
my $query;
|
|
|
|
|
|
|
|
# Create assertion query
|
|
|
|
eval { $query = Lasso::AssertionQuery->new($server); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Assertion query created", 'debug' );
|
|
|
|
|
|
|
|
# Process response
|
|
|
|
eval { Lasso::AssertionQuery::process_response_msg( $query, $response ); };
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->lmLog( "Attribute response is valid", 'debug' );
|
|
|
|
|
|
|
|
return $query;
|
|
|
|
}
|
|
|
|
|
2010-03-05 10:28:28 +01:00
|
|
|
## @method string getNameIDFormat(string format)
|
|
|
|
# Convert configuration string into SAML2 NameIDFormat string
|
|
|
|
# @param format configuration string
|
|
|
|
# @return SAML2 NameIDFormat string
|
|
|
|
sub getNameIDFormat {
|
|
|
|
my ( $self, $format ) = splice @_;
|
|
|
|
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED
|
|
|
|
if ( $format =~ /unspecified/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_EMAIL
|
|
|
|
if ( $format =~ /email/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_X509
|
|
|
|
if ( $format =~ /x509/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_WINDOWS
|
|
|
|
if ( $format =~ /windows/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_KERBEROS
|
|
|
|
if ( $format =~ /kerberos/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_ENTITY
|
|
|
|
if ( $format =~ /entity/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
|
|
|
|
if ( $format =~ /persistent/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT
|
|
|
|
if ( $format =~ /transient/i );
|
|
|
|
return Lasso::Constants::SAML2_NAME_IDENTIFIER_FORMAT_ENCRYPTED
|
|
|
|
if ( $format =~ /encrypted/i );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-05 17:57:11 +01:00
|
|
|
## @method int getHttpMethod(string method)
|
|
|
|
# Convert configuration string into Lasso HTTP Method integer
|
|
|
|
# @param method configuration string
|
|
|
|
# @return Lasso HTTP Method integer
|
|
|
|
sub getHttpMethod {
|
|
|
|
my ( $self, $method ) = splice @_;
|
|
|
|
|
|
|
|
return Lasso::Constants::HTTP_METHOD_GET
|
|
|
|
if ( $method =~ /^(http)?[-_]?get$/i );
|
|
|
|
return Lasso::Constants::HTTP_METHOD_POST
|
|
|
|
if ( $method =~ /^(http)?[-_]?post$/i );
|
|
|
|
return Lasso::Constants::HTTP_METHOD_REDIRECT
|
|
|
|
if ( $method =~ /^(http)?[-_]?redirect$/i );
|
|
|
|
return Lasso::Constants::HTTP_METHOD_SOAP
|
|
|
|
if ( $method =~ /^(http)?[-_]?soap$/i );
|
|
|
|
return Lasso::Constants::HTTP_METHOD_ARTIFACT_GET
|
|
|
|
if ( $method =~ /^(artifact)[-_]get$/i );
|
|
|
|
return Lasso::Constants::HTTP_METHOD_ARTIFACT_POST
|
|
|
|
if ( $method =~ /^(artifact)[-_]post$/i );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
## @method int getFirstHttpMethod(Lasso::Server server, string entityID, int protcolType)
|
|
|
|
# Find a suitable HTTP method for an entity with a given protocol
|
|
|
|
# @param server Lasso::Server object
|
|
|
|
# @param entityID entity ID
|
|
|
|
# @param protocolType Lasso protocol type
|
|
|
|
# @return Lasso HTTP Method
|
|
|
|
sub getFirstHttpMethod {
|
|
|
|
my ( $self, $server, $entityID, $protocolType ) = splice @_;
|
|
|
|
my $entity_provider;
|
|
|
|
my $method;
|
|
|
|
|
|
|
|
# Get Lasso::Provider object
|
|
|
|
eval {
|
|
|
|
$entity_provider = Lasso::Server::get_provider( $server, $entityID );
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Find HTTP method
|
|
|
|
eval {
|
|
|
|
$method =
|
|
|
|
Lasso::Provider::get_first_http_method( $server, $entity_provider,
|
|
|
|
$protocolType );
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
$self->checkLassoError($@);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $method;
|
|
|
|
}
|
|
|
|
|
2009-04-07 22:38:24 +02:00
|
|
|
1;
|
2010-01-29 18:33:35 +01:00
|
|
|
|
2009-04-07 22:38:24 +02:00
|
|
|
__END__
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
2010-01-03 09:09:59 +01:00
|
|
|
=encoding utf8
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
Lemonldap::NG::Portal::_SAML
|
2009-04-07 22:38:24 +02:00
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
use Lemonldap::NG::Portal::_SAML;
|
2009-04-07 22:38:24 +02:00
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
This module contains common methods for SAML authentication
|
|
|
|
and user information loading
|
|
|
|
|
|
|
|
=head1 METHODS
|
|
|
|
|
|
|
|
=head2 loadLasso
|
|
|
|
|
|
|
|
Load Lasso module
|
2009-04-07 22:38:24 +02:00
|
|
|
|
2010-01-29 18:33:35 +01:00
|
|
|
=head2 checkLassoError
|
|
|
|
|
|
|
|
Log Lasso error code and message if this is actually a Lasso::Error with code > 0
|
|
|
|
|
|
|
|
=head2 createServer
|
|
|
|
|
|
|
|
Load service metadata and create Lasso::Server object
|
|
|
|
|
2010-02-01 18:07:40 +01:00
|
|
|
=head2 addIDP
|
|
|
|
|
|
|
|
Add IDP to an existing Lasso::Server
|
|
|
|
|
|
|
|
=head2 addProvider
|
|
|
|
|
|
|
|
Add provider to an existing Lasso::Server
|
|
|
|
|
2010-02-05 18:18:09 +01:00
|
|
|
=head2 getOrganizationName
|
|
|
|
|
|
|
|
Return name of organization picked up from metadata
|
|
|
|
|
2010-02-04 17:02:02 +01:00
|
|
|
=head2 createAuthnRequest
|
|
|
|
|
|
|
|
Create authentication request for selected IDP
|
|
|
|
|
|
|
|
=head2 createLogin
|
|
|
|
|
|
|
|
Create Lasso::Login object
|
|
|
|
|
|
|
|
=head2 initAuthnRequest
|
|
|
|
|
|
|
|
Init authentication request
|
|
|
|
|
|
|
|
=head2 buildAuthnRequestMsg
|
|
|
|
|
|
|
|
Build authentication request message
|
|
|
|
|
2010-02-08 18:24:45 +01:00
|
|
|
=head2 processAuthnResponseMsg
|
|
|
|
|
|
|
|
Process authentication response message
|
|
|
|
|
2010-02-09 10:02:39 +01:00
|
|
|
=head2 getNameIdentifier
|
|
|
|
|
|
|
|
Get NameID from Lasso Profile
|
|
|
|
|
2010-02-11 13:39:42 +01:00
|
|
|
=head2 createIdentity
|
|
|
|
|
|
|
|
Create Lasso::Identity object
|
|
|
|
|
|
|
|
=head2 createSession
|
|
|
|
|
|
|
|
Create Lasso::Session object
|
|
|
|
|
|
|
|
=head2 acceptSSO
|
|
|
|
|
|
|
|
Accept SSO from IDP
|
|
|
|
|
2010-02-25 12:39:55 +01:00
|
|
|
=head2 storeRelayState
|
|
|
|
|
|
|
|
Store information in relayState database and return
|
|
|
|
|
2010-02-12 11:53:43 +01:00
|
|
|
=head2 extractRelayState
|
|
|
|
|
|
|
|
Extract RelayState information into $self
|
|
|
|
|
2010-02-12 15:26:45 +01:00
|
|
|
=head2 getAssertion
|
|
|
|
|
|
|
|
Get assertion in Lasso::Login object
|
|
|
|
|
|
|
|
=head2 getAttributeValue
|
|
|
|
|
|
|
|
Get SAML attribute value corresponding to name, format and friendly_name
|
|
|
|
Multivaluated values are separated by ';'
|
|
|
|
|
2010-02-15 18:03:07 +01:00
|
|
|
=head2 validateConditions
|
|
|
|
|
|
|
|
Validate conditions
|
|
|
|
|
2010-02-17 16:13:00 +01:00
|
|
|
=head2 createLogoutRequest
|
|
|
|
|
|
|
|
Create logout request for selected entity
|
|
|
|
|
|
|
|
=head2 createLogout
|
|
|
|
|
|
|
|
Create Lasso::Logout object
|
|
|
|
|
|
|
|
=head2 initLogoutRequest
|
|
|
|
|
|
|
|
Init logout request
|
|
|
|
|
|
|
|
=head2 buildLogoutRequestMsg
|
|
|
|
|
|
|
|
Build logout request message
|
|
|
|
|
2010-02-17 18:37:38 +01:00
|
|
|
=head2 setSessionFromDump
|
|
|
|
|
|
|
|
Set session from dump in Lasso::Profile object
|
|
|
|
|
2010-02-18 10:58:59 +01:00
|
|
|
=head2 getMetaDataURL
|
|
|
|
|
|
|
|
Get URL stored in a service metadata configuration key
|
|
|
|
|
2010-02-18 18:22:04 +01:00
|
|
|
=head2 processLogoutResponseMsg
|
|
|
|
|
|
|
|
Process logout response message
|
|
|
|
|
|
|
|
=head2 processLogoutRequestMsg
|
|
|
|
|
|
|
|
Process logout request message
|
|
|
|
|
2010-02-18 18:42:31 +01:00
|
|
|
=head2 validateLogoutRequest
|
|
|
|
|
|
|
|
Validate logout request
|
|
|
|
|
2010-02-19 12:33:34 +01:00
|
|
|
=head2 buildLogoutResponseMsg
|
|
|
|
|
|
|
|
Build logout response msg
|
|
|
|
|
2010-02-28 20:07:02 +01:00
|
|
|
=head2 storeReplayProtection
|
|
|
|
|
|
|
|
Store ID of an SAML message in Replay Protection base
|
|
|
|
|
2010-02-22 18:12:16 +01:00
|
|
|
=head2 replayProtection
|
|
|
|
|
|
|
|
Check if SAML message do not correspond to a previously responded message
|
|
|
|
|
2010-02-24 16:24:54 +01:00
|
|
|
=head2 resolveArtifact
|
|
|
|
|
|
|
|
Resolve artifact to get the real SAML message
|
|
|
|
|
|
|
|
=head2 processArtResponseMsg
|
|
|
|
|
|
|
|
Process artifact response message
|
|
|
|
|
2010-02-26 10:12:18 +01:00
|
|
|
=head2 sendSOAPMessage
|
|
|
|
|
|
|
|
Send SOAP message and get response
|
|
|
|
|
2010-03-03 17:54:23 +01:00
|
|
|
=head2 createAttributeRequest
|
|
|
|
|
|
|
|
Create an attribute request
|
|
|
|
|
|
|
|
=head2 processAttributeResponse
|
|
|
|
|
|
|
|
Process an attribute response
|
|
|
|
|
2010-03-05 10:28:28 +01:00
|
|
|
=head2 getNameIDFormat
|
|
|
|
|
|
|
|
Convert configuration string into SAML2 NameIDFormat string
|
|
|
|
|
2010-03-05 17:57:11 +01:00
|
|
|
=head2 getHttpMethod
|
|
|
|
|
|
|
|
Convert configuration string into Lasso HTTP Method integer
|
|
|
|
|
|
|
|
=head2 getFirstHttpMethod
|
|
|
|
|
|
|
|
Find a suitable HTTP method for an entity with a given protocol
|
|
|
|
|
2009-04-07 22:38:24 +02:00
|
|
|
=head1 SEE ALSO
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
L<Lemonldap::NG::Portal::AuthSAML>, L<Lemonldap::NG::Portal::UserDBSAML>
|
2009-04-07 22:38:24 +02:00
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
Xavier Guimard, E<lt>x.guimard@free.frE<gt>,
|
|
|
|
Clement Oudot, E<lt>coudot@linagora.comE<gt>
|
2009-04-07 22:38:24 +02:00
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
|
2010-01-29 11:44:56 +01:00
|
|
|
Copyright (C) 2009 by Xavier Guimard, Clement Oudot
|
2009-04-07 22:38:24 +02:00
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.10.0 or,
|
|
|
|
at your option, any later version of Perl 5 you may have available.
|
|
|
|
|
|
|
|
=cut
|