diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java index 2552519d4730..a6569cfa15bb 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfiguration.java @@ -32,12 +32,6 @@ public class ServiceBusConfiguration { */ public final static String WRAP_PASSWORD = "serviceBus.wrap.password"; - /** - * Defines the configuration wrap scope constant. - * - */ - public final static String WRAP_SCOPE = "serviceBus.wrap.scope"; - /** * Creates a service bus configuration using the specified namespace, name, and password. * @@ -124,7 +118,6 @@ else if (profile.length() != 0 && !profile.endsWith(".")) { configuration.setProperty(profile + WRAP_NAME, authenticationName); configuration.setProperty(profile + WRAP_PASSWORD, authenticationPassword); - configuration.setProperty(profile + WRAP_SCOPE, "http://" + namespace + ".servicebus.windows.net/"); return configuration; } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapFilter.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapFilter.java index 5a2356990c63..93aee9ad5eab 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapFilter.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapFilter.java @@ -1,5 +1,7 @@ package com.microsoft.windowsazure.services.serviceBus.implementation; +import java.net.URISyntaxException; + import com.microsoft.windowsazure.services.core.ServiceException; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientRequest; @@ -7,7 +9,7 @@ import com.sun.jersey.api.client.filter.ClientFilter; public class WrapFilter extends ClientFilter { - private WrapTokenManager tokenManager; + private final WrapTokenManager tokenManager; public WrapFilter(WrapTokenManager tokenManager) { this.tokenManager = tokenManager; @@ -18,12 +20,16 @@ public ClientResponse handle(ClientRequest cr) throws ClientHandlerException { String accessToken; try { - accessToken = tokenManager.getAccessToken(); + accessToken = tokenManager.getAccessToken(cr.getURI()); } catch (ServiceException e) { // must wrap exception because of base class signature throw new ClientHandlerException(e); } + catch (URISyntaxException e) { + // must wrap exception because of base class signature + throw new ClientHandlerException(e); + } cr.getHeaders().add("Authorization", "WRAP access_token=\"" + accessToken + "\""); diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManager.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManager.java index 4789c26c3a64..26b83dbf597c 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManager.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManager.java @@ -1,6 +1,12 @@ package com.microsoft.windowsazure.services.serviceBus.implementation; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Date; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.inject.Named; @@ -16,19 +22,18 @@ public class WrapTokenManager { private final String uri; private final String name; private final String password; - private final String scope; - private ActiveToken activeToken; + private final Map activeTokens; @Inject public WrapTokenManager(WrapContract contract, DateFactory dateFactory, @Named("wrap.uri") String uri, - @Named("wrap.scope") String scope, @Named("wrap.name") String name, @Named("wrap.password") String password) { + @Named("wrap.name") String name, @Named("wrap.password") String password) { this.contract = contract; this.dateFactory = dateFactory; this.uri = uri; - this.scope = scope; this.name = name; this.password = password; + activeTokens = new ConcurrentHashMap(); } /** @@ -46,21 +51,34 @@ public void setContract(WrapContract contract) { this.contract = contract; } - public String getAccessToken() throws ServiceException { + public String getAccessToken(URI targetUri) throws ServiceException, URISyntaxException { Date now = dateFactory.getDate(); - ActiveToken active = this.activeToken; + + URI scopeUri = new URI("http", targetUri.getAuthority(), targetUri.getPath(), null, null); + String scope = scopeUri.toString(); + + ActiveToken active = this.activeTokens.get(scope); if (active != null && now.before(active.getExpiresUtc())) { return active.getWrapResponse().getAccessToken(); } + // sweep expired tokens out of collection + Iterator> iterator = activeTokens.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + if (!now.before(entry.getValue().getExpiresUtc())) { + iterator.remove(); + } + } + WrapAccessTokenResult wrapResponse = getContract().wrapAccessToken(uri, name, password, scope); Date expiresUtc = new Date(now.getTime() + wrapResponse.getExpiresIn() * Timer.ONE_SECOND / 2); ActiveToken acquired = new ActiveToken(); acquired.setWrapResponse(wrapResponse); acquired.setExpiresUtc(expiresUtc); - this.activeToken = acquired; + this.activeTokens.put(scope, acquired); return wrapResponse.getAccessToken(); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/IntegrationTestBase.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/IntegrationTestBase.java index 3b016bd566c2..7ab9e45b6045 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/IntegrationTestBase.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/IntegrationTestBase.java @@ -12,6 +12,7 @@ public abstract class IntegrationTestBase { @BeforeClass public static void initializeSystem() { + System.setProperty("http.keepAlive", "false"); } @Before diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java index 0c9ba721a5de..685dd8e1fee0 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusConfigurationTest.java @@ -5,7 +5,6 @@ import org.junit.Test; import com.microsoft.windowsazure.services.core.Configuration; -import com.microsoft.windowsazure.services.serviceBus.ServiceBusConfiguration; public class ServiceBusConfigurationTest { @Test @@ -17,16 +16,10 @@ public void ConfigureSetsExpectedProperties() { ServiceBusConfiguration.configureWithWrapAuthentication(config, "alpha", "beta", "gamma"); // Assert - assertEquals("https://alpha.servicebus.windows.net/", - config.getProperty("serviceBus.uri")); - assertEquals("https://alpha-sb.accesscontrol.windows.net/WRAPv0.9", - config.getProperty("serviceBus.wrap.uri")); - assertEquals("beta", - config.getProperty("serviceBus.wrap.name")); - assertEquals("gamma", - config.getProperty("serviceBus.wrap.password")); - assertEquals("http://alpha.servicebus.windows.net/", - config.getProperty("serviceBus.wrap.scope")); + assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("serviceBus.uri")); + assertEquals("https://alpha-sb.accesscontrol.windows.net/WRAPv0.9", config.getProperty("serviceBus.wrap.uri")); + assertEquals("beta", config.getProperty("serviceBus.wrap.name")); + assertEquals("gamma", config.getProperty("serviceBus.wrap.password")); } @Test @@ -38,15 +31,10 @@ public void UsingProfileAddsPrefix() { ServiceBusConfiguration.configureWithWrapAuthentication("backup", config, "alpha", "beta", "gamma"); // Assert - assertEquals("https://alpha.servicebus.windows.net/", - config.getProperty("backup.serviceBus.uri")); + assertEquals("https://alpha.servicebus.windows.net/", config.getProperty("backup.serviceBus.uri")); assertEquals("https://alpha-sb.accesscontrol.windows.net/WRAPv0.9", config.getProperty("backup.serviceBus.wrap.uri")); - assertEquals("beta", - config.getProperty("backup.serviceBus.wrap.name")); - assertEquals("gamma", - config.getProperty("backup.serviceBus.wrap.password")); - assertEquals("http://alpha.servicebus.windows.net/", - config.getProperty("backup.serviceBus.wrap.scope")); + assertEquals("beta", config.getProperty("backup.serviceBus.wrap.name")); + assertEquals("gamma", config.getProperty("backup.serviceBus.wrap.password")); } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java index 0e07b91f0f46..8c89ec2a3c54 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java @@ -53,7 +53,6 @@ public void createService() throws Exception { overrideWithEnv(config, ServiceBusConfiguration.WRAP_URI); overrideWithEnv(config, ServiceBusConfiguration.WRAP_NAME); overrideWithEnv(config, ServiceBusConfiguration.WRAP_PASSWORD); - overrideWithEnv(config, ServiceBusConfiguration.WRAP_SCOPE); // add LoggingFilter to any pipeline that is created Registry builder = (Registry) config.getBuilder(); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapRestProxyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapRestProxyIntegrationTest.java index 03efe51854d7..cb6b1340244c 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapRestProxyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapRestProxyIntegrationTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.*; +import java.net.URI; + import org.junit.Test; import com.microsoft.windowsazure.services.core.Configuration; @@ -19,7 +21,7 @@ public void serviceCanBeCalledToCreateAccessToken() throws Exception { String uri = (String) config.getProperty(ServiceBusConfiguration.WRAP_URI); String name = (String) config.getProperty(ServiceBusConfiguration.WRAP_NAME); String password = (String) config.getProperty(ServiceBusConfiguration.WRAP_PASSWORD); - String scope = (String) config.getProperty(ServiceBusConfiguration.WRAP_SCOPE); + String scope = new URI("http", new URI(uri).getAuthority(), new URI(uri).getPath(), null, null).toString(); WrapAccessTokenResult result = contract.wrapAccessToken(uri, name, password, scope); // Assert diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerIntegrationTest.java index f5ade3677e5c..8f11704c116f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerIntegrationTest.java @@ -1,10 +1,13 @@ package com.microsoft.windowsazure.services.serviceBus.implementation; +import java.net.URI; + import junit.framework.Assert; import org.junit.Test; import com.microsoft.windowsazure.services.core.Configuration; +import com.microsoft.windowsazure.services.serviceBus.ServiceBusConfiguration; public class WrapTokenManagerIntegrationTest { @Test @@ -14,7 +17,8 @@ public void wrapClientWillAcquireAccessToken() throws Exception { WrapTokenManager client = config.create("serviceBus", WrapTokenManager.class); // Act - String accessToken = client.getAccessToken(); + URI serviceBusURI = new URI((String) config.getProperty(ServiceBusConfiguration.URI)); + String accessToken = client.getAccessToken(serviceBusURI); // Assert Assert.assertNotNull(accessToken); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerTest.java index 7119b7acd88d..8b4f3a6f4019 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/implementation/WrapTokenManagerTest.java @@ -3,6 +3,8 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; @@ -27,83 +29,113 @@ public void init() { dateFactory = mock(DateFactory.class); contract = mock(WrapContract.class); - client = new WrapTokenManager(contract, dateFactory, "testurl", "testscope", "testname", "testpassword"); + client = new WrapTokenManager(contract, dateFactory, "testurl", "testname", "testpassword"); when(dateFactory.getDate()).thenAnswer(new Answer() { + @Override public Date answer(InvocationOnMock invocation) throws Throwable { return calendar.getTime(); } }); + } + + private void doIncrementingTokens() throws ServiceException { + doAnswer(new Answer() { + int count = 0; + + @Override + public WrapAccessTokenResult answer(InvocationOnMock invocation) throws Throwable { + ++count; + WrapAccessTokenResult wrapResponse = new WrapAccessTokenResult(); + wrapResponse.setAccessToken("testaccesstoken1-" + count); + wrapResponse.setExpiresIn(83); + return wrapResponse; + } + }).when(contract).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope"); + doAnswer(new Answer() { + int count = 0; + + @Override + public WrapAccessTokenResult answer(InvocationOnMock invocation) throws Throwable { + ++count; + WrapAccessTokenResult wrapResponse = new WrapAccessTokenResult(); + wrapResponse.setAccessToken("testaccesstoken2-" + count); + wrapResponse.setExpiresIn(83); + return wrapResponse; + } + }).when(contract).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope2"); } @Test - public void clientUsesContractToGetToken() throws ServiceException { + public void clientUsesContractToGetToken() throws ServiceException, URISyntaxException { // Arrange - WrapAccessTokenResult wrapResponse = new WrapAccessTokenResult(); - wrapResponse.setAccessToken("testaccesstoken"); - wrapResponse.setExpiresIn(83); - - when(contract.wrapAccessToken("testurl", "testname", "testpassword", "testscope")).thenReturn(wrapResponse); + doIncrementingTokens(); // Act - String accessToken = client.getAccessToken(); + String accessToken = client.getAccessToken(new URI("https://test/scope")); // Assert assertNotNull(accessToken); - assertEquals("testaccesstoken", accessToken); + assertEquals("testaccesstoken1-1", accessToken); } @Test - public void clientWillNotCallMultipleTimesWhileAccessTokenIsValid() throws ServiceException { + public void clientWillNotCallMultipleTimesWhileAccessTokenIsValid() throws ServiceException, URISyntaxException { // Arrange - WrapAccessTokenResult wrapResponse = new WrapAccessTokenResult(); - wrapResponse.setAccessToken("testaccesstoken"); - wrapResponse.setExpiresIn(83); - - when(contract.wrapAccessToken("testurl", "testname", "testpassword", "testscope")).thenReturn(wrapResponse); + doIncrementingTokens(); // Act - String accessToken1 = client.getAccessToken(); - String accessToken2 = client.getAccessToken(); + String accessToken1 = client.getAccessToken(new URI("https://test/scope?arg=1")); + String accessToken2 = client.getAccessToken(new URI("https://test/scope?arg=2")); calendar.add(Calendar.SECOND, 40); - String accessToken3 = client.getAccessToken(); + String accessToken3 = client.getAccessToken(new URI("https://test/scope?arg=3")); // Assert - assertEquals("testaccesstoken", accessToken1); - assertEquals("testaccesstoken", accessToken2); - assertEquals("testaccesstoken", accessToken3); + assertEquals("testaccesstoken1-1", accessToken1); + assertEquals("testaccesstoken1-1", accessToken2); + assertEquals("testaccesstoken1-1", accessToken3); - verify(contract, times(1)).wrapAccessToken("testurl", "testname", "testpassword", "testscope"); + verify(contract, times(1)).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope"); } @Test - public void clientWillBeCalledWhenTokenIsHalfwayToExpiring() throws ServiceException { + public void callsToDifferentPathsWillResultInDifferentAccessTokens() throws ServiceException, URISyntaxException { // Arrange - doAnswer(new Answer() { - int count = 0; + doIncrementingTokens(); - public WrapAccessTokenResult answer(InvocationOnMock invocation) throws Throwable { - ++count; - WrapAccessTokenResult wrapResponse = new WrapAccessTokenResult(); - wrapResponse.setAccessToken("testaccesstoken" + count); - wrapResponse.setExpiresIn(83); - return wrapResponse; - } - }).when(contract).wrapAccessToken("testurl", "testname", "testpassword", "testscope"); + // Act + String accessToken1 = client.getAccessToken(new URI("https://test/scope?arg=1")); + String accessToken2 = client.getAccessToken(new URI("https://test/scope2?arg=2")); + calendar.add(Calendar.SECOND, 40); + String accessToken3 = client.getAccessToken(new URI("https://test/scope?arg=3")); + + // Assert + assertEquals("testaccesstoken1-1", accessToken1); + assertEquals("testaccesstoken2-1", accessToken2); + assertEquals("testaccesstoken1-1", accessToken3); + + verify(contract, times(1)).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope"); + verify(contract, times(1)).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope2"); + } + + @Test + public void clientWillBeCalledWhenTokenIsHalfwayToExpiring() throws ServiceException, URISyntaxException { + // Arrange + doIncrementingTokens(); // Act - String accessToken1 = client.getAccessToken(); - String accessToken2 = client.getAccessToken(); + String accessToken1 = client.getAccessToken(new URI("https://test/scope")); + String accessToken2 = client.getAccessToken(new URI("https://test/scope")); calendar.add(Calendar.SECOND, 45); - String accessToken3 = client.getAccessToken(); + String accessToken3 = client.getAccessToken(new URI("https://test/scope")); // Assert - assertEquals("testaccesstoken1", accessToken1); - assertEquals("testaccesstoken1", accessToken2); - assertEquals("testaccesstoken2", accessToken3); + assertEquals("testaccesstoken1-1", accessToken1); + assertEquals("testaccesstoken1-1", accessToken2); + assertEquals("testaccesstoken1-2", accessToken3); - verify(contract, times(2)).wrapAccessToken("testurl", "testname", "testpassword", "testscope"); + verify(contract, times(2)).wrapAccessToken("testurl", "testname", "testpassword", "http://test/scope"); } } diff --git a/microsoft-azure-api/src/test/resources/META-INF/com.microsoft.windowsazure.properties b/microsoft-azure-api/src/test/resources/META-INF/com.microsoft.windowsazure.properties index b50e1d15ff44..e7bf06b034de 100644 --- a/microsoft-azure-api/src/test/resources/META-INF/com.microsoft.windowsazure.properties +++ b/microsoft-azure-api/src/test/resources/META-INF/com.microsoft.windowsazure.properties @@ -1,6 +1,5 @@ serviceBus.uri=https://%SERVICEBUS_NAMESPACE%.servicebus.windows.net/ serviceBus.wrap.uri=https://%SERVICEBUS_NAMESPACE%-sb.accesscontrol.windows.net/WRAPv0.9 -serviceBus.wrap.scope=http://%SERVICEBUS_NAMESPACE%.servicebus.windows.net/ serviceBus.wrap.name=%SERVICEBUS_SERVICEIDENTITY% serviceBus.wrap.password=%SERVICEBUS_SHAREDSECRET% blob.accountName=%BLOB_ACCOUNTNAME%