Skip to content

Commit

Permalink
Add exclusion list option for calling DatabaseMetaData.getUserName (#…
Browse files Browse the repository at this point in the history
…3568)

* use a dummy user for testing

* exclusion list option for calling getUserName

* changelog and test break fixed
  • Loading branch information
jackshirazi authored Apr 2, 2024
1 parent a10ecd4 commit 2e1bce7
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
===== Features
* Differentiate Lambda URLs from API Gateway in AWS Lambda integration - {pull}3417[#3417]
* Added lambda support for ELB triggers {pull}#3411[#3411]
* Add exclusion list option for calling DatabaseMetaData.getUserName - {pull}#3568[#3568]
[[release-notes-1.x]]
=== Java Agent version 1.x
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jdbc.helper;

import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.tracer.configuration.WildcardMatcherValueConverter;
import org.stagemonitor.configuration.ConfigurationOption;
import org.stagemonitor.configuration.ConfigurationOptionProvider;
import org.stagemonitor.configuration.converter.DoubleValueConverter;
import org.stagemonitor.configuration.converter.ListValueConverter;
import org.stagemonitor.configuration.converter.StringValueConverter;

import java.util.Arrays;
import java.util.List;

public class JdbcConfiguration extends ConfigurationOptionProvider {

private final ConfigurationOption<List<String>> databaseMetaDataExclusionList = ConfigurationOption
.builder(new ListValueConverter<String>(StringValueConverter.INSTANCE), List.class)
.key("exclude_from_getting_username")
.configurationCategory("Datastore")
.description("If any of these strings match part of the package or class name of the DatabaseMetaData instance, getUserName() won't be called" +
"\n" +
WildcardMatcher.DOCUMENTATION
)
.tags("internal","added[1.49.0]")
.dynamic(true)
.buildWithDefault(Arrays.asList(
"hikari"
));

public List<String> getDatabaseMetaDataExclusionList() {
return databaseMetaDataExclusionList.get();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@
import co.elastic.apm.agent.sdk.internal.db.signature.Scanner;
import co.elastic.apm.agent.sdk.internal.db.signature.SignatureParser;
import co.elastic.apm.agent.tracer.AbstractSpan;
import co.elastic.apm.agent.tracer.GlobalTracer;
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.ElasticContext;
import co.elastic.apm.agent.jdbc.JdbcFilter;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
import co.elastic.apm.agent.tracer.Tracer;

import javax.annotation.Nullable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.concurrent.Callable;

import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.metaDataMap;
Expand All @@ -45,6 +48,11 @@ public class JdbcHelper {
public static final String DB_SPAN_ACTION = "query";

private static final JdbcHelper INSTANCE = new JdbcHelper();
private final JdbcConfiguration config;

public JdbcHelper() {
this.config = GlobalTracer.get().getConfig(JdbcConfiguration.class);
}

public static JdbcHelper get() {
return INSTANCE;
Expand Down Expand Up @@ -181,7 +189,7 @@ private ConnectionMetaData getConnectionMetaData(@Nullable Connection connection
DatabaseMetaData metaData = connection.getMetaData();
connectionMetaData = ConnectionMetaData.parse(metaData.getURL())
.withConnectionInstance(safeGetCatalog(connection))
.withConnectionUser(metaData.getUserName())
.withConnectionUser(maybeGetUserName(metaData, config))
.build();

if (logger.isDebugEnabled()) {
Expand All @@ -201,6 +209,17 @@ private ConnectionMetaData getConnectionMetaData(@Nullable Connection connection
return connectionMetaData;
}

static String maybeGetUserName(DatabaseMetaData metaData, JdbcConfiguration config) throws SQLException {
List<String> exclusionList = config.getDatabaseMetaDataExclusionList();
String classname = metaData.getClass().getName();
for (String exclude : exclusionList) {
if (classname.contains(exclude)) {
return null;
}
}
return metaData.getUserName();
}

@Nullable
private String safeGetCatalog(Connection connection) {
String catalog = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
co.elastic.apm.agent.jdbc.helper.JdbcConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.jdbc.helper;

import co.elastic.apm.agent.AbstractInstrumentationTest;
import co.elastic.apm.agent.MockTracer;
import co.elastic.apm.agent.bci.ElasticApmAgent;
import co.elastic.apm.agent.configuration.SpyConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.stagemonitor.configuration.ConfigurationRegistry;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;

import static co.elastic.apm.agent.testutils.assertions.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;

public class JdbcGetUserNameExclusionTest extends AbstractInstrumentationTest {

protected static JdbcConfiguration jdbcconfig;

@Test
public void hasUsernameCorrectlyExcludes() throws SQLException {
DatabaseMetaData meta = (DatabaseMetaData) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[] { DatabaseMetaData.class },
new MetadataInvocationHandler());

assertThat(JdbcHelper.maybeGetUserName(meta, config.getConfig(JdbcConfiguration.class))).isEqualTo("testuser");

String classname = meta.getClass().getName();
String excludeName = classname.substring(classname.indexOf('$')+1);
doReturn(List.of(excludeName))
.when(config.getConfig(JdbcConfiguration.class))
.getDatabaseMetaDataExclusionList();

assertThat(JdbcHelper.maybeGetUserName(meta, config.getConfig(JdbcConfiguration.class))).isEqualTo(null);
}

public class MetadataInvocationHandler implements InvocationHandler {

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getUserName")) {
return "testuser";
}
return null;
}
}

}

0 comments on commit 2e1bce7

Please sign in to comment.