Lemonldap-NG-Portal

 view release on metacpan or  search on metacpan

lib/Lemonldap/NG/Portal/Lib/SAML.pm  view on Meta::CPAN

        $moduleOptions->{backend} = $self->conf->{samlStorage};
    }
    else {
        $moduleOptions = $self->conf->{globalStorageOptions} || {};
        $moduleOptions->{backend} = $self->conf->{globalStorage};
    }
    $self->aModule( $moduleOptions->{backend} );
    $self->amOpts($moduleOptions);

    # Check for Lasso errors/messages (see BEGIN)
    unless (LASSO) {
        $self->logger->error("Module Lasso not loaded (see below)");
        return 0;
    }

    if (BADLASSO) {
        $self->logger->error('Lasso version >= 2.3.0 required');
        return 0;
    }

    unless (LASSOTHINSESSIONS) {
        $self->logger->warn('Lasso thin-sessions flag could not be set');
    }
    else {
        $self->logger->debug('Lasso thin-sessions flag set');
    }
    if (GLIB) {
        Glib::Log->set_handler(
            "Lasso",
            [qw/ error critical warning message info debug /],
            sub {
                $self->logger->debug(
                    $_[0] . " error " . $_[1] . ": " . $_[2] );
            }
        );
    }

    # Conf initialization

    return 0 unless ( $self->lassoServer( $self->loadService ) );
    $self->addUnauthRoute(
        ( $self->{path} || 'saml' ) =>
          { 'metadata' => { ':type' => 'metadata' } },
        ['GET']
    );
    $self->addAuthRoute(
        ( $self->{path} || 'saml' ) =>
          { 'metadata' => { ':type' => 'metadata' } },
        ['GET']
    );
    return 1;
}

# METHODS

sub loadService {
    my ($self) = @_;

    # Check if certificate is available
    my $signing_key =
      $self->get_private_key( $self->_get_default_signing_key_name );
    unless ($signing_key) {
        $self->logger->error(
            'SAML private and public key not found in configuration');
        return 0;
    }

    my $serviceCertificate;
    if (    $self->conf->{samlServiceUseCertificateInResponse}
        and $signing_key->{public} =~ /CERTIFICATE/ )
    {
        $serviceCertificate = $signing_key->{public};
        $self->logger->debug('Certificate will be used in SAML responses');

    }

    # Get metadata from configuration
    $self->logger->debug("Get Metadata for this service");
    my $metadata = $self->_get_metadata_xml( undef, "all" );

    $self->logger->debug("Create Lasso server");

    my $encryption_key =
      $self->get_private_key( $self->_get_default_encryption_key_name );

    # Create Lasso server with service metadata
    my $server = $self->createServer(
        $metadata,
        $signing_key->{private},
        $signing_key->{password},

        # use signature cert for encryption unless defined
        (
            $encryption_key
            ? ( $encryption_key->{private}, $encryption_key->{password}, )
            : ( $signing_key->{private}, $signing_key->{password}, )
        ),
        $serviceCertificate
    );

    unless ($server) {
        $self->logger->error('Unable to create Lasso server');
        return 0;
    }

    # Signature method
    my $method = $self->getDefaultSignatureMethod();
    $server->signature_method( $self->getSignatureMethod($method) );
    $self->logger->debug("Set $method as SAML server signature method ");

    # Log
    $self->logger->debug("Service created");

    return $server;
}

sub loadIDPs {
    my ($self) = @_;

    # Load identity provider metadata
    # IDP metadata are listed in $self->{samlIDPMetaDataXML}
    # Each key is the IDP name
    # Build IDP list for later use in extractFormInfo
    foreach ( keys %{ $self->conf->{samlIDPMetaDataXML} } ) {
        $self->loadIDP($_);
    }
    return 1;
}

