Skip to content

Commit

Permalink
Fix #98 Be able to provide a NameIDFormat to LogoutRequest.Fix getNam…
Browse files Browse the repository at this point in the history
…eIdData method.
  • Loading branch information
pitbulk committed Mar 9, 2017
1 parent 64b5d7f commit b8a7c92
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 15 deletions.
34 changes: 27 additions & 7 deletions core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -410,17 +410,20 @@ public boolean isValid() {
public HashMap<String,String> getNameIdData() throws Exception {
HashMap<String,String> nameIdData = new HashMap<String, String>();

NodeList encryptedIDNodes = this.queryAssertion("/saml:Subject/saml:EncryptedID/xenc:EncryptedData");
NodeList encryptedIDNodes = this.queryAssertion("/saml:Subject/saml:EncryptedID");
NodeList nameIdNodes;
Element nameIdElem;
if (encryptedIDNodes.getLength() == 1) {
Element encryptedData = (Element) encryptedIDNodes.item(0);
PrivateKey key = settings.getSPkey();
if (key == null) {
throw new SettingsException("Key is required in order to decrypt the NameID", SettingsException.PRIVATE_KEY_NOT_FOUND);
}
NodeList encryptedDataNodes = this.queryAssertion("/saml:Subject/saml:EncryptedID/xenc:EncryptedData");
if (encryptedDataNodes.getLength() == 1) {
Element encryptedData = (Element) encryptedDataNodes.item(0);
PrivateKey key = settings.getSPkey();
if (key == null) {
throw new SettingsException("Key is required in order to decrypt the NameID", SettingsException.PRIVATE_KEY_NOT_FOUND);
}

Util.decryptElement(encryptedData, key);
Util.decryptElement(encryptedData, key);
}
nameIdNodes = this.queryAssertion("/saml:Subject/saml:EncryptedID/saml:NameID|/saml:Subject/saml:NameID");

if (nameIdNodes == null || nameIdNodes.getLength() == 0) {
Expand Down Expand Up @@ -481,6 +484,23 @@ public String getNameId() throws Exception {
return nameID;
}

/**
* Gets the NameID Format provided from the SAML Response String.
*
* @return string NameID Format
*
* @throws Exception
*/
public String getNameIdFormat() throws Exception {
HashMap<String,String> nameIdData = getNameIdData();
String nameidFormat = null;
if (!nameIdData.isEmpty() && nameIdData.containsKey("Format")) {
LOGGER.debug("SAMLResponse has NameID Format --> " + nameIdData.get("Format"));
nameidFormat = nameIdData.get("Format");
}
return nameidFormat;
}

/**
* Gets the Attributes from the AttributeStatement element.
*
Expand Down
36 changes: 33 additions & 3 deletions core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ public class LogoutRequest {
* NameID.
*/
private String nameId;


/**
* NameID Format.
*/
private String nameIdFormat;

/**
* SessionIndex. When the user is logged, this stored it from the AuthnStatement of the SAML Response
*/
Expand Down Expand Up @@ -96,10 +101,12 @@ public class LogoutRequest {
* The NameID that will be set in the LogoutRequest.
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
* @param nameIdFormat
* The nameIdFormat that will be set in the LogoutRequest.
* @throws XMLEntityException
*
*/
public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex) throws XMLEntityException {
public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex, String nameIdFormat) throws XMLEntityException {
this.settings = settings;
this.request = request;

Expand All @@ -114,6 +121,7 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId,
id = Util.generateUniqueID();
issueInstant = Calendar.getInstance();
this.nameId = nameId;
this.nameIdFormat = nameIdFormat;
this.sessionIndex = sessionIndex;

StrSubstitutor substitutor = generateSubstitutor(settings);
Expand All @@ -124,6 +132,24 @@ public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId,
}
}

/**
* Constructs the LogoutRequest object.
*
* @param settings
* OneLogin_Saml2_Settings
* @param request
* the HttpRequest object to be processed (Contains GET and POST parameters, request URL, ...).
* @param nameId
* The NameID that will be set in the LogoutRequest.
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
*
* @throws XMLEntityException
*/
public LogoutRequest(Saml2Settings settings, HttpRequest request, String nameId, String sessionIndex) throws XMLEntityException {
this(settings, request, nameId, sessionIndex, null);
}

/**
* Constructs the LogoutRequest object.
*
Expand Down Expand Up @@ -215,7 +241,11 @@ private StrSubstitutor generateSubstitutor(Saml2Settings settings) {
String nameIdFormat = null;
String spNameQualifier = null;
if (nameId != null) {
nameIdFormat = settings.getSpNameIDFormat();
if (this.nameIdFormat == null) {
nameIdFormat = settings.getSpNameIDFormat();
} else {
nameIdFormat = this.nameIdFormat;
}
} else {
nameId = settings.getIdpEntityId();
nameIdFormat = Constants.NAMEID_ENTITY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,34 @@ public void testGetNameId() throws Exception {
assertNull(samlResponse.getNameId());
}

/**
* Tests the getNameIdFormat method of SamlResponse
*
* @throws Exception
*
* @see com.onelogin.saml2.authn.SamlResponse#getNameIdFormat
*/
@Test
public void testGetNameIdFormat() throws Exception {
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build();
String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64");
SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", samlResponse.getNameIdFormat());

samlResponseEncoded = Util.getFileAsString("data/responses/response_encrypted_nameid.xml.base64");
samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", samlResponse.getNameIdFormat());

samlResponseEncoded = Util.getFileAsString("data/responses/valid_encrypted_assertion.xml.base64");
samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
assertEquals("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", samlResponse.getNameIdFormat());

settings.setWantNameId(false);
samlResponseEncoded = Util.getFileAsString("data/responses/invalids/no_nameid.xml.base64");
samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
assertNull(samlResponse.getNameIdFormat());
}

/**
* Tests the getNameId method of SamlResponse
* Case: No NameId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ public void testGetNameIdData() throws Exception {
assertThat(nameIdDataStr, containsString("Value=ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c"));
assertThat(nameIdDataStr, not(containsString("SPNameQualifier")));

logoutRequest = new LogoutRequest(settings, null, "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", null, "urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress");
logoutRequestStringBase64 = logoutRequest.getEncodedLogoutRequest();
logoutRequestStr = Util.base64decodedInflated(logoutRequestStringBase64);
nameIdDataStr = LogoutRequest.getNameIdData(logoutRequestStr, key).toString();
assertThat(nameIdDataStr, containsString("urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress"));
assertThat(nameIdDataStr, containsString("Value=ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c"));
assertThat(nameIdDataStr, not(containsString("SPNameQualifier")));

String keyString = Util.getFileAsString("data/customPath/certs/sp.pem");
key = Util.loadPrivateKey(keyString);
logoutRequestStr = Util.getFileAsString("data/logout_requests/logout_request_encrypted_nameid.xml");
Expand Down
69 changes: 64 additions & 5 deletions toolkit/src/main/java/com/onelogin/saml2/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public class Auth {
*/
private String nameid;

/**
* NameIDFormat.
*/
private String nameidFormat;

/**
* SessionIndex. When the user is logged, this stored it from the AuthnStatement of the SAML Response
*/
Expand Down Expand Up @@ -345,18 +350,20 @@ public void login(String returnTo) throws IOException, SettingsException {
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
* @param stay
* True if we want to stay (returns the url string) False to execute redirection
* True if we want to stay (returns the url string) False to execute redirection
* @param nameidFormat
* The NameID Format will be set in the LogoutRequest.
*
* @return the SLO URL with the LogoutRequest if stay = True
*
* @throws IOException
* @throws XMLEntityException
* @throws SettingsException
*/
public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay) throws IOException, XMLEntityException, SettingsException {
public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay, String nameidFormat) throws IOException, XMLEntityException, SettingsException {
Map<String, String> parameters = new HashMap<String, String>();

LogoutRequest logoutRequest = new LogoutRequest(settings, null, nameId, sessionIndex);
LogoutRequest logoutRequest = new LogoutRequest(settings, null, nameId, sessionIndex, nameidFormat);
String samlLogoutRequest = logoutRequest.getEncodedLogoutRequest();
parameters.put("SAMLRequest", samlLogoutRequest);

Expand Down Expand Up @@ -399,13 +406,56 @@ public String logout(String returnTo, String nameId, String sessionIndex, Boolea
* The NameID that will be set in the LogoutRequest.
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
* @param stay
* True if we want to stay (returns the url string) False to execute redirection
*
* @return the SLO URL with the LogoutRequest if stay = True
*
* @throws IOException
* @throws XMLEntityException
* @throws SettingsException
*/
public String logout(String returnTo, String nameId, String sessionIndex, Boolean stay) throws IOException, XMLEntityException, SettingsException {
return logout(returnTo, nameId, sessionIndex, stay, null);
}

/**
* Initiates the SLO process.
*
* @param returnTo
* The target URL the user should be returned to after logout (relayState).
* Will be a self-routed URL when null, or not be appended at all when an empty string is provided
* @param nameId
* The NameID that will be set in the LogoutRequest.
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
* @param nameidFormat
* The NameID Format will be set in the LogoutRequest.
* @throws IOException
* @throws XMLEntityException
* @throws SettingsException
*/
public void logout(String returnTo, String nameId, String sessionIndex, String nameidFormat) throws IOException, XMLEntityException, SettingsException {
logout(returnTo, nameId, sessionIndex, false, nameidFormat);
}

/**
* Initiates the SLO process.
*
* @param returnTo
* The target URL the user should be returned to after logout (relayState).
* Will be a self-routed URL when null, or not be appended at all when an empty string is provided
* @param nameId
* The NameID that will be set in the LogoutRequest.
* @param sessionIndex
* The SessionIndex (taken from the SAML Response in the SSO process).
*
* @throws IOException
* @throws XMLEntityException
* @throws SettingsException
*/
public void logout(String returnTo, String nameId, String sessionIndex) throws IOException, XMLEntityException, SettingsException {
logout(returnTo, nameId, sessionIndex, false);
logout(returnTo, nameId, sessionIndex, false, null);
}

/**
Expand All @@ -416,7 +466,7 @@ public void logout(String returnTo, String nameId, String sessionIndex) throws I
* @throws SettingsException
*/
public void logout() throws IOException, XMLEntityException, SettingsException {
logout(null, null, null);
logout(null, null, null, false);
}

/**
Expand Down Expand Up @@ -475,6 +525,7 @@ public void processResponse(String requestId) throws Exception {

if (samlResponse.isValid(requestId)) {
nameid = samlResponse.getNameId();
nameidFormat = samlResponse.getNameIdFormat();
authenticated = true;
attributes = samlResponse.getAttributes();
sessionIndex = samlResponse.getSessionIndex();
Expand Down Expand Up @@ -643,6 +694,14 @@ public final String getNameId()
return nameid;
}

/**
* @return the nameID Format of the assertion
*/
public final String getNameIdFormat()
{
return nameidFormat;
}

/**
* @return the SessionIndex of the assertion
*/
Expand Down
42 changes: 42 additions & 0 deletions toolkit/src/test/java/com/onelogin/saml2/test/AuthTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,48 @@ public void testGetNameID() throws Exception {
assertEquals("2de11defd199f8d5bb63f9b7deb265ba5c675c10", auth3.getNameId());
}

/**
* Tests the getNameIdFormat method of Auth
* Case: get nameid format from a SAMLResponse
*
* @throws Exception
*
* @see com.onelogin.saml2.Auth#getNameIdFormat
*/
@Test
public void testGetNameIdFormat() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
String samlResponseEncoded = Util.getFileAsString("data/responses/response1.xml.base64");
when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded}));
when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/java-saml-jspsample/acs.jsp"));

Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build();
Auth auth = new Auth(settings, request, response);
assertNull(auth.getNameIdFormat());
auth.processResponse();
assertFalse(auth.isAuthenticated());
assertNull(auth.getNameIdFormat());

samlResponseEncoded = Util.getFileAsString("data/responses/valid_response.xml.base64");
when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded}));
Auth auth2 = new Auth(settings, request, response);
assertNull(auth2.getNameIdFormat());
auth2.processResponse();
assertTrue(auth2.isAuthenticated());
assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", auth2.getNameIdFormat());

samlResponseEncoded = Util.getFileAsString("data/responses/response_encrypted_nameid.xml.base64");
when(request.getParameterMap()).thenReturn(singletonMap("SAMLResponse", new String[]{samlResponseEncoded}));
when(request.getRequestURL()).thenReturn(new StringBuffer("https://pitbulk.no-ip.org/newonelogin/demo1/index.php?acs"));
settings.setStrict(false);
Auth auth3 = new Auth(settings, request, response);
assertNull(auth3.getNameIdFormat());
auth3.processResponse();
assertTrue(auth3.isAuthenticated());
assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", auth3.getNameIdFormat());
}

/**
* Tests the getNameId method of SamlResponse
*
Expand Down

0 comments on commit b8a7c92

Please sign in to comment.