java - In an multi-tenant enviroment how can I provide different metadata for different Service Providers at runtime on different urls (subdomains)? -


working on sp initiated single sign on (sso) both sp , idp self-hosted have flexibility of editing both. using spring-security-saml2-core-1.0.1.release hosting spring application (spring-security-3.2.8, spring-mvc-3.2.14.release), serves multiple tenants @ urls say: sp1.example.org,
sp2.example.org
idp hosted using shibboleth idpv3.2.1 working fine multiple applications hosted on different sp servers.

i trying send different metadata same server sp1 & sp2. read multi-tenant sp here , here custom logic overriding samlcontextproviderimpl populatepeerentityid, trying override populatelocalentityid because can't use alias.

can give example code overriding populatelocalentityid handling multi-tenants metadata ?

sp configuration shown below:

<!-- filters processing of saml messages --> <beans:bean id="samlfilter" class="org.springframework.security.web.filterchainproxy">     <filter-chain-map request-matcher="ant">         <filter-chain pattern="/saml/login/**" filters="samlentrypoint" />         <filter-chain pattern="/saml/logout/**" filters="samllogoutfilter" />         <filter-chain pattern="/saml/metadata/**" filters="metadatadisplayfilter" />         <filter-chain pattern="/saml/sso/**" filters="samlwebssoprocessingfilter" />         <filter-chain pattern="/saml/ssohok/**" filters="samlwebssohokprocessingfilter" />         <filter-chain pattern="/saml/singlelogout/**" filters="samllogoutprocessingfilter" />         <filter-chain pattern="/saml/discovery/**" filters="samlidpdiscovery" />     </filter-chain-map> </beans:bean>  <!-- handler deciding redirect user after successful login --> <beans:bean id="successredirecthandler" class="com.example.web.sso.customauthenticationsuccesshandler" ></beans:bean> <!-- <beans:bean id="successredirecthandler"          class="org.springframework.security.web.authentication.savedrequestawareauthenticationsuccesshandler">    <property name="defaulttargeturl" value="/web-inf/security/idpselection.jsp"/> </beans:bean>  -->    <!-- use following interpreting relaystate coming unsolicited      response redirect url:      <beans:bean id="successredirecthandler" class="org.springframework.security.saml.samlrelaystatesuccesshandler">      <property name="defaulttargeturl" value="/" /> </beans:bean> -->  <!-- handler deciding redirect user after failed login --> <beans:bean id="failureredirecthandler" class="com.example.web.sso.customauthenticationfailurehandler"></beans:bean>  <!-- <beans:bean id="failureredirecthandler"     class="org.springframework.security.web.authentication.simpleurlauthenticationfailurehandler">     <property name="useforward" value="true" />     <property name="defaultfailureurl" value="/error.jsp" /> </beans:bean>  -->  <!-- handler successful logout --> <beans:bean id="successlogouthandler"     class="com.example.web.sso.customlogoutsuccesshandler" ></beans:bean> <!-- <beans:bean id="successlogouthandler"     class="org.springframework.security.web.authentication.logout.simpleurllogoutsuccesshandler">     <property name="defaulttargeturl" value="/logout.jsp" /> </beans:bean> -->  <authentication-manager alias="samlauthenticationmanager">     <!-- register authentication manager saml provider -->     <authentication-provider ref="samlauthenticationprovider" />     <!-- register authentication manager administration ui -->     <authentication-provider>         <user-service id="admininterfaceservice">             <user name="admin" password="admin" authorities="role_admin" />         </user-service>     </authentication-provider> </authentication-manager>  <!-- logger saml messages , events --> <beans:bean id="samllogger" class="org.springframework.security.saml.log.samldefaultlogger" >     <beans:property name="logmessages" value="true" />     <beans:property name="logerrors" value="true" /> </beans:bean>  <!-- central storage of cryptographic keys --> <beans:bean id="keymanager" class="org.springframework.security.saml.key.jkskeymanager">      <beans:constructor-arg value="/web-inf/keys/samlkeystore.jks"></beans:constructor-arg>     <beans:constructor-arg type="java.lang.string" value="nalle123" />     <beans:constructor-arg>         <beans:map>             <beans:entry key="apollo" value="nalle123" />         </beans:map>     </beans:constructor-arg>     <beans:constructor-arg type="java.lang.string" value="apollo" /> </beans:bean>  <!-- entry point initialize authentication, default values taken      properties file --> <beans:bean id="samlentrypoint" class="com.example.web.sso.customsamlentrypoint">     <beans:property name="defaultprofileoptions">         <beans:bean class="org.springframework.security.saml.websso.webssoprofileoptions">             <beans:property name="binding" value="urn:oasis:names:tc:saml:2.0:bindings:http-post"/>             <beans:property name="nameid" value="urn:oasis:names:tc:saml:1.1:nameid-format:emailaddress" />             <beans:property name="includescoping" value="false" />             <beans:property name="forceauthn" value="false" />         </beans:bean>     </beans:property> </beans:bean>  <!-- idp discovery service --> <beans:bean id="samlidpdiscovery" class="org.springframework.security.saml.samldiscovery">     <beans:property name="idpselectionpath" value="/web-inf/security/idpselection.jsp" /> </beans:bean>  <!-- filter automatically generates default sp metadata -->  <beans:bean id="metadatageneratorfilter"     class="org.springframework.security.saml.metadata.metadatageneratorfilter">     <beans:constructor-arg>         <beans:bean class="org.springframework.security.saml.metadata.metadatagenerator">             <beans:property name="entityid" value="com:example:namespaceid:saml:poc" />             <!-- <beans:property name="entitybaseurl" value="https://sp1.example.com:8080/" /> -->             <beans:property name="requestsigned" value="true" />             <beans:property name="wantassertionsigned" value="true" />             <beans:property name="extendedmetadata">                 <beans:bean class="org.springframework.security.saml.metadata.extendedmetadata">                     <beans:property name="idpdiscoveryenabled" value="true" />                 </beans:bean>             </beans:property>         </beans:bean>     </beans:constructor-arg> </beans:bean>  <beans:bean id="metadatagenerator" class="org.springframework.security.saml.metadata.metadatagenerator">             <beans:property name="entityid" value="com:example:namespaceid:saml:poc" />             <beans:property name="entitybaseurl" value="https://sp1.example.com:8080/" />             <beans:property name="requestsigned" value="true" />             <beans:property name="wantassertionsigned" value="true" />             <beans:property name="extendedmetadata">                 <beans:bean class="org.springframework.security.saml.metadata.extendedmetadata">                     <beans:property name="idpdiscoveryenabled" value="true" />                 </beans:bean>             </beans:property>         </beans:bean>  <!-- filter waiting connections on url suffixed filtersuffix      , presents sp metadata there --> <beans:bean id="metadatadisplayfilter"     class="org.springframework.security.saml.metadata.metadatadisplayfilter" />  <!-- configure http client accept certificates keystore      https verification --> <!-- <beans:bean class="org.springframework.security.saml.trust.httpclient.tlsprotocolconfigurer">      <beans:property name="sslhostnameverification" value="default"/> </beans:bean> -->  <!-- idp metadata configuration - paths metadata of idps in circle of      trust here --> <beans:bean id="metadata"     class="org.springframework.security.saml.metadata.cachingmetadatamanager">     <beans:constructor-arg>         <beans:list>             <!-- example of classpath metadata extended metadata -->             <beans:bean                 class="org.springframework.security.saml.metadata.extendedmetadatadelegate">                 <beans:constructor-arg>                     <beans:bean                         class="org.opensaml.saml2.metadata.provider.resourcebackedmetadataprovider">                         <beans:constructor-arg>                             <beans:bean class="java.util.timer" />                         </beans:constructor-arg>                         <beans:constructor-arg>                             <beans:bean class="org.opensaml.util.resource.classpathresource">                             <!-- <beans:bean class="org.opensaml.util.resource.filesystemresource"> -->                                     <beans:constructor-arg value = "/web-inf/metadata/sp1-mymetadata.xml"></beans:constructor-arg>                             </beans:bean>                         </beans:constructor-arg>                         <beans:property name="parserpool" ref="parserpool" />                     </beans:bean>                 </beans:constructor-arg>                 <beans:constructor-arg>                      <beans:bean                         class="org.springframework.security.saml.metadata.extendedmetadata">                         <beans:property name="local" value="true" />                         <beans:property name="securityprofile" value="metaiop" />                         <beans:property name="sslsecurityprofile" value="pkix" />                         <beans:property name="sslhostnameverification" value="default" />                         <!-- <beans:property name="sslhostnameverification" value="allowall" /> -->                         <beans:property name="signmetadata" value="false" />                         <beans:property name="signingkey" value="apollo" />                         <beans:property name="encryptionkey" value="apollo" />                         <beans:property name="requireartifactresolvesigned" value="false" />                         <beans:property name="requirelogoutrequestsigned" value="false" />                         <beans:property name="requirelogoutresponsesigned" value="false" />                         <beans:property name="idpdiscoveryenabled" value="false" />                         <beans:property name="idpdiscoveryurl" value="https://sp1.example.com/saml/discovery" />                         <beans:property name="idpdiscoveryresponseurl" value="https://sp1.example.com/saml/login?disco=true" />                     </beans:bean>                 </beans:constructor-arg>              </beans:bean>             <!-- example of http metadata without extended metadata -->             <!-- <beans:bean class="org.opensaml.saml2.metadata.provider.httpmetadataprovider">                 url containing metadata                 <beans:constructor-arg>                     <beans:value type="java.lang.string">https://idp.ssocircle.com/idp-meta.xml</beans:value>                     <beans:value type="java.lang.string">https://sp1.example.com/idp-meta.xml</beans:value>                 </beans:constructor-arg>                 timeout metadata loading in ms                 <beans:constructor-arg>                     <beans:value type="int">15000</beans:value>                 </beans:constructor-arg>                 <beans:property name="parserpool" ref="parserpool" />             </beans:bean> -->              <beans:bean class="org.springframework.security.saml.metadata.extendedmetadatadelegate">                 <beans:constructor-arg>                     <beans:bean class="org.opensaml.saml2.metadata.provider.filesystemmetadataprovider">                         <beans:constructor-arg>                             <beans:value type="java.io.file">/shared/saml/idp-metadata-exampleidp.xml</beans:value>                         </beans:constructor-arg>                         <beans:property name="parserpool" ref="parserpool"/>                     </beans:bean>                 </beans:constructor-arg>                 <beans:constructor-arg>                     <beans:bean class="org.springframework.security.saml.metadata.extendedmetadata"/>                 </beans:constructor-arg>             </beans:bean>              <!-- example of file system metadata without extended metadata -->             <!-- <bean class="org.opensaml.saml2.metadata.provider.filesystemmetadataprovider">                  <constructor-arg> <value type="java.io.file">/usr/local/metadata/idp.xml</value>                  </constructor-arg> <property name="parserpool" ref="parserpool"/> </bean> -->         </beans:list>     </beans:constructor-arg>     <!-- optional used when 1 of metadata files contains information          service provider -->     <!-- <property name="hostedspname" value=""/> -->     <!-- optional property: can tell system idp should used          authenticating user default. -->     <!-- <property name="defaultidp" value="http://localhost:8080/opensso"/> -->     <beans:property name="defaultidp" value="https://login.example.com/idp/shibboleth"/> </beans:bean>  <!-- saml authentication provider responsible validating of received      saml messages --> <beans:bean id="samlauthenticationprovider"     class="org.springframework.security.saml.samlauthenticationprovider">     <!-- optional property: can used store/load user data after login -->     <beans:property name="userdetails" ref="samluserdetailsserviceimpl" />     <beans:property name="forceprincipalasstring" value="false" /> </beans:bean>  <beans:bean id="samluserdetailsserviceimpl"     class="com.example.service.impl.samluserdetailsserviceimpl"></beans:bean>  <!-- provider of default saml context --> <!-- <beans:bean id="contextprovider"     class="org.springframework.security.saml.context.samlcontextproviderimpl"> --> <beans:bean id="contextprovider"     class="com.example.service.impl.customsamlcontextproviderimpl">      <beans:property name="storagefactory">         <!-- <beans:bean class="org.springframework.security.saml.storage.emptystoragefactory" /> -->         <beans:bean class="org.springframework.security.saml.storage.httpsessionstoragefactory" />     </beans:property> </beans:bean>  <!-- <beans:bean id="contextprovider"     class="org.springframework.security.saml.context.samlcontextproviderlb">     <beans:property name="scheme" value="https" />     <beans:property name="servername" value="https://sp1.example.com" />     <beans:property name="serverport" value="443" />     <beans:property name="includeserverportinrequesturl" value="false" /> </beans:bean>  -->  <!-- processing filter websso profile messages --> <beans:bean id="samlwebssoprocessingfilter" class="org.springframework.security.saml.samlprocessingfilter">     <beans:property name="authenticationmanager" ref="samlauthenticationmanager" />     <beans:property name="authenticationsuccesshandler" ref="successredirecthandler" />     <beans:property name="authenticationfailurehandler" ref="failureredirecthandler" />     <beans:property name="sessionauthenticationstrategy" ref="sas"/> </beans:bean>  <!-- processing filter websso holder-of-key profile --> <beans:bean id="samlwebssohokprocessingfilter"     class="org.springframework.security.saml.samlwebssohokprocessingfilter">     <beans:property name="authenticationmanager" ref="samlauthenticationmanager" />     <beans:property name="authenticationsuccesshandler" ref="successredirecthandler" />     <beans:property name="authenticationfailurehandler" ref="failureredirecthandler" /> </beans:bean>  <!-- logout handler terminating local session --> <beans:bean id="logouthandler"     class="org.springframework.security.web.authentication.logout.securitycontextlogouthandler">     <beans:property name="invalidatehttpsession" value="true" /> </beans:bean>  <!-- override default logout processing filter 1 processing saml      messages --> <beans:bean id="samllogoutfilter" class="org.springframework.security.saml.samllogoutfilter">     <beans:constructor-arg index="0" ref="successlogouthandler" />     <beans:constructor-arg index="1" ref="logouthandler" />     <beans:constructor-arg index="2" ref="logouthandler" /> </beans:bean>  <!-- filter processing incoming logout messages --> <!-- first argument determines url user redirected after successful      global logout --> <beans:bean id="samllogoutprocessingfilter"     class="org.springframework.security.saml.samllogoutprocessingfilter">     <beans:constructor-arg index="0" ref="successlogouthandler" />     <beans:constructor-arg index="1" ref="logouthandler" /> </beans:bean>  <!-- class loading incoming saml messages httprequest stream --> <beans:bean id="processor"     class="org.springframework.security.saml.processor.samlprocessorimpl">     <beans:constructor-arg>         <beans:list>             <beans:ref bean="postbinding" />             <beans:ref bean="redirectbinding" />             <beans:ref bean="artifactbinding" />             <beans:ref bean="soapbinding" />             <beans:ref bean="paosbinding" />         </beans:list>     </beans:constructor-arg> </beans:bean>  <!-- saml 2.0 websso assertion consumer --> <beans:bean id="webssoprofileconsumer"     class="org.springframework.security.saml.websso.webssoprofileconsumerimpl" >     <!-- maximum lifetime of assertion issued idp default 3000-->     <!-- <beans:property name="maxassertiontime" value="300"></beans:property>  -->     <!-- maximum lifetime of authentication issued default 7200-->     <!-- <beans:property name="maxassertiontime" value="300"></beans:property>  --> </beans:bean>  <!-- saml 2.0 holder-of-key websso assertion consumer --> <beans:bean id="hokwebssoprofileconsumer"     class="org.springframework.security.saml.websso.webssoprofileconsumerhokimpl" />  <!-- saml 2.0 web sso profile --> <beans:bean id="webssoprofile"     class="org.springframework.security.saml.websso.webssoprofileimpl" />  <!-- saml 2.0 holder-of-key web sso profile --> <beans:bean id="hokwebssoprofile"     class="org.springframework.security.saml.websso.webssoprofileconsumerhokimpl" />  <!-- saml 2.0 ecp profile --> <beans:bean id="ecpprofile"     class="org.springframework.security.saml.websso.webssoprofileecpimpl" />  <!-- saml 2.0 logout profile --> <beans:bean id="logoutprofile"     class="org.springframework.security.saml.websso.singlelogoutprofileimpl" />  <!-- bindings, encoders , decoders used creating , parsing messages --> <beans:bean id="postbinding"     class="org.springframework.security.saml.processor.httppostbinding">     <beans:constructor-arg ref="parserpool" />     <beans:constructor-arg ref="velocityengine" /> </beans:bean>  <beans:bean id="redirectbinding"     class="org.springframework.security.saml.processor.httpredirectdeflatebinding">     <beans:constructor-arg ref="parserpool" /> </beans:bean>  <beans:bean id="artifactbinding"     class="org.springframework.security.saml.processor.httpartifactbinding">     <beans:constructor-arg ref="parserpool" />     <beans:constructor-arg ref="velocityengine" />     <beans:constructor-arg>         <beans:bean             class="org.springframework.security.saml.websso.artifactresolutionprofileimpl">             <beans:constructor-arg>                 <beans:bean class="org.apache.commons.httpclient.httpclient">                     <beans:constructor-arg>                         <beans:bean                             class="org.apache.commons.httpclient.multithreadedhttpconnectionmanager" />                     </beans:constructor-arg>                 </beans:bean>             </beans:constructor-arg>             <beans:property name="processor">                 <beans:bean                     class="org.springframework.security.saml.processor.samlprocessorimpl">                     <beans:constructor-arg ref="soapbinding" />                 </beans:bean>             </beans:property>         </beans:bean>     </beans:constructor-arg> </beans:bean>  <beans:bean id="soapbinding"     class="org.springframework.security.saml.processor.httpsoap11binding">     <beans:constructor-arg ref="parserpool" /> </beans:bean>  <beans:bean id="paosbinding"     class="org.springframework.security.saml.processor.httppaos11binding">     <beans:constructor-arg ref="parserpool" /> </beans:bean>  <!-- initialization of opensaml library --> <beans:bean class="org.springframework.security.saml.samlbootstrap" />  <!-- initialization of velocity engine --> <beans:bean id="velocityengine" class="org.springframework.security.saml.util.velocityfactory"     factory-method="getengine" />  <!-- xml parser pool needed opensaml parsing --> <beans:bean id="parserpool" class="org.opensaml.xml.parse.staticbasicparserpool"     init-method="initialize">     <beans:property name="builderfeatures">         <beans:map>             <beans:entry key="http://apache.org/xml/features/dom/defer-node-expansion"                 value="false" />         </beans:map>     </beans:property> </beans:bean>  <beans:bean id="parserpoolholder"     class="org.springframework.security.saml.parser.parserpoolholder" ></beans:bean> 

