Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,53 @@ public void testPolicyEvalCacheWithMultiplePolicySets() throws Exception {

}

/**
* This test makes sure that cached policy evaluation results are properly invalidated when an affected resource
* is created.
*/
@Test
public void testPolicyEvalCacheWhenResourceAdded() throws Exception {
String endpoint = this.acsUrl;

// create test subject
BaseSubject subject = MARISSA_V1;
this.privilegeHelper.putSubject(this.acsAdminRestTemplate, subject, endpoint, this.acsZone1Headers,
this.privilegeHelper.getDefaultAttribute(), this.privilegeHelper.getDefaultOrgAttribute());

// create test policy set
String policyFile = "src/test/resources/policies/single-org-based.json";
this.policyHelper.setTestPolicy(this.acsAdminRestTemplate, this.acsZone1Headers, endpoint, policyFile);

// post policy evaluation request
PolicyEvaluationRequestV1 policyEvaluationRequest = this.policyHelper
.createEvalRequest(MARISSA_V1.getSubjectIdentifier(), "sanramon");
ResponseEntity<PolicyEvaluationResult> postForEntity = this.acsAdminRestTemplate.postForEntity(
endpoint + PolicyHelper.ACS_POLICY_EVAL_API_PATH,
new HttpEntity<>(policyEvaluationRequest, this.acsZone1Headers), PolicyEvaluationResult.class);

Assert.assertEquals(postForEntity.getStatusCode(), HttpStatus.OK);
PolicyEvaluationResult responseBody = postForEntity.getBody();
Assert.assertEquals(responseBody.getEffect(), Effect.NOT_APPLICABLE);

// at this point evaluation decision is cached
// timestamps for subject and resource involved in the decision are also cached even though resource doesn't
// exist yet

// add a resource which is expected to reset resource cached timestamp and invalidate cached decision
BaseResource resource = new BaseResource("/secured-by-value/sites/sanramon");
this.privilegeHelper.putResource(this.acsAdminRestTemplate, resource, endpoint, this.acsZone1Headers,
this.privilegeHelper.getDefaultOrgAttribute());

// post policy evaluation request; decision should be reevaluated.
postForEntity = this.acsAdminRestTemplate.postForEntity(
endpoint + PolicyHelper.ACS_POLICY_EVAL_API_PATH,
new HttpEntity<>(policyEvaluationRequest, this.acsZone1Headers), PolicyEvaluationResult.class);

Assert.assertEquals(postForEntity.getStatusCode(), HttpStatus.OK);
responseBody = postForEntity.getBody();
Assert.assertEquals(responseBody.getEffect(), Effect.PERMIT);
}

/**
* This test makes sure that cached policy evaluation results are properly invalidated when an affected resource
* is updated with different attributes.
Expand Down Expand Up @@ -252,6 +299,53 @@ public void testPolicyEvalCacheWhenResourceChanges() throws Exception {

}

/**
* This test makes sure that cached policy evaluation results are properly invalidated when an affected subject
* is created.
*/
@Test
public void testPolicyEvalCacheWhenSubjectAdded() throws Exception {
String endpoint = this.acsUrl;

//create test resource
BaseResource resource = new BaseResource("/secured-by-value/sites/sanramon");
this.privilegeHelper.putResource(this.acsAdminRestTemplate, resource, endpoint, this.acsZone1Headers,
this.privilegeHelper.getDefaultAttribute());

// create test policy set
String policyFile = "src/test/resources/policies/single-site-based.json";
this.policyHelper.setTestPolicy(this.acsAdminRestTemplate, this.acsZone1Headers, endpoint, policyFile);

// post policy evaluation request
PolicyEvaluationRequestV1 policyEvaluationRequest = this.policyHelper
.createEvalRequest(MARISSA_V1.getSubjectIdentifier(), "sanramon");
ResponseEntity<PolicyEvaluationResult> postForEntity = this.acsAdminRestTemplate.postForEntity(
endpoint + PolicyHelper.ACS_POLICY_EVAL_API_PATH,
new HttpEntity<>(policyEvaluationRequest, this.acsZone1Headers), PolicyEvaluationResult.class);

Assert.assertEquals(postForEntity.getStatusCode(), HttpStatus.OK);
PolicyEvaluationResult responseBody = postForEntity.getBody();
Assert.assertEquals(responseBody.getEffect(), Effect.NOT_APPLICABLE);

// at this point evaluation decision is cached
// timestamps for resource and subject involved in the decision are also cached even though subject doesn't
// exist yet

// add a subject which is expected to reset subject cached timestamp and invalidate cached decision
BaseSubject subject = MARISSA_V1;
this.privilegeHelper.putSubject(this.acsAdminRestTemplate, subject, endpoint, this.acsZone1Headers,
this.privilegeHelper.getDefaultAttribute(), this.privilegeHelper.getDefaultAttribute());

// post policy evaluation request; decision should be reevaluated.
postForEntity = this.acsAdminRestTemplate.postForEntity(
endpoint + PolicyHelper.ACS_POLICY_EVAL_API_PATH,
new HttpEntity<>(policyEvaluationRequest, this.acsZone1Headers), PolicyEvaluationResult.class);

Assert.assertEquals(postForEntity.getStatusCode(), HttpStatus.OK);
responseBody = postForEntity.getBody();
Assert.assertEquals(responseBody.getEffect(), Effect.PERMIT);
}

