From c81e3847bd8ecf8964bd79187b1aa04474b6748c Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Sun, 18 Dec 2022 22:29:06 -0400 Subject: [PATCH] Fix LogoutRequest's missing NameQualifier and SPNameQualifier for samletest.id --- lib/Net/SAML2/Protocol/Assertion.pm | 33 ++++++++++++++++ lib/Net/SAML2/Protocol/LogoutRequest.pm | 17 +++++++-- lib/Net/SAML2/SP.pm | 26 +++++++++++-- t/03-assertions.t | 19 +++++++++ t/07-logout-request.t | 32 ++++++++++++++++ xt/testapp/lib/Saml2Test.pm | 51 ++++++++++++++++++++++--- xt/testapp/views/user.tt | 6 +-- 7 files changed, 170 insertions(+), 14 deletions(-) diff --git a/lib/Net/SAML2/Protocol/Assertion.pm b/lib/Net/SAML2/Protocol/Assertion.pm index d7a49a7d..047f6f7e 100644 --- a/lib/Net/SAML2/Protocol/Assertion.pm +++ b/lib/Net/SAML2/Protocol/Assertion.pm @@ -210,6 +210,39 @@ sub nameid_format { return $self->nameid_object->getAttribute('Format'); } +=head2 nameid_name_qualifier + +Returns the NameID NameQualifier + +=cut + +sub nameid_name_qualifier { + my $self = shift; + return $self->nameid_object->getAttribute('NameQualifier'); +} + +=head2 nameid_sp_name_qualifier + +Returns the NameID SPNameQualifier + +=cut + +sub nameid_sp_name_qualifier { + my $self = shift; + return $self->nameid_object->getAttribute('SPNameQualifier'); +} + +=head2 nameid_sp_provided_id + +Returns the NameID SPProvidedID + +=cut + +sub nameid_sp_provided_id { + my $self = shift; + return $self->nameid_object->getAttribute('SPProvidedID'); +} + =head2 valid( $audience, $in_response_to ) Returns true if this Assertion is currently valid for the given audience. diff --git a/lib/Net/SAML2/Protocol/LogoutRequest.pm b/lib/Net/SAML2/Protocol/LogoutRequest.pm index 7d38c3fa..b1a25d7e 100644 --- a/lib/Net/SAML2/Protocol/LogoutRequest.pm +++ b/lib/Net/SAML2/Protocol/LogoutRequest.pm @@ -63,6 +63,11 @@ Tell the module to include the NameQualifier and SPNameQualifier attributes in the NameID. Defaults to false unless the B equals C +=item B + +When supplied sets the NameQualifier attribute. When not supplied, this +defaults to the destination. + =item B When supplied sets the SPNameQualifier attribute. When not supplied, this @@ -101,6 +106,12 @@ has affiliation_group_id => ( predicate => 'has_affiliation_group_id' ); +has name_qualifier => ( + isa => NonEmptySimpleStr, + is => 'ro', + required => 0, + predicate => 'has_name_qualifier' +); has include_name_qualifier => (isa => 'Bool', is => 'ro', required => 0, default => 0); @@ -199,9 +210,9 @@ sub as_xml { ) : (), $self->include_name_qualifier ? ( - $self->has_destination - ? (NameQualifier => $self->destination) - : (), + $self->has_name_qualifier + ? (NameQualifier => $self->name_qualifier) + : ($self->has_destination ? (NameQualifier => $self->destination) : ()), SPNameQualifier => $self->has_affiliation_group_id ? $self->affiliation_group_id : $self->issuer ) diff --git a/lib/Net/SAML2/SP.pm b/lib/Net/SAML2/SP.pm index fb3d9da1..0c2e9161 100644 --- a/lib/Net/SAML2/SP.pm +++ b/lib/Net/SAML2/SP.pm @@ -318,17 +318,29 @@ sub authn_request { } -=head2 logout_request( $destination, $nameid, $nameid_format, $session ) +=head2 logout_request( $destination, $nameid, $nameid_format, $session, $params ) Returns a LogoutRequest object created by this SP, intended for the given destination, which should be the identity URI of the IdP. Also requires the nameid (+format) and session to be logged out. +=over + +$params is a HASH reference for parameters to Net::SAML2::Protocol::LogoutRequest + +$params = ( + # name qualifier parameters from Assertion NameId + name_qualifier => "https://idp.shibboleth.local/idp/shibboleth" + sp_name_qualifier => "https://netsaml2-testapp.local" + ); + +=back + =cut sub logout_request { - my ($self, $destination, $nameid, $nameid_format, $session) = @_; + my ($self, $destination, $nameid, $nameid_format, $session, $params) = @_; my $logout_req = Net::SAML2::Protocol::LogoutRequest->new( issuer => $self->id, @@ -338,8 +350,16 @@ sub logout_request { NonEmptySimpleStr->check($nameid_format) ? (nameid_format => $nameid_format) : (), + (defined $params->{sp_name_qualifier}) + ? (affiliation_group_id => $params->{sp_name_qualifier}) + : (), + (defined $params->{name_qualifier}) + ? (name_qualifier => $params->{name_qualifier}) + : (), + (defined $params->{include_name_qualifier}) + ? ( include_name_qualifier => $params->{include_name_qualifier} ) + : ( include_name_qualifier => 1), ); - return $logout_req; } diff --git a/t/03-assertions.t b/t/03-assertions.t index f537100d..eb31de36 100644 --- a/t/03-assertions.t +++ b/t/03-assertions.t @@ -2,6 +2,7 @@ use strict; use warnings; use Test::Lib; use Test::Net::SAML2; +use MIME::Base64 qw/decode_base64/; use Net::SAML2::Protocol::Assertion; @@ -109,4 +110,22 @@ $assertion->{not_before} = DateTime->now->add(minutes => 5); is($assertion->valid('http://ct.local'), 0, "and invalid again - InResponseTo not Checked"); is($assertion->valid('http://ct.local', 'N3k95Hg41WCHdwc9mqXynLPhB'), 0, "and invalid again - InResponseTo Checked"); +my $assertion_b64 = <<'BASE64'; +PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9uZXRzYW1sMi10ZXN0YXBwLmxvY2FsL2NvbnN1bWVyLXBvc3QiIElEPSJfYjE2MGU3MTBlYzg2OGNiNzhlN2QzMDNiOGRiOWI1MzYiIEluUmVzcG9uc2VUbz0iTkVUU0FNTDJfNzQzNDIxZWY1YzkzMDIxODZkODA1MGM0YzViYWI3ZTc2YThiOGZkNGUzMjliMGY5YTM5YTE4MjM1Njc2MGRlMSIgSXNzdWVJbnN0YW50PSIyMDIzLTAxLTE0VDE0OjM3OjE1LjgxNFoiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9pZHAuc2hpYmJvbGV0aC5sb2NhbC9pZHAvc2hpYmJvbGV0aDwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48ZHM6UmVmZXJlbmNlIFVSST0iI19iMTYwZTcxMGVjODY4Y2I3OGU3ZDMwM2I4ZGI5YjUzNiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPmNmUWdybXltUWxsangrNitYS2dFVFJLck1yVjBnZmEya2JVSmtWdmJhMUU9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkNLQk5RMFJpK2RZdFVmclUrdWhQSHNHYnBKc1h6VmUwSkFrbTJoRVF6R2NTWUdMcy9xTGdBK1NkQnFmaWtPQUthSm9YOENNOGt2WmhwenJpN1IrcFRVUU13dlZCTHFiYkJ0Mm9TaWZ5VFhuNEk5TEN3QWxOWHZmTCtZWGRmRGQ1dGtMcU8yMTNEWUlCcjdXS1NWRk9kZmNpa3NLZmlXUk1QM1FUL3F2Y2FlNktPN1U4TmpxREtScytWMlRaS0ErVHk0SWRjTHpzV3F3eWZEWWhKd0RDaEdsZm5hMDZZVjVmWWZMZnlCek1TNUU3MzU3MEt1Nm5jSDI3bEdIakdOcU1xVGg4VmZUM2FlVkNST3JCMkpEZ0pJZnlXaXZSWS9HaGk3TDBZUkl1ZldxbjRZdWZPMEZESndITXVMdG1FcDVaTmo1ZzdKcGZzc1F4OWRDZmN0b0dzMjcvM2tqZUhVVHJ2RVFoUTR3ZFdqMGVFZnltczVHTWpaYjlaSDk1RU9wQ1MzejhNaFBkL0ZpZFNSUUhCb3lOdFVjczhBQ3djZ0UyRTJFS3REWThhemJuRityQm9SZ2FYa29LcGlPa0ptVFh1bzFjR3ZVTXZXbkNoWTJ2NjBsWWZMVy85S1lrUjdKUEpHVFVzZEZCaFpCUGhaK2Vwa0hNUDlKekNEeGIrOWM1PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFT3pDQ0FxT2dBd0lCQWdJVVUyZnluVVFaUytzWFJxajBPYzRZZ0Y0bUlPb3dEUVlKS29aSWh2Y05BUUVMQlFBd0h6RWRNQnNHCkExVUVBd3dVYVdSd0xuTm9hV0ppYjJ4bGRHZ3ViRzlqWVd3d0hoY05Nakl4TWpJMU1qSXpNakE0V2hjTk5ESXhNakkxTWpJek1qQTQKV2pBZk1SMHdHd1lEVlFRRERCUnBaSEF1YzJocFltSnZiR1YwYUM1c2IyTmhiRENDQWFJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dHUApBRENDQVlvQ2dnR0JBS3YxY0hyNjJ0MUtON0NNNnBCaTRFNzhGN2Rqa08zQVNRalA1c21QdVppc0I0S1FRL29NS05jSjBCUkoyZjJtCjVLTlJqOFJuVDJuckQxeWNod1lxdGJUQWZRZGZVTWMrclZkaGc4Rm5zTi9lNDFmOFhjQmg1Zjc0dm50OUFjSGVDU1YvR0dsZGUvTUkKcHpsN2ZTeUh6TTlCNUJlWnBWOUl2YVg3NWR5ZVdwSEpPNjR6OVF2WWZMTTNackJHdWJLYVVES0UrNDJkcE9najdIUW83TmNwT2VEbwo2bG1oYVVzZUFIeDZqdUw2M0lYV3lqS0dLbFVoYzd5WW8rQWx1KzlWY2ZXSDRPL1VUNTlsenphakZkTXhiZGtxci9GUDJMVEtXWXBvClRlOWZ4elRwaGNUTnlscnY4WHlodEZGcVA1NzJ4ODVIcWJMeDN3eVV4dks3QTZYd0VEUW5pTWF0RW9IUnA2MjBVZis0WjFSWllhVXkKS1JtV2lsT2NNVG9YTU94Qng2OHkvejdPck9ZTEZqYml4WmVlajd0bFZFTXo3cHdVTDdJL2RaQTQvU0lGeVVFUGRqOHRIdzMxM0lIRAowME5NWkJmcS8xdTQxTEJzWWJaMmI4WXpSYzZmaHpxS3IrU2dSMWVxa09lUE5ZSEVOQTQ0Tm5XdFJyT1FQSkhkeEt4ekRRSURBUUFCCm8yOHdiVEFkQmdOVkhRNEVGZ1FVaGx2NDl5M2VWeXlFUXM0ZkZvU2FpNTIvSU53d1RBWURWUjBSQkVVd1E0SVVhV1J3TG5Ob2FXSmkKYjJ4bGRHZ3ViRzlqWVd5R0syaDBkSEJ6T2k4dmFXUndMbk5vYVdKaWIyeGxkR2d1Ykc5allXd3ZhV1J3TDNOb2FXSmliMnhsZEdndwpEUVlKS29aSWh2Y05BUUVMQlFBRGdnR0JBS1dXZXhSUGczV01lMG02djlKa0prWDNsdVBRdlNZNU1KbXczMm9nRU1RSmlhUmEzSlpUCll1TDV6NXplQmhpZFRRK0laUWJ0MmJpNm13NU15U2kvSk11aVc2OU45c2gyOElnY3ZHU1k3c1NyeTVIVnIycUVCeWQwUE1WbTZ4ZXoKcHVMOW1IRDUwQWxmQzVod1cxOWM0QzJIdmpMcWN6NzlYS3RMMDlGZDhRTGJqYzF2TTlFa3B0MTVINTVLenNGUzVHYlhBVWpaWWh6RwpJVzZ4V29HUjIya2QrZEliUDdsNTVMRDhMZHI4ZkpMZEZkRURBWjdGcHhzWkNJZWsrNWJLclBSdjc1c2VhRTJoeE8vWitUdUJTdU1tCjlxMUxoSFJVUVV5OUNLQUV3NGZGRnFlU1RnL05udzNsbm1BRi9UMDBBNWVLQ1c0dWsxVmVUT1U2NXl3dnhHeU9tME9VZmtPVHFzUVIKOHRxei80RlkrcUpJa3NmMFpwQ1p6dEgrN2pxUmR2OG5RN2xJbGVldFVhbVBlMmhCL2hPUzkreHJBRGM0YmQ1M2IxdnMvVXp4WlBoSApXUnRWLzEwMmU3U0xJVEJsbDViVi9jTlBFaUZ6WEE5V2VXR1RvUDQrUW5ZQmRhby9BbFBob2tISGlkeXpHbmdUQWd1WHg4NGkwOCtGCmUra01xUT09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwycDpTdGF0dXM+PHNhbWwycDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWwycDpTdGF0dXM+PHNhbWwyOkFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9Il82MWYzNzc3MTdjYWFkODdjZTJhZjBlNzk0NmNiOTdiMSIgSXNzdWVJbnN0YW50PSIyMDIzLTAxLTE0VDE0OjM3OjE1LjgxNFoiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5odHRwczovL2lkcC5zaGliYm9sZXRoLmxvY2FsL2lkcC9zaGliYm9sZXRoPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPjxkczpSZWZlcmVuY2UgVVJJPSIjXzYxZjM3NzcxN2NhYWQ4N2NlMmFmMGU3OTQ2Y2I5N2IxIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+TXdCRkxzMzRZWWtNUmUzcTFEa1dnTWpxaTZWOGJIYlQzSkNUZnh5WkxKaz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+b3FldklBRW5XTFF3U1p4OGh4aVNBcE5CVnJXWkxvRjFaNEtPVFJidk9HZFl2Z3F3N21lQnZkb1JVbEd1cHkzQy9qbjBVUVBCNVpTb1VXd1BnZWJMbUY1T2F5V0tBY0M2TVlvUStadHRxKzRrT1RKK0RLUGFLTzJmRXNDekNTK0hYOGFjK0x1YWpIZkt2cXRkMW1LUDlVMVRRNjNaMWpvRG8ySDFFWE1RZE9ablJ3V1hnS0ZlWEFXUEphYjB2ZnZWZllhdVZVcm4xWkMyMFFhTkx6QmpSREMwZDZsb1hMM2c3Q1R5L1lOZjJtSk5jUmpXRjRFUjRjcU9kVXdEZHRCZ1hWRkdqUXMzNDVySzMvb044bE81VDdUaW1nbDFhTVB4Q29mK0VpdnNXamtiaUF4VHM5TTA1UUVXUmovQld6S0dZR3l1OXNyRFpNaWQrWjVCYWNDUUl6RloxLzhrS0dJNitVWVZEeGFVQ0xUOWFRb3c0bEV1VDEwazJsNExYcWphWEEveW1PaWg0bklLSjFBc0dIMTNVTXVsNEJPRXlMTzVBRkFYejU3SDNveHJkbHZXN0NhU3dieTB5aHh5VGNLNTRWb0wybEMwaEZhejlYTHZzaDFqU0h3eVpmTis2M1ZCdTh3NTBTNWVnbjRlQVlCZXpZTHd6THVrSFhSd0lhekM8L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUVPekNDQXFPZ0F3SUJBZ0lVVTJmeW5VUVpTK3NYUnFqME9jNFlnRjRtSU9vd0RRWUpLb1pJaHZjTkFRRUxCUUF3SHpFZE1Cc0cKQTFVRUF3d1VhV1J3TG5Ob2FXSmliMnhsZEdndWJHOWpZV3d3SGhjTk1qSXhNakkxTWpJek1qQTRXaGNOTkRJeE1qSTFNakl6TWpBNApXakFmTVIwd0d3WURWUVFEREJScFpIQXVjMmhwWW1KdmJHVjBhQzVzYjJOaGJEQ0NBYUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0dQCkFEQ0NBWW9DZ2dHQkFLdjFjSHI2MnQxS043Q002cEJpNEU3OEY3ZGprTzNBU1FqUDVzbVB1WmlzQjRLUVEvb01LTmNKMEJSSjJmMm0KNUtOUmo4Um5UMm5yRDF5Y2h3WXF0YlRBZlFkZlVNYytyVmRoZzhGbnNOL2U0MWY4WGNCaDVmNzR2bnQ5QWNIZUNTVi9HR2xkZS9NSQpwemw3ZlN5SHpNOUI1QmVacFY5SXZhWDc1ZHllV3BISk82NHo5UXZZZkxNM1pyQkd1YkthVURLRSs0MmRwT2dqN0hRbzdOY3BPZURvCjZsbWhhVXNlQUh4Nmp1TDYzSVhXeWpLR0tsVWhjN3lZbytBbHUrOVZjZldINE8vVVQ1OWx6emFqRmRNeGJka3FyL0ZQMkxUS1dZcG8KVGU5Znh6VHBoY1ROeWxydjhYeWh0RkZxUDU3Mng4NUhxYkx4M3d5VXh2SzdBNlh3RURRbmlNYXRFb0hScDYyMFVmKzRaMVJaWWFVeQpLUm1XaWxPY01Ub1hNT3hCeDY4eS96N09yT1lMRmpiaXhaZWVqN3RsVkVNejdwd1VMN0kvZFpBNC9TSUZ5VUVQZGo4dEh3MzEzSUhECjAwTk1aQmZxLzF1NDFMQnNZYloyYjhZelJjNmZoenFLcitTZ1IxZXFrT2VQTllIRU5BNDRObld0UnJPUVBKSGR4S3h6RFFJREFRQUIKbzI4d2JUQWRCZ05WSFE0RUZnUVVobHY0OXkzZVZ5eUVRczRmRm9TYWk1Mi9JTnd3VEFZRFZSMFJCRVV3UTRJVWFXUndMbk5vYVdKaQpiMnhsZEdndWJHOWpZV3lHSzJoMGRIQnpPaTh2YVdSd0xuTm9hV0ppYjJ4bGRHZ3ViRzlqWVd3dmFXUndMM05vYVdKaWIyeGxkR2d3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFLV1dleFJQZzNXTWUwbTZ2OUprSmtYM2x1UFF2U1k1TUptdzMyb2dFTVFKaWFSYTNKWlQKWXVMNXo1emVCaGlkVFErSVpRYnQyYmk2bXc1TXlTaS9KTXVpVzY5TjlzaDI4SWdjdkdTWTdzU3J5NUhWcjJxRUJ5ZDBQTVZtNnhlegpwdUw5bUhENTBBbGZDNWh3VzE5YzRDMkh2akxxY3o3OVhLdEwwOUZkOFFMYmpjMXZNOUVrcHQxNUg1NUt6c0ZTNUdiWEFValpZaHpHCklXNnhXb0dSMjJrZCtkSWJQN2w1NUxEOExkcjhmSkxkRmRFREFaN0ZweHNaQ0llays1YktyUFJ2NzVzZWFFMmh4Ty9aK1R1QlN1TW0KOXExTGhIUlVRVXk5Q0tBRXc0ZkZGcWVTVGcvTm53M2xubUFGL1QwMEE1ZUtDVzR1azFWZVRPVTY1eXd2eEd5T20wT1Vma09UcXNRUgo4dHF6LzRGWStxSklrc2YwWnBDWnp0SCs3anFSZHY4blE3bElsZWV0VWFtUGUyaEIvaE9TOSt4ckFEYzRiZDUzYjF2cy9VenhaUGhICldSdFYvMTAyZTdTTElUQmxsNWJWL2NOUEVpRnpYQTlXZVdHVG9QNCtRbllCZGFvL0FsUGhva0hIaWR5ekduZ1RBZ3VYeDg0aTA4K0YKZStrTXFRPT08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6cGVyc2lzdGVudCIgTmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9pZHAuc2hpYmJvbGV0aC5sb2NhbC9pZHAvc2hpYmJvbGV0aCIgU1BOYW1lUXVhbGlmaWVyPSJodHRwczovL25ldHNhbWwyLXRlc3RhcHAubG9jYWwiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj43VlRCSDRXVlhNQ1lPS1dYS1ZBRkpMTVRNR05JSUdNWDwvc2FtbDI6TmFtZUlEPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uIE1ldGhvZD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmNtOmJlYXJlciI+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb25EYXRhIEFkZHJlc3M9IjE5Mi4xNjguMTIyLjEiIEluUmVzcG9uc2VUbz0iTkVUU0FNTDJfNzQzNDIxZWY1YzkzMDIxODZkODA1MGM0YzViYWI3ZTc2YThiOGZkNGUzMjliMGY5YTM5YTE4MjM1Njc2MGRlMSIgTm90T25PckFmdGVyPSIyMDIzLTAxLTE0VDE0OjQyOjE1LjgyNloiIFJlY2lwaWVudD0iaHR0cHM6Ly9uZXRzYW1sMi10ZXN0YXBwLmxvY2FsL2NvbnN1bWVyLXBvc3QiLz48L3NhbWwyOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sMjpTdWJqZWN0PjxzYW1sMjpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAyMy0wMS0xNFQxNDozNzoxNS44MTRaIiBOb3RPbk9yQWZ0ZXI9IjIwMjMtMDEtMTRUMTQ6NDI6MTUuODE0WiI+PHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWwyOkF1ZGllbmNlPmh0dHBzOi8vbmV0c2FtbDItdGVzdGFwcC5sb2NhbDwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDIzLTAxLTE0VDE0OjM3OjE1Ljc4MVoiIFNlc3Npb25JbmRleD0iXzEyMWNlZjU0MTkxZDU3ZjkwNzUzNjE5ZmY4MTg1NmJkIj48c2FtbDI6U3ViamVjdExvY2FsaXR5IEFkZHJlc3M9IjE5Mi4xNjguMTIyLjEiLz48c2FtbDI6QXV0aG5Db250ZXh0PjxzYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sMjpBdXRobkNvbnRleHQ+PC9zYW1sMjpBdXRoblN0YXRlbWVudD48c2FtbDI6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGUgRnJpZW5kbHlOYW1lPSJzY2hhY0hvbWVPcmdhbml6YXRpb24iIE5hbWU9InVybjpvaWQ6MS4zLjYuMS40LjEuMjUxNzguMS4yLjkiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6dXJpIj48c2FtbDI6QXR0cmlidXRlVmFsdWU+c2hpYmJvbGV0aC5sb2NhbDwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+PC9zYW1sMnA6UmVzcG9uc2U+ +BASE64 + +$xml = decode_base64($assertion_b64); +$assertion = Net::SAML2::Protocol::Assertion->new_from_xml(xml => $xml); +isa_ok($assertion, 'Net::SAML2::Protocol::Assertion'); + +is($assertion->nameid_name_qualifier, + "https://idp.shibboleth.local/idp/shibboleth", + "nameid_name_qualifier is ok"); +is($assertion->nameid_sp_name_qualifier, + "https://netsaml2-testapp.local", + "nameid_sp_name_qualifier is ok"); +is($assertion->nameid_sp_provided_id, + undef, + "nameid_sp_provided_id undefined as expected"); + done_testing; diff --git a/t/07-logout-request.t b/t/07-logout-request.t index 18a76058..cb67ae60 100644 --- a/t/07-logout-request.t +++ b/t/07-logout-request.t @@ -82,5 +82,37 @@ foreach (qw(NameQualifier SPNameQualifier SPProvidedID)) { $args{destination}, ".. and the NameQualifier"); } +{ + my $sp = net_saml2_sp( + authnreq_signed => 0, + want_assertions_signed => 0, + slo_url_post => '/sls-post-response', + slo_url_soap => '/slo-soap', + ); + + my %logout_params = ( + name_qualifier => 'https://idp.shibboleth.local/idp/shibboleth', + sp_name_qualifier => 'https://netsaml2-testapp', + ); + + my $logout_request = $sp->logout_request( + $args{destination}, + $args{nameid}, + $args{nameid_format}, + $args{session}, + \%logout_params); + my $xml = $logout_request->as_xml; + + my $xpath = get_xpath( + $xml, + samlp => URN_PROTOCOL, + saml => URN_ASSERTION, + ); + my $name_id = get_single_node_ok($xpath, '/samlp:LogoutRequest/saml:NameID'); + is($name_id->getAttribute('SPNameQualifier'), + $logout_params{sp_name_qualifier}, "We the SPNameQualifier"); + is($name_id->getAttribute('NameQualifier'), + $logout_params{name_qualifier}, ".. and the NameQualifier"); +} done_testing; diff --git a/xt/testapp/lib/Saml2Test.pm b/xt/testapp/lib/Saml2Test.pm index f373e041..93b3d404 100644 --- a/xt/testapp/lib/Saml2Test.pm +++ b/xt/testapp/lib/Saml2Test.pm @@ -97,11 +97,21 @@ get '/logout-redirect' => sub { return; # "Redirected\n"; } + my %logout_params = ( + params->{name_qualifier} ? + ( name_qualifier => params->{name_qualifier}) : + (), + params->{sp_name_qualifier} ? + (sp_name_qualifier => params->{sp_name_qualifier}) : + (), + ); + my $logoutreq = $sp->logout_request( $idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'), params->{nameid}, $idp->format || undef, - params->{session} + params->{session}, + \%logout_params, )->as_xml; my $redirect = $sp->slo_redirect_binding($idp, 'SAMLRequest'); @@ -123,11 +133,28 @@ get '/logout-soap' => sub { my $idp_cert = $idp->cert('signing'); my $sp = _sp(); + + my %logout_params = ( + params->{name_qualifier} ? + ( name_qualifier => params->{name_qualifier}) : + (), + params->{sp_name_qualifier} ? + (sp_name_qualifier => params->{sp_name_qualifier}) : + (), + ); + my $logoutreq = $sp->logout_request( - $idp->entityid, params->{nameid}, $idp->format, params->{session} + $idp->entityid, params->{nameid}, $idp->format, params->{session}, + \%logout_params )->as_xml; + my $ua = LWP::UserAgent->new; + + require LWP::Protocol::https; + $ua->ssl_opts( (verify_hostname => config->{ssl_verify_hostname})); + my $soap = Net::SAML2::Binding::SOAP->new( + ua => $ua, key => config->{key}, cert => config->{cert}, url => $slo_url, @@ -156,7 +183,14 @@ post '/consumer-post' => sub { cacert => config->{cacert}, ); - template 'user', { assertion => $assertion }; + my $name_qualifier = $assertion->nameid_name_qualifier(); + my $sp_name_qualifier = $assertion->nameid_sp_name_qualifier(); + + template 'user', { + assertion => $assertion, + (defined $name_qualifier ? (name_qualifier => $name_qualifier) : ()), + (defined $sp_name_qualifier ? (sp_name_qualifier => $sp_name_qualifier) : ()), + }; } else { return "
Bad Assertion
"; @@ -190,11 +224,18 @@ get '/consumer-artifact' => sub { if ($response) { my $assertion = Net::SAML2::Protocol::Assertion->new_from_xml( - xml => $response, key_file => config->{key}, + xml => $response ); - template 'user', { assertion => $assertion }; + my $name_qualifier = $assertion->nameid_name_qualifier(); + my $sp_name_qualifier = $assertion->nameid_sp_name_qualifier(); + + template 'user', { + assertion => $assertion, + ($name_qualifier ? (name_qualifier => $name_qualifier) : ()), + ($sp_name_qualifier ? (sp_name_qualifier => $sp_name_qualifier) : ()), + }; } else { return "
Bad Assertion
"; diff --git a/xt/testapp/views/user.tt b/xt/testapp/views/user.tt index cffe6c37..17afcfae 100644 --- a/xt/testapp/views/user.tt +++ b/xt/testapp/views/user.tt @@ -1,10 +1,10 @@

NameID: <% assertion.nameid %>

-

Logout (redirect binding)

+

Logout (redirect binding)

-

Logout (soap binding)

+

Logout (soap binding)

-

Logout (local)

+

Logout (local)

Attributes