How to automatically select a configured SAML identity provider in a multi-tenant environment to perform SSO with Spring SAML

I use Spring SAML in a multi-tenant application to provide SSO. Different tenants use different URLs to access the application, and each of them has a separate identity provider. How can I automatically assign the correct identity provider based on the URL used to access the application?

Example:

Tenant 1: http://tenant1.myapp.com

Tenant 2: http://tenant2.myapp.com

I saw that I can add the idp parameter to the url ( http://tenant1.myapp.com?idp=my.idp.entityid.com ) and SAMLContextProvider will select an identity provider with this object identifier. I developed a database-enabled MetadataProvider that takes the tenant host name as an initialization parameter to retrieve the metadata for this tenant from the database associated with this host name. Now I think that I need to somehow sort through the metadata providers to associate the metadata entityId with the host name. However, I do not see how I can get the entityId attribute from metadata. This will solve my problem.

+6
source share
2 answers

You can see how to analyze the available object identifiers from MetadataProvider in the MetadataManager#parseProvider . Please note that usually each provider can provide several IDP and SP definitions, not just one.

Alternatively, you can extend ExtendedMetadataDelegate with your own class, include any additional metadata (e.g. entityId) that you want, and then simply retype MetadataProvider in your custom class and get information from there when retransmitting data through MetadataManager .

If I were you, I would take a slightly different approach. I would extend SAMLContextProviderImpl , override the populatePeerEntityId method and do all the host / IDP mapping. See the original method for details.

+6
source

At the time of writing, Spring SAML is in version 1.0.1.FINAL. It does not support multi-user cleanliness out of the box. I found another way to achieve multi-tenancy, in addition to the proposals made by Vladimir at the top. It is very simple and straightforward and does not require the extension of any Spring SAML classes. Additionally, it uses Spring SAML's built-in alias handling in the CachingMetadataManager .

In the controller, grab the tenant name from the request and create an ExtendedMetadata object using the tenant name as an alias. Then create ExtendedMetadataDelegate from ExtendedMetadata and initialize it. Parse entity identifiers from it and check if they exist in the MetadataManager . If they do not exist, add vendor metadata and updates. Then enter the object identifier from MetadataManager using getEntityIdForAlias() .

Here is the code for the controller. There are comments explaining some reservations:

 @Controller public class SAMLController { @Autowired MetadataManager metadataManager; @Autowired ParserPool parserPool; @RequestMapping(value = "/login.do", method = RequestMethod.GET) public ModelAndView login(HttpServletRequest request, HttpServletResponse response, @RequestParam String tenantName) throws MetadataProviderException, ServletException, IOException{ //load metadata url using tenant name String tenantMetadataURL = loadTenantMetadataURL(tenantName); //Deprecated constructor, needs to change HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(tenantMetadataURL, 15000); httpMetadataProvider.setParserPool(parserPool); //Create extended metadata using tenant name as the alias ExtendedMetadata metadata = new ExtendedMetadata(); metadata.setLocal(true); metadata.setAlias(tenantName); //Create metadata provider and initialize it ExtendedMetadataDelegate metadataDelegate = new ExtendedMetadataDelegate(httpMetadataProvider, metadata); metadataDelegate.initialize(); //getEntityIdForAlias() in MetadataManager must only be called after the metadata provider //is added and the metadata is refreshed. Otherwise, the alias will be mapped to a null //value. The following code is a roundabout way to figure out whether the provider has already //been added or not. //The method parseProvider() has protected scope in MetadataManager so it was copied here Set<String> newEntityIds = parseProvider(metadataDelegate); Set<String> existingEntityIds = metadataManager.getIDPEntityNames(); //If one or more IDP entity ids do not exist in metadata manager, assume it a new provider. //If we always add a provider without this check, the initialize methods in refreshMetadata() //ignore the provider in case of a duplicate but the duplicate still gets added to the list //of providers because of the call to the superclass method addMetadataProvider(). Might be a bug. if(!existingEntityIds.containsAll(newEntityIds)) { metadataManager.addMetadataProvider(metadataDelegate); metadataManager.refreshMetadata(); } String entityId = metadataManager.getEntityIdForAlias(tenantName); return new ModelAndView("redirect:/saml/login?idp=" + URLEncoder.encode(entityId, "UTF-8")); } private Set<String> parseProvider(MetadataProvider provider) throws MetadataProviderException { Set<String> result = new HashSet<String>(); XMLObject object = provider.getMetadata(); if (object instanceof EntityDescriptor) { addDescriptor(result, (EntityDescriptor) object); } else if (object instanceof EntitiesDescriptor) { addDescriptors(result, (EntitiesDescriptor) object); } return result; } private void addDescriptors(Set<String> result, EntitiesDescriptor descriptors) throws MetadataProviderException { if (descriptors.getEntitiesDescriptors() != null) { for (EntitiesDescriptor descriptor : descriptors.getEntitiesDescriptors()) { addDescriptors(result, descriptor); } } if (descriptors.getEntityDescriptors() != null) { for (EntityDescriptor descriptor : descriptors.getEntityDescriptors()) { addDescriptor(result, descriptor); } } } private void addDescriptor(Set<String> result, EntityDescriptor descriptor) throws MetadataProviderException { String entityID = descriptor.getEntityID(); result.add(entityID); } } 

I believe that this directly solves the problem of OP in order to find out how to get an IDP for a given tenant. But this will only work for IDPs with a single object identifier.

+3
source

Source: https://habr.com/ru/post/981317/


All Articles