Skip to content

Commit f7364f7

Browse files
committed
Add support for multiple fully-fledged contacts for SP
It's now possible to specify an arbitrary number of contacts, with all the information supported by the SAML 2.0 specification, including multiple e-mail addresses and multiple phone numbers per contact. Since this is indeed information related to the Service Provider (which appears in the SP metadata), the new (indexed) properties used to declare contacts have been put in the "onelogin.saml2.sp" namespace and their parsing uses the same technique used to support multiple Attribute Consuming Services. The legacy way to specify just a technical and a support contacts is still supported (these "legacy" contacts are appended as the last two ones). The Contact class has been enhanced to support all ContactType attributes supported by the SAML 2.0 specification but still exposes the old constructor and single e-mail address getter (although being deprecated) to provide a 100% backward compatible API. Fixes SAML-Toolkits#326.
1 parent 043ca5e commit f7364f7

File tree

11 files changed

+620
-73
lines changed

11 files changed

+620
-73
lines changed

README.md

+25-12
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,31 @@ onelogin.saml2.sp.x509certNew =
250250
# If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem
251251
onelogin.saml2.sp.privatekey =
252252

253+
# Organization
254+
onelogin.saml2.organization.name = SP Java
255+
onelogin.saml2.organization.displayname = SP Java Example
256+
onelogin.saml2.organization.url = http://sp.example.com
257+
onelogin.saml2.organization.lang = en
258+
259+
# Contacts (use indexes to specify multiple contacts, multiple e-mail addresses per contact, multiple phone numbers per contact)
260+
onelogin.saml2.sp.contact[0].contactType=administrative
261+
onelogin.saml2.sp.contact[0].company=ACME
262+
onelogin.saml2.sp.contact[0].given_name=Guy
263+
onelogin.saml2.sp.contact[0].sur_name=Administrative
264+
onelogin.saml2.sp.contact[0].email_address[0][email protected]
265+
onelogin.saml2.sp.contact[0].email_address[1][email protected]
266+
onelogin.saml2.sp.contact[0].telephone_number[0]=+1-123456789
267+
onelogin.saml2.sp.contact[0].telephone_number[1]=+1-987654321
268+
onelogin.saml2.sp.contact[1].contactType=other
269+
onelogin.saml2.sp.contact[1].company=Big Corp
270+
onelogin.saml2.sp.contact[1].email_address[email protected]
271+
272+
# Legacy contacts (legacy way to specify just a technical and a support contact with minimal info)
273+
onelogin.saml2.contacts.technical.given_name = Technical Guy
274+
onelogin.saml2.contacts.technical.email_address = [email protected]
275+
onelogin.saml2.contacts.support.given_name = Support Guy
276+
onelogin.saml2.contacts.support.email_address = [email protected]
277+
253278
## Identity Provider Data that we want connect with our SP ##
254279

255280
# Identifier of the IdP entity (must be a URI)
@@ -374,18 +399,6 @@ onelogin.saml2.security.reject_deprecated_alg = true
374399
onelogin.saml2.parsing.trim_name_ids = false
375400
onelogin.saml2.parsing.trim_attribute_values = false
376401

377-
# Organization
378-
onelogin.saml2.organization.name = SP Java
379-
onelogin.saml2.organization.displayname = SP Java Example
380-
onelogin.saml2.organization.url = http://sp.example.com
381-
onelogin.saml2.organization.lang = en
382-
383-
# Contacts
384-
onelogin.saml2.contacts.technical.given_name = Technical Guy
385-
onelogin.saml2.contacts.technical.email_address = [email protected]
386-
onelogin.saml2.contacts.support.given_name = Support Guy
387-
onelogin.saml2.contacts.support.email_address = [email protected]
388-
389402
# Prefix used in generated Unique IDs.
390403
# Optional, defaults to ONELOGIN_ or full ID is like ONELOGIN_ebb0badd-4f60-4b38-b20a-a8e01f0592b1.
391404
# At minimun, the prefix can be non-numeric character such as "_".
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.onelogin.saml2.model;
22

3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.List;
36

