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 @@ -5,11 +5,10 @@
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.AppConfigurationWebConstants.DATA;
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.AppConfigurationWebConstants.SYNC_TOKEN;
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.AppConfigurationWebConstants.VALIDATION_CODE_KEY;
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.AppConfigurationWebConstants.VALIDATION_TOPIC;

import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

Expand All @@ -18,20 +17,17 @@
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.AccessToken;
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.PushNotification;
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.ConfigStore;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
* Common class for authenticating refresh requests.
*/
public class AppConfigurationEndpoint {

private static final String CONFIG_STORE_SUBJECT = "subject";

private static final String CONFIG_STORE_TOPIC = "configurationstores";

private final String endpoint;

private final String store;
private final URI endpoint;

private final List<ConfigStore> configStores;

Expand Down Expand Up @@ -75,12 +71,10 @@ public AppConfigurationEndpoint(HttpServletRequest request, List<ConfigStore> co

validationResponse = requestBody.findValue(VALIDATION_CODE_KEY);

JsonNode requestTopic = requestBody.findValue(VALIDATION_TOPIC);
if (requestTopic != null) {
String topic = requestTopic.asText();
store = topic.substring(
topic.toLowerCase(Locale.ROOT).indexOf(CONFIG_STORE_TOPIC) + CONFIG_STORE_TOPIC.length() + 1);
endpoint = String.format("https://%s", store);
JsonNode requestSubject = requestBody.findValue(CONFIG_STORE_SUBJECT);
if (requestSubject != null) {
String subject = requestSubject.asText();
endpoint = URI.create(subject);
} else {
throw new IllegalArgumentException("Refresh request missing topic field.");
}
Expand All @@ -94,7 +88,7 @@ public AppConfigurationEndpoint(HttpServletRequest request, List<ConfigStore> co
*/
public boolean authenticate() {
for (ConfigStore configStore : configStores) {
if (configStore.getEndpoint().startsWith(endpoint)) {
if (configStore.containsEndpoint(getEndpoint())) {
PushNotification pushNotification = configStore.getMonitoring().getPushNotification();

// One of these need to be set
Expand Down Expand Up @@ -129,7 +123,7 @@ private boolean isTokenMatch(AccessToken token) {
*/
public boolean triggerRefresh() {
for (ConfigStore configStore : configStores) {
if (configStore.getEndpoint().startsWith(endpoint) && configStore.getMonitoring().isEnabled()) {
if (configStore.containsEndpoint(getEndpoint()) && configStore.getMonitoring().isEnabled()) {
return true;
}
}
Expand All @@ -155,14 +149,6 @@ public JsonNode getValidationResponse() {
* @return the endpoint
*/
public String getEndpoint() {
return endpoint;
}

/**
* @return the store
*/
public String getStore() {
return store;
return endpoint.getScheme() + "://" + endpoint.getHost();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,13 @@ public void validationParsing() throws JsonGenerationException, JsonMappingExcep
Map<String, String> allRequestParams = new HashMap<String, String>();

AppConfigurationEndpoint endpoint = new AppConfigurationEndpoint(request, configStores, allRequestParams);
assertEquals("https://testConfig", endpoint.getEndpoint());
assertEquals("testConfig", endpoint.getStore());
assertEquals("https://fake.test.azconfig.io", endpoint.getEndpoint());

requestBody = mapper.readValue(new File(GET_TEST_REFRESH), JsonNode.class).toString();
when(lines.collect(Mockito.any())).thenReturn(requestBody);

endpoint = new AppConfigurationEndpoint(request, configStores, allRequestParams);
assertEquals("https://testConfig", endpoint.getEndpoint());
assertEquals("testConfig", endpoint.getStore());
assertEquals("https://testconfig.azconfig.io", endpoint.getEndpoint());
}

@Test
Expand Down Expand Up @@ -109,7 +107,7 @@ public void authenticate() throws JsonParseException, JsonMappingException, IOEx
assertFalse(endpoint.authenticate());

ConfigStore validConfigStore = new ConfigStore();
validConfigStore.setEndpoint("https://testConfig.azconfig.io");
validConfigStore.setEndpoint("https://fake.test.azconfig.io");
configStores.add(validConfigStore);
endpoint = new AppConfigurationEndpoint(request, configStores, allRequestParams);
// Valid Config Store, no secrets
Expand Down Expand Up @@ -174,7 +172,7 @@ public void triggerRefresh() throws JsonParseException, JsonMappingException, IO
assertFalse(endpoint.authenticate());

ConfigStore validConfigStore = new ConfigStore();
validConfigStore.setEndpoint("https://testConfig.azconfig.io");
validConfigStore.setEndpoint("https://fake.test.azconfig.io");

configStores.add(validConfigStore);
endpoint = new AppConfigurationEndpoint(request, configStores, allRequestParams);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.TRIGGER_LABEL;
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.VALIDATION_URL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.BufferedReader;
Expand Down Expand Up @@ -84,6 +87,7 @@ public void setup() throws IOException {
configStores = new ArrayList<>();
configStore.setMonitoring(monitoring);
configStore.setEndpoint("https://fake.test.azconfig.io");
configStore.validateAndInit();
configStores.add(configStore);

when(request.getReader()).thenReturn(reader);
Expand All @@ -110,10 +114,11 @@ public void webHookValidation() throws IOException {
when(lines.collect(Mockito.any())).thenReturn("[{\r\n"
+ " \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\",\r\n"
+ " \"topic\":" + TOPIC + ",\r\n"
+ " \"subject\": \"\",\r\n"
+ " \"subject\": \"https://fake.test.azconfig.io/kv/Foo?label=FizzBuzz\",\r\n"
+ " \"data\": {\r\n"
+ " \"validationCode\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\",\r\n"
+ " \"validationUrl\":" + VALIDATION_URL + "\r\n"
+ " \"validationUrl\":" + VALIDATION_URL + ",\r\n"
+ " \"syncToken\": \"zAJw6V16=MzoyMCMyODA3Mzc3;sn=2807377\"\r\n"
+ " },\r\n"
+ " \"eventType\": \"Microsoft.EventGrid.SubscriptionValidationEvent\",\r\n"
+ " \"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\r\n"
Expand All @@ -140,6 +145,11 @@ public void webHookRefresh() throws IOException {
when(lines.collect(Mockito.any())).thenReturn(getResetNotification());

assertEquals(HttpStatus.OK.getReasonPhrase(), endpoint.refresh(request, response, allRequestParams));
verify(publisher, times(1)).publishEvent(argThat(refreshEvent -> {
boolean hasEndpoint = ((AppConfigurationBusRefreshEvent) refreshEvent).getEndpoint().equals("https://fake.test.azconfig.io");
boolean hasSyncToken = ((AppConfigurationBusRefreshEvent) refreshEvent).getSyncToken().equals("zAJw6V16=MzoyMCMyODA3Mzc3;sn=2807377");
return hasEndpoint && hasSyncToken;
}));
}

@Test
Expand Down Expand Up @@ -255,7 +265,8 @@ private String getResetNotification() {
+ " \"data\" : {\r\n"
+ " \"key\" : \"trigger_key\",\r\n"
+ " \"label\" : \"trigger_label\",\r\n"
+ " \"etag\" : \"r05tB2hfMQs0vo6ITcXu7ScIOhR\"\r\n"
+ " \"etag\" : \"r05tB2hfMQs0vo6ITcXu7ScIOhR\",\r\n"
+ " \"syncToken\": \"zAJw6V16=MzoyMCMyODA3Mzc3;sn=2807377\"\r\n"
+ " },\r\n"
+ " \"eventType\" : \"Microsoft.AppConfiguration.KeyValueModified\",\r\n"
+ " \"dataVersion\" : \"1\",\r\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.TRIGGER_LABEL;
import static com.azure.spring.cloud.appconfiguration.config.web.implementation.TestConstants.VALIDATION_URL;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.BufferedReader;
Expand Down Expand Up @@ -61,6 +64,8 @@ public class AppConfigurationRefreshEndpointTest {
private ArrayList<AppConfigurationStoreTrigger> triggers;

private AppConfigurationStoreMonitoring monitoring;

private String endpoint = "https://fake.test.azconfig.io";

private String tokenName = "token";

Expand Down Expand Up @@ -112,10 +117,11 @@ public void webHookValidation() throws IOException {
when(lines.collect(Mockito.any())).thenReturn("[{\r\n"
+ " \"id\": \"2d1781af-3a4c-4d7c-bd0c-e34b19da4e66\",\r\n"
+ " \"topic\":" + TOPIC + ",\r\n"
+ " \"subject\": \"\",\r\n"
+ " \"subject\": \"https://fake.test.azconfig.io/kv/Foo?label=FizzBuzz\",\r\n"
+ " \"data\": {\r\n"
+ " \"validationCode\": \"512d38b6-c7b8-40c8-89fe-f46f9e9622b6\",\r\n"
+ " \"validationUrl\":" + VALIDATION_URL + "\r\n"
+ " \"validationUrl\":" + VALIDATION_URL + ",\r\n"
+ " \"syncToken\": \"zAJw6V16=MzoyMCMyODA3Mzc3;sn=2807377\"\r\n"
+ " },\r\n"
+ " \"eventType\": \"Microsoft.EventGrid.SubscriptionValidationEvent\",\r\n"
+ " \"eventTime\": \"2018-01-25T22:12:19.4556811Z\",\r\n"
Expand All @@ -141,6 +147,11 @@ public void webHookRefresh() throws IOException {
when(lines.collect(Mockito.any())).thenReturn(getResetNotification());

assertEquals(HttpStatus.OK.getReasonPhrase(), endpoint.refresh(request, response, allRequestParams));
verify(publisher, times(1)).publishEvent(argThat(refreshEvent -> {
boolean hasEndpoint = ((AppConfigurationRefreshEvent) refreshEvent).getEndpoint().equals("https://fake.test.azconfig.io");
boolean hasSyncToken = ((AppConfigurationRefreshEvent) refreshEvent).getSyncToken().equals("zAJw6V16=MzoyMCMyODA3Mzc3;sn=2807377");
return hasEndpoint && hasSyncToken;
}));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
"id": "2d1781af-3a4c-4d7c-bd0c-e34b19da4e66",
"topic": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/testGroup/providers/microsoft.appconfiguration/configurationstores/testConfig",
"subject": "",
"subject": "https://fake.test.azconfig.io/kv/Foo?label=FizzBuzz",
"data": {
"validationCode": "512d38b6-c7b8-40c8-89fe-f46f9e9622b8",
"validationUrl": "https://rp-eastus2.eventgrid.azure.net:553/eventsubscriptions/estest/validate?id=512d38b6-c7b8-40c8-89fe-f46f9e9622b6&t=2018-04-26T20:30:54.4538837Z&apiVersion=2018-05-01-preview&token=1A1A1A1A"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.appconfiguration.config.implementation.properties;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
Expand All @@ -20,7 +21,7 @@ public final class ConfigStore {

private static final String DEFAULT_KEYS = "/application/";

private String endpoint; // Config store endpoint
private String endpoint = ""; // Config store endpoint

private List<String> endpoints = new ArrayList<>();

Expand Down Expand Up @@ -165,6 +166,13 @@ public void setFeatureFlags(FeatureFlagStore featureFlags) {
this.featureFlags = featureFlags;
}

public boolean containsEndpoint(String endpoint) {
if (this.endpoint.startsWith(endpoint)) {
return true;
}
return endpoints.stream().anyMatch(storeEndpoint -> storeEndpoint.startsWith(endpoint));
}

/**
* @throws IllegalStateException Connection String URL endpoint is invalid
*/
Expand Down Expand Up @@ -193,11 +201,11 @@ public void validateAndInit() {
String endpoint = (AppConfigurationReplicaClientsBuilder.getEndpointFromConnectionString(connection));
try {
// new URI is used to validate the endpoint as a valid URI
new URI(endpoint);
new URI(endpoint).toURL();
if (!StringUtils.hasText(this.endpoint)) {
this.endpoint = endpoint;
}
} catch (URISyntaxException e) {
} catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) {
throw new IllegalStateException("Endpoint in connection string is not a valid URI.", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@


import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -75,5 +77,47 @@ public void getLabelsTest() {
configStore.setSelects(selects);
assertEquals("\0", configStore.getSelects().get(0).getLabelFilter(new ArrayList<>())[0]);
}

@Test
public void testContainsEndpoint() {
ConfigStore store = new ConfigStore();
store.setEndpoint("endpoint");
store.validateAndInit();
assertTrue(store.containsEndpoint("endpoint"));
assertFalse(store.containsEndpoint("invalidEndpoint"));

store = new ConfigStore();
List<String> endpoints = new ArrayList<>();
endpoints.add("endpoint");
endpoints.add("secondEndpoint");
store.setEndpoints(endpoints);
store.validateAndInit();
assertTrue(store.containsEndpoint("endpoint"));
assertTrue(store.containsEndpoint("secondEndpoint"));
assertFalse(store.containsEndpoint("invalidEndpoint"));
}

@Test
public void testValidateConnectionString() {
ConfigStore store = new ConfigStore();
store.setConnectionString("Endpoint=https://endpoint.io;Id=identifier;Secret=secret=");
store.validateAndInit();

store = new ConfigStore();
List<String> connectionStrings = new ArrayList<>();
connectionStrings.add("Endpoint=https://endpoint.io;Id=identifier;Secret=secret=");
connectionStrings.add("Endpoint=https://endpoint2.io;Id=identifier;Secret=secret=");
store.setConnectionStrings(connectionStrings);
store.validateAndInit();
}

@Test
public void testValidateConnectionStringInvalid() {
ConfigStore store = new ConfigStore();
List<String> connectionStrings = new ArrayList<>();
connectionStrings.add("Endpoint=endpoint;Id=identifier;Secret=secret=");
store.setConnectionStrings(connectionStrings);
assertThrows(IllegalStateException.class, () -> store.validateAndInit());
}

}