Newer
Older
renater.salaun
committed
use strict;
renater.salaun
committed
renater.salaun
committed
sub new {
renater.salaun
committed
die "missing argument 'file'" unless defined $args{file};
die "non-existing file $args{file}" unless -f $args{file};
die "non-readable file $args{file}" unless -r $args{file};
renater.salaun
committed
eval { $doc = XML::LibXML->load_xml(location => $args{file}); };
die "Failed to parse file: $EVAL_ERROR" if $EVAL_ERROR;
my $root = $doc->documentElement();
my $type = $root->nodeName();
die "incorrect root element type '$type' for file $args{file}, should be
'EntitiesDescriptor'" unless $type =~ /EntitiesDescriptor$/;
my $self = {
file => $args{file},
doc => $doc
};
renater.salaun
committed
}
## Parse XML structure of metadata to fill a hashref
sub parse {
@{ $self->{doc}->getElementsByLocalName('EntityDescriptor') })
my $id = $EntityDescriptor->getAttribute('entityID');
my $data = {
entityid => $id
};
foreach my $child ($EntityDescriptor->childNodes()) {
## Ignoring nodes of type XML::LibXML::Text or XML::LibXML::Comment
if ($child->localname() eq 'IDPSSODescriptor') {
next ENTITY if $args{type} && $args{type} ne 'idp';
$child->getChildrenByLocalName('SingleSignOnService'))
{
## On ne prend en compte que les endpoints prévus
#next unless ($sso->getAttribute('Binding') && defined $supported_saml_bindings{$sso->getAttribute('Binding')});
## On extrait les infos sur les endpoints
type => 'SingleSignOnService',
binding => $sso->getAttribute('Binding'),
location => $sso->getAttribute('Location'),
};
}
## Getting domains declared for scoped attributes
foreach my $scope ($child->getElementsByLocalName('Scope')) {
push @{ $data->{domain} }, $scope->textContent();
} elsif ($child->localname() eq 'SPSSODescriptor') {
next ENTITY if $args{type} && $args{type} ne 'sp';
## We check the Binding of the ACS that should match "urn:oasis:names:tc:SAML:1.0:profiles:browser-post"
## We also check the index to select the ACS that has the lower index
my ($index_saml1, $index_saml2);
foreach my $sso (
$child->getChildrenByLocalName('AssertionConsumerService')
type => 'AssertionConsumerService',
binding => $sso->getAttribute('Binding'),
location => $sso->getAttribute('Location'),
index => $sso->getAttribute('index'),
$child->getElementsByLocalName('RequestedAttribute')
) {
push @{ $data->{requested_attribute} }, {
friendly_name => $attribute->getAttribute('FriendlyName'),
name => $attribute->getAttribute('Name'),
is_required => _boolean2integer(
$attribute->getAttribute('isRequired')
)
} elsif ($child->localname() eq 'Extensions') {
$child->getChildrenByLocalName('RegistrationInfo')
$registrationinfo->getAttribute('registrationAuthority');
$registrationinfo->getAttribute('registrationInstant');
my $lang = $policy->getAttribute('lang');
next unless $lang && $lang eq 'en';
$data->{registration_info}->{registration_policy} =
$policy->textContent();
} elsif ($child->localname() eq 'ContactPerson') {
my $details;
$details->{type} = $child->getAttribute('contactType');
if (defined $details->{type}) {
foreach my $contact_child ($child->childNodes()) {
next unless $contact_child->nodeType() == XML_ELEMENT_NODE;
my $key = $contact_child->localname();
my $value = $contact_child->textContent();
$value =~ s/^mailto:// if $key eq 'EmailAddress';
$details->{$key} = $value;
push @{ $data->{contacts} }, $details;
} elsif ($child->localname() eq 'Organization') {
$data->{name} = _get_default_value(
$child, 'OrganizationName'
);
$data->{display_name} = _get_default_value(
$child, 'OrganizationDisplayName'
);
foreach my $cert (
$child->getElementsByLocalName('X509Certificate')
) {
$data->{certificate} = $cert->textContent();
renater.salaun
committed
}
## Dumps the SAML metadata content
sub print {
my ($self, $fd) = @_;
$fd = \*STDOUT unless $fd;
my $root = $self->{doc}->documentElement();
print $fd $root->toString();
}
! defined $_[0] ? undef :
$_[0] eq 'true' ? 1 :
$_[0] eq 'false' ? 0 :
sub _get_default_value {
my ($node, $child_name) = @_;
my %names;
$names{ $_->getAttribute('xml:lang') } = $_->textContent()
foreach $node->getChildrenByLocalName($child_name);
return $names{en} ? $names{en} : (values %names)[0];
}
renater.salaun
committed
__END__
=head1 NAME
SAMLMetadata - loading SAML federation metadata
renater.salaun
committed
=head1 SYNOPSIS
# instanciate metadata object
my $metadata = IdPAccountManager::SAMLMetadata->new(
file => '/tmp/edugain-saml-metadata.xml'
);
# extract metadata for a single SAML entity
my $entities = $metadata->parse(id => $id);
renater.salaun
committed
=head1 DESCRIPTION
Create a new IdPAccountManager::SAMLMetadata object.
Supported arguments include:
=head1 INSTANCE METHODS
=over
=item parse()
Parse the SAML metadata file.
Supported arguments include:
=item I<id>: keep only entity with matching ID
=item I<type>: keep only entity with matching type