package org.multibit.mbm.test; import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.test.framework.AppDescriptor; import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.LowLevelAppDescriptor; import com.yammer.dropwizard.bundles.JavaBundle; import com.yammer.dropwizard.jersey.DropwizardResourceConfig; import com.yammer.dropwizard.jersey.JacksonMessageBodyProvider; import com.yammer.dropwizard.jersey.OptionalQueryParamInjectableProvider; import com.yammer.dropwizard.json.Json; import org.codehaus.jackson.map.Module; import org.junit.After; import org.junit.Before; import org.multibit.mbm.auth.hmac.HmacClientFilter; import org.multibit.mbm.auth.hmac.HmacServerAuthenticator; import org.multibit.mbm.auth.hmac.HmacServerRestrictedToProvider; import org.multibit.mbm.db.DatabaseLoader; import org.multibit.mbm.db.dao.UserDao; import org.multibit.mbm.core.model.Role; import org.multibit.mbm.core.model.User; import org.multibit.mbm.core.model.UserBuilder; import javax.ws.rs.core.UriBuilder; import java.net.URI; import java.util.List; import java.util.Map; import java.util.Set; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * A base test class for testing Dropwizard resources against * an actual Jersey container using HMAC for authentication */ public abstract class BaseJerseyHmacResourceTest extends BaseResourceTest { private final Set<Object> singletons = Sets.newHashSet(); private final Set<Class<?>> providers = Sets.newHashSet(); private final List<Module> modules = Lists.newArrayList(); private final Map<String, Boolean> features = Maps.newHashMap(); private JerseyTest test; /** * The User providing authentication via HMAC */ protected User hmacUser; /** * <p>Subclasses must use this to configure mocks of any objects that the * test object depends on.</p> * * @throws Exception If something goes wrong */ protected abstract void setUpResources() throws Exception; protected void addSingleton(Object singleton) { singletons.add(singleton); } public void addProvider(Class<?> klass) { providers.add(klass); } protected void addJacksonModule(Module module) { modules.add(module); } protected void addFeature(String feature, Boolean value) { features.put(feature, value); } protected Json getJson() { final Json json = new Json(); for (Module module : modules) { json.registerModule(module); } return json; } protected Client client() { return test.client(); } @Before public void setUpJersey() throws Exception { // Provide any specialised resource configuration setUpResources(); this.test = new JerseyTest() { @Override protected URI getBaseURI() { return URI.create("http://localhost:8080/"); } @Override protected AppDescriptor configure() { final DropwizardResourceConfig config = new DropwizardResourceConfig(true); // Default singletons for (Object provider : JavaBundle.DEFAULT_PROVIDERS) { config.getSingletons().add(provider); } // Configure Jackson final Json json = getJson(); config.getSingletons().add(new JacksonMessageBodyProvider(json)); config.getSingletons().addAll(singletons); // Add providers for (Class<?> provider : providers) { config.getClasses().add(provider); } config.getClasses().add(OptionalQueryParamInjectableProvider.class); // Add any features (see FeaturesAndProperties) for (Map.Entry<String, Boolean> feature : features.entrySet()) { config.getFeatures().put(feature.getKey(), feature.getValue()); } return new LowLevelAppDescriptor .Builder(config) .build(); } }; // Allow final client request filtering for HMAC authentication (this allows for secure tests) test.client().addFilter(new HmacClientFilter( test.client().getProviders() ) ); test.setUp(); // Configure for weak hashes UserBuilder.isTestMode =true; } @After public void tearDownJersey() throws Exception { if (test != null) { test.tearDown(); } } /** * Provides the default client HMAC authentication settings (not a Customer) */ protected User setUpClientHmacAuthenticator() { Role clientRole = DatabaseLoader.buildClientRole(); User storeClient = DatabaseLoader.buildStoreClient(clientRole); return setUpHmacAuthenticator(storeClient); } /** * Provides HMAC authentication settings for Alice Customer */ protected User setUpAliceHmacAuthenticator() { Role customerRole = DatabaseLoader.buildCustomerRole(); User aliceCustomer = DatabaseLoader.buildAliceCustomer(customerRole); return setUpHmacAuthenticator(aliceCustomer); } /** * Provides HMAC authentication settings for Steve Supplier */ protected User setUpSteveHmacAuthenticator() { Role supplierRole = DatabaseLoader.buildSupplierRole(); User steveSupplier = DatabaseLoader.buildSteveSupplier(supplierRole); return setUpHmacAuthenticator(steveSupplier); } /** * Provides HMAC authentication settings for anonymous Public user */ protected User setUpPublicHmacAuthenticator() { Role publicRole = DatabaseLoader.buildPublicRole(); User anonymousPublic = DatabaseLoader.buildAnonymousPublic(publicRole); return setUpHmacAuthenticator(anonymousPublic); } /** * Provides HMAC authentication settings for Trent Admin */ protected User setUpTrentHmacAuthenticator() { Role adminRole = DatabaseLoader.buildAdminRole(); User trentAdmin = DatabaseLoader.buildTrentAdministrator(adminRole); return setUpHmacAuthenticator(trentAdmin); } /** * @param user The User to act as the authenticator for HMAC communications (could be a Customer) */ protected User setUpHmacAuthenticator(User user) { UserDao userDao = mock(UserDao.class); when(userDao.getByApiKey(user.getApiKey())).thenReturn(Optional.of(user)); HmacServerAuthenticator authenticator = new HmacServerAuthenticator(); authenticator.setUserDao(userDao); addSingleton(new HmacServerRestrictedToProvider<User>(authenticator, "REST")); // Set the HMAC authentication user hmacUser = user; return user; } /** * Configure request as a client to access the resource on behalf of a user * * @param path The relative path to the resource * * @return A web resource suitable for method chaining * */ protected WebResource configureAsClient(String path) { WebResource resource = client().resource(path); resource.setProperty(HmacClientFilter.MBM_API_KEY, hmacUser.getApiKey()); resource.setProperty(HmacClientFilter.MBM_SECRET_KEY, hmacUser.getSecretKey()); return resource; } /** * Configure request as a client to access the resource on behalf of a user * * @param clazz The class of the Resource * * @return A web resource suitable for method chaining * */ protected WebResource configureAsClient(Class clazz) { URI uri = UriBuilder .fromResource(clazz) .scheme("http") .host("localhost") .port(8080) .build(); WebResource resource = client().resource(uri); resource.setProperty(HmacClientFilter.MBM_API_KEY, hmacUser.getApiKey()); resource.setProperty(HmacClientFilter.MBM_SECRET_KEY, hmacUser.getSecretKey()); return resource; } }