package Lemonldap::NG::Portal::UserDB::SAML; use strict; use Encode; use Mouse; use Lemonldap::NG::Portal::Main::Constants qw( PE_OK PE_SAML_ATTR_ERROR PE_SAML_LOAD_IDP_ERROR PE_SAML_LOAD_SERVICE_ERROR ); our $VERSION = '2.0.12'; extends qw( Lemonldap::NG::Common::Module Lemonldap::NG::Portal::Lib::SAML ); # INITIALIZATION sub init { my ($self) = @_; # SAML service has been already loaded $self->lassoServer( $self->p->loadedModules->{'Lemonldap::NG::Portal::Auth::SAML'} ->lassoServer ); return 1; } # RUNNING METHODS # Does nothing sub getUser { return PE_OK; } sub findUser { return PE_OK; } # Get all required attributes sub setSessionInfo { my ( $self, $req ) = @_; my $idp = $req->data->{_idp}; my $idpConfKey = $req->data->{_idpConfKey}; my $nameid = $req->data->{_nameID}; my $exportedAttr; # Force UTF-8 my $force_utf8 = $self->conf->{samlIDPMetaDataOptions}->{$idpConfKey} ->{samlIDPMetaDataOptionsForceUTF8} if $idpConfKey; # Get all required attributes, not already set # in setAuthSessionInfo() if ($idpConfKey) { foreach ( keys %{ $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey} } ) { # Extract fields from exportedAttr value my ( $mandatory, $name, $format, $friendly_name ) = split( /;/, $self->conf->{samlIDPMetaDataExportedAttributes}->{$idpConfKey} ->{$_} ); # Keep mandatory attributes not sent in authentication response if ( $mandatory and not defined $req->{sessionInfo}->{$_} ) { $exportedAttr->{$_} = $self->conf->{samlIDPMetaDataExportedAttributes} ->{$idpConfKey}->{$_}; $self->logger->debug( "Attribute $_ will be requested to $idpConfKey"); } } } unless ( keys %$exportedAttr ) { $self->logger->debug( "All mandatory attributes were present in authentication response"); return PE_OK; } # Save current Lasso::Server object, and get a new one my $current_server = $self->lassoServer; $self->loadService(1); my $server = $self->lassoServer; unless ($server) { $self->logger->error('Unable to create service for attribute request'); return PE_SAML_LOAD_SERVICE_ERROR; } $self->logger->debug("Service for attribute request created"); # Add current IDP as Attribute Authority my $idp_metadata = $self->conf->{samlIDPMetaDataXML}->{$idpConfKey}->{samlIDPMetaDataXML}; if ( $self->conf->{samlMetadataForceUTF8} ) { $idp_metadata = encode( "utf8", $idp_metadata ); } # Add this IDP to Lasso::Server as AA unless ( $self->addAA( $server, $idp_metadata ) ) { $self->logger->error( "Fail to use IDP $idpConfKey Metadata as Attribute Authority"); return PE_SAML_LOAD_IDP_ERROR; } # Build Attribute Request my $query = $self->createAttributeRequest( $server, $idp, $exportedAttr, $nameid ); unless ($query) { $self->logger->error( "Unable to build attribute request for $idpConfKey"); return PE_SAML_ATTR_ERROR; } # Use SOAP to send request and get response my $query_url = $query->msg_url; my $query_body = $query->msg_body; # Send SOAP request and manage response my $response = $self->sendSOAPMessage( $query_url, $query_body ); unless ($response) { $self->logger->error("No attribute response to SOAP request"); return PE_SAML_ATTR_ERROR; } # Manage Attribute Response my $result = $self->processAttributeResponse( $server, $response ); unless ($result) { $self->logger->error("Fail to process attribute response"); return PE_SAML_ATTR_ERROR; } # Attributes in response my @response_attributes; eval { @response_attributes = $result->response()->Assertion()->AttributeStatement()->Attribute(); }; if ($@) { $self->logger->error("No attributes defined in attribute response"); return PE_SAML_ATTR_ERROR; } # Check we have all required attributes foreach ( keys %$exportedAttr ) { # Extract fields from exportedAttr value my ( $mandatory, $name, $format, $friendly_name ) = split( /;/, $exportedAttr->{$_} ); # Try to get value my $value = $self->getAttributeValue( $name, $format, $friendly_name, \@response_attributes, $force_utf8 ); unless ($value) { $self->logger->error( "Attribute $_ is mandatory, but was not delivered by $idpConfKey" ); return PE_SAML_ATTR_ERROR; } $self->logger->debug("Get value $value for attribute $_"); # Store value in sessionInfo $req->{sessionInfo}->{$_} = $value; } # Restore current Lasso::Server $self->lassoServer = $current_server; return PE_OK; } # Does nothing sub setGroups { return PE_OK; } 1;