/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.picketlink.idm.internal; import org.picketlink.idm.config.AbstractIdentityStoreConfiguration; import org.picketlink.idm.config.FileIdentityStoreConfiguration; import org.picketlink.idm.config.IdentityConfiguration; import org.picketlink.idm.config.IdentityStoreConfiguration; import org.picketlink.idm.config.IdentityStoreConfiguration.IdentityOperation; import org.picketlink.idm.config.JDBCIdentityStoreConfiguration; import org.picketlink.idm.config.JPAIdentityStoreConfiguration; import org.picketlink.idm.config.LDAPIdentityStoreConfiguration; import org.picketlink.idm.config.TokenStoreConfiguration; import org.picketlink.idm.credential.handler.CredentialHandler; import org.picketlink.idm.credential.handler.annotations.SupportsCredentials; import org.picketlink.idm.credential.storage.CredentialStorage; import org.picketlink.idm.file.internal.FileIdentityStore; import org.picketlink.idm.internal.util.RelationshipMetadata; import org.picketlink.idm.jdbc.internal.JDBCIdentityStore; import org.picketlink.idm.jpa.internal.JPAIdentityStore; import org.picketlink.idm.ldap.internal.LDAPIdentityStore; import org.picketlink.idm.model.AttributedType; import org.picketlink.idm.model.IdentityType; import org.picketlink.idm.model.Partition; import org.picketlink.idm.model.Relationship; import org.picketlink.idm.model.annotation.IdentityPartition; import org.picketlink.idm.permission.acl.spi.PermissionStore; import org.picketlink.idm.spi.AttributeStore; import org.picketlink.idm.spi.CredentialStore; import org.picketlink.idm.spi.IdentityContext; import org.picketlink.idm.spi.IdentityStore; import org.picketlink.idm.spi.PartitionStore; import org.picketlink.idm.spi.StoreSelector; import org.picketlink.idm.token.internal.TokenIdentityStore; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static org.picketlink.idm.IDMInternalMessages.MESSAGES; import static org.picketlink.idm.config.IdentityStoreConfiguration.IdentityOperation.create; import static org.picketlink.idm.config.IdentityStoreConfiguration.IdentityOperation.read; import static org.picketlink.idm.util.IDMUtil.isTypeSupported; import static org.picketlink.idm.util.IDMUtil.toSet; /** * @author pedroigor */ public class DefaultStoreSelector implements StoreSelector { private final PartitionManagerConfiguration configuration; /** * Cache for relationship metadata */ private RelationshipMetadata relationshipMetadata = new RelationshipMetadata(); /** * Each partition is governed by a specific IdentityConfiguration, indicated by this Map. Every * IdentityConfiguration instance will also be found in the configurations property above. */ private final Map<Partition, IdentityConfiguration> partitionConfigurations = new ConcurrentHashMap<Partition, IdentityConfiguration>(); /** * The store instances for each IdentityConfiguration, mapped by their corresponding IdentityStoreConfiguration */ private final Map<IdentityConfiguration, Map<IdentityStoreConfiguration, IdentityStore<?>>> stores; private final Map<String, Map<Class<? extends IdentityType>, Set<IdentityStoreConfiguration>>> identityQueryStoresCache = new ConcurrentHashMap<String, Map<Class<? extends IdentityType>, Set<IdentityStoreConfiguration>>>(); private final Map<String, Map<Class<?>, IdentityStoreConfiguration>> credentialStoresCache = new ConcurrentHashMap<String, Map<Class<?>, IdentityStoreConfiguration>>(); public DefaultStoreSelector(PartitionManagerConfiguration configuration) { this.configuration = configuration; Map<IdentityConfiguration, Map<IdentityStoreConfiguration, IdentityStore<?>>> configuredStores = new HashMap<IdentityConfiguration, Map<IdentityStoreConfiguration, IdentityStore<?>>>(); for (IdentityConfiguration config : this.configuration.getConfigurations()) { Map<IdentityStoreConfiguration, IdentityStore<?>> storeMap = new HashMap<IdentityStoreConfiguration, IdentityStore<?>>(); for (IdentityStoreConfiguration storeConfig : config.getStoreConfiguration()) { storeMap.put(storeConfig, createIdentityStore(storeConfig)); } configuredStores.put(config, Collections.unmodifiableMap(storeMap)); } this.stores = Collections.unmodifiableMap(configuredStores); } @Override public <T extends IdentityStore<?>> T getStoreForIdentityOperation(IdentityContext context, Class<T> storeType, Class<? extends AttributedType> type, IdentityOperation operation) { checkSupportedTypes(context.getPartition(), type); IdentityConfiguration identityConfiguration = getConfigurationForPartition(context, context.getPartition()); T identityStore = lookupStore(context, identityConfiguration, type, operation); if (identityStore == null) { throw MESSAGES.attributedTypeUnsupportedOperation(type, operation, type, operation); } return identityStore; } @Override public Set<IdentityStore<?>> getStoresForIdentityQuery(final IdentityContext context, final Class<? extends IdentityType> identityType) { IdentityConfiguration identityConfiguration = getConfigurationForPartition(context, context.getPartition()); Map<Class<? extends IdentityType>, Set<IdentityStoreConfiguration>> cachedStoresForType = this.identityQueryStoresCache.get(context.getPartition().getName()); if (cachedStoresForType != null) { Set<IdentityStoreConfiguration> storeConfigs = cachedStoresForType.get(identityType); if (storeConfigs != null) { Set<IdentityStore<?>> identityStores = new HashSet<IdentityStore<?>>(); for (IdentityStoreConfiguration storeConfig : storeConfigs) { identityStores.add(getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig)); } return identityStores; } } Set<IdentityStore<?>> identityStores = new HashSet<IdentityStore<?>>(); Set<IdentityStoreConfiguration> identityStoresConfig = new HashSet<IdentityStoreConfiguration>(); cachedStoresForType = new HashMap<Class<? extends IdentityType>, Set<IdentityStoreConfiguration>>(); cachedStoresForType.put(identityType, identityStoresConfig); for (IdentityStoreConfiguration storeConfig : identityConfiguration.getStoreConfiguration()) { if (storeConfig.supportsType(identityType, read)) { identityStores.add(getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig)); identityStoresConfig.add(storeConfig); } } if (identityStores.isEmpty()) { throw MESSAGES.attributedTypeUnsupportedOperation(identityType, read, identityType, read); } this.identityQueryStoresCache.put(context.getPartition().getName(), cachedStoresForType); return identityStores; } @Override public <T extends CredentialStore<?>> T getStoreForCredentialOperation(IdentityContext context, Class<?> credentialClass) { T store = null; IdentityConfiguration identityConfiguration = getConfigurationForPartition(context, context.getPartition()); if (identityConfiguration == null) { for (IdentityConfiguration configuration : this.configuration.getConfigurations()) { for (IdentityStoreConfiguration storeConfig : configuration.getStoreConfiguration()) { if (storeConfig.supportsCredential()) { identityConfiguration = configuration; } } } } if (identityConfiguration != null) { Map<Class<?>, IdentityStoreConfiguration> cachedStoresForType = this.credentialStoresCache.get(context.getPartition().getName()); if (cachedStoresForType != null) { IdentityStoreConfiguration storeConfig = cachedStoresForType.get(credentialClass); if (storeConfig != null) { return getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig); } } if (identityConfiguration.supportsCredential()) { for (IdentityStoreConfiguration storeConfig : identityConfiguration.getStoreConfiguration()) { if (storeConfig.supportsCredential()) { for (@SuppressWarnings("rawtypes") Class<? extends CredentialHandler> handlerClass : storeConfig.getCredentialHandlers()) { if (handlerClass.isAnnotationPresent(SupportsCredentials.class)) { for (Class<?> cls : handlerClass.getAnnotation(SupportsCredentials.class).credentialClass()) { if (cls.isAssignableFrom(credentialClass)) { IdentityStore<?> identityStore = getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig); try { store = (T) identityStore; } catch (ClassCastException cce) { throw MESSAGES.storeUnexpectedType(CredentialStore.class, identityStore.getClass()); } // if we found a specific handler for the credential, immediately return. if (cls.equals(credentialClass)) { return store; } } } } } } } } cachedStoresForType = new HashMap<Class<?>, IdentityStoreConfiguration>(); cachedStoresForType.put(credentialClass, store.getConfig()); this.credentialStoresCache.put(context.getPartition().getName(), cachedStoresForType); } if (store == null) { throw MESSAGES.credentialNoStoreForCredentials(credentialClass); } return store; } @Override public IdentityStore<?> getStoreForRelationshipOperation(IdentityContext context, Class<? extends Relationship> relationshipClass, Relationship relationship, IdentityOperation operation) { Set<Partition> partitions = getRelationshipPartitions(relationship); IdentityStore<?> store = null; // Check if the partition can manage its own relationship if (partitions.size() == 1) { IdentityConfiguration config = getConfigurationForPartition(context, partitions.iterator().next()); if (config.getRelationshipPolicy().isSelfRelationshipSupported(relationshipClass)) { for (IdentityStoreConfiguration storeConfig : config.getStoreConfiguration()) { if (storeConfig.supportsType(relationshipClass, operation)) { store = getIdentityStoreAndInitializeContext(context, config, storeConfig); } } } } else { // This is a multi-partition relationship - use the configuration that supports the global relationship type for (Partition partition : partitions) { IdentityConfiguration config = getConfigurationForPartition(context, partition); if (config.getRelationshipPolicy().isGlobalRelationshipSupported(relationshipClass)) { for (IdentityStoreConfiguration storeConfig : config.getStoreConfiguration()) { if (storeConfig.supportsType(relationshipClass, operation)) { store = getIdentityStoreAndInitializeContext(context, config, storeConfig); } } } } } // If none of the participating partition configurations support the relationship, try to find another configuration // that supports the global relationship as a last ditch effort if (store == null) { for (IdentityConfiguration cfg : this.configuration.getConfigurations()) { if (cfg.getRelationshipPolicy().isGlobalRelationshipSupported(relationshipClass)) { // found one for (IdentityStoreConfiguration storeConfig : cfg.getStoreConfiguration()) { if (storeConfig.supportsType(relationshipClass, operation)) { store = getIdentityStoreAndInitializeContext(context, cfg, storeConfig); } } } } } if (store == null) { throw MESSAGES.attributedTypeUnsupportedOperation(relationshipClass, operation, relationshipClass, operation); } return store; } @Override public Set<IdentityStore<?>> getStoresForRelationshipQuery(IdentityContext context, Class<? extends Relationship> relationshipClass, Set<Partition> partitions) { Set<IdentityStore<?>> identityStores = new HashSet<IdentityStore<?>>(); // If _no_ parameters have been specified for the query at all, we return all stores that support the // specified relationship class if (partitions.isEmpty()) { for (IdentityConfiguration config : this.configuration.getConfigurations()) { if (config.getRelationshipPolicy().isGlobalRelationshipSupported(relationshipClass) || config.getRelationshipPolicy().isSelfRelationshipSupported(relationshipClass)) { for (IdentityStoreConfiguration storeConfig : config.getStoreConfiguration()) { if (storeConfig.supportsType(relationshipClass, create) || Relationship.class.equals(relationshipClass)) { identityStores.add(getIdentityStoreAndInitializeContext(context, config, storeConfig)); } } } } } else { for (Partition partition : partitions) { IdentityConfiguration config = getConfigurationForPartition(context, partition); if (config.getRelationshipPolicy().isGlobalRelationshipSupported(relationshipClass)) { for (IdentityStoreConfiguration storeConfig : config.getStoreConfiguration()) { if (storeConfig.supportsType(relationshipClass, create) || Relationship.class.equals(relationshipClass)) { identityStores.add(getIdentityStoreAndInitializeContext(context, config, storeConfig)); } } } } } if (identityStores.isEmpty()) { throw MESSAGES.attributedTypeUnsupportedOperation(relationshipClass, read, relationshipClass, read); } return identityStores; } @Override public <T extends PartitionStore<?>> T getStoreForPartitionOperation(IdentityContext context, Class<? extends Partition> partitionClass) { IdentityConfiguration partitionManagementConfig = this.configuration.getPartitionManagementConfig(); Map<IdentityStoreConfiguration, IdentityStore<?>> configStores = stores.get(partitionManagementConfig); for (IdentityStoreConfiguration cfg : configStores.keySet()) { if (cfg.supportsType(partitionClass, create)) { T store = getIdentityStoreAndInitializeContext(context, partitionManagementConfig, cfg); if (!PartitionStore.class.isInstance(store)) { throw MESSAGES.storeUnexpectedType(store.getClass(), PartitionStore.class); } return store; } } throw MESSAGES.storeNotFound(PartitionStore.class, partitionClass); } @Override public <T extends AttributeStore<?>> T getStoreForAttributeOperation(IdentityContext context) { IdentityConfiguration attributeManagementConfig = this.configuration.getAttributeManagementConfig(); if (attributeManagementConfig != null) { Map<IdentityStoreConfiguration, IdentityStore<?>> configStores = stores.get(attributeManagementConfig); for (IdentityStoreConfiguration cfg : configStores.keySet()) { if (cfg.supportsAttribute()) { T store = getIdentityStoreAndInitializeContext(context, attributeManagementConfig, cfg); if (!AttributeStore.class.isInstance(store)) { throw MESSAGES.storeUnexpectedType(store.getClass(), AttributeStore.class); } return store; } } } return null; } @Override public Set<CredentialStore<?>> getStoresForCredentialStorage(final IdentityContext context, Class<? extends CredentialStorage> storageClass) { IdentityConfiguration identityConfiguration = getConfigurationForPartition(context, context.getPartition()); Map<IdentityStoreConfiguration, IdentityStore<?>> storesConfig = this.stores.get(identityConfiguration); Set<CredentialStore<?>> credentialStores = new HashSet<CredentialStore<?>>(); if (storesConfig != null) { for (IdentityStoreConfiguration storeConfig : storesConfig.keySet()) { if (storeConfig.supportsCredential()) { for (Class<? extends CredentialHandler> credentialHandler : storeConfig.getCredentialHandlers()) { SupportsCredentials supportedCredentials = credentialHandler.getAnnotation(SupportsCredentials.class); if (supportedCredentials != null) { if (supportedCredentials.credentialStorage().equals(storageClass)) { CredentialStore<?> credentialStore = (CredentialStore<?>) getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig); credentialStores.add(credentialStore); } } } } } } return credentialStores; } @Override public PermissionStore getStoreForPermissionOperation(IdentityContext context) { IdentityConfiguration identityConfiguration = getConfigurationForPartition(context, context.getPartition()); if (identityConfiguration == null) { for (IdentityConfiguration configuration : this.configuration.getConfigurations()) { for (IdentityStoreConfiguration storeConfig : configuration.getStoreConfiguration()) { if (storeConfig.supportsPermissions()) { return (PermissionStore) getIdentityStoreAndInitializeContext(context, configuration, storeConfig); } } } } else { for (IdentityStoreConfiguration storeConfig : identityConfiguration.getStoreConfiguration()) { if (storeConfig.supportsPermissions()) { return (PermissionStore) getIdentityStoreAndInitializeContext(context, identityConfiguration, storeConfig); } } } return null; } @SuppressWarnings({"unchecked", "rawtypes"}) private <T extends IdentityStore> T createIdentityStore(IdentityStoreConfiguration storeConfiguration) { Class<T> storeClass = (Class<T>) storeConfiguration.getIdentityStoreType(); if (storeClass == null) { // If no store class is configured, default to the built-in types for known configurations if (FileIdentityStoreConfiguration.class.isInstance(storeConfiguration)) { storeClass = (Class<T>) FileIdentityStore.class; } else if (JPAIdentityStoreConfiguration.class.isInstance(storeConfiguration)) { storeClass = (Class<T>) JPAIdentityStore.class; } else if (LDAPIdentityStoreConfiguration.class.isInstance(storeConfiguration)) { storeClass = (Class<T>) LDAPIdentityStore.class; } else if (JDBCIdentityStoreConfiguration.class.isInstance(storeConfiguration)) { storeClass = (Class<T>) JDBCIdentityStore.class; } else if (TokenStoreConfiguration.class.isInstance(storeConfiguration)) { storeClass = (Class<T>) TokenIdentityStore.class; } } if (storeClass == null) { throw MESSAGES.configUnknownStoreForConfiguration(storeConfiguration); } try { if (storeConfiguration instanceof AbstractIdentityStoreConfiguration) { ((AbstractIdentityStoreConfiguration) storeConfiguration).setIdentityStoreType(storeClass); } T store = storeClass.newInstance(); store.setup(storeConfiguration); return store; } catch (Exception ex) { throw MESSAGES.configCouldNotCreateStore(storeClass, storeConfiguration, ex); } } public <T extends IdentityStore<?>> T lookupStore(IdentityContext context, IdentityConfiguration configuration, Class<? extends AttributedType> type, IdentityOperation operation) { for (IdentityStoreConfiguration storeConfig : configuration.getStoreConfiguration()) { if (storeConfig.supportsType(type, operation)) { return getIdentityStoreAndInitializeContext(context, configuration, storeConfig); } } return null; } /** * <p>Returns a {@link IdentityStore} instance considering the given {@link IdentityConfiguration} and {@link * IdentityStoreConfiguration}.</p> * * <p>Before returning the instance, the {@link IdentityContext} is initialized.</p> * * @param context * @param configuration * @param storeConfig * @param <T> * * @return */ private <T extends IdentityStore<?>> T getIdentityStoreAndInitializeContext(final IdentityContext context, final IdentityConfiguration configuration, final IdentityStoreConfiguration storeConfig) { IdentityStore<?> store = this.stores.get(configuration).get(storeConfig); storeConfig.initializeContext(context, store); return (T) store; } private void checkSupportedTypes(Partition partition, Class<? extends AttributedType> type) { if (partition != null) { if (IdentityType.class.isAssignableFrom(type)) { IdentityPartition identityPartition = partition.getClass().getAnnotation(IdentityPartition.class); if (identityPartition != null && isTypeSupported((Class<? extends IdentityType>) type, toSet(identityPartition.supportedTypes()), toSet(identityPartition.unsupportedTypes())) == -1) { throw MESSAGES.partitionUnsupportedType(partition, type); } } } } IdentityConfiguration getConfigurationForPartition(IdentityContext identityContext, Partition partition) { IdentityConfiguration partitionManagementConfig = this.configuration.getPartitionManagementConfig(); if (partitionManagementConfig == null) { Collection<IdentityConfiguration> configurations = this.configuration.getConfigurations(); if (configurations.size() == 1) { return configurations.iterator().next(); } } if (!this.partitionConfigurations.containsKey(partition)) { PartitionStore<?> store = getStoreForPartitionOperation(identityContext, partition.getClass()); partitionConfigurations.put(partition, this.configuration.getConfigurationByName(store.getConfigurationName(identityContext, partition))); } IdentityConfiguration identityConfiguration = partitionConfigurations.get(partition); if (identityConfiguration == null) { throw MESSAGES.partitionReferencesInvalidConfiguration(partition); } return identityConfiguration; } private Set<Partition> getRelationshipPartitions(Relationship relationship) { return this.relationshipMetadata.getRelationshipPartitions(relationship); } }