Skip to content

Commit

Permalink
Merge pull request Azure#147 from loudej/wrap-token-scope
Browse files Browse the repository at this point in the history
SB Review: BUG: authentication tokens must have a granular scope. Fixes Azure#98
  • Loading branch information
rpaquay committed Nov 30, 2011
2 parents a7e1e86 + 83cdd1d commit ddb6835
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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;
import com.sun.jersey.api.client.ClientResponse;
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;
Expand All @@ -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 + "\"");

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<String, ActiveToken> 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<String, ActiveToken>();
}

/**
Expand All @@ -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<Entry<String, ActiveToken>> iterator = activeTokens.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, ActiveToken> 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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
public abstract class IntegrationTestBase {
@BeforeClass
public static void initializeSystem() {
System.setProperty("http.keepAlive", "false");
}

@Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static org.junit.Assert.*;

import java.net.URI;

import org.junit.Test;

import com.microsoft.windowsazure.services.core.Configuration;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Date>() {
@Override
public Date answer(InvocationOnMock invocation) throws Throwable {
return calendar.getTime();
}
});
}

private void doIncrementingTokens() throws ServiceException {
doAnswer(new Answer<WrapAccessTokenResult>() {
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<WrapAccessTokenResult>() {
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<WrapAccessTokenResult>() {
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");
}

}
Loading

0 comments on commit ddb6835

Please sign in to comment.