By: Won Kim user 08 Nov 2016 at 4:57 p.m. CST

49 Responses
Won Kim gravatar
it is not clear how to register the gluu server which is acting like an SP to our internal IDP. Normally we would have an SP metadata file that we can register to our Siteminder IDP. But it is not clear if that is what we do and if it is where to find the gluu server’s SP metatdata file. I was able to connect an apache service to work with the outbound SAML of the gluu server and establish the trust relationship. But when we tried to use the inbound SAML it wasn’t clear how to make the gluu to external IDP relationship.

By William Lowe user 09 Nov 2016 at 9:43 a.m. CST

William Lowe gravatar
Hi Won, You can follow [these instructions](https://gluu.org/docs/how-to/saml_proxy_end_to_end/) for testing the inbound SAML components. Let us know how it goes. Thank you!

By Michael Schwartz Account Admin 09 Nov 2016 at 3:58 p.m. CST

Michael Schwartz gravatar
Per our Gotomeeting, it would be more appropriate to use the SAML authentication script if there is only one inbound IDP. See [https://github.com/GluuFederation/oxAuth/tree/master/Server/integrations/saml](https://github.com/GluuFederation/oxAuth/tree/master/Server/integrations/saml) for a good starting point for what this script would look like.

By Won Kim user 10 Nov 2016 at 1:17 p.m. CST

Won Kim gravatar
hey thanks so much for the discussion. We tried to use the script as is at first but I am not sure how and when this script runs. We setup a trusted SP and we can authenticate with the default method fine, but when we switch the authentication method I don't see any error messages in the oxauth.log file. I am not 100% sure if we even get to the script running since we don't see any logs and the only thing I see in the SAML interaction with my local browser is the following below with no response. What is the best way to debug the script? <samlp :AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" AssertionConsumerServiceURL="https://gluu-sp.qualcomm.com/Shibboleth.sso/SAML2/POST-SimpleSign" Destination="https://gluu.qualcomm.com/idp/profile/SAML2/Redirect/SSO" ID="_92a95bbb85cb1ff53ba082d4a1888f23" IssueInstant="2016-11-10T18:55:17Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Version="2.0"> <saml :Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://gluu-sp.qualcomm.com/shibboleth</saml:Issuer> <samlp :NameIDPolicy AllowCreate="1" /> </samlp:AuthnRequest>

By Won Kim user 10 Nov 2016 at 1:33 p.m. CST

Won Kim gravatar
Oh one more question I noticed that you got access to the gluu server's ldap persistence store. I assume it is local to the gluu server and what is the port so I can get direct access and what do I use to login?

By Won Kim user 10 Nov 2016 at 1:43 p.m. CST

Won Kim gravatar
I have seemed to have bricked myself out of our gluu server. Is there a back end way of reverting back to the gluu server's authentication method back to internal?

By Mohib Zico staff 10 Nov 2016 at 1:51 p.m. CST

Mohib Zico gravatar
Won, [This](https://support.gluu.org/other/2667/i-may-have-locked-myself-out-of-gluu/) ticket might assist you.

By Mohib Zico staff 10 Nov 2016 at 1:56 p.m. CST

Mohib Zico gravatar
>> What is the best way to debug the script? After enabling script from 'Manage Custom Script' ( note: not 'Manage Authentication' section ): you can tail 'oxauth_script.log'; it's inside /opt/tomcat/logs/ >> . I assume it is local to the gluu server and what is the port so I can get direct access and what do I use to login? There are two ways: - You need a reverse tunnel from your computer ( localhost ) to your Gluu Server's 1636 port, then use any ldap browser to browse the tree. or - You can temporary open 1636 to the world or to your own IP and you can directly use your Gluu Server's LDAP without any tunneling.

By Won Kim user 10 Nov 2016 at 3:42 p.m. CST

Won Kim gravatar
so I have found that script log oxauth_script.log but the only log we get was the initialized successfully log and nothing further. When we try to login using the saml auth method no further logs are created. Is there any way to find out what is happening before the script gets called?

By Won Kim user 10 Nov 2016 at 6:22 p.m. CST

Won Kim gravatar
Ok I got myself back into the gluu admin server. So now that I look more carefully at the oxauth_script.log file. I see the error below. Is there a special format we need to have the saml certificate in? 2016-11-11 00:13:53,527 INFO [org.xdi.service.PythonService] (pool-2-thread-10) Saml. Initialization 2016-11-11 00:13:53,528 ERROR [org.xdi.service.custom.script.CustomScriptManager] (pool-2-thread-10) Failed to initialize custom script: 'saml' at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:105) at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339) at org.xdi.util.security.CertificateHelper.loadCertificate(CertificateHelper.java:30) at org.xdi.util.security.CertificateHelper.loadCertificate(CertificateHelper.java:24) at org.gluu.saml.SamlConfiguration.loadCertificateFromString(SamlConfiguration.java:79) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) java.security.cert.CertificateException: java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Empty input

By Mohib Zico staff 10 Nov 2016 at 9:52 p.m. CST