sub loadIDP {
    my ( $self, $idpConfKey ) = @_;

    $self->logger->debug("Get Metadata for IDP $idpConfKey");

    # If XML metadata has been set in conf, load this provider from conf
    my $idp_metadata =
      $self->conf->{samlIDPMetaDataXML}->{$idpConfKey}->{samlIDPMetaDataXML};
    if ($idp_metadata) {
        if ( $self->load_idp_from_conf( $idpConfKey, $idp_metadata ) ) {
            $self->logger->debug("IDP $idpConfKey added");
        }
        else {
            $self->logger->warn("Ignoring IDP $idpConfKey");
        }

lib/Lemonldap/NG/Portal/Lib/SAML.pm  view on Meta::CPAN


    my $type = eval {
        my $resp = Lasso::Samlp2ArtifactResponse->new;
        $resp->init_from_message($message);
        $resp->any->get_name;
    };

    if ($@) {
        $self->logger->warn("Could not detect type of Artifact response");
        return;
    }

    $self->logger->debug("Artifact response type is $type");
    if ( $type eq "Response" ) {
        return 1;
    }
    else {
        return 0;
    }
}

## @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 error Lasso error object
# @param level optional log level (debug by default)
# @return 1 if no error
sub checkLassoError {
    my ( $self, $error, $level ) = @_;
    my ( $lasso_result, $lasso_error_code ) =
      $self->getLassoError( $error, $level );
    return $lasso_result;
}

## @method boolean getLassoError(Lasso::Error error, string level)
# Log Lasso error code and message if this is actually a Lasso::Error with code > 0
# @param error Lasso error object
# @param level optional log level (debug by default)
# @return (success status, lasso error code)
sub getLassoError {
    my ( $self, $error, $level ) = @_;
    $level ||= 'error';

    # If $error is not a Lasso::Error object, display error string
    unless ( ref($error) and $error->isa("Lasso::Error") ) {
        return ( 1, undef ) unless $error;
        $self->p->lmLog( "Lasso error: $error", $level );
        return ( 0, undef );
    }

    # Else check error code and error message
    if ( $error->{code} ) {
        $self->p->lmLog(
            "Lasso error code " . $error->{code} . ": " . $error->{message},
            $level );
        return ( 0, $error->{code} );
    }

    return ( 1, undef );
}

## @method Lasso::Server createServer(string metadata, string private_key, string private_key_password, string private_key_enc, string private_key_enc_password, string certificate)
# Load service metadata and create Lasso::Server object
# @param metadata SAML metadata
# @param private_key private key
# @param private_key_password optional private key password
# @param private_key_enc optional private key for encryption
# @param private_key_enc_password optional private key password for encryption
# @param certificate optional certificate
# @return Lasso::Server object
sub createServer {
    my ( $self, $metadata, $private_key, $private_key_password,
        $private_key_enc, $private_key_enc_password, $certificate )
      = @_;
    my $server;

    # https://dev.entrouvert.org/issues/51415
    my $save_env = $ENV{'SSL_CERT_FILE'};
    $ENV{'SSL_CERT_FILE'} = "/dev/null";

    eval {
        $server = Lasso::Server::new_from_buffers( $metadata, $private_key,
            $private_key_password, $certificate );

        # Set private key for encryption
        if ( $server && $private_key_enc ) {
            Lasso::Server::set_encryption_private_key_with_password( $server,
                $private_key_enc, $private_key_enc_password );
        }
    };

    if ( defined $save_env ) {
        $ENV{'SSL_CERT_FILE'} = $save_env;
    }
    else {
        delete $ENV{'SSL_CERT_FILE'};
    }

    if ($@) {
        $self->checkLassoError($@);
        return;
    }

    return $server;
}

## @method boolean addIDP(Lasso::Server server, string metadata, string public_key, string ca_cert_chain)
# Add IDP to an existing Lasso::Server
# @param server Lasso::Server object
# @param metadata IDP metadata
# @param public_key optional public key
# @param ca_cert_chain optional ca cert chain
# @return boolean result
sub addIDP {
    my ( $self, $server, $metadata, $public_key, $ca_cert_chain ) = @_;

    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 addSP(Lasso::Server server, string metadata, string public_key, string ca_cert_chain)
# Add SP to an existing Lasso::Server
# @param server Lasso::Server object
# @param metadata SP metadata
# @param public_key optional public key
# @param ca_cert_chain optional ca cert chain
# @return boolean result
sub addSP {
    my ( $self, $server, $metadata, $public_key, $ca_cert_chain ) = @_;

    return 0 unless ( $server->isa("Lasso::Server") and defined $metadata );

    return $self->addProvider( $server, Lasso::Constants::PROVIDER_ROLE_SP,
        $metadata, $public_key, $ca_cert_chain );
}

## @method boolean addAA(Lasso::Server server, string metadata, string public_key, string ca_cert_chain)
# Add Attribute Authority to an existing Lasso::Server
# @param server Lasso::Server object
# @param metadata AA metadata
# @param public_key optional public key
# @param ca_cert_chain optional ca cert chain
# @return boolean result
sub addAA {
    my ( $self, $server, $metadata, $public_key, $ca_cert_chain ) = @_;

lib/Lemonldap/NG/Portal/Lib/SAML.pm  view on Meta::CPAN

        for my $key_id ( split( /\s*,\s*/, $list_of_keys ) ) {
            my $key = $self->get_public_key($key_id);
            if ($key) {
                push @keys, $key;
            }
            else {
                $self->logger->debug("Could not publish $key_id in metadata");
            }
        }
    }
    return @keys;
}

sub metadata {
    my ( $self, $req ) = @_;
    my $type = $req->param('type') || 'all';
    my $idp  = $req->param('idp');
    my $sp   = $req->param('sp');
    if ( my $metadata = $self->_get_metadata_xml( $req, $type, $sp, $idp ) ) {
        return $self->p->sendTextResponse( $req, $metadata,
            type => 'application/xml' );
    }
    return $self->p->sendError( $req, 'Unable to build Metadata', 500 );
}

## @method int getSignatureMethod(string signature_method)
# Return Lasso signature method
# @param signature_method Signature method string
# @return Lasso signature method
sub getSignatureMethod {
    my ( $self, $signature_method ) = @_;

    $signature_method ||= $self->getDefaultSignatureMethod();

    if ( my $const =
        Lasso::Constants->can( "SIGNATURE_METHOD_" . uc($signature_method) ) )
    {
        return $const->();
    }
    else {
        return 0;
    }
}

sub getDefaultSignatureMethod {
    my ($self) = @_;

    my $signature_method =
      $self->conf->{samlServiceSignatureMethod} || 'RSA_SHA1';
    return $signature_method;
}

## @method boolean setProviderSignatureMethod(Lasso::Provider provider, int signature_method)
# Set signature method on a provider
# @param provider Lasso::Provider object
# @param signature_method Lasso signature method
# @return result
sub setProviderSignatureMethod {
    my ( $self, $provider, $signature_method ) = @_;

    my $key = $self->get_private_key( $self->_get_default_signing_key_name );
    return $self->setProviderSignatureOptions( $provider, $signature_method,
        $key );
}

sub setProviderSignatureOptions {
    my ( $self, $provider, $signature_method, $key ) = @_;

    # We have to use an intermediate variable to avoid interferences between
    # Lasso binding and tied hashes (#3105)
    my $priv = $key->{private};
    my $pass = $key->{password};
    my $cert = $key->{public};

    eval {
        my $key = Lasso::Key::new_for_signature_from_file( $priv, $pass,
            $signature_method, $cert, );
        $provider->set_server_signing_key($key);
    };

    return $self->checkLassoError($@);
}

sub _override_signature_or_key {
    my ( $self, $entityID, $log_description, $signature_method,
        $signature_key_list )
      = @_;
    if ( $signature_method or $signature_key_list ) {
        $signature_key_list //= "";
        my $signature_key = ( split( /\s*,\s*/, $signature_key_list ) )[0];

        $signature_method ||= $self->getDefaultSignatureMethod;
        my $lasso_signature_method =
          $self->getSignatureMethod($signature_method);

        $signature_key ||= $self->_get_default_signing_key_name;
        my $key = $self->get_private_key($signature_key);

        # Fallback to default key if custom key was not found
        if ( !$key ) {
            $signature_key = $self->_get_default_signing_key_name;
            $key           = $self->get_private_key($signature_key);
        }

        $self->logger->debug( "Setting signature key $signature_key"
              . " and signature method $signature_method"
              . " on $log_description" );

        unless (
            $self->setProviderSignatureOptions(
                $self->lassoServer->get_provider($entityID),
                $lasso_signature_method, $key
            )
          )
        {
            $self->logger->error(
                "Unable to set signature options on $log_description");
            return;
        }
    }
}

# The default signing key is the first key listed in samlServiceSignatureKey, with
# fallback to default-saml-sig
sub _get_default_signing_key_name {
    my ($self) = @_;
    my @key_list =
      split( /\s*,\s*/, ( $self->conf->{samlServiceSignatureKey} || "" ) );

    return ( $key_list[0] || "default-saml-sig" );
}

# The default encryption key is the first key listed in samlServiceEncryptionKey, with
# fallback to default-saml-enc
sub _get_default_encryption_key_name {
    my ($self) = @_;
    my @key_list =
      split( /\s*,\s*/, ( $self->conf->{samlServiceEncryptionKey} || "" ) );

    return ( $key_list[0] || "default-saml-enc" );
}

1;

__END__

=head1 NAME

=encoding utf8

Lemonldap::NG::Portal::Lib::SAML - Common SAML functions

=head1 SYNOPSIS

use Lemonldap::NG::Portal::Lib::SAML;

=head1 DESCRIPTION

This module contains common methods for SAML authentication
and user information loading

=head1 METHODS



( run in 0.803 second using v1.01-cache-2.11-cpan-13bb782fe5a )