Lemonldap-NG-Manager
view release on metacpan or search on metacpan
lib/Lemonldap/NG/Manager/Api/Providers/SamlSp.pm view on Meta::CPAN
sub updateSamlSp {
my ( $self, $req ) = @_;
my $res;
my $confKey = $req->params('confKey')
or return $self->sendError( $req, 'confKey is missing', 400 );
my $update = $req->jsonBodyToObj;
return $self->sendError( $req, "Invalid input: " . $req->error, 400 )
unless ($update);
$self->logger->debug(
"[API] SAML SP $confKey configuration update requested");
# Get latest configuration
my $conf = $self->_confAcc->getConf( { noCache => 1 } );
my $current = $self->_getSamlSpByConfKey( $conf, $confKey );
# Return 404 if not found
return $self->sendError( $req,
"SAML service provider '$confKey' not found", 404 )
unless ( defined $current );
if ( defined $update->{metadata} ) {
# check if new entityId exists already
$res = $self->_isNewSamlSpEntityIdUnique( $conf, $confKey, $update );
return $self->sendError( $req, $res->{msg}, 409 )
unless ( $res->{res} eq 'ok' );
}
$res = $self->_pushSamlSp( $conf, $confKey, $update, 0 );
return $self->sendError( $req, $res->{msg}, $res->{code} || 400 )
unless ( $res->{res} eq 'ok' );
return $self->sendJSONresponse( $req, undef, code => 204 );
}
sub deleteSamlSp {
my ( $self, $req ) = @_;
my $confKey = $req->params('confKey')
or return $self->sendError( $req, 'confKey is missing', 400 );
# Get latest configuration
my $conf = $self->_confAcc->getConf( { noCache => 1 } );
my $delete = $self->_getSamlSpByConfKey( $conf, $confKey );
# Return 404 if not found
return $self->sendError( $req,
"SAML service provider '$confKey' not found", 404 )
unless ( defined $delete );
delete $conf->{samlSPMetaDataXML}->{$confKey};
delete $conf->{samlSPMetaDataOptions}->{$confKey};
delete $conf->{samlSPMetaDataExportedAttributes}->{$confKey};
delete $conf->{samlSPMetaDataMacros}->{$confKey};
# Save configuration
if ( $self->_saveApplyConf($conf) ) {
return $self->sendJSONresponse( $req, undef, code => 204 );
}
else {
return $self->sendError( $req,
"Failed to save configuration, please try again later", 503 );
}
}
sub _getSamlSpByConfKey {
my ( $self, $conf, $confKey ) = @_;
# Check if confKey is defined
return undef unless ( defined $conf->{samlSPMetaDataXML}->{$confKey} );
# Get metadata
my $metadata = $conf->{samlSPMetaDataXML}->{$confKey}->{samlSPMetaDataXML};
# Get options
my $options = {};
for my $confOption ( keys %{ $conf->{samlSPMetaDataOptions}->{$confKey} } )
{
my $optionName = $self->_translateOptionConfToApi($confOption);
my $optionValue = $self->_translateValueConfToApi( $confOption,
$conf->{samlSPMetaDataOptions}->{$confKey}->{$confOption} );
$options->{$optionName} = $optionValue;
}
# Get macros
my $macros = $conf->{samlSPMetaDataMacros}->{$confKey} || {};
my $samlSp = {
confKey => $confKey,
metadata => $metadata,
exportedAttributes => {},
macros => $macros,
options => $options
};
# Get exported attributes
foreach ( keys %{ $conf->{samlSPMetaDataExportedAttributes}->{$confKey} } )
{
# Extract fields from exportedAttr value
my ( $mandatory, $name, $format, $friendly_name ) =
split( /;/,
$conf->{samlSPMetaDataExportedAttributes}->{$confKey}->{$_} );
$mandatory = !!$mandatory ? 'true' : 'false'; # ????????????
$samlSp->{exportedAttributes}->{$_} = {
name => $name,
mandatory => $mandatory
};
$samlSp->{exportedAttributes}->{$_}->{friendlyName} = $friendly_name
if ( defined $friendly_name && $friendly_name ne '' );
$samlSp->{exportedAttributes}->{$_}->{format} = $format
if ( defined $format && $format ne '' );
}
return $samlSp;
}
sub _getSamlSpByEntityId {
my ( $self, $conf, $entityId ) = @_;
foreach ( keys %{ $conf->{samlSPMetaDataXML} } ) {
my $metadata_entityId = $self->_readSamlSpEntityId(
$conf->{samlSPMetaDataXML}->{$_}->{samlSPMetaDataXML} )
|| '';
return $self->_getSamlSpByConfKey( $conf, $_ )
if ( $metadata_entityId eq $entityId );
}
foreach ( keys %{ $conf->{samlSPMetaDataOptions} } ) {
my $federated_entityId = $conf->{samlSPMetaDataOptions}->{$_}
->{samlSPMetaDataOptionsFederationEntityID} || '';
return $self->_getSamlSpByConfKey( $conf, $_ )
if ( $federated_entityId eq $entityId );
}
return undef;
}
sub _readSamlSpEntityId {
my ( $self, $metadata ) = @_;
return undef unless $metadata;
my ($entityID) = $metadata =~ /entityID=['"](.+?)['"]/;
return $entityID;
}
sub _readSamlSpExportedAttributes {
my ( $self, $attrs, $mergeWith ) = @_;
my $allowedFormats = [
"urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
"urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
];
foreach ( keys %{$attrs} ) {
return { res => "ko", msg => "Exported attribute $_ has no name" }
unless ( defined $attrs->{$_}->{name} );
my $mandatory = 0;
my $name = $attrs->{$_}->{name};
my $format = '';
my $friendlyName = '';
( $mandatory, $name, $format, $friendlyName ) =
split( /;/, $mergeWith->{$_} )
if ( defined $mergeWith->{$_} );
if ( defined $attrs->{$_}->{mandatory} ) {
$mandatory = (
$attrs->{$_}->{mandatory} eq '1'
or $attrs->{$_}->{mandatory} eq 'true'
) ? 1 : 0;
}
if ( defined $attrs->{$_}->{format} ) {
$format = $attrs->{$_}->{format};
return {
res => "ko",
msg => "Exported attribute $_ format does not exist."
}
unless ( length( grep { /^$format$/ } @{$allowedFormats} ) );
}
$friendlyName = $attrs->{$_}->{friendlyName}
if ( defined $attrs->{$_}->{friendlyName} );
$mergeWith->{$_} = "$mandatory;$name;$format;$friendlyName";
}
return { res => "ok", exportedAttributes => $mergeWith };
}
sub _pushSamlSp {
my ( $self, $conf, $confKey, $push, $replace ) = @_;
my $translatedOptions = {};
if ($replace) {
$conf->{samlSPMetaDataXML}->{$confKey} = {};
$conf->{samlSPMetaDataOptions}->{$confKey} = {};
$conf->{samlSPMetaDataMacros}->{$confKey} = {};
$conf->{samlSPMetaDataExportedAttributes}->{$confKey} = {};
$translatedOptions = $self->_getDefaultValues('samlSPMetaDataNode');
}
$conf->{samlSPMetaDataXML}->{$confKey}->{samlSPMetaDataXML} =
$push->{metadata}
if defined $push->{metadata};
if ( defined $push->{options} ) {
foreach ( keys %{ $push->{options} } ) {
my $optionName = $self->_translateOptionApiToConf( $_, 'samlSP' );
my $optionValue =
$self->_translateValueApiToConf( $optionName,
$push->{options}->{$_} );
$translatedOptions->{$optionName} = $optionValue;
}
my $res = $self->_hasAllowedAttributes( $translatedOptions,
'samlSPMetaDataNode' );
return $res unless ( $res->{res} eq 'ok' );
foreach ( keys %{$translatedOptions} ) {
$conf->{samlSPMetaDataOptions}->{$confKey}->{$_} =
$translatedOptions->{$_};
}
}
if ( defined $push->{macros} ) {
if ( $self->_isSimpleKeyValueHash( $push->{macros} ) ) {
foreach ( keys %{ $push->{macros} } ) {
$conf->{samlSPMetaDataMacros}->{$confKey}->{$_} =
$push->{macros}->{$_};
}
}
else {
return {
res => 'ko',
msg =>
"Invalid input: macros is not a hash object with \"key\":\"value\" attributes"
};
}
}
if ( defined $push->{exportedAttributes} ) {
my $res =
$self->_readSamlSpExportedAttributes( $push->{exportedAttributes},
$conf->{samlSPMetaDataExportedAttributes}->{$confKey} );
return $res unless ( $res->{res} eq 'ok' );
$conf->{samlSPMetaDataExportedAttributes}->{$confKey} =
$res->{exportedAttributes};
}
# Test new configuration
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
refConf => $self->_confAcc->getConf,
newConf => $conf,
req => {},
}
);
unless ( $parser->testNewConf( $self->p ) ) {
return {
res => 'ko',
msg => "Configuration error: "
. join( ". ", map { $_->{message} } @{ $parser->errors } ),
};
}
# Save configuration
if ( $self->_saveApplyConf($conf) ) {
return { res => 'ok' };
}
else {
return {
res => 'ko',
msg => "Failed to save configuration, please try again later",
code => 503,
};
}
}
sub _isNewSamlSpEntityIdUnique {
my ( $self, $conf, $confKey, $newSp ) = @_;
my $option_entityid =
$newSp->{options} ? $newSp->{options}->{federationEntityID} : undef;
my $newEntityId =
$option_entityid || $self->_readSamlSpEntityId( $newSp->{metadata} );
my $curEntityId =
$self->_readSamlSpEntityId(
$self->_getSamlSpByConfKey( $conf, $confKey )->{metadata} );
( run in 0.832 second using v1.01-cache-2.11-cpan-39bf76dae61 )