/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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.hawkular.inventory.rest.security.accounts; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import javax.enterprise.inject.Default; import javax.inject.Inject; import javax.inject.Singleton; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import org.hawkular.accounts.api.OperationService; import org.hawkular.accounts.api.PermissionChecker; import org.hawkular.accounts.api.model.Operation; import org.hawkular.inventory.api.model.Entity; import org.hawkular.inventory.api.model.Environment; import org.hawkular.inventory.api.model.Feed; import org.hawkular.inventory.api.model.MetadataPack; import org.hawkular.inventory.api.model.Metric; import org.hawkular.inventory.api.model.MetricType; import org.hawkular.inventory.api.model.Relationship; import org.hawkular.inventory.api.model.Resource; import org.hawkular.inventory.api.model.ResourceType; import org.hawkular.inventory.api.model.Tenant; import org.hawkular.inventory.paths.CanonicalPath; import org.hawkular.inventory.rest.security.Security; /** * CDI bean that provides inventory-focused abstractions over Hawkular accounts. * It defines all the operations available in inventory and implements permission checking methods. * * @author Lukas Krejci * @since 0.0.2 */ @Singleton @Default public class InventorySecurity implements Security { private static final SecurityAccountsLogger log = SecurityAccountsLogger.getLogger(InventorySecurity.class); private final Map<Class<?>, Map<OperationType, Operation>> operationsByType = new HashMap<>(); @Inject private PermissionChecker permissions; @Inject private OperationService operations; @javax.annotation.Resource private UserTransaction transaction; public CreatePermissionCheckerFinisher canCreate(Class<?> entityType) { return new CreatePermissionCheckerFinisherImpl(entityType); } public boolean canUpdate(CanonicalPath path) { return safePermissionCheck(path, update(Entity.typeFromSegmentType(path.getSegment().getElementType()))); } public boolean canDelete(CanonicalPath path) { return safePermissionCheck(path, delete(Entity.typeFromSegmentType(path.getSegment().getElementType()))); } public boolean canAssociateFrom(CanonicalPath path) { return safePermissionCheck(path, associate()); } public boolean canCopyEnvironment(CanonicalPath path) { return safePermissionCheck(path, copy()); } private Operation create(Class<?> entityType) { return getOperation(entityType, OperationType.CREATE); } private Operation update(Class<?> entityType) { return getOperation(entityType, OperationType.UPDATE); } private Operation delete(Class<?> entityType) { return getOperation(entityType, OperationType.DELETE); } private Operation associate() { return operationsByType.get(Relationship.class).get(OperationType.ASSOCIATE); } private Operation copy() { return operationsByType.get(Environment.class).get(OperationType.COPY); } private Operation getOperation(Class<?> cls, OperationType operationType) { Map<OperationType, Operation> ops = operationsByType.get(cls); if (ops == null) { throw new IllegalArgumentException("There is no " + operationType + " operation for elements of type " + cls); } return ops.get(operationType); } private boolean safePermissionCheck(CanonicalPath path, Operation operation) { return safePermissionCheck(Entity.typeFromSegmentType(path.getSegment().getElementType()), path.getSegment().getElementId(), operation, AccountsSecurityUtils.getStableId(path)); } private boolean safePermissionCheck(Class<?> entityType, String entityId, Operation operation, String stableId) { try { log.debugf("Permission check for operation '%s' for entity with stable ID '%s'", operation.getName(), stableId); return permissions.isAllowedTo(operation, stableId); } catch (Exception e) { log.securityCheckFailed(stableId, e); return false; } } @PostConstruct public void initOperationsMap() { // Monitor – a read-only role. Cannot modify any resource. // Operator – Monitor permissions, plus can modify runtime state, but cannot modify anything that ends up in the // persistent configuration. Could, for example, restart a server. // Maintainer – Operator permissions, plus can modify the persistent configuration. // Deployer – like a Maintainer, but with permission to modify persistent configuration constrained to resources // that are considered to be "application resources". A deployment is an application resource. The // messaging server is not. Items like datasources and JMS destinations are not considered to be // application resources by default, but this is configurable. // // Three roles are granted permissions for security sensitive items: // // SuperUser – has all permissions. Equivalent to a JBoss AS 7 administrator. // Administrator – has all permissions except cannot read or write resources related to the administrative audit // logging system. // Auditor – can read anything. Can only modify the resources related to the administrative audit logging // system. try { transaction.begin(); operations.setup("update-tenant").add("SuperUser").persist(); operations.setup("delete-tenant").add("SuperUser").persist(); operations.setup("create-environment").add("Administrator").persist(); operations.setup("update-environment").add("Administrator").persist(); operations.setup("delete-environment").add("Administrator").persist(); operations.setup("copy-environment").add("Administrator").persist(); operations.setup("create-resourceType").add("Administrator").persist(); operations.setup("update-resourceType").add("Administrator").persist(); operations.setup("delete-resourceType").add("Administrator").persist(); operations.setup("create-metricType").add("Administrator").persist(); operations.setup("update-metricType").add("Administrator").persist(); operations.setup("delete-metricType").add("Administrator").persist(); operations.setup("create-operationType").add("Administrator").persist(); operations.setup("update-operationType").add("Administrator").persist(); operations.setup("delete-operationType").add("Administrator").persist(); operations.setup("create-feed").add("Administrator").persist(); operations.setup("update-feed").add("Administrator").persist(); operations.setup("delete-feed").add("Administrator").persist(); operations.setup("create-resource").add("Maintainer").persist(); operations.setup("update-resource").add("Maintainer").persist(); operations.setup("delete-resource").add("Maintainer").persist(); operations.setup("create-metric").add("Maintainer").persist(); operations.setup("update-metric").add("Maintainer").persist(); operations.setup("delete-metric").add("Maintainer").persist(); operations.setup("create-metadataPack").add("Maintainer").persist(); operations.setup("update-metadataPack").add("Maintainer").persist(); operations.setup("delete-metadataPack").add("Maintainer").persist(); operations.setup("associate").add("Operator").persist(); transaction.commit(); } catch (Throwable t) { try { transaction.rollback(); } catch (SystemException e) { throw new IllegalStateException("Unable to do the rollback: " + e.getMessage(), t); } throw new IllegalStateException(t); } Operation updateTenantOperation = operations.getByName("update-tenant"); Operation deleteTenantOperation = operations.getByName("delete-tenant"); Operation createEnvironmentOperation = operations.getByName("create-environment"); Operation updateEnvironmentOperation = operations.getByName("update-environment"); Operation deleteEnvironmentOperation = operations.getByName("delete-environment"); Operation copyEnvironmentOperation = operations.getByName("copy-environment"); Operation createResourceTypeOperation = operations.getByName("create-resourceType"); Operation updateResourceTypeOperation = operations.getByName("update-resourceType"); Operation deleteResourceTypeOperation = operations.getByName("delete-resourceType"); Operation createMetricTypeOperation = operations.getByName("create-metricType"); Operation updateMetricTypeOperation = operations.getByName("update-metricType"); Operation deleteMetricTypeOperation = operations.getByName("delete-metricType"); Operation createOperationTypeOperation = operations.getByName("create-operationType"); Operation updateOperationTypeOperation = operations.getByName("update-operationType"); Operation deleteOperationTypeOperation = operations.getByName("delete-operationType"); Operation createFeedOperation = operations.getByName("create-feed"); Operation updateFeedOperation = operations.getByName("update-feed"); Operation deleteFeedOperation = operations.getByName("delete-feed"); Operation createResourceOperation = operations.getByName("create-resource"); Operation updateResourceOperation = operations.getByName("update-resource"); Operation deleteResourceOperation = operations.getByName("delete-resource"); Operation createMetricOperation = operations.getByName("create-metric"); Operation updateMetricOperation = operations.getByName("update-metric"); Operation deleteMetricOperation = operations.getByName("delete-metric"); Operation createMetadataPackOperation = operations.getByName("create-metadataPack"); Operation updateMetadataPackOperation = operations.getByName("update-metadataPack"); Operation deleteMetadataPackOperation = operations.getByName("delete-metadataPack"); Operation associate = operations.getByName("associate"); operationsByType.put(Tenant.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.UPDATE, updateTenantOperation); put(OperationType.DELETE, deleteTenantOperation); }}); operationsByType.put(Environment.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createEnvironmentOperation); put(OperationType.UPDATE, updateEnvironmentOperation); put(OperationType.DELETE, deleteEnvironmentOperation); put(OperationType.COPY, copyEnvironmentOperation); }}); operationsByType.put(ResourceType.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createResourceTypeOperation); put(OperationType.UPDATE, updateResourceTypeOperation); put(OperationType.DELETE, deleteResourceTypeOperation); }}); operationsByType.put(MetricType.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createMetricTypeOperation); put(OperationType.UPDATE, updateMetricTypeOperation); put(OperationType.DELETE, deleteMetricTypeOperation); }}); operationsByType.put(Feed.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createFeedOperation); put(OperationType.UPDATE, updateFeedOperation); put(OperationType.DELETE, deleteFeedOperation); }}); operationsByType.put(Resource.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createResourceOperation); put(OperationType.UPDATE, updateResourceOperation); put(OperationType.DELETE, deleteResourceOperation); }}); operationsByType.put(Metric.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createMetricOperation); put(OperationType.UPDATE, updateMetricOperation); put(OperationType.DELETE, deleteMetricOperation); }}); operationsByType.put(Relationship.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.ASSOCIATE, associate); }}); operationsByType.put(org.hawkular.inventory.api.model.OperationType.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createOperationTypeOperation); put(OperationType.UPDATE, updateOperationTypeOperation); put(OperationType.DELETE, deleteOperationTypeOperation); }}); operationsByType.put(MetadataPack.class, new EnumMap<OperationType, Operation>(OperationType.class) {{ put(OperationType.CREATE, createMetadataPackOperation); put(OperationType.UPDATE, updateMetadataPackOperation); put(OperationType.DELETE, deleteMetadataPackOperation); }}); } private enum OperationType { CREATE, UPDATE, DELETE, COPY, ASSOCIATE } public final class CreatePermissionCheckerFinisherImpl implements Security.CreatePermissionCheckerFinisher { private final Class<?> createdType; private CreatePermissionCheckerFinisherImpl(Class<?> createdType) { this.createdType = createdType; } public boolean under(CanonicalPath path) { String entityId = AccountsSecurityUtils.getStableId(path); return safePermissionCheck(createdType, path.getSegment().getElementId(), create(createdType), entityId); } } }