Skip to content

Commit

Permalink
Support for jcache-1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
monu-k2io committed Nov 16, 2023
1 parent 60e97fa commit d3c5ce4
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 1 deletion.
21 changes: 21 additions & 0 deletions instrumentation-security/jcache-1.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

dependencies {
implementation(project(":newrelic-security-api"))
implementation("com.newrelic.agent.java:newrelic-api:${nrAPIVersion}")
implementation("com.newrelic.agent.java:newrelic-weaver-api:${nrAPIVersion}")
implementation("javax.cache:cache-api:1.0.0")
testImplementation("com.hazelcast:hazelcast:4.2.8")
}

jar {
manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.security.jcache-1.0.0' }
}

verifyInstrumentation {
passes 'javax.cache:cache-api:[1.0.0,)'
}

site {
title 'JCache API'
type 'Framework'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.newrelic.agent.security.instrumentation.jcache_1_0_0;

import com.newrelic.api.agent.security.NewRelicSecurity;
import com.newrelic.api.agent.security.instrumentation.helpers.GenericHelper;
import com.newrelic.api.agent.security.schema.AbstractOperation;
import com.newrelic.api.agent.security.schema.exceptions.NewRelicSecurityException;
import com.newrelic.api.agent.security.schema.operation.JCacheOperation;

import java.util.List;

public class JCacheHelper {
public static final String READ = "read";
public static final String WRITE = "write";
public static final String DELETE = "delete";
public static final String UPDATE = "update";
public static final String NR_SEC_CUSTOM_ATTRIB_NAME = "JCACHE-OPERATION-LOCK-";

public static AbstractOperation preprocessSecurityHook(String command, List<Object> args, String klass, String method) {
try {
if (!NewRelicSecurity.isHookProcessingActive() || NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty()){
return null;
}
JCacheOperation operation = new JCacheOperation(klass, method, command, args);
NewRelicSecurity.getAgent().registerOperation(operation);
return operation;
} catch (Throwable e) {
if (e instanceof NewRelicSecurityException) {
throw e;
}
}
return null;
}

public static void registerExitOperation(boolean isProcessingAllowed, AbstractOperation operation) {
try {
if (operation == null || !isProcessingAllowed || !NewRelicSecurity.isHookProcessingActive() ||
NewRelicSecurity.getAgent().getSecurityMetaData().getRequest().isEmpty()) {
return;
}
NewRelicSecurity.getAgent().registerExitEvent(operation);
} catch (Throwable ignored){}
}

public static void releaseLock(int hashcode) {
try {
GenericHelper.releaseLock(NR_SEC_CUSTOM_ATTRIB_NAME, hashcode);
} catch (Throwable ignored) {}
}

public static boolean acquireLockIfPossible(int hashcode) {
try {
return GenericHelper.acquireLockIfPossible(NR_SEC_CUSTOM_ATTRIB_NAME, hashcode);
} catch (Throwable ignored) {}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package javax.cache;

import com.newrelic.agent.security.instrumentation.jcache_1_0_0.JCacheHelper;
import com.newrelic.api.agent.security.schema.AbstractOperation;
import com.newrelic.api.agent.weaver.MatchType;
import com.newrelic.api.agent.weaver.Weave;
import com.newrelic.api.agent.weaver.Weaver;

import javax.cache.integration.CompletionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Weave(type = MatchType.Interface, originalName = "javax.cache.Cache")
public abstract class Cache_Instrumentation<K, V> {
public V get(K key) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.READ, Collections.singletonList(key), this.getClass().getName(), "get");
}
V returnValue = null;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public Map<K, V> getAll(Set<? extends K> keys) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.READ, new ArrayList<Object>() { { addAll(keys); } }, this.getClass().getName(), "getAll");
}
Map<K, V> returnValue = null;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public boolean containsKey(K key) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.READ, Collections.singletonList(key), this.getClass().getName(), "containsKey");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener completionListener) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.READ, new ArrayList<Object>() { { addAll(keys); } }, this.getClass().getName(), "loadAll");
}
try {
Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
}

public void put(K key, V value) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.WRITE, Arrays.asList(key, value), this.getClass().getName(), "put");
}
try {
Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
}

public V getAndPut(K key, V value) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.WRITE, Arrays.asList(key, value), this.getClass().getName(), "getAndPut");
}
V returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public void putAll(Map<? extends K, ? extends V> map) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
List<Object> argList = new ArrayList<>();
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
argList.add(entry.getKey());
argList.add(entry.getValue());
}
// do not call register exit operation method, this will lead to a verify error
// Type 'java/lang/Object' (current frame, stack[0]) is not assignable to 'com/newrelic/api/agent/security/schema/AbstractOperation'
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.WRITE, argList, this.getClass().getName(), "putAll");
}
try {
Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
}

public boolean putIfAbsent(K key, V value) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.WRITE, Arrays.asList(key, value), this.getClass().getName(), "putIfAbsent");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public boolean remove(K key) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.DELETE, Collections.singletonList(key), this.getClass().getName(), "remove");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public boolean remove(K key, V oldValue) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.DELETE, Arrays.asList(key, oldValue), this.getClass().getName(), "remove");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public V getAndRemove(K key) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.DELETE, Collections.singletonList(key), this.getClass().getName(), "getAndRemove");
}
V returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public boolean replace(K key, V oldValue) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.UPDATE, Arrays.asList(key, oldValue), this.getClass().getName(), "replace");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public boolean replace(K key, V oldValue, V newValue) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.UPDATE, Arrays.asList(key, oldValue, newValue), this.getClass().getName(), "replace");
}
boolean returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public V getAndReplace(K key, V value) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.UPDATE, Arrays.asList(key, value), this.getClass().getName(), "getAndReplace");
}
V returnValue;
try {
returnValue = Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
return returnValue;
}

public void removeAll(Set<? extends K> keys) {
boolean isLockAcquired = JCacheHelper.acquireLockIfPossible(this.hashCode());
AbstractOperation operation = null;
if (isLockAcquired) {
operation = JCacheHelper.preprocessSecurityHook(JCacheHelper.DELETE, new ArrayList<Object>() { { addAll(keys); } }, this.getClass().getName(), "removeAll");
}
try {
Weaver.callOriginal();
} finally {
if(isLockAcquired) {
JCacheHelper.releaseLock(this.hashCode());
}
}
JCacheHelper.registerExitOperation(isLockAcquired, operation);
}
}
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,5 @@ include 'instrumentation:commons-jxpath'
//include 'instrumentation:apache-wicket-6.4'
//include 'instrumentation:apache-wicket-7.0'
//include 'instrumentation:apache-wicket-8.0'
include 'instrumentation:spring-data-redis'
include 'instrumentation:spring-data-redis'
include 'instrumentation:jcache-1.0.0'

0 comments on commit d3c5ce4

Please sign in to comment.