Al momento della scrittura, Spring SAML è alla versione 1.0.1.FINAL. Non supporta la multi-tenancy in modo pulito e immediato. Ho trovato un altro modo per raggiungere la multi-tenancy oltre ai suggerimenti forniti da Vladimir sopra. È molto semplice e diretto e non richiede l'estensione di alcuna classe Spring SAML. Inoltre, utilizza la gestione incorporata degli alias di Spring SAML in CachingMetadataManager
.
Nel controller, acquisire il nome del titolare dalla richiesta e creare un oggetto ExtendedMetadata
utilizzando il nome del titolare come alias. Quindi creare uno ExtendedMetadataDelegate
dal ExtendedMetadata
e inizializzarlo. Analizza gli ID entità fuori da esso e controlla se esistono in MetadataManager
. Se non esistono, aggiungere il provider e aggiornare i metadati. Quindi ottenere l'ID entità da MetadataManager
utilizzando getEntityIdForAlias()
.
Ecco il codice per il controller. Ci sono commenti in linea che spiegano alcuni avvertimenti:
@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's 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);
}
}
Credo che questo risolve direttamente il problema del PO di capire come ottenere l'IDP per un dato inquilino. Ma questo funzionerà solo per gli IDP con un singolo ID entità.
Ho creato il mio SAMLContextProvider e ho scavalcato populatePeerIdentityId. Ha funzionato alla grande Una volta completato, mi sono reso conto che SAMLContextProvider viene utilizzato solo durante l'avvio SSO avviato da SP. Per la maggior parte usiamo SSO avviato da IDP, quindi ho dovuto occuparmene anch'io.Ho finito per controllare il peerEntityID del messaggio in arrivo con l'ID entità IDP configurato per quel tenant nel mio SAMLAuthenticationProvider personalizzato. – MarcFasel
Questa funzionalità di associazione del provider di identità al provider di servizi è fondamentale per supportare la multi-tenancy. È programmato nelle prossime uscite? – MarcFasel
Vedremo, il progetto dipende dal mio tempo libero (non è sponsorizzato da nessuno) e non c'è molto di questo. Migliorare la multi-tenancy è qualcosa che mi piacerebbe fare. –