Skip to content

Commit ecee8bf

Browse files
waterkiptimlegge
authored andcommitted
Add predicates to Net::SAML2::Protocol::AuthnRequest
Additionally create functions for setting bits of the XML to make the function smaller and easier to the eyes. Signed-off-by: Wesley Schwengle <[email protected]>
1 parent 7562ff1 commit ecee8bf

File tree

4 files changed

+168
-82
lines changed

4 files changed

+168
-82
lines changed

Makefile.PL

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ my %WriteMakefileArgs = (
3333
"IO::Compress::RawDeflate" => 0,
3434
"IO::Uncompress::RawInflate" => 0,
3535
"LWP::UserAgent" => 0,
36+
"List::Util" => 0,
3637
"MIME::Base64" => 0,
3738
"Moose" => 0,
3839
"Moose::Role" => 0,
@@ -95,6 +96,7 @@ my %FallbackPrereqs = (
9596
"IO::Uncompress::RawInflate" => 0,
9697
"Import::Into" => 0,
9798
"LWP::UserAgent" => 0,
99+
"List::Util" => 0,
98100
"MIME::Base64" => 0,
99101
"Moose" => 0,
100102
"Moose::Role" => 0,

cpanfile

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ requires "HTTP::Request::Common" => "0";
1717
requires "IO::Compress::RawDeflate" => "0";
1818
requires "IO::Uncompress::RawInflate" => "0";
1919
requires "LWP::UserAgent" => "0";
20+
requires "List::Util" => "0";
2021
requires "MIME::Base64" => "0";
2122
requires "Moose" => "0";
2223
requires "Moose::Role" => "0";
+156-76
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package Net::SAML2::Protocol::AuthnRequest;
2+
23
use Moose;
3-
use MooseX::Types::Moose qw /Str Int/;
44
use MooseX::Types::URI qw/ Uri /;
55
use MooseX::Types::Common::String qw/ NonEmptySimpleStr /;
66
use XML::Writer;
7+
use List::Util qw(any);
78

89
with 'Net::SAML2::Role::ProtocolMessage';
910

10-
=head1 NAME
11-
12-
Net::SAML2::Protocol::AuthnRequest - SAML2 AuthnRequest object
11+
# ABSTRACT: SAML2 AuthnRequest object
1312

1413
=head1 SYNOPSIS
1514
@@ -72,9 +71,17 @@ Value for the I<Comparison> attribute in case I<RequestedAuthnContext> is includ
7271
7372
=cut
7473

75-
has 'nameid' => (isa => NonEmptySimpleStr, is => 'rw', required => 0);
74+
has 'nameid' => (
75+
isa => NonEmptySimpleStr,
76+
is => 'rw',
77+
predicate => 'has_nameid'
78+
);
7679

77-
has 'nameidpolicy_format' => (isa => Str, is => 'rw', required => 0);
80+
has 'nameidpolicy_format' => (
81+
isa => 'Str',
82+
is => 'rw',
83+
predicate => 'has_nameidpolicy_format'
84+
);
7885

7986
has 'nameid_allow_create' => (
8087
isa => 'Bool',
@@ -83,16 +90,55 @@ has 'nameid_allow_create' => (
8390
predicate => 'has_nameid_allow_create'
8491
);
8592

86-
has 'assertion_url' => (isa => Uri, is => 'rw', required => 0, coerce => 1);
87-
has 'assertion_index' => (isa => Int, is => 'rw', required => 0);
88-
has 'attribute_index' => (isa => Int, is => 'rw', required => 0);
89-
has 'protocol_binding' => (isa => Uri, is => 'rw', required => 0, coerce => 1);
90-
has 'provider_name' => (isa => Str, is => 'rw', required => 0);
93+
has 'assertion_url' => (
94+
isa => Uri,
95+
is => 'rw',
96+
coerce => 1,
97+
predicate => 'has_assertion_url',
98+
);
99+
100+
has 'assertion_index' => (
101+
isa => 'Int',
102+
is => 'rw',
103+
predicate => 'has_assertion_index',
104+
);
105+
106+
has 'attribute_index' => (
107+
isa => 'Int',
108+
is => 'rw',
109+
predicate => 'has_attribute_index',
110+
);
111+
112+
has 'protocol_binding' => (
113+
isa => Uri,
114+
is => 'rw',
115+
coerce => 1,
116+
predicate => 'has_protocol_binding',
117+
);
118+
has 'provider_name' => (
119+
isa => 'Str',
120+
is => 'rw',
121+
predicate => 'has_provider_name',
122+
);
91123

92124
# RequestedAuthnContext:
93-
has 'AuthnContextClassRef' => (isa => 'ArrayRef[Str]', is => 'rw', required => 0, default => sub {[]});
94-
has 'AuthnContextDeclRef' => (isa => 'ArrayRef[Str]', is => 'rw', required => 0, default => sub {[]});
95-
has 'RequestedAuthnContext_Comparison' => (isa => Str, is => 'rw', required => 0, default => 'exact');
125+
has 'AuthnContextClassRef' => (
126+
isa => 'ArrayRef[Str]',
127+
is => 'rw',
128+
default => sub {[]}
129+
);
130+
131+
has 'AuthnContextDeclRef' => (
132+
isa => 'ArrayRef[Str]',
133+
is => 'rw',
134+
default => sub {[]}
135+
);
136+
137+
has 'RequestedAuthnContext_Comparison' => (
138+
isa => 'Str',
139+
is => 'rw',
140+
default => 'exact'
141+
);
96142

97143
around BUILDARGS => sub {
98144
my $orig = shift;
@@ -114,10 +160,11 @@ Returns the AuthnRequest as XML.
114160
115161
=cut
116162

163+
my $saml = 'urn:oasis:names:tc:SAML:2.0:assertion';
164+
my $samlp = 'urn:oasis:names:tc:SAML:2.0:protocol';
165+
117166
sub as_xml {
118167
my ($self) = @_;
119-
my $saml = 'urn:oasis:names:tc:SAML:2.0:assertion';
120-
my $samlp = 'urn:oasis:names:tc:SAML:2.0:protocol';
121168
my $x = XML::Writer->new(
122169
OUTPUT => 'self',
123170
NAMESPACES => 1,
@@ -128,71 +175,104 @@ sub as_xml {
128175
}
129176
);
130177

131-
my $req_atts = {
132-
ID => $self->id,
133-
IssueInstant => $self->issue_instant,
134-
Version => '2.0',
135-
};
136-
137-
my $issuer_attrs = {};
138-
139-
my $protocol_bindings = {
140-
'HTTP-POST' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
141-
};
142-
143-
my $att_map = {
144-
'assertion_url' => 'AssertionConsumerServiceURL',
145-
'assertion_index' => 'AssertionConsumerServiceIndex',
146-
'attribute_index' => 'AttributeConsumingServiceIndex',
147-
'protocol_binding' => 'ProtocolBinding',
148-
'provider_name' => 'ProviderName',
149-
'destination' => 'Destination',
150-
'issuer_namequalifier' => 'NameQualifier',
151-
'issuer_format' => 'Format',
152-
};
153-
154-
foreach my $opt ( qw(assertion_url assertion_index protocol_binding
155-
attribute_index provider_name destination
156-
issuer_namequalifier issuer_format) ) {
157-
if (defined (my $val = $self->$opt())) {
158-
if ( $opt eq 'protocol_binding' ) {
159-
$req_atts->{ $att_map->{$opt} } = $protocol_bindings->{$val};
160-
} elsif ($opt eq 'issuer_namequalifier' || $opt eq 'issuer_format') {
161-
$issuer_attrs->{ $att_map->{$opt} } = $val;
162-
} else {
163-
$req_atts->{ $att_map->{$opt} } = $val;
164-
}
165-
}
166-
}
178+
my %req_atts = (
179+
ID => $self->id,
180+
IssueInstant => $self->issue_instant,
181+
Version => '2.0',
182+
);
167183

168-
$x->startTag([$samlp, 'AuthnRequest'], %$req_atts);
169-
$x->dataElement([$saml, 'Issuer'], $self->issuer, %$issuer_attrs);
170-
if ($self->nameid) {
171-
$x->startTag([$saml, 'Subject']);
172-
$x->dataElement([$saml, 'NameID'], undef, NameQualifier => $self->nameid);
173-
$x->endTag(); # Subject
174-
}
175-
if ($self->nameidpolicy_format) {
176-
$x->dataElement([$samlp, 'NameIDPolicy'],
177-
undef,
178-
Format => $self->nameidpolicy_format,
179-
$self->has_nameid_allow_create
180-
? (AllowCreate => $self->nameid_allow_create)
181-
: (),
182-
);
183-
}
184-
if (@{$self->AuthnContextClassRef} || @{$self->AuthnContextDeclRef}) {
185-
$x->startTag([$samlp, 'RequestedAuthnContext'], Comparison => $self->RequestedAuthnContext_Comparison);
186-
foreach my $ref (@{$self->AuthnContextClassRef}) {
187-
$x->dataElement([$saml, 'AuthnContextClassRef'], $ref);
184+
my %issuer_attrs = ();
185+
186+
my %protocol_bindings = (
187+
'HTTP-POST' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
188+
);
189+
190+
my %att_map = (
191+
'assertion_url' => 'AssertionConsumerServiceURL',
192+
'assertion_index' => 'AssertionConsumerServiceIndex',
193+
'attribute_index' => 'AttributeConsumingServiceIndex',
194+
'protocol_binding' => 'ProtocolBinding',
195+
'provider_name' => 'ProviderName',
196+
'destination' => 'Destination',
197+
'issuer_namequalifier' => 'NameQualifier',
198+
'issuer_format' => 'Format',
199+
);
200+
201+
my @opts = qw(
202+
assertion_url assertion_index protocol_binding
203+
attribute_index provider_name destination
204+
issuer_namequalifier issuer_format
205+
);
206+
207+
foreach my $opt (@opts) {
208+
my $predicate = 'has_' . $opt;
209+
next unless $self->$predicate;
210+
211+
my $val = $self->$opt;
212+
if ($opt eq 'protocol_binding') {
213+
$req_atts{ $att_map{$opt} } = $protocol_bindings{$val};
214+
}
215+
elsif (any { $opt eq $_ } qw(issuer_namequalifier issuer_format)) {
216+
$issuer_attrs{ $att_map{$opt} } = $val;
188217
}
189-
foreach my $ref (@{$self->AuthnContextDeclRef}) {
190-
$x->dataElement([$saml, 'AuthnContextDeclRef'], $ref);
218+
else {
219+
$req_atts{ $att_map{$opt} } = $val;
191220
}
192-
$x->endTag(); # RequestedAuthnContext
193221
}
194-
$x->endTag(); #AuthnRequest
222+
223+
$x->startTag([$samlp, 'AuthnRequest'], %req_atts);
224+
$x->dataElement([$saml, 'Issuer'], $self->issuer, %issuer_attrs);
225+
226+
$self->_set_name_id($x);
227+
$self->_set_name_policy_format($x);
228+
$self->_set_requested_authn_context($x);
229+
230+
$x->endTag();
195231
$x->end();
196232
}
197233

234+
sub _set_name_id {
235+
my ($self, $x) = @_;
236+
return if !$self->has_nameid;
237+
$x->startTag([$saml, 'Subject']);
238+
$x->dataElement([$saml, 'NameID'], undef, NameQualifier => $self->nameid);
239+
$x->endTag();
240+
return;
241+
}
242+
243+
sub _set_name_policy_format {
244+
my ($self, $x) = @_;
245+
return if !$self->has_nameidpolicy_format;
246+
247+
$x->dataElement([$samlp, 'NameIDPolicy'],
248+
undef,
249+
Format => $self->nameidpolicy_format,
250+
$self->has_nameid_allow_create
251+
? (AllowCreate => $self->nameid_allow_create)
252+
: (),
253+
);
254+
return;
255+
}
256+
257+
sub _set_requested_authn_context {
258+
my ($self, $x) = @_;
259+
260+
if (!@{ $self->AuthnContextClassRef } && !@{ $self->AuthnContextDeclRef })
261+
{
262+
return;
263+
}
264+
265+
$x->startTag([$samlp, 'RequestedAuthnContext'],
266+
Comparison => $self->RequestedAuthnContext_Comparison);
267+
268+
foreach my $ref (@{ $self->AuthnContextClassRef }) {
269+
$x->dataElement([$saml, 'AuthnContextClassRef'], $ref);
270+
}
271+
foreach my $ref (@{ $self->AuthnContextDeclRef }) {
272+
$x->dataElement([$saml, 'AuthnContextDeclRef'], $ref);
273+
}
274+
275+
$x->endTag();
276+
}
277+
198278
__PACKAGE__->meta->make_immutable;

lib/Net/SAML2/Role/ProtocolMessage.pm

+9-6
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,22 @@ has issuer => (
3939
);
4040

4141
has issuer_namequalifier => (
42-
isa => 'Str',
43-
is => 'rw'
42+
isa => 'Str',
43+
is => 'rw',
44+
predicate => 'has_issuer_namequalifier',
4445
);
4546

4647
has issuer_format => (
4748
isa => 'Str',
48-
is => 'rw'
49+
is => 'rw',
50+
predicate => 'has_issuer_format',
4951
);
5052

5153
has destination => (
52-
isa => Uri,
53-
is => 'rw',
54-
coerce => 1
54+
isa => Uri,
55+
is => 'rw',
56+
coerce => 1,
57+
predicate => 'has_destination',
5558
);
5659

5760
sub _build_issue_instant {

0 commit comments

Comments
 (0)