/* * 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.common.properties.Property; import org.picketlink.common.properties.query.AnnotatedPropertyCriteria; import org.picketlink.common.properties.query.PropertyQueries; import org.picketlink.common.properties.query.PropertyQuery; import org.picketlink.idm.IdGenerator; import org.picketlink.idm.IdentityManagementException; import org.picketlink.idm.IdentityManager; import org.picketlink.idm.PermissionManager; import org.picketlink.idm.RelationshipManager; import org.picketlink.idm.config.IdentityStoreConfiguration.IdentityOperation; import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.storage.CredentialStorage; import org.picketlink.idm.event.CredentialUpdatedEvent; import org.picketlink.idm.event.EventBridge; import org.picketlink.idm.event.IdentityTypeCreatedEvent; import org.picketlink.idm.event.IdentityTypeDeletedEvent; import org.picketlink.idm.event.IdentityTypeUpdatedEvent; import org.picketlink.idm.model.Account; 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.Unique; import org.picketlink.idm.permission.Permission; import org.picketlink.idm.query.IdentityQuery; import org.picketlink.idm.query.IdentityQueryBuilder; import org.picketlink.idm.query.RelationshipQuery; import org.picketlink.idm.query.internal.DefaultIdentityQuery; import org.picketlink.idm.query.internal.DefaultQueryBuilder; import org.picketlink.idm.spi.CredentialStore; import org.picketlink.idm.spi.IdentityContext; import org.picketlink.idm.spi.IdentityStore; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import static org.picketlink.idm.IDMInternalMessages.MESSAGES; import static org.picketlink.idm.util.IDMUtil.configureDefaultPartition; /** * <p>Default implementation of the IdentityManager interface.<p/> <p/> <p> This lightweight class is intended to be * created any time a batch of partition-specific identity management operations are to be performed. In a web * environment, it is recommended that instances are scoped to the web request lifecycle. <p/> <p/> <p> This class is * not thread-safe. </p> * * @author Shane Bryzak * @author anil saldhana */ public class ContextualIdentityManager extends AbstractAttributedTypeManager<IdentityType> implements IdentityManager { private final DefaultPartitionManager partitionManager; private final RelationshipManager relationshipManager; private final PermissionManager permissionManager; public ContextualIdentityManager(Partition partition, DefaultPartitionManager partitionManager) { super(partitionManager.getConfiguration(), partition); this.partitionManager = partitionManager; IdentityContext identityContext = getIdentityContext(); if (getStoreSelector().getStoreForPermissionOperation(identityContext) != null) { this.permissionManager = this.partitionManager.createPermissionManager(partition); } else { this.permissionManager = null; } this.relationshipManager = this.partitionManager.createRelationshipManager(); } @Override protected void doAdd(IdentityType attributedType) { IdentityContext identityContext = getIdentityContext(); IdentityStore identityStore = getStoreSelector().getStoreForIdentityOperation(identityContext, IdentityStore.class, attributedType.getClass(), IdentityOperation.create); identityStore.add(identityContext, attributedType); configureDefaultPartition(identityContext, attributedType, identityStore, this.partitionManager); } @Override protected void fireAttributedTypeAddedEvent(IdentityType attributedType) { fireEvent(new IdentityTypeCreatedEvent(attributedType, this.partitionManager)); } @Override protected void doUpdate(IdentityType attributedType) { if (attributedType.getPartition() == null) { throw MESSAGES.attributedUndefinedPartition(attributedType); } IdentityContext identityContext = getIdentityContext(); getStoreSelector().getStoreForIdentityOperation(identityContext, IdentityStore.class, attributedType.getClass(), IdentityOperation.update) .update(identityContext, attributedType); } @Override protected void fireAttributedTypeUpdatedEvent(IdentityType attributedType) { fireEvent(new IdentityTypeUpdatedEvent(attributedType, this.partitionManager)); } @Override protected void fireAttributedTypeRemovedEvent(IdentityType attributedType) { fireEvent(new IdentityTypeDeletedEvent(attributedType, this.partitionManager)); } @Override protected void doRemove(IdentityType attributedType) { RelationshipQuery<Relationship> query = this.relationshipManager.createRelationshipQuery(Relationship.class); query.setParameter(Relationship.IDENTITY, attributedType); for (Relationship relationship : query.getResultList()) { this.relationshipManager.remove(relationship); } if (this.permissionManager != null) { List<Permission> permissions = this.permissionManager.listPermissions(attributedType); for (Permission permission : permissions) { this.permissionManager.revokePermission(attributedType, permission.getResourceClass(), permission.getOperation()); } } IdentityContext identityContext = getIdentityContext(); getStoreSelector().getStoreForIdentityOperation(identityContext, IdentityStore.class, attributedType.getClass(), IdentityOperation.delete) .remove(identityContext, attributedType); } @Override public <T extends IdentityType> T lookupIdentityById(Class<T> identityType, String id) { return lookupById(identityType, id); } @Override public <C extends IdentityType> C lookupById(Class<C> attributedType, String id) throws IdentityManagementException { if (attributedType == null) { throw MESSAGES.nullArgument("IdentityType class"); } if (id == null) { throw MESSAGES.nullArgument("Identifier"); } IdentityQueryBuilder queryBuilder = getQueryBuilder(); IdentityQuery<C> query = queryBuilder.createIdentityQuery(attributedType); query.where(queryBuilder.equal(IdentityType.ID, id)); List<C> result = query.getResultList(); C identity = null; if (!result.isEmpty()) { if (result.size() > 1) { throw MESSAGES.attributedTypeAmbiguosFoundWithId(id); } else { identity = result.get(0); } } return identity; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public <T extends IdentityType> IdentityQuery<T> createIdentityQuery(Class<T> identityType) { if (identityType == null) { throw MESSAGES.nullArgument("IdentityType class"); } return new DefaultIdentityQuery(getQueryBuilder(), getIdentityContext(), identityType, this.partitionManager, getStoreSelector()); } @Override public void validateCredentials(Credentials credentials) { if (credentials == null) { throw MESSAGES.nullArgument("Credentials"); } try { IdentityContext identityContext = getIdentityContext(); getStoreSelector().getStoreForCredentialOperation(identityContext, credentials.getClass()).validateCredentials(identityContext, credentials); } catch (Exception e) { throw MESSAGES.credentialValidationFailed(credentials, e); } } @Override public void updateCredential(Account account, Object credential) { updateCredential(account, credential, null, null); } @Override public void updateCredential(Account account, Object credential, Date effectiveDate, Date expiryDate) { checkIfExists(account); if (credential == null) { throw MESSAGES.nullArgument("Credential"); } try { IdentityContext identityContext = getIdentityContext(); getStoreSelector().getStoreForCredentialOperation(identityContext, credential.getClass()).updateCredential(identityContext, account, credential, effectiveDate, expiryDate); fireEvent(new CredentialUpdatedEvent(account, credential, effectiveDate, expiryDate, this.partitionManager)); } catch (Exception e) { throw MESSAGES.credentialUpdateFailed(account, credential, e); } } @Override public <T extends CredentialStorage> T retrieveCurrentCredential(Account account, Class<T> storageClass) { checkIfExists(account); if (storageClass == null) { throw MESSAGES.nullArgument("CredentialStorage type"); } try { IdentityContext identityContext = getIdentityContext(); for (CredentialStore credentialStore : getStoreSelector().getStoresForCredentialStorage(identityContext, storageClass)) { T credentialStorage = (T) credentialStore.retrieveCurrentCredential(identityContext, account, storageClass); if (credentialStorage != null) { return credentialStorage; } } } catch (Exception e) { throw MESSAGES.credentialRetrievalFailed(account, storageClass, e); } return null; } @Override public <T extends CredentialStorage> List<T> retrieveCredentials(Account account, Class<T> storageClass) { checkIfExists(account); if (storageClass == null) { throw MESSAGES.nullArgument("CredentialStorage type"); } List<T> storages = new ArrayList<T>(); try { IdentityContext identityContext = getIdentityContext(); for (CredentialStore credentialStore : getStoreSelector().getStoresForCredentialStorage(identityContext, storageClass)) { storages.addAll(credentialStore.retrieveCredentials(identityContext, account, storageClass)); } } catch (Exception e) { throw MESSAGES.credentialRetrievalFailed(account, storageClass, e); } return storages; } @Override public void removeCredential(Account account, Class<? extends CredentialStorage> storageClass) { checkIfExists(account); if (storageClass == null) { throw MESSAGES.nullArgument("CredentialStorage type"); } try { IdentityContext identityContext = getIdentityContext(); for (CredentialStore credentialStore : getStoreSelector().getStoresForCredentialStorage(identityContext, storageClass)) { credentialStore.removeCredential(identityContext, account, storageClass); } } catch (Exception e) { throw MESSAGES.credentialRetrievalFailed(account, storageClass, e); } } @Override public IdentityQueryBuilder getQueryBuilder() { return new DefaultQueryBuilder(getIdentityContext(), this.partitionManager, getStoreSelector()); } @Override protected IdentityContext createIdentityContext(Partition partition, EventBridge eventBridge, IdGenerator idGenerator) { IdentityContext identityContext = super.createIdentityContext(partition, eventBridge, idGenerator); identityContext.setParameter(IDENTITY_MANAGER_CTX_PARAMETER, this); return identityContext; } @Override protected void checkIfExists(IdentityType identityType) throws IdentityManagementException { if (identityType == null) { throw MESSAGES.nullArgument("IdentityType"); } if (lookupIdentityById(identityType.getClass(), identityType.getId()) == null) { throw MESSAGES.attributedTypeNotFoundWithId(identityType.getClass(), identityType.getId(), identityType.getPartition()); } } @Override protected void checkUniqueness(IdentityType identityType) { if (identityType == null) { throw MESSAGES.nullArgument("IdentityType"); } PropertyQuery<Serializable> propertyQuery = PropertyQueries.createQuery(identityType.getClass()); propertyQuery.addCriteria(new AnnotatedPropertyCriteria(Unique.class)); IdentityQueryBuilder queryBuilder = getQueryBuilder(); IdentityQuery<? extends IdentityType> identityQuery = queryBuilder.createIdentityQuery(identityType.getClass()); for (Property<Serializable> property : propertyQuery.getResultList()) { identityQuery.where(queryBuilder .equal(AttributedType.QUERY_ATTRIBUTE.byName(property.getName()), property.getValue(identityType))); } List<? extends IdentityType> result = identityQuery.getResultList(); if (!result.isEmpty()) { // we need to check the unique property values again because some properties are not stored and are calculated // based on the values of other properties. Eg.: Group.path for (Property<Serializable> property : propertyQuery.getResultList()) { for (IdentityType storedType: result) { if (property.getValue(storedType).equals(property.getValue(identityType))) { throw MESSAGES.identityTypeAlreadyExists(identityType.getClass(), identityType.getId(), identityType.getPartition()); } } } } } }