update 1: better solution extend samlcontextproviderimpl , override populatelocalentityid, getlocalentity, getlocalandpeerentity set proper samlmessagecontext new samlmessagecontext created each request.

@override public samlmessagecontext getlocalandpeerentity(httpservletrequest request, httpservletresponse response) throws metadataproviderexception {          samlmessagecontext context = new samlmessagecontext();         populategenericcontext(request, response, context);         //changed send url instead of uri         populatelocalentityid(context, request.getrequesturl().tostring());         populatelocalcontext(context);         populatepeerentityid(context);         populatepeercontext(context);         return context;  }  @override public samlmessagecontext getlocalentity(httpservletrequest request, httpservletresponse response) throws metadataproviderexception {          samlmessagecontext context = new samlmessagecontext();         populategenericcontext(request, response, context);         populatelocalentityid(context, request.getrequesturl().tostring());         populatelocalcontext(context);         return context;  }      @override     protected void populatelocalentityid(samlmessagecontext context, string requesturl) throws metadataproviderexception {              string entityid;             httpintransport intransport = (httpintransport) context.getinboundmessagetransport();              // pre-configured entity id             entityid = (string) intransport.getattribute(org.springframework.security.saml.samlconstants.local_entity_id);             if (entityid != null) {                     // same code super class              } else { // defaults                    //now setting proper entityid required                //in case https://sp1.wooqer.com/sp                if(org.apache.commons.lang.stringutils.ordinalindexof(requesturl, "/", 3) != -1) {                         context.setlocalentityid(requesturl.substring(0, org.apache.commons.lang.stringutils.ordinalindexof(requesturl, "/", 3)).concat("/sp"));                 } else {                         context.setlocalentityid(requesturl.concat("/sp"));                 }                 context.setlocalentityrole(spssodescriptor.default_element_name);             }     } 

setting hostedspname in metadatageneratorfilter not solution @autowired metadatamanager has set under synchronized block ensure multiple requests don't overwrite values. metadatamanager still can used outside filter cannot sure of state.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this can done creating federation of sps (subdomains running under 1 application in case) , selecting appropriate entityid & metadata @ runtime.

i did following steps handle issue. first create metadata federation , add subdomains (sps) running on 1 application in 1 federation:

    <?xml version="1.0" encoding="utf-8"?> <entitiesdescriptor xmlns="urn:oasis:names:tc:saml:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" name="https://example-federation.org/metadata/example-federation-name.xml">    <md:entitydescriptor xmlns:md="urn:oasis:names:tc:saml:2.0:metadata" xmlns:mdui="urn:oasis:names:tc:saml:metadata:ui" id="org_example_shagunakarsh_saml_poc_sp1" entityid="org:example:shagunakarsh:saml:poc:sp1">       <!--other params-->       ......    </md:entitydescriptor>    <md:entitydescriptor xmlns:md="urn:oasis:names:tc:saml:2.0:metadata" id="org_example_shagunakarsh_saml_poc_sp2" entityid="org:example:shagunakarsh:saml:poc:sp2">       <!--other params-->       ......    </md:entitydescriptor> </entitiesdescriptor> 

now need specify metadata file in metadata bean in security-applicationcontext.xml:

<beans:bean id="metadata" class="org.springframework.security.saml.metadata.cachingmetadatamanager">     ....      <beans:constructor-arg value = "/path/to/metadata/federation-mymetadata.xml"></beans:constructor-arg> 

(read update 1, overriding filter purpose not recommended) need select appropriate metadata @ runtime using accessed url, can achieved extending metadatageneratorfilter , overriding processmetadatainitialization function:

    @override     protected void processmetadatainitialization(httpservletrequest request) throws servletexception {                      // in case hosted sp metadata weren't initialized, let's                     if (manager.gethostedspname() == null) {                          synchronized (metadatamanager.class) {                               //same code base class                         }                      } else {                             // if known sp found federation metadata                             string requesturl = request.getrequesturl().tostring();                             string subdomain = requesturl.substring(requesturl.indexof("//") + 2, requesturl.indexof("."));                             //set proper sp entityid                             manager.sethostedspname("org:example:shagunakarsh:saml:poc:" + subdomain);                     }             } 

then update security-applicationcontext.xml custommetadatageneratorfilter:

<beans:bean id="metadatageneratorfilter" class="org.springframework.security.saml.metadata.custommetadatageneratorfilter"> 

also don't forget update metadata in idp (this case shibboleth idpv3). redeploy both idp , sp , should work.


Comments

Popular posts from this blog

Hatching array of circles in AutoCAD using c# -

ios - UITEXTFIELD InputView Uipicker not working in swift -

Python Pig Latin Translator -