47
/**
58
* Contact class of OneLogin's Java Toolkit.
@@ -8,34 +11,78 @@
811
*/
912
public class Contact {
1013
/**
11-
* Contact type
12-
*/
14+
* Contact type
15+
*/
1316
private final String contactType;
1417

1518
/**
16-
* Contact given name
17-
*/
19+
* Contact company
20+
*/
21+
private final String company;
22+
23+
/**
24+
* Contact given name
25+
*/
1826
private final String givenName;
27+
28+
/**
29+
* Contact surname
30+
*/
31+
private final String surName;
1932

2033
/**
21-
* Contact email
22-
*/
23-
private final String emailAddress;
34+
* Contact email
35+
*/
36+
private final List<String> emailAddresses;
2437

2538
/**
26-
* Constructor
39+
* Contact phone number
40+
*/
41+
private final List<String> telephoneNumbers;
42+
43+
/**
44+
* Constructor to specify minimal contact data.
45+
* <p>
46+
* To maintain backward compatibility, a <code>null</code> given name and a
47+
* <code>null</code> e-mail address are handled as being empty strings.
2748
*
2849
* @param contactType
29-
* String. Contact type
50+
* Contact type
3051
* @param givenName
31-
* String. Contact given name
52+
* Contact given name
3253
* @param emailAddress
33-
* String. Contact email
54+
* Contact e-mail
55+
* @deprecated use {@link #Contact(String, String, String, String, List, List)}
3456
*/
57+
@Deprecated
3558
public Contact(String contactType, String givenName, String emailAddress) {
59+
this(contactType, null, givenName != null ? givenName : "", null,
60+
Arrays.asList(emailAddress != null ? emailAddress : ""), null);
61+
}
62+
63+
/**
64+
* Constructor
65+
*
66+
* @param contactType
67+
* Contact type
68+
* @param givenName
69+
* Contact given name
70+
* @param surName
71+
* Contact surname
72+
* @param company
73+
* Contact company
74+
* @param emailAddresses
75+
* Contact e-mails
76+
* @param telephoneNumbers
77+
* Contact phone numbers
78+
*/
79+
public Contact(String contactType, String company, String givenName, String surName, List<String> emailAddresses, List<String> telephoneNumbers) {
3680
this.contactType = contactType != null? contactType : "";
37-
this.givenName = givenName != null? givenName : "";
38-
this.emailAddress = emailAddress != null? emailAddress : "";
81+
this.company = company;
82+
this.givenName = givenName;
83+
this.surName = surName;
84+
this.emailAddresses = emailAddresses != null? emailAddresses: Collections.emptyList();
85+
this.telephoneNumbers = telephoneNumbers != null? telephoneNumbers: Collections.emptyList();
3986
}
4087

4188
/**
@@ -46,17 +93,46 @@ public final String getContactType() {
4693
}
4794

4895
/**
49-
* @return string the contact email
96+
* @return the contact email
97+
* @deprecated this returns just the first e-mail address in {@link #getEmailAddresses()}
5098
*/
99+
@Deprecated
51100
public final String getEmailAddress() {
52-
return emailAddress;
101+
return emailAddresses.size() > 0? emailAddresses.get(0): null;
53102
}
54103

55104
/**
56-
* @return string the contact given name
105+
* @return a list containing the contact e-mail addresses (never <code>null</code>)
106+
*/
107+
public final List<String> getEmailAddresses() {
108+
return emailAddresses;
109+
}
110+
111+
/**
112+
* @return the contact given name
57113
*/
58114
public final String getGivenName() {
59115
return givenName;
60116
}
117+
118+
/**
119+
* @return the contact surname
120+
*/
121+
public final String getSurName() {
122+
return surName;
123+
}
124+
125+
/**
126+
* @return the contact company
127+
*/
128+
public final String getCompany() {
129+
return company;
130+
}
61131

132+
/**
133+
* @return a list containing the contact phone numbers (never <code>null</code>)
134+
*/
135+
public final List<String> getTelephoneNumbers() {
136+
return telephoneNumbers;
137+
}
62138
}

core/src/main/java/com/onelogin/saml2/settings/Metadata.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,21 @@ private String toContactsXml(List<Contact> contacts) {
277277

278278
for (Contact contact : contacts) {
279279
contactsXml.append("<md:ContactPerson contactType=\"" + Util.toXml(contact.getContactType()) + "\">");
280-
contactsXml.append("<md:GivenName>" + Util.toXml(contact.getGivenName()) + "</md:GivenName>");
281-
contactsXml.append("<md:EmailAddress>" + Util.toXml(contact.getEmailAddress()) + "</md:EmailAddress>");
280+
final String company = contact.getCompany();
281+
if(company != null)
282+
contactsXml.append("<md:Company>" + Util.toXml(company) + "</md:Company>");
283+
final String givenName = contact.getGivenName();
284+
if(givenName != null)
285+
contactsXml.append("<md:GivenName>" + Util.toXml(givenName) + "</md:GivenName>");
286+
final String surName = contact.getSurName();
287+
if(surName != null)
288+
contactsXml.append("<md:SurName>" + Util.toXml(surName) + "</md:SurName>");
289+
final List<String> emailAddresses = contact.getEmailAddresses();
290+
emailAddresses.forEach(emailAddress -> contactsXml
291+
.append("<md:EmailAddress>" + Util.toXml(emailAddress) + "</md:EmailAddress>"));
292+
final List<String> telephoneNumbers = contact.getTelephoneNumbers();
293+
telephoneNumbers.forEach(telephoneNumber -> contactsXml
294+
.append("<md:TelephoneNumber>" + Util.toXml(telephoneNumber) + "</md:TelephoneNumber>"));
282295
contactsXml.append("</md:ContactPerson>");
283296
}
284297

core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.List;
1010

1111
import com.onelogin.saml2.model.hsm.HSM;
12+
13+
import org.apache.commons.lang3.StringUtils;
1214
import org.slf4j.Logger;
1315
import org.slf4j.LoggerFactory;
1416
import org.w3c.dom.Document;
@@ -1032,7 +1034,10 @@ public List<String> checkSPSettings() {
10321034
}
10331035
*/
10341036

1035-
if (contact.getEmailAddress().isEmpty() || contact.getGivenName().isEmpty()) {
1037+
if (contact.getEmailAddresses().isEmpty() || contact.getEmailAddresses().stream().allMatch(StringUtils::isEmpty) ||
1038+
(StringUtils.isEmpty(contact.getCompany()) &&
1039+
StringUtils.isEmpty(contact.getGivenName()) &&
1040+
StringUtils.isEmpty(contact.getSurName()))) {
10361041
errorMsg = "contact_not_enough_data";
10371042
errors.add(errorMsg);
10381043
LOGGER.error(errorMsg);

0 commit comments

Comments
 (0)