Mohib Zico gravatar
>> java.security.cert.CertificateException: java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Empty input Most probably your 'saml.pem' cert is empty. From [this](https://github.com/GluuFederation/docs/blob/master/sources/how-to/saml_proxy_end_to_end.md) doc: _"Deployer need to copy 'asimba.crt' in 'saml.pem' without any 'BEGIN CERTIFICATE' and 'END CERTIFICATE' tag."_

By Michael Schwartz Account Admin 16 Nov 2016 at 10:31 a.m. CST

Michael Schwartz gravatar
Won, this is not an easy task. The SAML script was really built for Asimba, so it's not as user friendly for a generic SAML IDP. Are you making progress? If you had some budget, we can assign this to a developer and he'd have it done for you in about a week.

By Won Kim user 16 Nov 2016 at 12:25 p.m. CST

Won Kim gravatar
yeah this seems a bit more challenging then I originally planned. We don't have a budge for now...technically I am going to try a swipe at this with some help with the Identity team. The challenge for me right now is testing the script. Unfortunately I am not a python expert I am more of a java developer so this has been challenging for me to setup my environment to figure out what is failing in the protocol. Any guidance would be helpful. If we can't come up with a solution I may bring this up with my manager and maybe we can come up with a budget. thanks won

By Won Kim user 16 Nov 2016 at 12:45 p.m. CST

Won Kim gravatar
I think if we can understand what the gluu server expects from the SAML IDP and on our IAM team they know what they want and what can be provided in the SAML responses, it would be helpful. Are there any documentations on what the gluu server requires from the script? thanks won

By Michael Schwartz Account Admin 16 Nov 2016 at 12:56 p.m. CST

Michael Schwartz gravatar
I'm assigning this to a resource to help you. Can you send test creds to shouro@gluu.org and he can take a look? Also please email the SAML IDP metadata if available.

By Aliaksandr Samuseu staff 16 Nov 2016 at 1:05 p.m. CST

Aliaksandr Samuseu gravatar
Hi, Won. I tried to understand what is your current problem with the script is while reading the ticket, and still not sure I completely getting it. Zico explained script's requirement to certificate, so it should resolve your issue with it. What is your current problem? I can't get what do you mean by this: >Are there any documentations on what the gluu server requires from the script? There is [readme file](https://github.com/GluuFederation/oxAuth/tree/master/Server/integrations/saml) on github explaining its properties. As it has been under heavy redesigning for last weeks, it may not include all of them, though, or may not explain full meaning of some of them. If it still fails for you, you should check `wrapper.log` and `oxauth_scripts.log` for any hints, and provide your findings to us. In a nutshell, script works pretty simply: it will initiate SAML inter-exchange with some IdP to delegate authentication of the user to it; then after it will get user's attributes from that IdP, it can create/update a user entry locally, at the Gluu instance where it runs, using received attributes. As Michael mentioned, it's also possible to point it to locally installed Asimba module, instead of to some specific remote IdP, what is useful when you have many IdPs.

By Won Kim user 16 Nov 2016 at 1:36 p.m. CST

Won Kim gravatar
I think the place where we are failing is the configuration of the IDP and local Attribute mapping. At first we kept getting null values in the attribute now we are getting null pointer exception when we get to line 200 on the original gluu saml script which is the following code snippet. print "Saml. Authenticate for step 1. saml_response_name_id:", saml_response_name_id saml_response_attributes = samlResponse.getAttributes() We see the saml_response_name_id being the user that logged in on our IDP login page but the response fails to map. Maybe we have the "saml_local_attributes_list" and the "saml_idp_attributes_list" wrong? We paired it down to just having the uid but that doesn't help. Maybe the parsing of the saml response from our IDP is not working properly. Not sure if the saml_name_identifier_format is correct or not. Here is the error message we get in the wrapper.log ----------------- INFO | jvm 1 | 2016/11/16 19:31:46 | 2016-11-16 19:31:46,108 INFO [org.jasig.cas.services.DefaultServicesManagerImpl] - <Reloading registered services.> INFO | jvm 1 | 2016/11/16 19:31:46 | 2016-11-16 19:31:46,108 INFO [org.jasig.cas.services.DefaultServicesManagerImpl] - <Loaded 1 services.> INFO | jvm 1 | 2016/11/16 19:32:05 | 2016-11-16 19:32:05,521 ERROR [org.xdi.oxauth.service.external.ExternalAuthenticationService] INFO | jvm 1 | 2016/11/16 19:32:05 | java.lang.NullPointerException INFO | jvm 1 | 2016/11/16 19:32:05 | at org.gluu.saml.Response.getAttributes(Response.java:157) INFO | jvm 1 | 2016/11/16 19:32:05 | at sun.reflect.GeneratedMethodAccessor2047.invoke(Unknown Source) INFO | jvm 1 | 2016/11/16 19:32:05 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) INFO | jvm 1 | 2016/11/16 19:32:05 | at java.lang.reflect.Method.invoke(Method.java:606) ----------------- Here is the error message we get from the oxauth_script.log --------- 2016-11-16 19:32:05,503 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) Saml. Authenticate for step 1 2016-11-16 19:32:05,503 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) Saml. Authenticate for step 1. saml_response: 2016-11-16 19:32:05,503 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl 2016-11-16 19:32:05,503 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) c3RpbmF0aW9uPSJodHRwczovL2dsdXUucXVhbGNvbW0uY29tL294YXV0aC9wb3N0bG9naW4i ... (saml _response) 2016-11-16 19:32:05,508 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) ZXJ0aW9uPgo8L1Jlc3BvbnNlPg== 2016-11-16 19:32:05,519 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) Saml. Authenticate for step 1. saml_response_name_id: 2016-11-16 19:32:05,519 INFO [org.xdi.service.PythonService] (ajp-bio-127.0.0.1-8009-exec-334) wkim --------------- Here is the last SAML Response we sent back from the IDP. ------ <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://gluu.qualcomm.com/oxauth/postlogin" ID="_9a8ae578899c27c1e7a54e821df4f6789cc6" InResponseTo="_6b3c47d9-55d8-4fc9-a2e3-889ca68a47aa" IssueInstant="2016-11-16T19:32:05Z" Version="2.0" > <ns1:Issuer xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" >qctestidp</ns1:Issuer> <Status> <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> </Status> <ns2:Assertion xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_1e038922bb8b1a460a3192eaa2129ae4b5aa" IssueInstant="2016-11-16T19:32:05Z" Version="2.0" > <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">qctestidp</ns2:Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <ds:Reference URI="#_1e038922bb8b1a460a3192eaa2129ae4b5aa"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <ds:DigestValue>4xoB2d5CwCsGOoandIvugCog/Bg=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue> c6dipWhDI/oJiGjWLKnvElfUg/dcGlXoQVn9uGmHRUS/0JcjFgzZVrclbicpfy3wiu+FiijooyzC MRO7VQbScelURGjhyQRdrPjOX6tigpyUBWRplfFyqMdM2VvfA75i6hh/CFHfwx164xcDYL0JhJjw 0z1Vli07OMaaDYz/qeg= </ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509Certificate> MIIC0zCCAjwCCQDsRVf5KhWEszANBgkqhkiG9w0BAQUFADCBrTELMAkGA1UEBhMCVVMxCzAJBgNV BAgTAkNBMRIwEAYDVQQHEwlTYW4gRGllZ28xFTATBgNVBAoTDFF1YWxjb21tIEluYzEQMA4GA1UE CxMHQ29ycCBJVDEgMB4GA1UEAxMXdHN0Y29ubmVjdC5xdWFsY29tbS5jb20xMjAwBgkqhkiG9w0B CQEWI3dlYi1hdXRoLWFkbWluLmludEBxdGkucXVhbGNvbW0uY29tMB4XDTE0MDIyNjE3NTMwN1oX DTI0MDIyNDE3NTMwN1owga0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2Fu IERpZWdvMRUwEwYDVQQKEwxRdWFsY29tbSBJbmMxEDAOBgNVBAsTB0NvcnAgSVQxIDAeBgNVBAMT F3RzdGNvbm5lY3QucXVhbGNvbW0uY29tMTIwMAYJKoZIhvcNAQkBFiN3ZWItYXV0aC1hZG1pbi5p bnRAcXRpLnF1YWxjb21tLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvosbQrqETNKP jhD9wQ0bmzz+y9Vzq0Idim5MXPSx9qtvbiIgqRCl5hKOy5wa4+nK/7k2ibw7AbGt/M3sBWRLdm4+ QVXosE5dw0KZpy9VdBUjlpNBz5dA5nDK/RTYnFQHUE/iqsokzX8cvHabSEN5/+cebk2NcV9wOndr GbOY3scCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAbuIY6505IfrRYfQGVvGe89/9gWsmYv4wYAGwJ IkfTqAIPDfUUz4oy36hqkG+yhjsk5yirLuxFNfmfCsDrh7ORveWVMsZmW/8pM6xNfyQCfOdajDA4 X7urnFffAts64F/cumRJLmDVVA69Vvihnkjd/ElubXRUG+5zv6+svfeJ9g== </ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </ds:Signature> <ns2:Subject> <ns2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">wkim</ns2:NameID> <ns2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <ns2:SubjectConfirmationData InResponseTo="_6b3c47d9-55d8-4fc9-a2e3-889ca68a47aa" NotOnOrAfter="2016-11-16T19:33:35Z" Recipient="https://gluu.qualcomm.com/oxauth/postlogin" /> </ns2:SubjectConfirmation> </ns2:Subject> <ns2:Conditions NotBefore="2016-11-16T19:31:35Z" NotOnOrAfter="2016-11-16T19:33:35Z" > <ns2:AudienceRestriction> <ns2:Audience>https://gluu.qualcomm.com/asimba/profiles/saml2</ns2:Audience> </ns2:AudienceRestriction> </ns2:Conditions> <ns2:AuthnStatement AuthnInstant="2016-11-16T19:32:04Z" SessionIndex="wJlwlMsKJdotQ+/M+dp3jo/1vCU=Spb1KA==" SessionNotOnOrAfter="2016-11-16T19:33:35Z" > <ns2:AuthnContext> <ns2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</ns2:AuthnContextClassRef> </ns2:AuthnContext> </ns2:AuthnStatement> <ns2:AttributeStatement> <ns2:Attribute Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="emailaddress" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim@qualcomm.com</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>Won</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="SurName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>Kim</ns2:AttributeValue> </ns2:Attribute> </ns2:AttributeStatement> </ns2:Assertion> </Response> -------------

By Michael Schwartz Account Admin 16 Nov 2016 at 1:59 p.m. CST

Michael Schwartz gravatar
Oh wow, you are getting pretty close. So in the mapping for `saml_idp_attributes_list` I would have: ``` name,emailaddress,givenname,uid,SurName ``` And for `saml_local_attributes_list` put: ``` displayName,mail,givenName,uid,sn ``` See if that works...

By Won Kim user 16 Nov 2016 at 2:43 p.m. CST

Won Kim gravatar
So we tried your updated Attribute list but we still are getting the same error. The error is occurring at the java.lang.NullPointerException org.gluu.saml.Response.getAttributes(Response.java:157) We are getting the value from the response object for the name identity. The following line of code works. saml_response_name_id = samlResponse.getNameId()  but the next line of code saml_response_attributes = samlResponse.getAttributes()  fails due to the Nullpointer exception. I am not sure why the getAttributes Here is the source code we are looking at. https://github.com/GluuFederation/oxCore/blob/master/oxSaml/src/main/java/org/gluu/saml/Response.java Starting at line 144 in the GitHub repo we seem to be getting the NPE.

By Michael Schwartz Account Admin 16 Nov 2016 at 2:50 p.m. CST

Michael Schwartz gravatar
Yes, the mapping is failing... I'm going to re-assign this to a different engineer. We may need to add a line to debug the issue. Can you also copy and paste the script into a file and upload it as an attachment to this issue. You're using the default script that ships with version 2.4.4, right?

By Won Kim user 16 Nov 2016 at 3:08 p.m. CST

Won Kim gravatar
Yes this is the default version. Pasting the script not sure how to attach a file. ----- ``` from org.jboss.seam.contexts import Context, Contexts from org.jboss.seam.security import Identity from javax.faces.context import FacesContext from org.xdi.model.custom.script.type.auth import PersonAuthenticationType from org.xdi.oxauth.service import UserService, ClientService, AuthenticationService, AttributeService from org.xdi.oxauth.service.net import HttpService from org.xdi.util import StringHelper from org.xdi.util import ArrayHelper from org.gluu.saml import SamlConfiguration, AuthRequest, Response from java.util import Arrays, ArrayList, HashMap, IdentityHashMap from org.xdi.oxauth.model.common import User from org.xdi.ldap.model import CustomAttribute import java try: import json except ImportError: import simplejson as json class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis def init(self, configurationAttributes): print "Saml. Initialization" saml_certificate_file = configurationAttributes.get("saml_certificate_file").getValue2() saml_idp_sso_target_url = configurationAttributes.get("saml_idp_sso_target_url").getValue2() saml_issuer = configurationAttributes.get("saml_issuer").getValue2() saml_use_authn_context = StringHelper.toBoolean(configurationAttributes.get("saml_use_authn_context").getValue2(), True) if (saml_use_authn_context): saml_name_identifier_format = configurationAttributes.get("saml_name_identifier_format").getValue2() else: saml_name_identifier_format = None saml_certificate = self.loadCeritificate(saml_certificate_file) if (StringHelper.isEmpty(saml_certificate)): print "Saml. Initialization. File with x509 certificate should be not empty" return False samlConfiguration = SamlConfiguration() print "configured saml certificate file" # Set the issuer of the authentication request. This would usually be the URL of the issuing web application samlConfiguration.setIssuer(saml_issuer) # Tells the IdP to return a persistent identifier for the user samlConfiguration.setNameIdentifierFormat(saml_name_identifier_format) # The URL at the Identity Provider where to the authentication request should be sent samlConfiguration.setIdpSsoTargetUrl(saml_idp_sso_target_url) print "setup SSO Target" # Enablediable RequestedAuthnContext #samlConfiguration.setUseRequestedAuthnContext(saml_use_authn_context) print "Set User Requested Auth" # Load x509 certificate samlConfiguration.loadCertificateFromString(saml_certificate) self.samlConfiguration = samlConfiguration self.attributesMapping = None if (configurationAttributes.containsKey("saml_idp_attributes_list") and configurationAttributes.containsKey("saml_local_attributes_list")): saml_idp_attributes_list = configurationAttributes.get("saml_idp_attributes_list").getValue2() if (StringHelper.isEmpty(saml_idp_attributes_list)): print "Saml. Initialization. The property saml_idp_attributes_list is empty" return False saml_local_attributes_list = configurationAttributes.get("saml_local_attributes_list").getValue2() if (StringHelper.isEmpty(saml_local_attributes_list)): print "Saml. Initialization. The property saml_local_attributes_list is empty" return False self.attributesMapping = self.prepareAttributesMapping(saml_idp_attributes_list, saml_local_attributes_list) if (self.attributesMapping == None): print "Saml. Initialization. The attributes mapping isn't valid" return False self.samlExtensionModule = None if (configurationAttributes.containsKey("saml_extension_module")): saml_extension_module_name = configurationAttributes.get("saml_extension_module").getValue2() try: self.samlExtensionModule = __import__(saml_extension_module_name) saml_extension_module_init_result = self.samlExtensionModule.init(configurationAttributes) if (not saml_extension_module_init_result): return False except ImportError, ex: print "Saml. Initialization. Failed to load saml_extension_module:", saml_extension_module_name print "Saml. Initialization. Unexpected error:", ex return False print "Saml. Initialized successfully" return True def destroy(self, configurationAttributes): print "Saml. Destroy" print "Saml. Destroyed successfully" return True def getApiVersion(self): return 1 def isValidAuthenticationMethod(self, usageType, configurationAttributes): return True def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): return None def authenticate(self, configurationAttributes, requestParameters, step): context = Contexts.getEventContext() authenticationService = AuthenticationService.instance() userService = UserService.instance() saml_map_user = False saml_enroll_user = False saml_enroll_all_user_attr = False # Use saml_deployment_type only if there is no attributes mapping if (configurationAttributes.containsKey("saml_deployment_type")): saml_deployment_type = StringHelper.toLowerCase(configurationAttributes.get("saml_deployment_type").getValue2()) if (StringHelper.equalsIgnoreCase(saml_deployment_type, "map")): saml_map_user = True if (StringHelper.equalsIgnoreCase(saml_deployment_type, "enroll")): saml_enroll_user = True if (StringHelper.equalsIgnoreCase(saml_deployment_type, "enroll_all_attr")): saml_enroll_all_user_attr = True saml_allow_basic_login = False if (configurationAttributes.containsKey("saml_allow_basic_login")): saml_allow_basic_login = StringHelper.toBoolean(configurationAttributes.get("saml_allow_basic_login").getValue2(), False) use_basic_auth = False if (saml_allow_basic_login): # Detect if user used basic authnetication method credentials = Identity.instance().getCredentials() user_name = credentials.getUsername() user_password = credentials.getPassword() if (StringHelper.isNotEmpty(user_name) and StringHelper.isNotEmpty(user_password)): use_basic_auth = True if ((step == 1) and saml_allow_basic_login and use_basic_auth): print "Saml. Authenticate for step 1. Basic authentication" context.set("saml_count_login_steps", 1) credentials = Identity.instance().getCredentials() user_name = credentials.getUsername() user_password = credentials.getPassword() logged_in = False if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)): userService = UserService.instance() logged_in = userService.authenticate(user_name, user_password) if (not logged_in): return False return True if (step == 1): print "Saml. Authenticate for step 1" currentSamlConfiguration = self.getCurrentSamlConfiguration(self.samlConfiguration, configurationAttributes, requestParameters) if (currentSamlConfiguration == None): print "Saml. Prepare for step 1. Client saml configuration is invalid" return False saml_response_array = requestParameters.get("SAMLResponse") if ArrayHelper.isEmpty(saml_response_array): print "Saml. Authenticate for step 1. saml_response is empty" return False saml_response = saml_response_array[0] print "Saml. Authenticate for step 1. saml_response:", saml_response samlResponse = Response(currentSamlConfiguration) samlResponse.loadXmlFromBase64(saml_response) saml_validate_response = True if (configurationAttributes.containsKey("saml_validate_response")): saml_validate_response = StringHelper.toBoolean(configurationAttributes.get("saml_validate_response").getValue2(), False) if (saml_validate_response): if (not samlResponse.isValid()): print "Saml. Authenticate for step 1. saml_response isn't valid" saml_response_name_id = samlResponse.getNameId() if (StringHelper.isEmpty(saml_response_name_id)): print "Saml. Authenticate for step 1. saml_response_name_id is invalid" return False print "Saml. Authenticate for step 1. saml_response_name_id:", saml_response_name_id saml_response_attributes = samlResponse.getAttributes() print "Saml. Authenticate for step 1. attributes: ", saml_response_attributes # Use persistent Id as saml_user_uid saml_user_uid = saml_response_name_id if (saml_map_user): # Use mapping to local IDP user print "Saml. Authenticate for step 1. Attempting to find user by oxExternalUid: saml:", saml_user_uid # Check if the is user with specified saml_user_uid find_user_by_uid = userService.getUserByAttribute("oxExternalUid", "saml:" + saml_user_uid) if (find_user_by_uid == None): print "Saml. Authenticate for step 1. Failed to find user" print "Saml. Authenticate for step 1. Setting count steps to 2" context.set("saml_count_login_steps", 2) context.set("saml_user_uid", saml_user_uid) return True found_user_name = find_user_by_uid.getUserId() print "Saml. Authenticate for step 1. found_user_name:", found_user_name user_authenticated = authenticationService.authenticate(found_user_name) if (user_authenticated == False): print "Saml. Authenticate for step 1. Failed to authenticate user" return False print "Saml. Authenticate for step 1. Setting count steps to 1" context.set("saml_count_login_steps", 1) post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 1. post_login_result:", post_login_result return post_login_result elif (saml_enroll_user): # Use auto enrollment to local IDP print "Saml. Authenticate for step 1. Attempting to find user by oxExternalUid: saml:", saml_user_uid # Check if the is user with specified saml_user_uid find_user_by_uid = userService.getUserByAttribute("oxExternalUid", "saml:" + saml_user_uid) if (find_user_by_uid == None): # Auto user enrollemnt print "Saml. Authenticate for step 1. There is no user in LDAP. Adding user to local LDAP" # Convert saml result attributes keys to lover case saml_response_normalized_attributes = HashMap() for saml_response_attribute_entry in saml_response_attributes.entrySet(): saml_response_normalized_attributes.put( StringHelper.toLowerCase(saml_response_attribute_entry.getKey()), saml_response_attribute_entry.getValue()) currentAttributesMapping = self.prepareCurrentAttributesMapping(self.attributesMapping, configurationAttributes, requestParameters) print "Saml. Authenticate for step 1. Using next attributes mapping", currentAttributesMapping newUser = User() for attributesMappingEntry in currentAttributesMapping.entrySet(): idpAttribute = attributesMappingEntry.getKey() localAttribute = attributesMappingEntry.getValue() localAttributeValue = saml_response_normalized_attributes.get(idpAttribute) if (localAttribute != None): newUser.setAttribute(localAttribute, localAttributeValue) newUser.setAttribute("oxExternalUid", "saml:" + saml_user_uid) print "Saml. Authenticate for step 1. Attempting to add user", saml_user_uid, " with next attributes", newUser.getCustomAttributes() find_user_by_uid = userService.addUser(newUser, True) print "Saml. Authenticate for step 1. Added new user with UID", find_user_by_uid.getUserId() found_user_name = find_user_by_uid.getUserId() print "Saml. Authenticate for step 1. found_user_name:", found_user_name user_authenticated = authenticationService.authenticate(found_user_name) if (user_authenticated == False): print "Saml. Authenticate for step 1. Failed to authenticate user" return False print "Saml. Authenticate for step 1. Setting count steps to 1" context.set("saml_count_login_steps", 1) post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 1. post_login_result:", post_login_result return post_login_result elif (saml_enroll_all_user_attr): print "Saml. Authenticate for step 1. Attempting to find user by oxExternalUid: saml:" + saml_user_uid # Check if the is user with specified saml_user_uid find_user_by_uid = userService.getUserByAttribute("oxExternalUid", "saml:" + saml_user_uid) if (find_user_by_uid == None): print "Saml. Authenticate for step 1. Failed to find user" user = User() customAttributes = ArrayList() for key in attributes.keySet(): ldapAttributes = attributeService.getAllAttributes() for ldapAttribute in ldapAttributes: saml2Uri = ldapAttribute.getSaml2Uri() if(saml2Uri == None): saml2Uri = attributeService.getDefaultSaml2Uri(ldapAttribute.getName()) if(saml2Uri == key): attribute = CustomAttribute(ldapAttribute.getName()) attribute.setValues(attributes.get(key)) customAttributes.add(attribute) attribute = CustomAttribute("oxExternalUid") attribute.setValue("saml:" + saml_user_uid) customAttributes.add(attribute) user.setCustomAttributes(customAttributes) if(user.getAttribute("sn") == None): attribute = CustomAttribute("sn") attribute.setValue(saml_user_uid) customAttributes.add(attribute) if(user.getAttribute("cn") == None): attribute = CustomAttribute("cn") attribute.setValue(saml_user_uid) customAttributes.add(attribute) find_user_by_uid = userService.addUser(user, True) print "Saml. Authenticate for step 1. Added new user with UID", find_user_by_uid.getUserId() found_user_name = find_user_by_uid.getUserId() print "Saml. Authenticate for step 1. found_user_name:", found_user_name user_authenticated = authenticationService.authenticate(found_user_name) if (user_authenticated == False): print "Saml. Authenticate for step 1. Failed to authenticate user" return False print "Saml. Authenticate for step 1. Setting count steps to 1" context.set("saml_count_login_steps", 1) post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 1. post_login_result:", post_login_result return post_login_result else: # Check if the is user with specified saml_user_uid print "Saml. Authenticate for step 1. Attempting to find user by uid:", saml_user_uid find_user_by_uid = userService.getUser(saml_user_uid) if (find_user_by_uid == None): print "Saml. Authenticate for step 1. Failed to find user" return False found_user_name = find_user_by_uid.getUserId() print "Saml. Authenticate for step 1. found_user_name:", found_user_name user_authenticated = authenticationService.authenticate(found_user_name) if (user_authenticated == False): print "Saml. Authenticate for step 1. Failed to authenticate user" return False print "Saml. Authenticate for step 1. Setting count steps to 1" context.set("saml_count_login_steps", 1) post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 1. post_login_result:", post_login_result return post_login_result elif (step == 2): print "Saml. Authenticate for step 2" sessionAttributes = context.get("sessionAttributes") if (sessionAttributes == None) or not sessionAttributes.containsKey("saml_user_uid"): print "Saml. Authenticate for step 2. saml_user_uid is empty" return False saml_user_uid = sessionAttributes.get("saml_user_uid") passed_step1 = StringHelper.isNotEmptyString(saml_user_uid) if (not passed_step1): return False credentials = Identity.instance().getCredentials() user_name = credentials.getUsername() user_password = credentials.getPassword() logged_in = False if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)): logged_in = userService.authenticate(user_name, user_password) if (not logged_in): return False # Check if there is user which has saml_user_uid # Avoid mapping Saml account to more than one IDP account find_user_by_uid = userService.getUserByAttribute("oxExternalUid", "saml:" + saml_user_uid) if (find_user_by_uid == None): # Add saml_user_uid to user one id UIDs find_user_by_uid = userService.addUserAttribute(user_name, "oxExternalUid", "saml:" + saml_user_uid) if (find_user_by_uid == None): print "Saml. Authenticate for step 2. Failed to update current user" return False post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 2. post_login_result:", post_login_result return post_login_result else: found_user_name = find_user_by_uid.getUserId() print "Saml. Authenticate for step 2. found_user_name:", found_user_name if StringHelper.equals(user_name, found_user_name): post_login_result = self.samlExtensionPostLogin(configurationAttributes, find_user_by_uid) print "Saml. Authenticate for step 2. post_login_result:", post_login_result return post_login_result return False else: return False def prepareForStep(self, configurationAttributes, requestParameters, step): context = Contexts.getEventContext() authenticationService = AuthenticationService.instance() if (step == 1): print "Saml. Prepare for step 1" httpService = HttpService.instance(); request = FacesContext.getCurrentInstance().getExternalContext().getRequest() assertionConsumerServiceUrl = httpService.constructServerUrl(request) + "/postlogin" print "Saml. Prepare for step 1. Prepared assertionConsumerServiceUrl:", assertionConsumerServiceUrl currentSamlConfiguration = self.getCurrentSamlConfiguration(self.samlConfiguration, configurationAttributes, requestParameters) if (currentSamlConfiguration == None): print "Saml. Prepare for step 1. Client saml configuration is invalid" return False # Generate an AuthRequest and send it to the identity provider samlAuthRequest = AuthRequest(currentSamlConfiguration) external_auth_request_uri = currentSamlConfiguration.getIdpSsoTargetUrl() + "?SAMLRequest=" + samlAuthRequest.getRequest(True, assertionConsumerServiceUrl) print "Saml. Prepare for step 1. external_auth_request_uri:", external_auth_request_uri context.set("external_auth_request_uri", external_auth_request_uri) return True elif (step == 2): print "Saml. Prepare for step 2" return True else: return False def getExtraParametersForStep(self, configurationAttributes, step): if (step == 2): return Arrays.asList("saml_user_uid") return None def getCountAuthenticationSteps(self, configurationAttributes): context = Contexts.getEventContext() if (context.isSet("saml_count_login_steps")): return context.get("saml_count_login_steps") return 2 def getPageForStep(self, configurationAttributes, step): if (step == 1): saml_allow_basic_login = False if (configurationAttributes.containsKey("saml_allow_basic_login")): saml_allow_basic_login = StringHelper.toBoolean(configurationAttributes.get("saml_allow_basic_login").getValue2(), False) if (saml_allow_basic_login): return "/login.xhtml" else: return "/auth/saml/samllogin.xhtml" return "/auth/saml/samlpostlogin.xhtml" def logout(self, configurationAttributes, requestParameters): return True def isPassedStep1(): credentials = Identity.instance().getCredentials() user_name = credentials.getUsername() passed_step1 = StringHelper.isNotEmptyString(user_name) return passed_step1 def loadCeritificate(self, saml_certificate_file): saml_certificate = None # Load certificate from file f = open(saml_certificate_file, 'r') try: saml_certificate = f.read() except: print "Failed to load certificate from file:", saml_certificate_file return None finally: f.close() return saml_certificate def getClientConfiguration(self, configurationAttributes, requestParameters): # Get client configuration if (configurationAttributes.containsKey("saml_client_configuration_attribute")): saml_client_configuration_attribute = configurationAttributes.get("saml_client_configuration_attribute").getValue2() print "Saml. GetClientConfiguration. Using client attribute:", saml_client_configuration_attribute if (requestParameters == None): return None client_id = None client_id_array = requestParameters.get("client_id") if (ArrayHelper.isNotEmpty(client_id_array) and StringHelper.isNotEmptyString(client_id_array[0])): client_id = client_id_array[0] if (client_id == None): eventContext = Contexts.getEventContext() if (eventContext.isSet("sessionAttributes")): client_id = eventContext.get("sessionAttributes").get("client_id") if (client_id == None): print "Saml. GetClientConfiguration. client_id is empty" return None clientService = ClientService.instance() client = clientService.getClient(client_id) if (client == None): print "Saml. GetClientConfiguration. Failed to find client", client_id, " in local LDAP" return None saml_client_configuration = clientService.getCustomAttribute(client, saml_client_configuration_attribute) if ((saml_client_configuration == None) or StringHelper.isEmpty(saml_client_configuration.getValue())): print "Saml. GetClientConfiguration. Client", client_id, " attribute", saml_client_configuration_attribute, " is empty" else: print "Saml. GetClientConfiguration. Client", client_id, " attribute", saml_client_configuration_attribute, " is", saml_client_configuration return saml_client_configuration return None def getCurrentSamlConfiguration(self, currentSamlConfiguration, configurationAttributes, requestParameters): saml_client_configuration = self.getClientConfiguration(configurationAttributes, requestParameters) if (saml_client_configuration == None): return currentSamlConfiguration saml_client_configuration_value = json.loads(saml_client_configuration.getValue()) client_saml_certificate = None client_saml_certificate_file = saml_client_configuration_value["saml_certificate_file"] if (StringHelper.isNotEmpty(client_saml_certificate_file)): client_saml_certificate = self.loadCeritificate(client_saml_certificate_file) if (StringHelper.isEmpty(client_saml_certificate)): print "Saml. BuildClientSamlConfiguration. File with x509 certificate should be not empty. Using default configuration" return currentSamlConfiguration clientSamlConfiguration = currentSamlConfiguration.clone() if (client_saml_certificate != None): clientSamlConfiguration.loadCertificateFromString(client_saml_certificate) client_saml_issuer = saml_client_configuration_value["saml_issuer"] clientSamlConfiguration.setIssuer(client_saml_issuer) #saml_use_authn_context = saml_client_configuration_value["saml_use_authn_context"] #client_use_saml_use_authn_context = StringHelper.toBoolean(saml_use_authn_context, True) #clientSamlConfiguration.setUseRequestedAuthnContext(client_use_saml_use_authn_context) return clientSamlConfiguration def prepareAttributesMapping(self, saml_idp_attributes_list, saml_local_attributes_list): saml_idp_attributes_list_array = StringHelper.split(saml_idp_attributes_list, ",") if (ArrayHelper.isEmpty(saml_idp_attributes_list_array)): print "Saml. PrepareAttributesMapping. There is no attributes specified in saml_idp_attributes_list property" return None saml_local_attributes_list_array = StringHelper.split(saml_local_attributes_list, ",") if (ArrayHelper.isEmpty(saml_local_attributes_list_array)): print "Saml. PrepareAttributesMapping. There is no attributes specified in saml_local_attributes_list property" return None if (len(saml_idp_attributes_list_array) != len(saml_local_attributes_list_array)): print "Saml. PrepareAttributesMapping. The number of attributes in saml_idp_attributes_list and saml_local_attributes_list isn't equal" return None attributeMapping = IdentityHashMap() containsUid = False i = 0 count = len(saml_idp_attributes_list_array) while (i < count): idpAttribute = StringHelper.toLowerCase(saml_idp_attributes_list_array[i]) localAttribute = StringHelper.toLowerCase(saml_local_attributes_list_array[i]) attributeMapping.put(idpAttribute, localAttribute) if (StringHelper.equalsIgnoreCase(localAttribute, "uid")): containsUid = True i = i + 1 if (not containsUid): print "Saml. PrepareAttributesMapping. There is no mapping to mandatory 'uid' attribute" return None return attributeMapping def prepareCurrentAttributesMapping(self, currentAttributesMapping, configurationAttributes, requestParameters): saml_client_configuration = self.getClientConfiguration(configurationAttributes, requestParameters) if (saml_client_configuration == None): return currentAttributesMapping saml_client_configuration_value = json.loads(saml_client_configuration.getValue()) clientAttributesMapping = self.prepareAttributesMapping(saml_client_configuration_value["saml_idp_attributes_list"], saml_client_configuration_value["saml_local_attributes_list"]) if (clientAttributesMapping == None): print "Saml. PrepareCurrentAttributesMapping. Client attributes mapping is invalid. Using default one" return currentAttributesMapping return clientAttributesMapping def samlExtensionPostLogin(self, configurationAttributes, user): if (self.samlExtensionModule != None): try: post_login_result = self.samlExtensionModule.postLogin(configurationAttributes, user) print "Saml. ExtensionPostlogin result:", post_login_result return post_login_result except Exception, ex: print "Saml. ExtensionPostlogin. Failed to execute postLogin method" print "Saml. ExtensionPostlogin. Unexpected error:", ex return False except java.lang.Throwable, ex: print "Saml. ExtensionPostlogin. Failed to execute postLogin method" ex.printStackTrace() return False return True ```

By Won Kim user 16 Nov 2016 at 3:13 p.m. CST

Won Kim gravatar
Here is the SAML Response which it is trying to parse in that response.getAttributes --- ``` <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://gluu.qualcomm.com/oxauth/postlogin" ID="_aa029b7fd4d7c884e2bafc302d683b5b0e35" InResponseTo="_5bf7130d-f282-461c-9310-ae1c7a2cf544" IssueInstant="2016-11-16T20:37:29Z" Version="2.0" > <ns1:Issuer xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" >qctestidp</ns1:Issuer> <Status> <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> </Status> <ns2:Assertion xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_7c2b6cece1380d4cfbdebd8903245e7be36f" IssueInstant="2016-11-16T20:37:29Z" Version="2.0" > <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">qctestidp</ns2:Issuer> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <ds:Reference URI="#_7c2b6cece1380d4cfbdebd8903245e7be36f"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <ds:DigestValue>+Fkrw2tppak50IQcBVU+QGeXUXY=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue> HLGIqwpdLNPnyHoYIRhpZ/Vwokrzp5nE8L1147r1VeQzCEzWhRlLqGM8mfU4mIAabe6g8vaNzyj/ 4edf5TJSMLQnHi9q3j0muWX7ZvFh4Os8lE05HcpTcSGFqC8B0gvIkCf44DfeF+EdslR0t963z/Mp /A66Jdf9mkYUpXLLTxQ= </ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509Certificate> MIIC0zCCAjwCCQDsRVf5KhWEszANBgkqhkiG9w0BAQUFADCBrTELMAkGA1UEBhMCVVMxCzAJBgNV BAgTAkNBMRIwEAYDVQQHEwlTYW4gRGllZ28xFTATBgNVBAoTDFF1YWxjb21tIEluYzEQMA4GA1UE CxMHQ29ycCBJVDEgMB4GA1UEAxMXdHN0Y29ubmVjdC5xdWFsY29tbS5jb20xMjAwBgkqhkiG9w0B CQEWI3dlYi1hdXRoLWFkbWluLmludEBxdGkucXVhbGNvbW0uY29tMB4XDTE0MDIyNjE3NTMwN1oX DTI0MDIyNDE3NTMwN1owga0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2Fu IERpZWdvMRUwEwYDVQQKEwxRdWFsY29tbSBJbmMxEDAOBgNVBAsTB0NvcnAgSVQxIDAeBgNVBAMT F3RzdGNvbm5lY3QucXVhbGNvbW0uY29tMTIwMAYJKoZIhvcNAQkBFiN3ZWItYXV0aC1hZG1pbi5p bnRAcXRpLnF1YWxjb21tLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvosbQrqETNKP jhD9wQ0bmzz+y9Vzq0Idim5MXPSx9qtvbiIgqRCl5hKOy5wa4+nK/7k2ibw7AbGt/M3sBWRLdm4+ QVXosE5dw0KZpy9VdBUjlpNBz5dA5nDK/RTYnFQHUE/iqsokzX8cvHabSEN5/+cebk2NcV9wOndr GbOY3scCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAbuIY6505IfrRYfQGVvGe89/9gWsmYv4wYAGwJ IkfTqAIPDfUUz4oy36hqkG+yhjsk5yirLuxFNfmfCsDrh7ORveWVMsZmW/8pM6xNfyQCfOdajDA4 X7urnFffAts64F/cumRJLmDVVA69Vvihnkjd/ElubXRUG+5zv6+svfeJ9g== </ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </ds:Signature> <ns2:Subject> <ns2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">wkim</ns2:NameID> <ns2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <ns2:SubjectConfirmationData InResponseTo="_5bf7130d-f282-461c-9310-ae1c7a2cf544" NotOnOrAfter="2016-11-16T20:38:59Z" Recipient="https://gluu.qualcomm.com/oxauth/postlogin" /> </ns2:SubjectConfirmation> </ns2:Subject> <ns2:Conditions NotBefore="2016-11-16T20:36:59Z" NotOnOrAfter="2016-11-16T20:38:59Z" > <ns2:AudienceRestriction> <ns2:Audience>https://gluu.qualcomm.com/asimba/profiles/saml2</ns2:Audience> </ns2:AudienceRestriction> </ns2:Conditions> <ns2:AuthnStatement AuthnInstant="2016-11-16T20:37:29Z" SessionIndex="9Ee611UjpoqBVjdJPYZOdEXcqXk=tBv4Hw==" SessionNotOnOrAfter="2016-11-16T20:38:59Z" > <ns2:AuthnContext> <ns2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</ns2:AuthnContextClassRef> </ns2:AuthnContext> </ns2:AuthnStatement> <ns2:AttributeStatement> <ns2:Attribute Name="name" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="emailaddress" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim@qualcomm.com</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>Won</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>wkim</ns2:AttributeValue> </ns2:Attribute> <ns2:Attribute Name="SurName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" > <ns2:AttributeValue>Kim</ns2:AttributeValue> </ns2:Attribute> </ns2:AttributeStatement> </ns2:Assertion> </Response> ```

By Michael Schwartz Account Admin 16 Nov 2016 at 4:04 p.m. CST

Michael Schwartz gravatar
ok, we'll take a look. Thanks for the info.

By Won Kim user 16 Nov 2016 at 5:38 p.m. CST

Won Kim gravatar
ok looking back at the history of the Response class I can only see one version where there is any relevant code that can get a NPE at line 157 which is in this version of the Response code. [https://github.com/GluuFederation/oxCore/blob/4b71f9e229e1bd18627d3a5f98b4e8b4e3426da8/oxSaml/src/main/java/org/gluu/saml/Response.java](https://github.com/GluuFederation/oxCore/blob/4b71f9e229e1bd18627d3a5f98b4e8b4e3426da8/oxSaml/src/main/java/org/gluu/saml/Response.java) I am wondering if the gluu server 2.4.4 has this version of the code and if there is an issue with the response object at this point. ``` if (nameChildNode.getNamespaceURI().equalsIgnoreCase("urn:oasis:names:tc:SAML:2.0:assertion") && nameChildNode.getLocalName().equals("AttributeValue")) { ``` I would make the code like this so you don't get a NPE. ``` if ("urn:oasis:names:tc:SAML:2.0:assertion".equalsIgnoreCase(nameChildNode.getNamespaceURI()) && "AttributeValue".equals(nameChildNode.getLocalName())) { ``` BTW this possible bug is still lurking in the current version.

By Mohib Zico staff 17 Nov 2016 at 4:13 a.m. CST

Mohib Zico gravatar
Hi Won, We have added couple of points in md [doc](https://github.com/GluuFederation/docs/blob/master/sources/how-to/saml_proxy_end_to_end.md). Please take a look and try to sync your setup like this way. Here are two points you need to confirm: - 'Asimba core configuration file modification' - 'saml_idp_attributes_mapping'. These are just example there... but you need to make sure that your attribute formats are like this.... `"uid": ["uid", "urn:oid:0.9.2342.19200300.100.1.1"]` Also one point on selecting 'Default Authentication Mode'. Whenever you are going to use 'saml' for your SSO workflow.. here is how you might wanna apply your changes here in this configuration: - 'Default Authentication Method' ( Configuration --> Manage Authentication ) - Authentication mode: saml - oxTrust authentication mode: basic ( enable 'basic' from Manage Custom Scripts as well ). - Update - Use a different browser / incognito one to check how your SSO working.

By Won Kim user 17 Nov 2016 at 11:22 a.m. CST

Won Kim gravatar
Assume the top 2 bullet points you replied with only the 2nd one applies since we are not using Asimba and the header before the 'saml_idp_attributes_mapping' is under the banner "SAML custom script configuration". And I assume the 2nd bullet point is for the field name saml_idp_attributes_list and not saml_idp_attributes_mapping which is what the script on my version has as the idp names. Also where do find the "urn:oid:xxx" field names for the various other kinds of fields we want to use?

By Won Kim user 17 Nov 2016 at 11:28 a.m. CST

Won Kim gravatar
Ok when I tried to put that saml_idp_attributes_mapping update but it fails and looking over the code it doesn't seem to work in my version of the saml script because it expects a List and not a Map. Should I use the latest version of the script from GitHub.

By Aliaksandr Samuseu staff 17 Nov 2016 at 11:34 a.m. CST

Aliaksandr Samuseu gravatar
Hi, Won. Zico provided some good points above which you should consider. And now when docs are updated, it should be much easier for you to make it work. A couple of points: 1) The script you posted above is outdated, and it seems there is incorrectly accessed variable in the part responsible for "enroll_all_attr" mode of operation (it seems to me you use this mode in your setup, so it's probably why you are getting NPE). There is no such problem in the latest version, you should get it from [here](https://raw.githubusercontent.com/GluuFederation/oxAuth/master/Server/integrations/saml/SamlExternalAuthenticator.py) 2) Github's read.me page for the script currently covers the old version, though. There are no such script properties as "saml_local_attributes_list" and "saml_idp_attributes_list" in the new version any more (they were used for mappings), instead a single property "saml_idp_attributes_mapping" formatted as json object is used for this. Below is what you could use in this attribute (just an example so you could understand how it's formatted): ``` {"uid": ["uid", "urn:oid:0.9.2342.19200300.100.1.1"], "mail": ["mail", "urn:oid:0.9.2342.19200300.100.1.3"],"givenName": ["givenName", "urn:oid:2.5.4.42"], "sn": ["sn", "urn:oid:2.5.4.4"], "eduPersonPrincipalName": ["eduPersonPrincipalName", "urn:oid:1.3.6.1.4.1.5923.1.1.1.6"] } ``` First goes a local attribute name, then in square brackets is list of possible attributes names from IdPs that will map to this one local attribute. So you can have a bit of diversity here. 3) From saml responses you posted, it seems your IdP uses basic format for names of saml attributes in it. In that case you must not use "enroll_all_attr" mode of operation, as it's currently can only work with attributes' names formatted as saml uris and doesn't use mappings you set in "saml_idp_attributes_mapping". Just use "enroll" method instead.

By Won Kim user 17 Nov 2016 at 12:10 p.m. CST

Won Kim gravatar
We have been using the enroll and not the enroll_all_attr. Let me try to update the script with the latest version and will post how it goes. thanks

By Mohib Zico staff 17 Nov 2016 at 12:27 p.m. CST

Mohib Zico gravatar
Won, One quick question... you are just using SAML_script to connect your one SP and one remote AuthN server with your Gluu Server? Is that what you are trying to do?

By Won Kim user 17 Nov 2016 at 12:39 p.m. CST

Won Kim gravatar
Ok I put in the new code but I think our 2.4.4 version has the old java code on the Response object which is throwing the following exception. ``` INFO | jvm 1 | 2016/11/17 18:29:33 | 2016-11-17 18:29:33,686 ERROR [org.xdi.oxauth.service.external.ExternalAuthenticationService] INFO | jvm 1 | 2016/11/17 18:29:33 | at org.gluu.saml.Response.isValid(Response.java:91) INFO | jvm 1 | 2016/11/17 18:29:33 | at sun.reflect.GeneratedMethodAccessor2094.invoke(Unknown Source) INFO | jvm 1 | 2016/11/17 18:29:33 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) INFO | jvm 1 | 2016/11/17 18:29:33 | at java.lang.reflect.Method.invoke(Method.java:606) INFO | jvm 1 | 2016/11/17 18:29:33 | INFO | jvm 1 | 2016/11/17 18:29:33 | java.lang.Exception: java.lang.Exception: Can't find signature in document. ... ``` I believe it is coming from the following code which is not the latest java code. [https://github.com/GluuFederation/oxCore/blob/4b71f9e229e1bd18627d3a5f98b4e8b4e3426da8/oxSaml/src/main/java/org/gluu/saml/Response.java](https://github.com/GluuFederation/oxCore/blob/4b71f9e229e1bd18627d3a5f98b4e8b4e3426da8/oxSaml/src/main/java/org/gluu/saml/Response.java)

By Won Kim user 17 Nov 2016 at 12:43 p.m. CST

Won Kim gravatar
Oh, never mind my last post...I see that our SAML response is not working properly and is not sending back a signature. Let me look into on our side first. won

By Aliaksandr Samuseu staff 17 Nov 2016 at 12:55 p.m. CST

Aliaksandr Samuseu gravatar
You could try to set script's property "saml_validate_response" to "False", that should disable validation for now (for test purposes)

By Won Kim user 17 Nov 2016 at 1:12 p.m. CST

Won Kim gravatar
Ok I put in the new jython script from GitHub and we are still getting the same error. I think there is something wrong with the 2.4.4 version of the oxCore's org.gluu.saml.Response object is not working. It is the same error as before where we get an NPE error when it tries to read the getAttributes at line 157 of the Response class. Should I update our gluu server to the latest version of the gluu server? thanks won

By Won Kim user 17 Nov 2016 at 1:12 p.m. CST

Won Kim gravatar
will try disabling validation for now. won

By Aliaksandr Samuseu staff 17 Nov 2016 at 1:28 p.m. CST

Aliaksandr Samuseu gravatar
Any log entries you could share? From wrapper.log and oxauth_script.log, first of all. >Should I update our gluu server to the latest version of the gluu server? That wouldn't hurt, perhaps, just make sure you'll backup your current instance before doing that, just in case. Update procedure is described [here](https://www.gluu.org/docs/deployment/updating/) and is pretty simple. Atm only SP1 patch is available. Sorry for asking again, but just to make sure: you have followed Zico's suggestion [here](https://support.gluu.org/single-sign-on/3453/how-to-configure-inbound-saml-relationship-with-external-idp/#at16796) and modified your script's certificate file, correct?

By Won Kim user 17 Nov 2016 at 1:29 p.m. CST

Won Kim gravatar
ok disabling validation still gives the same error. ``` INFO | jvm 1 | 2016/11/17 19:26:20 | java.lang.NullPointerException INFO | jvm 1 | 2016/11/17 19:26:20 | at org.gluu.saml.Response.getAttributes(Response.java:157) ``` Can I upgrade the gluu server to the latest gluu oxCore code? won

By Won Kim user 17 Nov 2016 at 1:43 p.m. CST

Won Kim gravatar
yes I have been sharing snippets of both logs on this thread. I can send you the whole thing but I think we are back at the same point we were before. Let me update the gluu server first to see if this fixes the issues. And yes I did remove the opening and closing CERT BEGIN and END delimiters. That has not been a problem in a while. It is getting the attributes that continues to fail.

By Aliaksandr Samuseu staff 17 Nov 2016 at 1:46 p.m. CST

Aliaksandr Samuseu gravatar
May I also ask you to provide full list of properties you use for script atm? I believe you haven't shared it yet.

By Won Kim user 17 Nov 2016 at 1:57 p.m. CST

Won Kim gravatar
This is for the latest saml script from GitHub asimba_saml_certificate_file = /etc/certs/saml.pem saml_idp_sso_target_url = https://tstconnect.qualcomm.com/affwebservices/public/saml2sso asimba_entity_id = https://gluu.qualcomm.com/asimba/profiles/saml2 saml_name_identifier_format = urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified saml_idp_attributes_mapping = {"uid": ["uid", "urn:oid:0.9.2342.19200300.100.1.1"], "mail": ["mail", "urn:oid:0.9.2342.19200300.100.1.3"], "givenName": ["givenName", "urn:oid:2.5.4.42"], "sn": ["sn", "urn:oid:2.5.4.4"]} saml_local_attributes_list = uid,emailaddress,givenname,SurName enforce_uniqueness_attr_list = uid saml_allow_basic_login = false saml_use_authn_context = false saml_validate_response = false saml_deployment_type = enroll #And I tried "map"

By Aliaksandr Samuseu staff 17 Nov 2016 at 2:49 p.m. CST

Aliaksandr Samuseu gravatar
Thanks, Won. >saml_local_attributes_list = uid,emailaddress,givenname,SurName This should be removed, new script doesn't use this property either. Though not sure it's the cause of your issue. >saml_deployment_type = enroll #And I tried "map" Correct, "map" requires that user already exists at instance where script runs. "enroll" is "all-in-one" atm. Other also seem correct to me. We probably will need to give it a quick QA run internally.

By Yuriy Movchan staff 17 Nov 2016 at 3:13 p.m. CST

Yuriy Movchan gravatar
Hi, I think the problem in namespaces in your Saml Response. This is [line ](https://github.com/GluuFederation/oxCore/blob/master/oxSaml/src/main/java/org/gluu/saml/Response.java#L162)which throw NPE: ``` NodeList nameChildNodes = node.getChildNodes(); ``` Hence XPath **/samlp:Response/saml:Assertion/saml:AttributeStatement/saml:Attribute** request can't find attributes in your sample response. Why in your response namespaces are "ns1", "ns2", "ds"? This [code](https://github.com/GluuFederation/oxCore/blob/master/oxSaml/src/main/java/org/gluu/saml/Response.java#L58) expects standard namespaces: ``` put("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); put("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); ```

By Won Kim user 17 Nov 2016 at 4:01 p.m. CST

Won Kim gravatar
yeah, that is what I suspected is the issue. This is why I hate XML :( and I only use JSON these days. But I think namespace can always be different and isn't always guaranteed to be a specific namespace from different vendors as long as they keep the tag names unique. I have seen this happen in many codes before when using xml. Not sure if you can change your code to be less strict about the namespace...maybe something to think about. But for our current situation we can put into the jython script to do a find and replace for the ns1, ns2 and ds namespaces with the proper ones prior to passing it into the following line of code. samlResponse.loadXmlFromBase64(saml_response)

By Won Kim user 18 Nov 2016 at 12:37 p.m. CST

Won Kim gravatar
If the NPE is occurring at line 162 NodeList nameChildNodes = node.getChildNodes(); That would imply the node object is null so then how did it get past line 154 Node nameNode = node.getAttributes().getNamedItem("Name"); I would still think it could of occurred on line 166 with multi method calls for an Object NamespaceURI that may not exist on that node. In an older version of your code that maps well with my stack trace where it is stating that line 157 is where the NPE is occurring. BTW I updated the saml_response string to have both the samlp and saml namespace prefix and I am still getting the NPE. Since you are calling the getNamespaceURI on each childNode do I need to put the namespace URI on each attribute tag? Again this is why I hate XML :( thanks won

By Michael Schwartz Account Admin 18 Nov 2016 at 12:47 p.m. CST

Michael Schwartz gravatar
Give us another day on this. We just setup a test enviornment to try to replicate your enviornment. We'll update the ticket by Monday.

By Mohib Zico staff 21 Nov 2016 at 2:44 a.m. CST

Mohib Zico gravatar
Hi Won, A [doc](https://github.com/GluuFederation/docs/blob/master/sources/how-to/saml_script_end_to_end.md) specially created on your case. Please feel free to test this.

By Won Kim user 21 Nov 2016 at 3:45 p.m. CST

Won Kim gravatar
The good news is that we figured out how to get it working just by modifying the script and circumventing the java Response object. The current challenge right now is that we need to have the SAML AuthnRequest to be digitally signed using the public key of the IDP. Does the script do that currently? i am trying to look through the code to find where that would happen but if you have any insight that would be great.

By Michael Schwartz Account Admin 21 Nov 2016 at 3:59 p.m. CST

Michael Schwartz gravatar
It's totally possible, but we'd have to asssign that to a developer. It's out of scope of community support.

By William Lowe user 21 Nov 2016 at 4:03 p.m. CST

William Lowe gravatar
I'm going to close this ticket out, as the thread has gotten pretty long and it seems the initial issue has been resolved. If you have additional questions that are within the scope of community support, please feel free to open a new ticket. Thanks, Won!

By Won Kim user 21 Nov 2016 at 4:07 p.m. CST

Won Kim gravatar
sounds good, thank you guys for the help. Not sure if you were able to find that NPE in the Response object, but otherwise this is working great.