/**
* This test makes sure that cached policy evaluation results are properly invalidated when an affected subject is
* updated or deleted.
Expand Down Expand Up @@ -290,7 +384,7 @@ public void testPolicyEvalCacheWhenSubjectChanges() throws Exception {
* This test makes sure that cached policy evaluation results are properly invalidated when a policy has multiple
* resource attribute URI templates that match the request.
*/
@Test(enabled = false)
@Test
public void testPolicyWithMultAttrUriTemplatatesEvalCache() throws Exception {
BaseSubject subject = MARISSA_V1;
PolicyEvaluationRequestV1 policyEvaluationRequest = this.policyHelper.createEvalRequest("GET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
public class AttributeCacheFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(AttributeCacheFactory.class);

@Value("${ENABLE_RESOURCE_CACHING:false}")
@Value("${ENABLE_RESOURCE_CACHING:true}")
private boolean resourceCachingEnabled;

@Value("${ENABLE_SUBJECT_CACHING:false}")
@Value("${ENABLE_SUBJECT_CACHING:true}")
private boolean subjectCachingEnabled;

private Environment environment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@
public class PolicyEvaluationCacheConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(PolicyEvaluationCacheConfig.class);

@Value("${ENABLE_DECISION_CACHING:false}")
private boolean cachingEnabled;

@Autowired
private Environment environment;

@Bean
public PolicyEvaluationCache cache() {
if (!this.cachingEnabled) {
public PolicyEvaluationCache cache(@Value("${ENABLE_DECISION_CACHING:true}") final boolean cachingEnabled) {
if (!cachingEnabled) {
LOGGER.info("Caching disabled for policy evaluation");
return new NonCachingPolicyEvaluationCache();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public abstract class AbstractPolicyEvaluationCache implements PolicyEvaluationC
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

/**
* This method will get the Policy Evaluation Result in the cache. It will check if any of the subject, policy
* This method will get the Policy Evaluation Result from the cache. It will check if any of the subject, policy
* sets or resolved resource URI's have a timestamp in the cache after the timestamp of the Policy Evaluation
* Result. If the key is not in the cache or the result is invalidated, it will return null. Also it will remove
* the Policy EvaluationResult so that subsequent evaluations won't find the key in the cache.
Expand All @@ -94,6 +94,7 @@ public PolicyEvaluationResult get(final PolicyEvaluationRequestCacheKey evalRequ
policyInvalidationTimeStamps.addAll(cachedEntries.getPolicySetsLastModified());

Set<String> cachedResolvedResourceUris = cachedEvalResult.getResolvedResourceUris();

//is requested resource id same as resolved resource uri ?
if (cachedResolvedResourceUris.size() == 1 && cachedResolvedResourceUris.iterator().next()
.equals(evalRequestkey.getResourceId())) {
Expand All @@ -106,7 +107,7 @@ public PolicyEvaluationResult get(final PolicyEvaluationRequestCacheKey evalRequ
}

if (isCachedRequestInvalid(attributeInvalidationTimeStamps, policyInvalidationTimeStamps,
new DateTime(cachedEvalResult.getTimestamp()))) {
timestampToDateUTC(cachedEvalResult.getTimestamp()))) {
delete(cachedEntries.getDecisionKey());
LOGGER.debug("Cached decision for key '{}' is not valid.", cachedEntries.getDecisionKey());
return null;
Expand Down Expand Up @@ -195,25 +196,25 @@ List<String> getPolicySetsLastModified() {

private void logCacheGetDebugMessages(final PolicyEvaluationRequestCacheKey key, final String redisKey,
final List<String> keys, final List<String> values) {
if (LOGGER.isDebugEnabled()) {
LinkedHashSet<String> policySetIds = key.getPolicySetIds();
policySetIds.forEach(policySetId -> LOGGER.debug(String
.format("Getting timestamp for policy set: '%s', key: '%s', timestamp:'%s'.", policySetId,
keys.get(0), values.get(0))));
LOGGER.debug("Getting timestamp for resource: '{}', key: '{}', timestamp:'{}'.", key.getResourceId(),
keys.get(1), values.get(1));
LOGGER.debug("Getting timestamp for subject: '{}', key: '{}', timestamp:'{}'.", key.getSubjectId(),
keys.get(2), values.get(2));
LOGGER.debug("Getting policy evaluation from cache; key: '{}', value: '{}'.", redisKey, values.get(3));
LinkedHashSet<String> policySetIds = key.getPolicySetIds();
int idx = 0;
for (String policySetId : policySetIds) {
LOGGER.debug("Getting timestamp for policy set: '{}', key: '{}', timestamp:'{}'.", policySetId,
keys.get(idx), values.get(idx++));
}
LOGGER.debug("Getting timestamp for subject: '{}', key: '{}', timestamp:'{}'.", key.getSubjectId(),
keys.get(idx), values.get(idx++));
LOGGER.debug("Getting timestamp for resource: '{}', key: '{}', timestamp:'{}'.", key.getResourceId(),
keys.get(idx), values.get(idx++));
LOGGER.debug("Getting policy evaluation from cache; key: '{}', value: '{}'.", redisKey, values.get(idx));
}

// Set's the policy evaluation key to the policy evaluation result in the cache
@Override
public void set(final PolicyEvaluationRequestCacheKey key, final PolicyEvaluationResult result) {
try {
setEntityTimestamps(key, result);
result.setTimestamp(new DateTime().getMillis());
result.setTimestamp(currentDateUTC().getMillis());
String value = OBJECT_MAPPER.writeValueAsString(result);
set(key.toDecisionKey(), value);
LOGGER.debug("Setting policy evaluation to cache; key: '{}', value: '{}'.", key.toDecisionKey(), value);
Expand All @@ -228,6 +229,8 @@ private void setEntityTimestamps(final PolicyEvaluationRequestCacheKey key, fina
// decision.
// We reset the timestamp to now for entities only if they do not exist in the cache so that we don't
// invalidate previous cached decisions.
LOGGER.debug("Setting timestamp to now for entities if they do not exist in the cache");

String zoneId = key.getZoneId();
setSubjectIfNotExists(zoneId, key.getSubjectId());

Expand Down Expand Up @@ -257,15 +260,15 @@ public void reset(final PolicyEvaluationRequestCacheKey key) {
private void resetForEntity(final String zoneId, final String entityId, final EntityType entityType,
final BiFunction<String, String, String> getKey) {
String key = getKey.apply(zoneId, entityId);
String timestamp = timestampValue();
String timestamp = timestampUTC();
logSetEntityTimestampsDebugMessage(timestamp, key, entityId, entityType);
set(key, timestamp);
}

private void setEntityIfNotExists(final String zoneId, final String entityId,
final BiFunction<String, String, String> getKey) {
String key = getKey.apply(zoneId, entityId);
setIfNotExists(key, timestampValue());
setIfNotExists(key, timestampUTC());
}

private void logSetEntityTimestampsDebugMessage(final String timestamp, final String key, final String entityId,
Expand Down Expand Up @@ -343,17 +346,17 @@ public void resetForSubjects(final String zoneId, final List<SubjectEntity> subj
private void createMutliSetEntityMap(final String zoneId, final Map<String, String> map, final String subjectId,
final EntityType entityType, final BiFunction<String, String, String> getKey) {
String key = getKey.apply(zoneId, subjectId);
String timestamp = timestampValue();
String timestamp = timestampUTC();
logSetEntityTimestampsDebugMessage(key, timestamp, subjectId, entityType);
map.put(key, timestamp);
}

private boolean isCachedRequestInvalid(final List<String> attributeInvalidationTimeStamps,
final List<String> policyInvalidationTimeStamps, final DateTime policyEvalTimestamp) {
DateTime policyEvalTimestampUTC = policyEvalTimestamp.withZone(DateTimeZone.UTC);
final List<String> policyInvalidationTimeStamps, final DateTime policyEvalTimestampUTC) {
if (haveEntitiesChanged(policyInvalidationTimeStamps, policyEvalTimestampUTC)) {
return true;
}

if (this.connectorService.isResourceAttributeConnectorConfigured() || this.connectorService
.isSubjectAttributeConnectorConfigured()) {
return haveConnectorCacheIntervalsLapsed(this.connectorService, policyEvalTimestampUTC);
Expand All @@ -376,12 +379,7 @@ boolean haveEntitiesChanged(final List<String> values, final DateTime policyEval
if (null == value) {
return true;
}
DateTime invalidationTimestampUTC;
try {
invalidationTimestampUTC = (OBJECT_MAPPER.readValue(value, DateTime.class)).withZone(DateTimeZone.UTC);
} catch (IOException e) {
throw new IllegalStateException("Failed to read timestamp from JSON.", e);
}
DateTime invalidationTimestampUTC = timestampToDateUTC(value);
if (invalidationTimestampUTC.isAfter(policyEvalTimestampUTC)) {
LOGGER.debug("Privilege service attributes have timestamp '{}' which is after "
+ "policy evaluation timestamp '{}'", invalidationTimestampUTC, policyEvalTimestampUTC);
Expand All @@ -393,7 +391,7 @@ boolean haveEntitiesChanged(final List<String> values, final DateTime policyEval

boolean haveConnectorCacheIntervalsLapsed(final AttributeConnectorService localConnectorService,
final DateTime policyEvalTimestampUTC) {
DateTime nowUTC = new DateTime().withZone(DateTimeZone.UTC);
DateTime nowUTC = currentDateUTC();

int decisionAgeMinutes = Minutes.minutesBetween(policyEvalTimestampUTC, nowUTC).getMinutes();

Expand Down Expand Up @@ -436,14 +434,26 @@ static boolean isSubjectChangedKey(final String key) {
return key.matches("^[^:]*:sub-id:[^:]*$");
}

private String timestampValue() {
private static DateTime currentDateUTC() {
return new DateTime().withZone(DateTimeZone.UTC);
}

private static String timestampUTC() {
try {
return OBJECT_MAPPER.writeValueAsString(new DateTime());
return OBJECT_MAPPER.writeValueAsString(currentDateUTC().getMillis());
} catch (IOException e) {
throw new IllegalStateException("Failed to write timestamp as JSON.", e);
}
}

private static DateTime timestampToDateUTC(final long timestamp) {
return new DateTime(timestamp).withZone(DateTimeZone.UTC);
}

private static DateTime timestampToDateUTC(final String timestamp) {
return new DateTime(Long.valueOf(timestamp)).withZone(DateTimeZone.UTC);
}

abstract void delete(String key);

abstract void delete(Collection<String> keys);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.ge.predix.acs.privilege.management;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -176,15 +177,17 @@ private ResourceEntity upsertResourceInTransaction(final BaseResource resource,
LOGGER.debug("Found an existing resource with resourceIdentifier = {}, " + "zone = {}. Upserting the same.",
resource.getResourceIdentifier(), zone);
updatedResource.setId(persistedResource.getId());
this.cache.resetForResourcesByIds(zone.getName(),
this.resourceRepository.getResourceEntityAndDescendantsIds(updatedResource));
} else {
LOGGER.debug(
"Found no existing resource. Creating a new one with the resourceIdentifier = {}," + " zone = {}.",
resource.getResourceIdentifier(), zone);
this.cache.resetForResourcesByIds(zone.getName(),
Collections.singleton(updatedResource.getResourceIdentifier()));
}

try {
this.cache.resetForResourcesByIds(zone.getName(),
this.resourceRepository.getResourceEntityAndDescendantsIds(updatedResource));
this.resourceRepository.save(updatedResource);
} catch (Exception e) {
String message = String
Expand Down Expand Up @@ -339,11 +342,14 @@ private SubjectEntity upsertSubjectInTransaction(final BaseSubject subject, fina

if (persistedSubject != null) {
updatedSubject.setId(persistedSubject.getId());
this.cache.resetForSubjectsByIds(zone.getName(),
this.subjectRepository.getSubjectEntityAndDescendantsIds(updatedSubject));
} else {
this.cache.resetForSubjectsByIds(zone.getName(),
Collections.singleton(updatedSubject.getSubjectIdentifier()));
}

try {
this.cache.resetForSubjectsByIds(zone.getName(),
this.subjectRepository.getSubjectEntityAndDescendantsIds(updatedSubject));
this.subjectRepository.save(updatedSubject);
} catch (Exception e) {
String message = String
Expand Down
Loading