Skip to content

Commit 2e1bce7

Browse files
authored
Add exclusion list option for calling DatabaseMetaData.getUserName (#3568)
* use a dummy user for testing * exclusion list option for calling getUserName * changelog and test break fixed
1 parent a10ecd4 commit 2e1bce7

File tree

5 files changed

+151
-1
lines changed

5 files changed

+151
-1
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
3939
===== Features
4040
* Differentiate Lambda URLs from API Gateway in AWS Lambda integration - {pull}3417[#3417]
4141
* Added lambda support for ELB triggers {pull}#3411[#3411]
42+
* Add exclusion list option for calling DatabaseMetaData.getUserName - {pull}#3568[#3568]
4243
4344
[[release-notes-1.x]]
4445
=== Java Agent version 1.x
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.jdbc.helper;
20+
21+
import co.elastic.apm.agent.common.util.WildcardMatcher;
22+
import co.elastic.apm.agent.tracer.configuration.WildcardMatcherValueConverter;
23+
import org.stagemonitor.configuration.ConfigurationOption;
24+
import org.stagemonitor.configuration.ConfigurationOptionProvider;
25+
import org.stagemonitor.configuration.converter.DoubleValueConverter;
26+
import org.stagemonitor.configuration.converter.ListValueConverter;
27+
import org.stagemonitor.configuration.converter.StringValueConverter;
28+
29+
import java.util.Arrays;
30+
import java.util.List;
31+
32+
public class JdbcConfiguration extends ConfigurationOptionProvider {
33+
34+
private final ConfigurationOption<List<String>> databaseMetaDataExclusionList = ConfigurationOption
35+
.builder(new ListValueConverter<String>(StringValueConverter.INSTANCE), List.class)
36+
.key("exclude_from_getting_username")
37+
.configurationCategory("Datastore")
38+
.description("If any of these strings match part of the package or class name of the DatabaseMetaData instance, getUserName() won't be called" +
39+
"\n" +
40+
WildcardMatcher.DOCUMENTATION
41+
)
42+
.tags("internal","added[1.49.0]")
43+
.dynamic(true)
44+
.buildWithDefault(Arrays.asList(
45+
"hikari"
46+
));
47+
48+
public List<String> getDatabaseMetaDataExclusionList() {
49+
return databaseMetaDataExclusionList.get();
50+
}
51+
52+
}

apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@
2121
import co.elastic.apm.agent.sdk.internal.db.signature.Scanner;
2222
import co.elastic.apm.agent.sdk.internal.db.signature.SignatureParser;
2323
import co.elastic.apm.agent.tracer.AbstractSpan;
24+
import co.elastic.apm.agent.tracer.GlobalTracer;
2425
import co.elastic.apm.agent.tracer.Span;
2526
import co.elastic.apm.agent.tracer.ElasticContext;
2627
import co.elastic.apm.agent.jdbc.JdbcFilter;
2728
import co.elastic.apm.agent.sdk.logging.Logger;
2829
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
2930
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
31+
import co.elastic.apm.agent.tracer.Tracer;
3032

3133
import javax.annotation.Nullable;
3234
import java.sql.Connection;
3335
import java.sql.DatabaseMetaData;
3436
import java.sql.SQLException;
3537
import java.sql.Statement;
38+
import java.util.List;
3639
import java.util.concurrent.Callable;
3740

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

4750
private static final JdbcHelper INSTANCE = new JdbcHelper();
51+
private final JdbcConfiguration config;
52+
53+
public JdbcHelper() {
54+
this.config = GlobalTracer.get().getConfig(JdbcConfiguration.class);
55+
}
4856

4957
public static JdbcHelper get() {
5058
return INSTANCE;
@@ -181,7 +189,7 @@ private ConnectionMetaData getConnectionMetaData(@Nullable Connection connection
181189
DatabaseMetaData metaData = connection.getMetaData();
182190
connectionMetaData = ConnectionMetaData.parse(metaData.getURL())
183191
.withConnectionInstance(safeGetCatalog(connection))
184-
.withConnectionUser(metaData.getUserName())
192+
.withConnectionUser(maybeGetUserName(metaData, config))
185193
.build();
186194

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

212+
static String maybeGetUserName(DatabaseMetaData metaData, JdbcConfiguration config) throws SQLException {
213+
List<String> exclusionList = config.getDatabaseMetaDataExclusionList();
214+
String classname = metaData.getClass().getName();
215+
for (String exclude : exclusionList) {
216+
if (classname.contains(exclude)) {
217+
return null;
218+
}
219+
}
220+
return metaData.getUserName();
221+
}
222+
204223
@Nullable
205224
private String safeGetCatalog(Connection connection) {
206225
String catalog = null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
co.elastic.apm.agent.jdbc.helper.JdbcConfiguration
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.jdbc.helper;
20+
21+
import co.elastic.apm.agent.AbstractInstrumentationTest;
22+
import co.elastic.apm.agent.MockTracer;
23+
import co.elastic.apm.agent.bci.ElasticApmAgent;
24+
import co.elastic.apm.agent.configuration.SpyConfiguration;
25+
import co.elastic.apm.agent.impl.ElasticApmTracer;
26+
import net.bytebuddy.agent.ByteBuddyAgent;
27+
import org.junit.After;
28+
import org.junit.BeforeClass;
29+
import org.junit.jupiter.api.AfterEach;
30+
import org.junit.jupiter.api.BeforeAll;
31+
import org.junit.jupiter.api.Test;
32+
import org.stagemonitor.configuration.ConfigurationRegistry;
33+
34+
import java.lang.reflect.InvocationHandler;
35+
import java.lang.reflect.Method;
36+
import java.lang.reflect.Proxy;
37+
import java.sql.DatabaseMetaData;
38+
import java.sql.SQLException;
39+
import java.util.List;
40+
41+
import static co.elastic.apm.agent.testutils.assertions.Assertions.assertThat;
42+
import static org.mockito.Mockito.doReturn;
43+
44+
public class JdbcGetUserNameExclusionTest extends AbstractInstrumentationTest {
45+
46+
protected static JdbcConfiguration jdbcconfig;
47+
48+
@Test
49+
public void hasUsernameCorrectlyExcludes() throws SQLException {
50+
DatabaseMetaData meta = (DatabaseMetaData) Proxy.newProxyInstance(
51+
this.getClass().getClassLoader(),
52+
new Class[] { DatabaseMetaData.class },
53+
new MetadataInvocationHandler());
54+
55+
assertThat(JdbcHelper.maybeGetUserName(meta, config.getConfig(JdbcConfiguration.class))).isEqualTo("testuser");
56+
57+
String classname = meta.getClass().getName();
58+
String excludeName = classname.substring(classname.indexOf('$')+1);
59+
doReturn(List.of(excludeName))
60+
.when(config.getConfig(JdbcConfiguration.class))
61+
.getDatabaseMetaDataExclusionList();
62+
63+
assertThat(JdbcHelper.maybeGetUserName(meta, config.getConfig(JdbcConfiguration.class))).isEqualTo(null);
64+
}
65+
66+
public class MetadataInvocationHandler implements InvocationHandler {
67+
68+
@Override
69+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
70+
if (method.getName().equals("getUserName")) {
71+
return "testuser";
72+
}
73+
return null;
74+
}
75+
}
76+
77+
}

0 commit comments

Comments
 (0)