Skip to content

Commit 3d291ff

Browse files
committed
testapp: allow attribute mapping by IdP
1 parent 60a3592 commit 3d291ff

File tree

3 files changed

+86
-25
lines changed

3 files changed

+86
-25
lines changed

xt/testapp/README.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The testapp now supports a simplified automatic configuration for testing agains
5050
2. Download the metadata from your IdP and save it as IdPs/google/metadata.xml
5151
3. Download the cacert.pem from the IdP and save it as IdPs/google/cacert.pem
5252
4. Optionally create IdPs/google/config.yml for custom settings for the IdP (if the a custom config.yml does not exist it will refresh the settings from the default config.yml.
53+
4. Optionally create IdPs/google/mappings.yml for custom IdP attribute mappings. If a custom mappings.yml does not does not exist it will use the defaul mappings.
5354

5455
The index page will automatically list each configured Identity Provider as a link to initiate login against that IdP.
5556

@@ -66,6 +67,10 @@ IdPs/
6667
google/
6768
cacert.pem
6869
metadata.yml
70+
shibboleth
71+
cacert.pem
72+
metadata.yml
73+
mappings.yml (optional)
6974

7075
### Run lighttpd to deliver metadata.xml
7176

@@ -94,15 +99,21 @@ If there is an option to upload the metadata.xml that is probably your first ste
9499

95100
Saml2Test expects the Identity Provider to provide an assertion with the following values:
96101

97-
1. DN
98-
2. CN
99-
3. EmailAddress
100-
4. FirstName
101-
5. Address
102-
6. Phone
103-
7. EmployeeNumber
104-
105-
Note that DN and CN (and others) may not be available. That can be customized in views/user.tt if you want to choose other options. However the Identity Provider must provide the assertion attributes that match the expected names in views/user.tt.
102+
1. EmailAddress
103+
2. FirstName
104+
3. LastName
105+
4. Address
106+
5. PhoneNumber
107+
6. EmployeeNumber
108+
109+
If the Identity Provider does not provide assertion attributes that match the expected names above you can create a custom mapping in IdPs/idp_name/mappings.yml in the following format. The setting is the name the testapp expects and the value is the attribure name that the IdP provides in the Assertion.
110+
111+
EmailAddress: "urn:oid:0.9.2342.19200300.100.1.3"
112+
FirstName: "urn:oid:2.5.4.42"
113+
LastName: "urn:oid:2.5.4.4"
114+
Address: "urn:oid:2.5.4.9"
115+
PhoneNumber: "urn:oid:2.5.4.20"
116+
EmployeeNumber: "urn:oid:0.9.2342.19200300.100.1.1"
106117

107118
## Debugging
108119

xt/testapp/lib/Saml2Test.pm

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ get '/' => sub {
6060
get '/login' => sub {
6161

6262
config->{cacert} = 'IdPs/' . params->{idp} . '/cacert.pem';
63+
config->{idp_name} = params->{idp};
6364
config->{idp} = 'http://localhost:8880/IdPs/' . params->{idp} . '/metadata.xml';
6465
if ( -f 'IdPs/' . params->{idp} . '/config.yml' ) {
6566
my $config_file = YAML::LoadFile('IdPs/' . params->{idp} . '/config.yml');
@@ -71,7 +72,6 @@ get '/login' => sub {
7172
for my $key (keys %$config_file) {
7273
config->{$key} = $config_file->{$key};
7374
}
74-
7575
}
7676
my $idp = _idp();
7777
my $sp = _sp();
@@ -196,27 +196,35 @@ post '/consumer-post' => sub {
196196
my $post = Net::SAML2::Binding::POST->new(
197197
cacert => config->{cacert},
198198
);
199+
199200
my $ret = $post->handle_response(
200201
params->{SAMLResponse}
201202
);
202203

203204
if ($ret) {
205+
204206
my $assertion = Net::SAML2::Protocol::Assertion->new_from_xml(
205207
xml => decode_base64(params->{SAMLResponse}),
206208
key_file => config->{key},
207209
cacert => config->{cacert},
208210
);
209211

212+
if (! $assertion->valid(config->{issuer})) {
213+
return '<html><pre>Bad Assertion</pre></html>';
214+
}
215+
210216
my $name_qualifier = $assertion->nameid_name_qualifier();
211217
my $sp_name_qualifier = $assertion->nameid_sp_name_qualifier();
212218

213219
my $slo_urls = config->{slo_urls};
214220

221+
my $user_attributes = get_user_attributes($assertion);
222+
215223
template 'user', {
216-
assertion => $assertion,
224+
user_attributes => $user_attributes,
217225
(defined $name_qualifier ? (name_qualifier => $name_qualifier) : ()),
218226
(defined $sp_name_qualifier ? (sp_name_qualifier => $sp_name_qualifier) : ()),
219-
slo_urls => ($slo_urls ? $slo_urls : ()),
227+
(defined $slo_urls ? (slo_urls => $slo_urls) : ()),
220228
message => 'Successful Login via POST',
221229
};
222230
}
@@ -225,6 +233,46 @@ post '/consumer-post' => sub {
225233
}
226234
};
227235

236+
sub get_attribute_map {
237+
238+
my $attribute_mappings;
239+
240+
my $file = 'IdPs/' . config->{idp_name} . '/mappings.yml';
241+
242+
if ( -e $file ) {
243+
$attribute_mappings = YAML::LoadFile($file);
244+
} else {
245+
246+
$attribute_mappings->{EmailAddress} = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress';
247+
$attribute_mappings->{FirstName} = 'fname';
248+
$attribute_mappings->{LastName} = 'lname';
249+
$attribute_mappings->{Address} = 'Address';
250+
$attribute_mappings->{PhoneNumber} = 'PhoneNumber';
251+
$attribute_mappings->{EmployeeNumber} = 'EmployeeNumber';
252+
}
253+
254+
return $attribute_mappings;
255+
}
256+
257+
sub get_user_attributes {
258+
my $assertion = shift;
259+
260+
my %user;
261+
262+
my $map = get_attribute_map();
263+
$user{issuer} = $assertion->{issuer};
264+
$user{session} = $assertion->{session};
265+
$user{nameid} = $assertion->nameid();
266+
$user{EmailAddress} = $assertion->{attributes}{$map->{EmailAddress}}[0];
267+
$user{FirstName} = $assertion->{attributes}{$map->{FirstName}}[0];
268+
$user{LastName} = $assertion->{attributes}{$map->{LastName}}[0];
269+
$user{Address} = $assertion->{attributes}{$map->{Address}}[0];
270+
$user{PhoneNumber} = $assertion->{attributes}{$map->{PhoneNumber}}[0];
271+
$user{EmployeeNumber} = $assertion->{attributes}{$map->{EmployeeNumber}}[0];
272+
273+
return \%user;
274+
}
275+
228276
get '/consumer-artifact' => sub {
229277
my $idp = _idp();
230278
my $idp_cert = $idp->cert('signing');
@@ -265,8 +313,10 @@ get '/consumer-artifact' => sub {
265313

266314
my $slo_urls = config->{slo_urls};
267315

316+
my $user_attributes = get_user_attributes($assertion);
317+
268318
template 'user', {
269-
assertion => $assertion,
319+
user_attributes => $user_attributes,
270320
($name_qualifier ? (name_qualifier => $name_qualifier) : ()),
271321
($sp_name_qualifier ? (sp_name_qualifier => $sp_name_qualifier) : ()),
272322
slo_urls => ($slo_urls ? $slo_urls : ()),

xt/testapp/views/user.tt

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
<h2>NameID: <% assertion.nameid %></h2>
1+
<h2>NameID: <% user_attributes.nameid %></h2>
22

33
<% FOREACH type IN slo_urls.keys.sort %>
44
<% slo_url = slo_urls.$type %>
55
<% if type == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' %>
6-
<p><a href="/logout-redirect?nameid=<% assertion.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% assertion.session | html %>">Logout (redirect binding)</a></p>
6+
<p><a href="/logout-redirect?nameid=<% user_attributes.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% user_attributes.session | html %>">Logout (redirect binding)</a></p>
77
<% end %>
88
<% if type == 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP' %>
9-
<p><a href="/logout-soap?nameid=<% assertion.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% assertion.session | html %>">Logout (soap binding)</a></p>
9+
<p><a href="/logout-soap?nameid=<% user_attributes.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% user_attributes.session | html %>">Logout (soap binding)</a></p>
1010
<% end %>
1111
<% if type == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' %>
1212
<p>Logout (post binding) - Unsupported</p>
1313
<% end %>
1414
<% if type == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' %>
15-
<!-- <p><a href="/logout-soap?nameid=<% assertion.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% assertion.session | html %>">Logout (soap artifact)</a></p> -->
15+
<!-- <p><a href="/logout-soap?nameid=<% user_attributes.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% user_attributes.session | html %>">Logout (soap artifact)</a></p> -->
1616
<p>Logout (Artifact binding) - Unsupported</p>
1717
<% end %>
1818

1919
<% END %>
2020

21-
<p><a href="/logout-local?nameid=<% assertion.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% assertion.session | html %>">Logout (local)</a></p>
21+
<p><a href="/logout-local?nameid=<% user_attributes.nameid | html %>&name_qualifier=<% name_qualifier | html %>&sp_name_qualifier=<% sp_name_qualifier | html %>&session=<% user_attributes.session | html %>">Logout (local)</a></p>
2222

2323
<% if message %>
2424
<div id="hideMe"><p><% message %></p></div>
@@ -33,30 +33,30 @@
3333
</tr>
3434
<tr>
3535
<td>Issuer</td>
36-
<td><% assertion.issuer %></td>
36+
<td><% user_attributes.issuer %></td>
3737
</tr>
3838
<tr>
3939
<td>EmailAddress</td>
40-
<td><% assertion.attributes.EmailAddress.0 %></td>
40+
<td><% user_attributes.EmailAddress %></td>
4141
</tr>
4242
<tr>
4343
<td>FirstName</td>
44-
<td><% assertion.attributes.FirstName.0 %><% assertion.attributes.fname.0 %></td>
44+
<td><% user_attributes.FirstName %></td>
4545
</tr>
4646
<tr>
4747
<td>LastName</td>
48-
<td><% assertion.attributes.LastName.0 %><% assertion.attributes.lname.0 %></td>
48+
<td><% user_attributes.LastName %></td>
4949
</tr>
5050
<tr>
5151
<td>Address</td>
52-
<td><% assertion.attributes.Address.0 %></td>
52+
<td><% user_attributes.Address %></td>
5353
</tr>
5454
<tr>
5555
<td>Phone</td>
56-
<td><% assertion.attributes.Phone %></td>
56+
<td><% user_attributes.PhoneNumber %></td>
5757
</tr>
5858
<tr>
5959
<td>EmployeeNumber</td>
60-
<td><% assertion.attributes.EmployeeNumber %></td>
60+
<td><% user_attributes.EmployeeNumber %></td>
6161
</tr>
6262
</table>

0 commit comments

Comments
 (0)