/* * #%L * BroadleafCommerce Open Admin Platform * %% * Copyright (C) 2009 - 2013 Broadleaf Commerce * %% * 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. * #L% */ package org.broadleafcommerce.openadmin.server.security.remote; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.broadleafcommerce.common.exception.SecurityServiceException; import org.broadleafcommerce.common.exception.ServiceException; import org.broadleafcommerce.common.security.service.ExploitProtectionService; import org.broadleafcommerce.common.web.SandBoxContext; import org.broadleafcommerce.openadmin.dto.Entity; import org.broadleafcommerce.openadmin.dto.PersistencePackage; import org.broadleafcommerce.openadmin.dto.SectionCrumb; import org.broadleafcommerce.openadmin.server.security.domain.AdminPermission; import org.broadleafcommerce.openadmin.server.security.domain.AdminRole; import org.broadleafcommerce.openadmin.server.security.domain.AdminUser; import org.broadleafcommerce.openadmin.server.security.service.RowLevelSecurityService; import org.broadleafcommerce.openadmin.server.security.service.type.PermissionType; import org.broadleafcommerce.openadmin.server.service.ValidationException; import org.broadleafcommerce.openadmin.server.service.persistence.validation.GlobalValidationResult; import org.springframework.cglib.core.CollectionUtils; import org.springframework.cglib.core.Transformer; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; /** * Service for handeling security with Ajax components. Serves two functions. * <ul> * <li> * Converts the ServerSide AdminUser to a client level admin user with * appropriate roles defined. * </li> * <li> * Provides a method to check if the current logged in user matches the * client side user and verifies whether that user has access to the * entity operation they are trying to perform. * </li> * </ul> * 1. * @author jfischer * */ @Service("blAdminSecurityRemoteService") public class AdminSecurityServiceRemote implements AdminSecurityService, SecurityVerifier { private static final String ANONYMOUS_USER_NAME = "anonymousUser"; private static final Log LOG = LogFactory.getLog(AdminSecurityServiceRemote.class); @Resource(name="blAdminSecurityService") protected org.broadleafcommerce.openadmin.server.security.service.AdminSecurityService securityService; @Resource(name="blExploitProtectionService") protected ExploitProtectionService exploitProtectionService; @Resource(name = "blRowLevelSecurityService") protected RowLevelSecurityService rowLevelSecurityService; @Override public org.broadleafcommerce.openadmin.server.security.remote.AdminUser getAdminUser() throws ServiceException { AdminUser persistentAdminUser = getPersistentAdminUser(); if (persistentAdminUser != null) { org.broadleafcommerce.openadmin.server.security.remote.AdminUser response = new org.broadleafcommerce.openadmin.server.security.remote.AdminUser(); for (AdminRole role : persistentAdminUser.getAllRoles()) { response.getRoles().add(role.getName()); for (AdminPermission permission : role.getAllPermissions()) { response.getPermissions().add(permission.getName()); } } for (AdminPermission permission : persistentAdminUser.getAllPermissions()) { response.getPermissions().add(permission.getName()); } response.setUserName(persistentAdminUser.getLogin()); response.setCurrentSandBoxId(String.valueOf(SandBoxContext.getSandBoxContext().getSandBoxId())); response.setEmail(persistentAdminUser.getEmail()); response.setName(persistentAdminUser.getName()); response.setPhoneNumber(persistentAdminUser.getPhoneNumber()); response.setId(persistentAdminUser.getId()); return response; } return null; } @Override public AdminUser getPersistentAdminUser() { SecurityContext ctx = SecurityContextHolder.getContext(); if (ctx != null) { Authentication auth = ctx.getAuthentication(); if (auth != null && !auth.getName().equals(ANONYMOUS_USER_NAME)) { UserDetails temp = (UserDetails) auth.getPrincipal(); return securityService.readAdminUserByUserName(temp.getUsername()); } } return null; } @Override public void securityCheck(PersistencePackage persistencePackage, EntityOperationType operationType) throws ServiceException { Set<String> ceilingNames = new HashSet<String>(); ceilingNames.add(persistencePackage.getSecurityCeilingEntityFullyQualifiedClassname()); if (!ArrayUtils.isEmpty(persistencePackage.getSectionCrumbs())) { ceilingNames.addAll(CollectionUtils.transform(Arrays.asList(persistencePackage.getSectionCrumbs()), new Transformer() { @Override public Object transform(Object o) { return ((SectionCrumb) o).getSectionIdentifier(); } })); } Entity entity = persistencePackage.getEntity(); GlobalValidationResult globalValidationResult = null; if (operationType.equals(EntityOperationType.UPDATE)) { globalValidationResult = rowLevelSecurityService.validateUpdateRequest(getPersistentAdminUser(), entity, persistencePackage); } else if (operationType.equals(EntityOperationType.REMOVE)) { globalValidationResult = rowLevelSecurityService.validateRemoveRequest(getPersistentAdminUser(), entity, persistencePackage); } if (globalValidationResult != null) { if (!globalValidationResult.isValid()) { if (StringUtils.isEmpty(globalValidationResult.getErrorMessage())) { entity.addGlobalValidationError("rowLevelSecurityFailed"); } else { entity.addGlobalValidationErrors(globalValidationResult.getErrorMessages()); } throw new ValidationException(entity, "Row level security check failed for " + operationType); } } securityCheck(ceilingNames.toArray(new String[ceilingNames.size()]), operationType); } @Override public void securityCheck(String ceilingEntityFullyQualifiedName, EntityOperationType operationType) throws ServiceException { securityCheck(new String[]{ceilingEntityFullyQualifiedName}, operationType); } protected void securityCheck(String[] ceilingNames, EntityOperationType operationType) throws ServiceException { if (ArrayUtils.isEmpty(ceilingNames)) { throw new SecurityServiceException("Security Check Failed: ceilingNames not specified"); } AdminUser persistentAdminUser = getPersistentAdminUser(); PermissionType permissionType; switch(operationType){ case ADD: permissionType = PermissionType.CREATE; break; case FETCH: permissionType = PermissionType.READ; break; case REMOVE: permissionType = PermissionType.DELETE; break; case UPDATE: permissionType = PermissionType.UPDATE; break; case INSPECT: permissionType = PermissionType.READ; break; default: permissionType = PermissionType.OTHER; break; } SecurityServiceException primaryException = null; boolean isQualified = false; for (String ceilingEntityFullyQualifiedName : ceilingNames) { isQualified = securityService.isUserQualifiedForOperationOnCeilingEntity(persistentAdminUser, permissionType, ceilingEntityFullyQualifiedName); if (!isQualified){ if (primaryException == null) { primaryException = new SecurityServiceException("Security Check Failed for entity operation: " + operationType.toString() + " (" + ceilingEntityFullyQualifiedName + ")"); } } else { break; } } if (!isQualified) { //check if the requested entity is not configured and warn if (!securityService.doesOperationExistForCeilingEntity(permissionType, ceilingNames[0])) { if (LOG.isWarnEnabled()) { LOG.warn("Detected security request for an unregistered ceiling entity (" + ceilingNames[0] + "). " + "As a result, the request failed. Please make sure to configure security for any ceiling entities " + "referenced via the admin. This is usually accomplished by adding records in the " + "BLC_ADMIN_PERMISSION_ENTITY table. Note, depending on how the entity in question is used, you " + "may need to add to BLC_ADMIN_PERMISSION, BLC_ADMIN_ROLE_PERMISSION_XREF and BLC_ADMIN_SEC_PERM_XREF.", primaryException); } } throw primaryException; } } }