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
@@ -1,14 +1,8 @@
// Copyright (c) 2022, Oracle and/or its affiliates.
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.buildhelper;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
Expand All @@ -17,6 +11,12 @@
import java.util.Collections;
import java.util.List;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

@Mojo(name = "gitVersion", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
public class GitTagMojo extends AbstractMojo {

Expand Down Expand Up @@ -61,7 +61,7 @@ private String toVersionString(String gitResponse) {
}

private String formatVersionString(String[] segments) {
final String commit = segments[segments.length - 1];
final String commit = segments[segments.length - 1].substring(1);
final String numCommits = segments[segments.length - 2];
final String versionParts = String.join("-", Arrays.copyOfRange(segments, 0, segments.length - 2));
return formatVersionString(commit, numCommits, versionParts);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
// Copyright (c) 2022, Oracle and/or its affiliates.
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.buildhelper;

import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertThrows;

class GitTagMojoTest {
Expand Down Expand Up @@ -88,7 +92,7 @@ void whenGitResponseIsVersionPlusHistory_createVersionProperty() throws Exceptio
mojo.execute();

assertThat(inMemoryFileSystem.getContents(outputFile.getAbsolutePath()),
containsString("version=gcb4385f3aa (946 commits since v3.3.5-3)"));
containsString("version=cb4385f3aa (946 commits since v3.3.5-3)"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2021, 2022, Oracle and/or its affiliates.
// Copyright (c) 2021, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.exporter;
Expand Down Expand Up @@ -52,9 +52,7 @@ private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MB
if (!metrics.isEmpty())
sort(metrics).forEach(metricsStream::printMetric);
} catch (RestQueryException e) {
metricsStream.println(
withCommentMarkers("REST service was unable to handle this query and returned a " + HTTP_BAD_REQUEST + "\n"
+ selector.getPrintableRequest()));
reportProblem(metricsStream, selector);
} catch (AuthenticationChallengeException e) { // don't add a message for this case
throw e;
} catch (IOException | RuntimeException e) {
Expand All @@ -63,6 +61,19 @@ private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MB
}
}

private void reportProblem(MetricsStream metricsStream, MBeanSelector selector) {
metricsStream.println(withCommentMarkers(getProblem(selector) + "\n" + selector.getPrintableRequest()));
}

private String getProblem(MBeanSelector selector) {
if (selector.isRequestForPrivilegedProperty())
return "You seem to have encountered a bug in the WebLogic REST API.\n" +
Copy link
Member

@rjeberhard rjeberhard Jan 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this text come from a resource bundle?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, nevermind. This is for an internal error. If it's easy then do it, but it's not critical.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could; actually, it could probably be removed, since I know have altered the code to exclude it.

" The JDBCServiceRuntime.JDBCDataSourceRuntimeMBeans.properties property " +
" may only be accessed by a user with administrator privileges.";
else
return "REST service was unable to handle this query and returned a " + HTTP_BAD_REQUEST;
}

private static String withCommentMarkers(String string) {
StringBuilder sb = new StringBuilder();
for (String s : string.split("\\r?\\n"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;

/**
* A client for sending http requests. Note that it does not do any authentication by itself.
Expand Down Expand Up @@ -227,7 +226,7 @@ private void processStatusCode() {
case HTTP_FORBIDDEN:
throw new ForbiddenException();
default:
if (response.getResponseCode() > SC_BAD_REQUEST)
if (response.getResponseCode() > HTTP_BAD_REQUEST)
throw createServerErrorException();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.exporter.domain;
Expand All @@ -24,6 +24,10 @@
* @author Russell Gold
*/
public class ExporterConfig implements MetricsProcessor {
// Due to a bug in the WLS REST API, a query that includes this field will fail if not run with admin privileges.
// The code thus ensures that it is excluded from all queries.
private static final String[] FORBIDDEN_FIELDS = {"JDBCServiceRuntime:JDBCDataSourceRuntimeMBeans:properties"};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious... Is this the only affected field or just the only one that we know about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the only one we've found so far. But I have written the exclusion code to make it easy to add others if we find them.


public static final String DOMAIN_NAME_PROPERTY = "DOMAIN";

private static final String QUERY_SYNC = "query_sync";
Expand Down Expand Up @@ -173,7 +177,7 @@ private QuerySyncConfiguration loadQuerySync(Object o) {

private void appendQueries(Object queriesYaml) {
for (Map<String,Object> selectorSpec : getAsListOfMaps(queriesYaml)) {
appendQuery(MBeanSelector.create(selectorSpec));
appendQuery(MBeanSelector.create(selectorSpec).withForbiddenFields(FORBIDDEN_FIELDS));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2017, 2020, Oracle and/or its affiliates.
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.exporter.domain;
Expand All @@ -8,6 +8,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.google.gson.Gson;
Expand All @@ -27,14 +28,15 @@ class JsonQuerySpec {
private Map<String, JsonQuerySpec> children = null;
private String keyName = null;
private List<String> selectedKeys = null;
private List<String> excludeFields = null;

/**
* Specifies the name of any mbean values which should be retrieved.
* @param newFields the field names to add to any previous defined
*/
void addFields(String... newFields) {
if (fields == null) fields = new ArrayList<>();
fields.addAll(Arrays.asList(newFields));
Arrays.stream(newFields).filter(Objects::nonNull).forEach(fields::add);
}

/**
Expand Down Expand Up @@ -62,6 +64,7 @@ JsonObject toJsonObject() {

result.add("links", new JsonArray());
if (fields != null) result.add("fields", asStringArray(fields));
if (excludeFields != null) result.add("excludeFields", asStringArray(excludeFields));
if (keyName != null) result.add(keyName, asStringArray(selectedKeys));
if (children != null) asChildObject(result);

Expand All @@ -81,4 +84,9 @@ private void asChildObject(JsonObject result) {
nesting.add(entry.getKey(), entry.getValue().toJsonObject());
}
}

public void excludeField(String fieldName) {
if (excludeFields == null) excludeFields = new ArrayList<>();
excludeFields.add(fieldName);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.exporter.domain;

import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand All @@ -15,6 +16,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

Expand Down Expand Up @@ -61,6 +63,7 @@ public class MBeanSelector {
private Map<String, MBeanSelector> nestedSelectors = new LinkedHashMap<>();
private QueryType queryType = QueryType.RUNTIME;
private long lastKeyTime = 0;
private String[] forbiddenFields;

private static MBeanSelector createDomainNameSelector() {
Map<String,Object> yaml = new HashMap<>();
Expand Down Expand Up @@ -228,6 +231,39 @@ public static MBeanSelector create(Map<String, Object> map) {
return new MBeanSelector(map);
}

MBeanSelector withForbiddenFields(String... forbiddenFields) {
setForbiddenFields(forbiddenFields);
return this;
}

private void setForbiddenFields(String[] forbiddenFields) {
this.forbiddenFields = forbiddenFields;
nestedSelectors.entrySet().forEach(this::defineNestedForbiddenFields);
}

private void defineNestedForbiddenFields(Map.Entry<String, MBeanSelector> entry) {
entry.getValue().setForbiddenFields(getNestedForbiddenFields(entry.getKey()));
}

private String[] getNestedForbiddenFields(String key) {
return Arrays.stream(forbiddenFields)
.map(f -> withoutMatchingTopLevel(f, key))
.filter(Objects::nonNull)
.toArray(String[]::new);
}

private String withoutMatchingTopLevel(String forbiddenField, String key) {
final int index = forbiddenField.indexOf(':');
if (index < 0)
return null;
else if (!key.equalsIgnoreCase(forbiddenField.substring(0, index))) {
return null;
} else {
return forbiddenField.substring(index+1);
}
}


/**
* Returns the type of mbean to process, from among those captured by this selector. If empty or null,
* processes all captured mbeans.
Expand Down Expand Up @@ -284,6 +320,7 @@ String getExcludedKeys() {
String[] getQueryValues() {
final List<String> result = new ArrayList<>(values);
if (stringValues != null) result.addAll(stringValues.keySet());
getActiveForbiddenFields().forEach(result::remove);

return result.toArray(new String[0]);
}
Expand Down Expand Up @@ -318,8 +355,11 @@ public String getRequest() {

JsonQuerySpec toQuerySpec() {
JsonQuerySpec spec = new JsonQuerySpec();
if (!useAllValues())
if (useAllValues()) {
getActiveForbiddenFields().forEach(spec::excludeField);
} else {
selectQueryFields(spec, getQueryValues());
}
if (currentSelectorHasFilter() && !filter.isEmpty())
spec.setFilter(key, filter);

Expand All @@ -330,6 +370,17 @@ JsonQuerySpec toQuerySpec() {
return spec;
}

private List<String> getActiveForbiddenFields() {
if (forbiddenFields == null)
return Collections.emptyList();
else
return Arrays.stream(forbiddenFields).filter(this::isLeafField).collect(Collectors.toList());
}

private boolean isLeafField(String forbiddenField) {
return !forbiddenField.contains(":");
}

private boolean isEnabled() {
return !filter.isEmpty() || !currentSelectorHasFilter();
}
Expand All @@ -354,10 +405,14 @@ public String getKeyRequest() {

JsonQuerySpec toKeyQuerySpec() {
JsonQuerySpec spec = new JsonQuerySpec();
if (currentSelectorHasFilter()) spec.addFields(key);
if (currentSelectorHasFilter())
spec.addFields(key);
else
spec.addFields();

for (Map.Entry<String, MBeanSelector> selector : nestedSelectors.entrySet())
spec.addChild(selector.getKey(), selector.getValue().toKeyQuerySpec());
if (selector.getValue().hasFilter())
spec.addChild(selector.getKey(), selector.getValue().toKeyQuerySpec());

return spec;
}
Expand Down Expand Up @@ -552,4 +607,19 @@ private int getIndex(String value, List<String> fieldValues) {
void postProcessMetrics(Map<String, Object> metrics, MetricsProcessor processor) {
queryType.postProcessMetrics(metrics, processor);
}

public boolean isRequestForPrivilegedProperty() {
if (queryType != QueryType.RUNTIME) return false;

return Optional.ofNullable(nestedSelectors.get("JDBCServiceRuntime"))
.map(MBeanSelector::isJDBCDataSourceRuntimeMBeansPropertiesRequest)
.orElse(false);
}

private boolean isJDBCDataSourceRuntimeMBeansPropertiesRequest() {
final MBeanSelector jdbcSourceRuntimeSelector = nestedSelectors.get("JDBCDataSourceRuntimeMBeans");
if (jdbcSourceRuntimeSelector == null) return false;
return jdbcSourceRuntimeSelector.values.isEmpty() || jdbcSourceRuntimeSelector.values.contains("properties");
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.

package com.oracle.wls.exporter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class ExporterCallTest {
private static final String ONE_VALUE_CONFIG = "queries:\n- groups:\n key: name\n values: testSample1";
private static final String CONFIG_WITH_FILTER = "queries:" +
"\n- groups:\n key: name\n includedKeyValues: abc.*\n values: testSample1";
private static final String REQUEST_FOR_PRIVILEGED_PROPERTY = "queries:" +
"\n- JDBCServiceRuntime:\n JDBCDataSourceRuntimeMBeans:\n key: name\n values: properties";
private static final String REQUEST_INCLUDES_PRIVILEGED_PROPERTY = "queries:" +
"\n- JDBCServiceRuntime:\n JDBCDataSourceRuntimeMBeans:\n prefix: ds_\n key: name\n";
private static final String DUAL_QUERY_CONFIG = ONE_VALUE_CONFIG +
"\n- clubs:\n key: name\n values: testSample2";

Expand Down Expand Up @@ -117,6 +121,26 @@ void whenQuerySentWithFilter_twoRequestsAreMade() throws IOException {
assertThat(factory.getNumQueriesSent(), equalTo(2));
}

@Test
void whenBadQueryReceivedAndConfigurationSelectsPrivilegedPropertiesProperty_explainProblem() throws IOException {
factory.reportBadQuery();
LiveConfiguration.loadFromString(REQUEST_FOR_PRIVILEGED_PROPERTY);

handleMetricsCall(context.withHttps());

assertThat(context.getResponse(), containsString("bug in the WebLogic REST API"));
}

@Test
void whenBadQueryReceivedAndConfigurationSelectsPropertiesIncludingPrivilegedProperty_explainProblem() throws IOException {
factory.reportBadQuery();
LiveConfiguration.loadFromString(REQUEST_INCLUDES_PRIVILEGED_PROPERTY);

handleMetricsCall(context.withHttps());

assertThat(context.getResponse(), containsString("bug in the WebLogic REST API"));
}

@Test
void whenHaveMultipleQueries_sendServerDefinedCookieOnSecondQuery() throws IOException {
factory.forJson(QUERY_RESPONSE1_JSON).withResponseHeader(SET_COOKIE_HEADER, "cookieName=newValue").addResponse();
Expand Down
Loading