/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.wso2.carbon.identity.entitlement.policy.search;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.balana.ctx.AbstractRequestCtx;
import org.wso2.balana.ctx.AbstractResult;
import org.wso2.balana.ctx.ResponseCtx;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.identity.entitlement.EntitlementException;
import org.wso2.carbon.identity.entitlement.EntitlementUtil;
import org.wso2.carbon.identity.entitlement.PDPConstants;
import org.wso2.carbon.identity.entitlement.cache.PolicySearchCache;
import org.wso2.carbon.identity.entitlement.dto.AttributeDTO;
import org.wso2.carbon.identity.entitlement.dto.EntitledAttributesDTO;
import org.wso2.carbon.identity.entitlement.dto.EntitledResultSetDTO;
import org.wso2.carbon.identity.entitlement.internal.EntitlementServiceComponent;
import org.wso2.carbon.identity.entitlement.pdp.EntitlementEngine;
import org.wso2.carbon.identity.entitlement.policy.finder.PolicyFinderModule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* This contains the searching methods for policies based on policy attribute values and how subjects
* are entitled to resources
*/
public class PolicySearch {
private static Log log = LogFactory.getLog(PolicySearch.class);
private List<PolicyFinderModule> finderModules = null;
private boolean cachingEnable;
private PolicySearchCache policySearchCache = null;
public PolicySearch(boolean cachingEnable, int cachingInterval) {
// get registered finder modules
Map<PolicyFinderModule, Properties> finderModules = EntitlementServiceComponent.
getEntitlementConfig().getPolicyFinderModules();
if (finderModules != null) {
this.finderModules = new ArrayList<PolicyFinderModule>(finderModules.keySet());
}
this.cachingEnable = cachingEnable;
// Note that PolicySearchCache also uses EntitlementEngine.getInstance().getPdpDecisionCacheEnable()
// to set cache timeout.
this.policySearchCache = new PolicySearchCache(cachingInterval);
}
/**
* This returns resource name as the list of the entitled attributes for given
* user or role and action, after evaluating the all the active policies in the PDP
*
* @param subjectName subject name
* @param resourceName resource name
* @param subjectId subject attribute Id
* @param action Action Name
* @param enableChildSearch whether search is done for the child resources under the given resource name
* @return entitled resource id set
* @throws EntitlementException throws
*/
public EntitledResultSetDTO getEntitledAttributes(String subjectName, String resourceName,
String subjectId, String action, boolean enableChildSearch)
throws EntitlementException {
String cacheKey = "";
if (cachingEnable) {
/*
if(DecisionInvalidationCache.getInstance().isInvalidate()){
policySearchCache.clearCache();
}*/
cacheKey = (subjectId != null ? subjectId : "") + (subjectName != null ? subjectName : "") +
(resourceName != null ? resourceName : "") +
(action != null ? action : "") + enableChildSearch;
SearchResult searchResult = policySearchCache.getFromCache(cacheKey);
if (searchResult != null) {
return searchResult.getResultSetDTO();
}
}
AttributeDTO subjectAttributeDTO;
boolean hierarchicalResource = false;
EntitledResultSetDTO resultSetDTO = new EntitledResultSetDTO();
Set<EntitledAttributesDTO> resultSet = new HashSet<EntitledAttributesDTO>();
if (subjectName != null && subjectName.trim().length() > 0) {
subjectAttributeDTO = new AttributeDTO();
subjectAttributeDTO.setCategory(PDPConstants.SUBJECT_CATEGORY_URI);
subjectAttributeDTO.setAttributeValue(subjectName);
subjectAttributeDTO.setAttributeDataType(PDPConstants.STRING_DATA_TYPE);
if (subjectId != null && subjectId.trim().length() > 0) {
subjectAttributeDTO.setAttributeId(subjectId);
} else {
subjectAttributeDTO.setAttributeId(PDPConstants.SUBJECT_ID_DEFAULT);
}
} else {
throw new EntitlementException("Error : subject value can not be null");
}
if (getResponse(Arrays.asList(subjectAttributeDTO))) {
EntitledAttributesDTO dto = new EntitledAttributesDTO();
dto.setAllActions(true);
dto.setAllResources(true);
EntitledResultSetDTO setDTO = new EntitledResultSetDTO();
setDTO.setEntitledAttributesDTOs(new EntitledAttributesDTO[]{dto});
return setDTO;
}
for (PolicyFinderModule module : finderModules) {
if (module.isDefaultCategoriesSupported() &&
PolicyFinderModule.COMBINATIONS_BY_CATEGORY_AND_PARAMETER ==
module.getSupportedSearchAttributesScheme()) {
Map<String, Set<AttributeDTO>> requestMap = module.
getSearchAttributes(null, new HashSet<AttributeDTO>(Arrays.asList(subjectAttributeDTO)));
for (Map.Entry<String, Set<AttributeDTO>> entry : requestMap.entrySet()) {
Set<AttributeDTO> attributeDTOs = entry.getValue();
if (attributeDTOs != null) {
Set<AttributeDTO> actions = new HashSet<AttributeDTO>();
Set<AttributeDTO> resources = new HashSet<AttributeDTO>();
Set<AttributeDTO> requestAttributes = new HashSet<AttributeDTO>();
if (resourceName != null && resourceName.trim().length() > 0) {
AttributeDTO resourceAttribute = new AttributeDTO();
resourceAttribute.setAttributeValue(resourceName);
resourceAttribute.setAttributeDataType(PDPConstants.STRING_DATA_TYPE);
resourceAttribute.setAttributeId(PDPConstants.RESOURCE_ID_DEFAULT);
resourceAttribute.setCategory(PDPConstants.RESOURCE_CATEGORY_URI);
resources.add(resourceAttribute);
hierarchicalResource = true;
}
AttributeDTO resourceScopeAttribute = new AttributeDTO();
resourceScopeAttribute.setAttributeValue(PDPConstants.RESOURCE_DESCENDANTS);
resourceScopeAttribute.setAttributeDataType(PDPConstants.STRING_DATA_TYPE);
resourceScopeAttribute.setAttributeId(PDPConstants.RESOURCE_SCOPE_ID);
resourceScopeAttribute.setCategory(PDPConstants.RESOURCE_CATEGORY_URI);
for (AttributeDTO attributeDTO : attributeDTOs) {
if (PDPConstants.ENVIRONMENT_CATEGORY_URI.equals(attributeDTO.getCategory()) ||
PDPConstants.ENVIRONMENT_ELEMENT.equals(attributeDTO.getCategory())) {
requestAttributes.add(attributeDTO);
attributeDTO.setAttributeId(PDPConstants.ENVIRONMENT_ID_DEFAULT);
requestAttributes.add(attributeDTO);
} else if (PDPConstants.ACTION_CATEGORY_URI.equals(attributeDTO.getCategory()) ||
PDPConstants.ACTION_ELEMENT.equals(attributeDTO.getCategory())) {
if (action != null && action.trim().length() > 0) {
attributeDTO.setAttributeValue(action);
}
actions.add(attributeDTO);
attributeDTO.setAttributeId(PDPConstants.ACTION_ID_DEFAULT);
actions.add(attributeDTO);
} else if ((PDPConstants.RESOURCE_CATEGORY_URI.equals(attributeDTO.getCategory()) ||
PDPConstants.RESOURCE_ELEMENT.equals(attributeDTO
.getCategory())) &&
!hierarchicalResource) {
attributeDTO.setAttributeId(PDPConstants.RESOURCE_ID_DEFAULT);
resources.add(attributeDTO);
}
}
if (resultSetDTO.getMessage() == null) {
List<String> entitledActions = new ArrayList<String>();
for (AttributeDTO actionDTO : actions) {
List<AttributeDTO> currentRequestAttributes =
new ArrayList<AttributeDTO>();
currentRequestAttributes.add(subjectAttributeDTO);
currentRequestAttributes.add(actionDTO);
if (getResponse(currentRequestAttributes)) {
EntitledAttributesDTO dto = new EntitledAttributesDTO();
dto.setAllResources(true);
dto.setAction(actionDTO.getAttributeValue());
resultSet.add(dto);
entitledActions.add(actionDTO.getAttributeValue());
}
}
for (AttributeDTO resource : resources) {
if (PDPConstants.RESOURCE_CATEGORY_URI.equals(resource.getCategory())
|| PDPConstants.RESOURCE_ELEMENT.equals(resource.getCategory())) {
boolean allActionsAllowed = false;
int noOfRequests = 1;
if (enableChildSearch) {
noOfRequests = 0;
}
while (noOfRequests < 2) {
List<AttributeDTO> currentRequestAttributes =
new ArrayList<AttributeDTO>();
for (AttributeDTO dto : requestAttributes) {
currentRequestAttributes.add(dto);
}
if (noOfRequests < 1) {
currentRequestAttributes.add(resourceScopeAttribute);
}
currentRequestAttributes.add(subjectAttributeDTO);
currentRequestAttributes.add(resource);
if (getResponse(currentRequestAttributes)) {
EntitledAttributesDTO dto = new EntitledAttributesDTO();
dto.setResourceName(resource.getAttributeValue());
dto.setAllActions(true);
resultSet.add(dto);
allActionsAllowed = true;
}
noOfRequests++;
}
if (allActionsAllowed) {
continue;
}
for (AttributeDTO actionAttributeDTO : actions) {
if (entitledActions.contains(actionAttributeDTO.getAttributeValue())) {
continue;
}
noOfRequests = 1;
if (enableChildSearch) {
noOfRequests = 0;
}
while (noOfRequests < 2) {
List<AttributeDTO> currentRequestAttributes =
new ArrayList<AttributeDTO>();
for (AttributeDTO dto : requestAttributes) {
currentRequestAttributes.add(dto);
}
if (noOfRequests < 1) {
currentRequestAttributes.add(resourceScopeAttribute);
}
currentRequestAttributes.add(subjectAttributeDTO);
currentRequestAttributes.add(resource);
currentRequestAttributes.add(actionAttributeDTO);
if (getResponse(currentRequestAttributes)) {
EntitledAttributesDTO dto = new EntitledAttributesDTO();
dto.setResourceName(resource.getAttributeValue());
dto.setAction(actionAttributeDTO.getAttributeValue());
resultSet.add(dto);
}
noOfRequests++;
}
}
}
}
}
}
}
}
}
resultSetDTO.setEntitledAttributesDTOs(resultSet.
toArray(new EntitledAttributesDTO[resultSet.size()]));
if (cachingEnable) {
SearchResult result = new SearchResult();
result.setResultSetDTO(resultSetDTO);
policySearchCache.addToCache(cacheKey, result);
if (log.isDebugEnabled()) {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
log.debug("PDP Decision Cache Updated for tenantId " + tenantId);
}
}
return resultSetDTO;
}
/**
* gets all entitled attributes for given set of attributes
* this an universal method to do policy search and find entitlement attributes
*
* @param identifier identifier to separate out the attributes that is used for search
* this is not required and can be null
* @param givenAttributes user provided attributes
* @return all the attributes that is entitled
*/
public EntitledResultSetDTO getEntitledAttributes(String identifier, AttributeDTO[] givenAttributes) {
String cacheKey = "";
if (cachingEnable) {
/*if(DecisionInvalidationCache.getInstance().isInvalidate()){
policySearchCache.clearCache();
}*/
int hashCode = 0;
for (AttributeDTO dto : givenAttributes) {
hashCode = hashCode + (31 * dto.hashCode());
}
cacheKey = identifier + hashCode;
SearchResult searchResult = policySearchCache.getFromCache(cacheKey);
if (searchResult != null) {
if (log.isDebugEnabled()) {
log.debug("PDP Search Cache Hit");
}
return searchResult.getResultSetDTO();
} else {
if (log.isDebugEnabled()) {
log.debug("PDP Search Cache Miss");
}
}
}
EntitledResultSetDTO result = new EntitledResultSetDTO();
Set<EntitledAttributesDTO> resultAttributes = new HashSet<EntitledAttributesDTO>();
Set<AttributeDTO> attributeDTOs = new HashSet<AttributeDTO>(Arrays.asList(givenAttributes));
for (PolicyFinderModule finderModule : finderModules) {
Map<String, Set<AttributeDTO>> attributesMap = finderModule.
getSearchAttributes(identifier, attributeDTOs);
int supportedSearchScheme = finderModule.getSupportedSearchAttributesScheme();
Set<List<AttributeDTO>> requestSet = getPossibleRequests(attributesMap, supportedSearchScheme);
if (requestSet == null) {
log.error("Invalid Search scheme in policy finder : " + finderModule.getModuleName());
} else {
for (List<AttributeDTO> attributeDTOList : requestSet) {
if (getResponse(attributeDTOList)) {
EntitledAttributesDTO dto = new EntitledAttributesDTO();
dto.setAttributeDTOs(attributeDTOList.
toArray(new AttributeDTO[attributeDTOList.size()]));
resultAttributes.add(dto);
}
}
}
}
result.setAdvanceResult(true);
result.setEntitledAttributesDTOs(resultAttributes.
toArray(new EntitledAttributesDTO[resultAttributes.size()]));
if (cachingEnable) {
SearchResult searchResult = new SearchResult();
searchResult.setResultSetDTO(result);
policySearchCache.addToCache(cacheKey, searchResult);
if (log.isDebugEnabled()) {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
log.debug("PDP Decision Cache Updated for tenantId " + tenantId);
}
}
return result;
}
/**
* Helper method to get possible XACML requests with attributes
*
* @param attributesMap
* @param supportedSearchScheme
* @return
*/
private Set<List<AttributeDTO>> getPossibleRequests(Map<String, Set<AttributeDTO>> attributesMap,
int supportedSearchScheme) {
if (PolicyFinderModule.ALL_COMBINATIONS == supportedSearchScheme) {
if (attributesMap.entrySet() != null) {
return getAllCombinations(attributesMap.entrySet().iterator().next().getValue());
}
} else if (PolicyFinderModule.COMBINATIONS_BY_CATEGORY == supportedSearchScheme) {
return getAllCombinationsWithCategory(attributesMap);
} else if (PolicyFinderModule.COMBINATIONS_BY_PARAMETER == supportedSearchScheme) {
Set<List<AttributeDTO>> requestSet = new HashSet<List<AttributeDTO>>();
for (Map.Entry<String, Set<AttributeDTO>> entry : attributesMap.entrySet()) {
requestSet.addAll(getAllCombinations(entry.getValue()));
}
return requestSet;
} else if (PolicyFinderModule.COMBINATIONS_BY_CATEGORY_AND_PARAMETER == supportedSearchScheme) {
Set<List<AttributeDTO>> requestSet = new HashSet<List<AttributeDTO>>();
for (Map.Entry<String, Set<AttributeDTO>> entry : attributesMap.entrySet()) {
Map<String, Set<AttributeDTO>> map = new HashMap<String, Set<AttributeDTO>>();
for (AttributeDTO dto : entry.getValue()) {
if (!map.containsKey(dto.getCategory())) {
Set<AttributeDTO> attributeDTOSet = new HashSet<AttributeDTO>();
attributeDTOSet.add(dto);
map.put(dto.getCategory(), attributeDTOSet);
}
map.get(dto.getCategory()).add(dto);
}
requestSet.addAll(getAllCombinationsWithCategory(map));
}
return requestSet;
} else if (PolicyFinderModule.NO_COMBINATIONS == supportedSearchScheme) {
Set<List<AttributeDTO>> requestSet = new HashSet<List<AttributeDTO>>();
for (Map.Entry<String, Set<AttributeDTO>> entry : attributesMap.entrySet()) {
requestSet.add(new ArrayList<AttributeDTO>(entry.getValue()));
}
return requestSet;
}
return null;
}
/**
* Helper method to get all possible combination for given set of attributes
*
* @param allAttributes
* @return
*/
private Set<List<AttributeDTO>> getAllCombinations(Set<AttributeDTO> allAttributes) {
Set<List<AttributeDTO>> requestSet = new HashSet<List<AttributeDTO>>();
if (allAttributes.isEmpty()) {
requestSet.add(new ArrayList<AttributeDTO>());
return requestSet;
}
List<AttributeDTO> list = new ArrayList<AttributeDTO>(allAttributes);
AttributeDTO head = list.get(0);
Set<AttributeDTO> rest = new HashSet<AttributeDTO>(list.subList(1, list.size()));
for (List<AttributeDTO> set : getAllCombinations(rest)) {
List<AttributeDTO> newSet = new ArrayList<AttributeDTO>();
newSet.add(head);
newSet.addAll(set);
requestSet.add(newSet);
requestSet.add(set);
}
return requestSet;
}
/**
* Helper method to get all possible combination for given set of attributes based on category
*
* @param attributesMap
* @return
*/
private Set<List<AttributeDTO>> getAllCombinationsWithCategory(Map<String, Set<AttributeDTO>> attributesMap) {
Set<List<AttributeDTO>> requestSet = new HashSet<List<AttributeDTO>>();
List<String> categories = new ArrayList<String>(attributesMap.keySet());
if (!categories.isEmpty()) {
String category = categories.get(0);
Set<AttributeDTO> attributeDTOs = attributesMap.get(category);
List<AttributeDTO> dtoList;
for (AttributeDTO dto : attributeDTOs) {
dtoList = new ArrayList<AttributeDTO>();
dtoList.add(dto);
if (categories.get(1) != null) {
processCombinations(1, categories, attributesMap, dtoList, requestSet);
}
}
}
return requestSet;
}
/**
* Helper method to get all possible combination for given set of attributes based on category
*
* @param i
* @param categories
* @param attributesMap
* @param dtoList
* @param requestSet
*/
private void processCombinations(int i, List<String> categories, Map<String,
Set<AttributeDTO>> attributesMap, List<AttributeDTO> dtoList,
Set<List<AttributeDTO>> requestSet) {
if (categories.size() > i) {
String category = categories.get(i);
i++;
if (category != null) {
List<AttributeDTO> currentList = new ArrayList<AttributeDTO>(dtoList);
Set<AttributeDTO> attributeDTOs = attributesMap.get(category);
for (AttributeDTO dto : attributeDTOs) {
dtoList.add(dto);
processCombinations(i, categories, attributesMap, dtoList, requestSet);
requestSet.add(dtoList);
dtoList = new ArrayList<AttributeDTO>(currentList);
}
}
}
}
/**
* Helper method to get XACML decision
*
* @param requestAttributes XACML request attributes
* @return whether permit or deny
*/
private boolean getResponse(List<AttributeDTO> requestAttributes) {
ResponseCtx responseCtx;
AbstractRequestCtx requestCtx = EntitlementUtil.createRequestContext(requestAttributes);
responseCtx = EntitlementEngine.getInstance().evaluateByContext(requestCtx);
if (responseCtx != null) {
Set<AbstractResult> results = responseCtx.getResults();
for (AbstractResult result : results) {
if (result.getDecision() == AbstractResult.DECISION_PERMIT) {
return true;
}
}
}
return false;
}
}