11package Net::SAML2::Protocol::AuthnRequest ;
2+
23use Moose;
3- use MooseX::Types::Moose qw / Str Int/ ;
44use MooseX::Types::URI qw/ Uri / ;
55use MooseX::Types::Common::String qw/ NonEmptySimpleStr / ;
66use XML::Writer;
7+ use List::Util qw( any) ;
78
89with ' 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
7986has ' 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
97143around 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+
117166sub 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;
0 commit comments