1- use strict;
2- use warnings;
31package Net::SAML2::Protocol::AuthnRequest ;
4- # VERSION
5-
62use Moose;
7- use MooseX::Types::URI qw/ Uri / ;
3+
4+ # VERSION
5+ use MooseX::Types::URI qw/ Uri / ;
86use MooseX::Types::Common::String qw/ NonEmptySimpleStr / ;
9- use XML::Writer 0.625;
10- use List::Util qw( any) ;
7+ use XML::Generator;
8+ use List::Util qw( any) ;
9+ use URN::OASIS::SAML2 qw( :urn BINDING_HTTP_POST) ;
1110
1211with ' Net::SAML2::Role::ProtocolMessage' ;
1312
@@ -54,7 +53,7 @@ Net::SAML2::Protocol::AuthnRequest - SAML2 AuthnRequest object
5453Constructor. Creates an instance of the AuthnRequest object.
5554
5655Important Note: Best practice is to always do this first. While it is possible
57- to call as_xml() first you do not have to set the id as it will be set for you
56+ to call C< as_xml() > first you do not have to set the id as it will be set for you
5857automatically.
5958
6059However tracking the id is important for security to ensure that the response
@@ -64,19 +63,25 @@ Arguments:
6463
6564=over
6665
67- =item B< nameidpolicy_format >
66+ =item nameidpolicy_format
6867
6968Format attribute for NameIDPolicy
7069
71- =item B<AuthnContextClassRef > , B<AuthnContextDeclRef >
70+ =item AuthnContextClassRef, <AuthnContextDeclRef
71+
72+ Each one is an arrayref containing values for AuthnContextClassRef and
73+ AuthnContextDeclRef. If any is populated, the RequestedAuthnContext will be
74+ included in the request.
7275
73- Each one is an arrayref containing values for AuthnContextClassRef and AuthnContextDeclRef.
74- If any is populated, the RequestedAuthnContext will be included in the request.
76+ =item RequestedAuthnContext_Comparison
7577
76- =item B<RequestedAuthnContext_Comparison >
78+ Value for the I<Comparison > attribute in case I<RequestedAuthnContext > is
79+ included (see above). Default value is I<exact > .
7780
78- Value for the I<Comparison > attribute in case I<RequestedAuthnContext > is included
79- (see above). Default value is I<exact > .
81+ =item identity_providers
82+
83+ An arrayref of Identity providers, if used the Scoping element is added to the
84+ XML
8085
8186=back
8287
@@ -102,46 +107,46 @@ has 'nameid_allow_create' => (
102107);
103108
104109has ' assertion_url' => (
105- isa => Uri,
106- is => ' rw' ,
107- coerce => 1,
110+ isa => Uri,
111+ is => ' rw' ,
112+ coerce => 1,
108113 predicate => ' has_assertion_url' ,
109114);
110115
111116has ' assertion_index' => (
112- isa => ' Int' ,
113- is => ' rw' ,
117+ isa => ' Int' ,
118+ is => ' rw' ,
114119 predicate => ' has_assertion_index' ,
115120);
116121
117122has ' attribute_index' => (
118- isa => ' Int' ,
119- is => ' rw' ,
123+ isa => ' Int' ,
124+ is => ' rw' ,
120125 predicate => ' has_attribute_index' ,
121126);
122127
123128has ' protocol_binding' => (
124- isa => Uri,
125- is => ' rw' ,
126- coerce => 1,
129+ isa => Uri,
130+ is => ' rw' ,
131+ coerce => 1,
127132 predicate => ' has_protocol_binding' ,
128133);
129134has ' provider_name' => (
130- isa => ' Str' ,
131- is => ' rw' ,
135+ isa => ' Str' ,
136+ is => ' rw' ,
132137 predicate => ' has_provider_name' ,
133138);
134139
135140has ' AuthnContextClassRef' => (
136- isa => ' ArrayRef[Str]' ,
137- is => ' rw' ,
138- default => sub {[] }
141+ isa => ' ArrayRef[Str]' ,
142+ is => ' rw' ,
143+ default => sub { [] }
139144);
140145
141146has ' AuthnContextDeclRef' => (
142- isa => ' ArrayRef[Str]' ,
143- is => ' rw' ,
144- default => sub {[] }
147+ isa => ' ArrayRef[Str]' ,
148+ is => ' rw' ,
149+ default => sub { [] }
145150);
146151
147152has ' RequestedAuthnContext_Comparison' => (
@@ -151,17 +156,23 @@ has 'RequestedAuthnContext_Comparison' => (
151156);
152157
153158has ' force_authn' => (
154- isa => ' Bool' ,
155- is => ' ro' ,
159+ isa => ' Bool' ,
160+ is => ' ro' ,
156161 predicate => ' has_force_authn' ,
157162);
158163
159164has ' is_passive' => (
160- isa => ' Bool' ,
161- is => ' ro' ,
165+ isa => ' Bool' ,
166+ is => ' ro' ,
162167 predicate => ' has_is_passive' ,
163168);
164169
170+ has identity_providers => (
171+ isa => ' ArrayRef[Str]' ,
172+ is => ' ro' ,
173+ predicate => ' has_identity_providers' ,
174+ );
175+
165176around BUILDARGS => sub {
166177 my $orig = shift ;
167178 my $self = shift ;
@@ -180,20 +191,13 @@ Returns the AuthnRequest as XML.
180191
181192=cut
182193
183- my $saml = ' urn:oasis:names:tc:SAML:2.0:assertion ' ;
184- my $samlp = ' urn:oasis:names:tc:SAML:2.0:protocol ' ;
194+ my $samlp = [ ' samlp ' => URN_PROTOCOL] ;
195+ my $saml = [ ' samlp ' => URN_ASSERTION] ;
185196
186197sub as_xml {
187198 my ($self ) = @_ ;
188- my $x = XML::Writer-> new(
189- OUTPUT => ' self' ,
190- NAMESPACES => 1,
191- FORCED_NS_DECLS => [$saml , $samlp ],
192- PREFIX_MAP => {
193- $saml => ' saml2' ,
194- $samlp => ' saml2p'
195- }
196- );
199+
200+ my $x = XML::Generator-> new(' :std' );
197201
198202 my %req_atts = (
199203 ID => $self -> id,
@@ -203,9 +207,8 @@ sub as_xml {
203207
204208 my %issuer_attrs = ();
205209
206- my %protocol_bindings = (
207- ' HTTP-POST' => ' urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
208- );
210+ my %protocol_bindings
211+ = (' HTTP-POST' => BINDING_HTTP_POST);
209212
210213 my %att_map = (
211214 ' assertion_url' => ' AssertionConsumerServiceURL' ,
@@ -235,7 +238,7 @@ sub as_xml {
235238 $req_atts { $att_map {$opt } } = $protocol_bindings {$val };
236239 }
237240 elsif (any { $opt eq $_ } qw( force_authn is_passive) ) {
238- $req_atts { $att_map {$opt } } = ( $val ? ' true' : ' false' );
241+ $req_atts { $att_map {$opt } } = ($val ? ' true' : ' false' );
239242 }
240243 else {
241244 $req_atts { $att_map {$opt } } = $val ;
@@ -250,59 +253,68 @@ sub as_xml {
250253 $issuer_attrs { $att_map {$opt } } = $val ;
251254 }
252255
253- $x -> startTag([$samlp , ' AuthnRequest' ], %req_atts );
254- $x -> dataElement([$saml , ' Issuer' ], $self -> issuer, %issuer_attrs );
256+ return $x -> AuthnRequest($samlp ,
257+ \%req_atts ,
258+ $x -> Issuer($saml , \%issuer_attrs , $self -> issuer),
259+ $self -> _set_name_id($x ),
260+ $self -> _set_name_policy_format($x ),
261+ $self -> _set_requested_authn_context($x ),
262+ $self -> _set_scoping($x ),
263+ );
264+
265+ }
255266
256- $self -> _set_name_id($x );
257- $self -> _set_name_policy_format($x );
258- $self -> _set_requested_authn_context($x );
267+ sub _set_scoping {
268+ my $self = shift ;
269+ return unless $self -> has_identity_providers;
270+ my $x = shift ;
259271
260- $x -> endTag();
261- $x -> end();
272+ my @providers = map { $x -> IDPEntry($samlp , { ProviderID => $_ }) }
273+ @{ $self -> identity_providers };
274+ return $x -> Scoping($samlp , $x -> IDPList($samlp , @providers ));
262275}
263276
264277sub _set_name_id {
265- my ($self , $x ) = @_ ;
266- return if !$self -> has_nameid;
267- $x -> startTag([$saml , ' Subject' ]);
268- $x -> dataElement([$saml , ' NameID' ], undef , NameQualifier => $self -> nameid);
269- $x -> endTag();
270- return ;
278+ my $self = shift ;
279+ return unless $self -> has_nameid;
280+ my $x = shift ;
281+ return $x -> Subject($saml , $x -> NameID($saml , {NameQualifier => $self -> nameid}));
271282}
272283
273284sub _set_name_policy_format {
274- my ($self , $x ) = @_ ;
275- return if !$self -> has_nameidpolicy_format;
276-
277- $x -> dataElement([$samlp , ' NameIDPolicy' ],
278- undef ,
279- Format => $self -> nameidpolicy_format,
280- $self -> has_nameid_allow_create
285+ my $self = shift ;
286+ return unless $self -> has_nameidpolicy_format;
287+ my $x = shift ;
288+ return $x -> NameIDPolicy(
289+ $samlp ,
290+ {
291+ Format => $self -> nameidpolicy_format,
292+ $self -> has_nameid_allow_create
281293 ? (AllowCreate => $self -> nameid_allow_create)
282294 : (),
295+ }
283296 );
284- return ;
297+
285298}
286299
287300sub _set_requested_authn_context {
288- my ($self , $x ) = @_ ;
301+ my ($self , $x ) = @_ ;
289302
290- if (!@{ $self -> AuthnContextClassRef } && !@{ $self -> AuthnContextDeclRef })
291- {
292- return ;
293- }
303+ return
304+ if !@{ $self -> AuthnContextClassRef }
305+ && !@{ $self -> AuthnContextDeclRef };
294306
295- $x -> startTag([ $samlp , ' RequestedAuthnContext ' ],
296- Comparison => $self -> RequestedAuthnContext_Comparison) ;
307+ my @class = map { $x -> AuthnContextClassRef( $saml , undef , $_ ) }
308+ @{ $self -> AuthnContextClassRef } ;
297309
298- foreach my $ref (@{ $self -> AuthnContextClassRef }) {
299- $x -> dataElement([$saml , ' AuthnContextClassRef' ], $ref );
300- }
301- foreach my $ref (@{ $self -> AuthnContextDeclRef }) {
302- $x -> dataElement([$saml , ' AuthnContextDeclRef' ], $ref );
303- }
310+ my @decl = map { $x -> AuthnContextDeclRef($saml , undef , $_ ) }
311+ @{ $self -> AuthnContextDeclRef };
304312
305- $x -> endTag();
313+ return $x -> RequestedAuthnContext(
314+ $samlp ,
315+ { Comparison => $self -> RequestedAuthnContext_Comparison },
316+ @class , @decl
317+ );
306318}
307319
308320__PACKAGE__ -> meta-> make_immutable;
0 commit comments