Added test for key rotation, and fixed the bugs it turned up.
diff --git a/api/src/main/java/io/mifos/anubis/api/v1/client/Anubis.java b/api/src/main/java/io/mifos/anubis/api/v1/client/Anubis.java
index 05284aa..6f7ce43 100644
--- a/api/src/main/java/io/mifos/anubis/api/v1/client/Anubis.java
+++ b/api/src/main/java/io/mifos/anubis/api/v1/client/Anubis.java
@@ -19,10 +19,12 @@
import io.mifos.anubis.api.v1.domain.PermittableEndpoint;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.api.v1.validation.ValidKeyTimestamp;
-import io.mifos.core.api.util.InvalidTokenException;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
@@ -37,33 +39,34 @@
)
List<PermittableEndpoint> getPermittableEndpoints();
+ @RequestMapping(value = "/signatures", method = RequestMethod.GET,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ List<String> getAllSignatureSets();
+
@RequestMapping(value = "/signatures/{timestamp}", method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
ApplicationSignatureSet createSignatureSet(@PathVariable("timestamp") @ValidKeyTimestamp String timestamp,
- @RequestBody Signature identityManagerSignature)
- throws InvalidTokenException, TenantNotFoundException;
+ @RequestBody Signature identityManagerSignature);
@RequestMapping(value = "/signatures/{timestamp}", method = RequestMethod.GET,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
- ApplicationSignatureSet getSignatureSet(@PathVariable("timestamp") String timestamp)
- throws InvalidTokenException, TenantNotFoundException;
+ ApplicationSignatureSet getSignatureSet(@PathVariable("timestamp") String timestamp);
@RequestMapping(value = "/signatures/{timestamp}", method = RequestMethod.DELETE,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
- void deleteSignatureSet(@PathVariable("timestamp") String timestamp)
- throws InvalidTokenException, TenantNotFoundException;
+ void deleteSignatureSet(@PathVariable("timestamp") String timestamp);
@RequestMapping(value = "/signatures/{timestamp}/application", method = RequestMethod.GET,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
- Signature getApplicationSignature(@PathVariable("timestamp") String timestamp)
- throws InvalidTokenException, TenantNotFoundException;
+ Signature getApplicationSignature(@PathVariable("timestamp") String timestamp);
@RequestMapping(value = "/initialize", method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
- void initializeResources() throws InvalidTokenException, TenantNotFoundException;
+ void initializeResources();
}
diff --git a/api/src/main/java/io/mifos/anubis/api/v1/client/AnubisApiFactory.java b/api/src/main/java/io/mifos/anubis/api/v1/client/AnubisApiFactory.java
index 623c74b..799109e 100644
--- a/api/src/main/java/io/mifos/anubis/api/v1/client/AnubisApiFactory.java
+++ b/api/src/main/java/io/mifos/anubis/api/v1/client/AnubisApiFactory.java
@@ -18,21 +18,23 @@
import feign.Feign;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
+import io.mifos.core.api.util.AnnotatedErrorDecoder;
import io.mifos.core.api.util.TenantedTargetInterceptor;
import io.mifos.core.api.util.TokenedTargetInterceptor;
+import org.slf4j.Logger;
import org.springframework.cloud.netflix.feign.support.SpringMvcContract;
@SuppressWarnings("unused")
public interface AnubisApiFactory {
- static Anubis create(final String target) {
+ static Anubis create(final String target, final Logger logger) {
return Feign.builder()
- .contract(new SpringMvcContract())
- .errorDecoder(new InitializeErrorDecoder())
- .requestInterceptor(new TenantedTargetInterceptor())
- .requestInterceptor(new TokenedTargetInterceptor())
- .decoder(new GsonDecoder())
- .encoder(new GsonEncoder())
- .target(Anubis.class, target);
+ .contract(new SpringMvcContract())
+ .errorDecoder(new AnnotatedErrorDecoder(logger, Anubis.class))
+ .requestInterceptor(new TenantedTargetInterceptor())
+ .requestInterceptor(new TokenedTargetInterceptor())
+ .decoder(new GsonDecoder())
+ .encoder(new GsonEncoder())
+ .target(Anubis.class, target);
}
}
diff --git a/api/src/main/java/io/mifos/anubis/api/v1/client/InitializeErrorDecoder.java b/api/src/main/java/io/mifos/anubis/api/v1/client/InitializeErrorDecoder.java
deleted file mode 100644
index c1a0dab..0000000
--- a/api/src/main/java/io/mifos/anubis/api/v1/client/InitializeErrorDecoder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2017 The Mifos Initiative.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.mifos.anubis.api.v1.client;
-
-import feign.Feign;
-import feign.FeignException;
-import feign.Response;
-import feign.codec.ErrorDecoder;
-import io.mifos.anubis.api.v1.domain.Signature;
-import io.mifos.core.api.util.InvalidTokenException;
-import org.apache.http.HttpStatus;
-
-import java.lang.reflect.Method;
-
-/**
- * @author Myrle Krantz
- */
-class InitializeErrorDecoder implements ErrorDecoder {
- @Override public Exception decode(final String methodKey, final Response response) {
- try {
- final Method createSignatureSetMethod = Anubis.class.getDeclaredMethod("createSignatureSet", String.class, Signature.class);
- final String createSignatureSetMethodKey = Feign.configKey(Anubis.class, createSignatureSetMethod);
- final Method initializeResourcesMethod = Anubis.class.getDeclaredMethod("initializeResources");
- final String initializeResourcesMethodKey = Feign.configKey(Anubis.class, initializeResourcesMethod);
- if (createSignatureSetMethodKey.equals(methodKey) || initializeResourcesMethodKey.equals(methodKey))
- {
- if (response.status() == HttpStatus.SC_BAD_REQUEST)
- return new IllegalArgumentException();
- else if (response.status() == HttpStatus.SC_NOT_FOUND)
- return new TenantNotFoundException();
- else if (response.status() == HttpStatus.SC_FORBIDDEN)
- return new InvalidTokenException(response.reason());
- }
-
- return FeignException.errorStatus(methodKey, response);
- }
- catch (final NoSuchMethodException e) {
- throw new IllegalStateException("Could not find createSignatureSet method."); //TODO:
- }
- }
-}
diff --git a/api/src/main/java/io/mifos/anubis/api/v1/client/TenantNotFoundException.java b/api/src/main/java/io/mifos/anubis/api/v1/client/TenantNotFoundException.java
deleted file mode 100644
index 5f0b5c4..0000000
--- a/api/src/main/java/io/mifos/anubis/api/v1/client/TenantNotFoundException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2017 The Mifos Initiative.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.mifos.anubis.api.v1.client;
-
-/**
- * @author Myrle Krantz
- */
-@SuppressWarnings("WeakerAccess")
-public class TenantNotFoundException extends RuntimeException {
-}
diff --git a/component-test/src/main/java/TestAnubisInitialize.java b/component-test/src/main/java/TestAnubisInitialize.java
index d97fc0b..9d6343d 100644
--- a/component-test/src/main/java/TestAnubisInitialize.java
+++ b/component-test/src/main/java/TestAnubisInitialize.java
@@ -16,7 +16,6 @@
import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.client.AnubisApiFactory;
-import io.mifos.anubis.api.v1.client.TenantNotFoundException;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.example.simple.Example;
import io.mifos.anubis.example.simple.ExampleConfiguration;
@@ -24,6 +23,7 @@
import io.mifos.core.api.context.AutoSeshat;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.InvalidTokenException;
+import io.mifos.core.api.util.NotFoundException;
import io.mifos.core.lang.AutoTenantContext;
import io.mifos.core.test.env.TestEnvironment;
import io.mifos.core.test.fixture.TenantDataStoreTestContext;
@@ -35,6 +35,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@@ -52,6 +53,7 @@
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestAnubisInitialize {
private static final String APP_NAME = "anubis-v1";
+ private static final String LOGGER_QUALIFIER = "test-logger";
@Configuration
@EnableFeignClients(basePackages = {"io.mifos.anubis.example.simple"})
@@ -62,7 +64,7 @@
super();
}
- @Bean()
+ @Bean(name = LOGGER_QUALIFIER)
public Logger logger() {
return LoggerFactory.getLogger(APP_NAME + "-logger");
}
@@ -78,6 +80,11 @@
@Autowired
Example example;
+ @SuppressWarnings("SpringAutowiredFieldsWarningInspection")
+ @Autowired
+ @Qualifier(value = LOGGER_QUALIFIER)
+ Logger logger;
+
@Test
public void testBrokenToken()
{
@@ -88,7 +95,7 @@
try {
- final Anubis anubis = AnubisApiFactory.create(testEnvironment.serverURI());
+ final Anubis anubis = AnubisApiFactory.create(testEnvironment.serverURI(), logger);
try (final AutoSeshat ignored2 = new AutoSeshat(brokenSeshatToken)) {
final TenantApplicationSecurityEnvironmentTestRule securityMock = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);
@@ -144,7 +151,7 @@
}
}
- @Test(expected = TenantNotFoundException.class)
+ @Test(expected = NotFoundException.class)
public void testNonExistentTenant() {
try (final AutoTenantContext ignored = new AutoTenantContext("monster_under_your_bed")) {
initialize();
diff --git a/component-test/src/main/java/TestAnubisInitializeWithSpecialTenantSignatureRepository.java b/component-test/src/main/java/TestAnubisInitializeWithSpecialTenantSignatureRepository.java
new file mode 100644
index 0000000..b9b973a
--- /dev/null
+++ b/component-test/src/main/java/TestAnubisInitializeWithSpecialTenantSignatureRepository.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import io.mifos.anubis.example.nokeystorage.Example;
+import io.mifos.anubis.example.nokeystorage.ExampleConfiguration;
+import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
+import io.mifos.core.api.context.AutoSeshat;
+import io.mifos.core.lang.AutoTenantContext;
+import io.mifos.core.lang.TenantContextHolder;
+import io.mifos.core.test.env.TestEnvironment;
+import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
+import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.cloud.netflix.ribbon.RibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+public class TestAnubisInitializeWithSpecialTenantSignatureRepository {
+ private static final String APP_NAME = "anubis-v1";
+
+ @Configuration
+ @EnableFeignClients(basePackages = {"io.mifos.anubis.example.nokeystorage"})
+ @RibbonClient(name = APP_NAME)
+ @Import({ExampleConfiguration.class})
+ static public class TestConfiguration {
+ public TestConfiguration() {
+ super();
+ }
+
+ @Bean()
+ public Logger logger() {
+ return LoggerFactory.getLogger("initialize-with-special-tenant-signature-repository-test-logger");
+ }
+ }
+
+ @ClassRule
+ public final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
+
+ @ClassRule
+ public final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
+
+ @Rule
+ public final TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer);
+
+ @SuppressWarnings({"SpringAutowiredFieldsWarningInspection", "SpringJavaAutowiredMembersInspection"})
+ @Autowired
+ protected Example example;
+
+ @Test
+ public void test()
+ {
+ final SystemSecurityEnvironment systemSecurityEnvironment = new SystemSecurityEnvironment(
+ testEnvironment.getSystemKeyTimestamp(),
+ testEnvironment.getSystemPublicKey(),
+ testEnvironment.getSystemPrivateKey());
+
+ final String systemToken = systemSecurityEnvironment.systemToken(APP_NAME);
+
+ try (final AutoTenantContext ignored = new AutoTenantContext(TenantContextHolder.checkedGetIdentifier())) {
+ try (final AutoSeshat ignored2 = new AutoSeshat(systemToken)) {
+ example.initialize();
+ }}
+ }
+}
diff --git a/component-test/src/main/java/TestAnubisKeyRotation.java b/component-test/src/main/java/TestAnubisKeyRotation.java
new file mode 100644
index 0000000..9348fa0
--- /dev/null
+++ b/component-test/src/main/java/TestAnubisKeyRotation.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import io.mifos.anubis.api.v1.client.Anubis;
+import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
+import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.anubis.example.simple.Example;
+import io.mifos.anubis.example.simple.ExampleConfiguration;
+import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
+import io.mifos.core.api.context.AutoSeshat;
+import io.mifos.core.api.util.NotFoundException;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
+import io.mifos.core.test.env.TestEnvironment;
+import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
+import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.cloud.netflix.ribbon.RibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ * @author Myrle Krantz
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+public class TestAnubisKeyRotation {
+ private static final String APP_NAME = "anubis-v1";
+
+ @Configuration
+ @EnableFeignClients(basePackages = {"io.mifos.anubis.example.simple"})
+ @RibbonClient(name = APP_NAME)
+ @Import({ExampleConfiguration.class})
+ static public class TestConfiguration {
+ public TestConfiguration() {
+ super();
+ }
+
+ @Bean()
+ public Logger logger() {
+ return LoggerFactory.getLogger("key-rotation-test-logger");
+ }
+ }
+
+ private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
+ private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
+ private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer);
+
+ @ClassRule
+ public static TestRule orderClassRules = RuleChain
+ .outerRule(testEnvironment)
+ .around(cassandraInitializer)
+ .around(tenantDataStoreContext);
+
+ @Rule
+ public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
+ = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);
+
+ @SuppressWarnings({"SpringAutowiredFieldsWarningInspection", "SpringJavaAutowiringInspection", "SpringJavaAutowiredMembersInspection"})
+ @Autowired
+ protected Example example;
+
+ @Test
+ public void testKeyRotation()
+ {
+ final Anubis anubis = tenantApplicationSecurityEnvironment.getAnubis();
+
+ final String systemToken = tenantApplicationSecurityEnvironment.getSystemSecurityEnvironment().systemToken(APP_NAME);
+
+ try (final AutoSeshat ignored1 = new AutoSeshat(systemToken)) {
+ //Create a signature set then test that it is listed.
+ final RsaKeyPairFactory.KeyPairHolder identityManagerKeyPair = RsaKeyPairFactory.createKeyPair();
+ final Signature identityManagerSignature = new Signature(identityManagerKeyPair.getPublicKeyMod(), identityManagerKeyPair.getPublicKeyExp());
+
+ anubis.createSignatureSet(identityManagerKeyPair.getTimestamp(), identityManagerSignature);
+ {
+ final List<String> signatureSets = anubis.getAllSignatureSets();
+ Assert.assertTrue(signatureSets.contains(identityManagerKeyPair.getTimestamp()));
+ }
+
+ //Get the newly created signature set, and test that it's contents are correct.
+ final ApplicationSignatureSet signatureSet = anubis.getSignatureSet(identityManagerKeyPair.getTimestamp());
+ Assert.assertEquals(identityManagerSignature, signatureSet.getIdentityManagerSignature());
+
+ //Get just the application signature, and test that it's contents match the results of the whole signature set.
+ final Signature applicationSignature = anubis.getApplicationSignature(identityManagerKeyPair.getTimestamp());
+ Assert.assertEquals(signatureSet.getApplicationSignature(), applicationSignature);
+
+ //Create a second signature set and test that it and the prevous signature set are listed.
+ final RsaKeyPairFactory.KeyPairHolder identityManagerKeyPair2 = RsaKeyPairFactory.createKeyPair();
+ final Signature identityManagerSignature2 = new Signature(identityManagerKeyPair2.getPublicKeyMod(), identityManagerKeyPair2.getPublicKeyExp());
+
+ anubis.createSignatureSet(identityManagerKeyPair2.getTimestamp(), identityManagerSignature2);
+ {
+ final List<String> signatureSets = anubis.getAllSignatureSets();
+ Assert.assertTrue(signatureSets.contains(identityManagerKeyPair.getTimestamp()));
+ Assert.assertTrue(signatureSets.contains(identityManagerKeyPair2.getTimestamp()));
+ }
+
+ //Get the newly created signature set, and test that it's contents are correct.
+ final ApplicationSignatureSet signatureSet2 = anubis.getSignatureSet(identityManagerKeyPair2.getTimestamp());
+ Assert.assertEquals(identityManagerSignature2, signatureSet2.getIdentityManagerSignature());
+
+ //Delete one of the signature sets and test that it is no longer listed.
+ anubis.deleteSignatureSet(identityManagerKeyPair.getTimestamp());
+ {
+ final List<String> signatureSets = anubis.getAllSignatureSets();
+ Assert.assertFalse(signatureSets.contains(identityManagerKeyPair.getTimestamp()));
+ }
+
+ //Getting the newly deleted signature set should fail.
+ try {
+ anubis.getSignatureSet(identityManagerKeyPair.getTimestamp());
+ Assert.fail("Not found exception should be thrown.");
+ } catch (final NotFoundException ignored) {
+ }
+
+ //Getting the newly deleted application signature set should likewise fail.
+ try {
+ anubis.getApplicationSignature(identityManagerKeyPair.getTimestamp());
+ Assert.fail("Not found exception should be thrown.");
+ } catch (final NotFoundException ignored) {
+ }
+ }
+ }
+}
diff --git a/component-test/src/main/java/TestPermittableEndpoints.java b/component-test/src/main/java/TestPermittableEndpoints.java
index 455cbb4..64a4f4e 100644
--- a/component-test/src/main/java/TestPermittableEndpoints.java
+++ b/component-test/src/main/java/TestPermittableEndpoints.java
@@ -28,6 +28,8 @@
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -41,6 +43,7 @@
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestPermittableEndpoints {
private static final String APP_NAME = "anubis-v1";
+ private static final String LOGGER_QUALIFIER = "test-logger";
@Configuration
@Import({ExampleConfiguration.class})
@@ -49,12 +52,17 @@
super();
}
- @Bean()
+ @Bean(name=LOGGER_QUALIFIER)
public Logger logger() {
return LoggerFactory.getLogger("permittable-test-logger");
}
}
+ @SuppressWarnings("SpringAutowiredFieldsWarningInspection")
+ @Autowired
+ @Qualifier(value = LOGGER_QUALIFIER)
+ Logger logger;
+
private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer);
@@ -67,7 +75,7 @@
@Test
public void shouldFindPermittableEndpoints() throws Exception {
- final Anubis anubis = AnubisApiFactory.create(TestPermittableEndpoints.testEnvironment.serverURI());
+ final Anubis anubis = AnubisApiFactory.create(TestPermittableEndpoints.testEnvironment.serverURI(), logger);
final List<PermittableEndpoint> permittableEndpoints = anubis.getPermittableEndpoints();
Assert.assertNotNull(permittableEndpoints);
Assert.assertEquals(6, permittableEndpoints.size());
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/Example.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/Example.java
new file mode 100644
index 0000000..a50edbc
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/Example.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.example.nokeystorage;
+
+import io.mifos.core.api.util.CustomFeignClientsConfiguration;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@FeignClient(name="anubis-v1", path="/anubis/v1", configuration = CustomFeignClientsConfiguration.class)
+public interface Example {
+ @RequestMapping(value = "initialize", method = RequestMethod.POST)
+ void initialize();
+
+ @RequestMapping(value = "initialize", method = RequestMethod.GET)
+ boolean initialized();
+
+ @RequestMapping(value = "initialize", method = RequestMethod.DELETE)
+ void uninitialize();
+}
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleConfiguration.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleConfiguration.java
new file mode 100644
index 0000000..f0ab242
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.example.nokeystorage;
+
+import io.mifos.anubis.config.EnableAnubis;
+import io.mifos.core.lang.config.EnableServiceException;
+import io.mifos.core.lang.config.EnableTenantContext;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Myrle Krantz
+ */
+@Configuration
+@EnableAutoConfiguration
+@EnableTenantContext
+@EnableAnubis(storeTenantKeysAtInitialization = false)
+@EnableServiceException
+@ComponentScan({
+ "io.mifos.anubis.example.nokeystorage"
+})
+public class ExampleConfiguration {
+}
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java
new file mode 100644
index 0000000..a88dffa
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.example.nokeystorage;
+
+import io.mifos.anubis.annotation.AcceptedTokenType;
+import io.mifos.anubis.annotation.Permittable;
+import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
+import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+@RestController
+@RequestMapping()
+public class ExampleRestController {
+ private boolean initialized = false;
+ private final SpecialTenantSignatureRepository specialTenantSignatureRepository;
+
+ @Autowired
+ public ExampleRestController(final SpecialTenantSignatureRepository specialTenantSignatureRepository) {
+ this.specialTenantSignatureRepository = specialTenantSignatureRepository;
+ }
+
+ @RequestMapping(value = "/initialize", method = RequestMethod.POST)
+ @Permittable(AcceptedTokenType.SYSTEM)
+ public ResponseEntity<Void> initialize()
+ {
+ final RsaKeyPairFactory.KeyPairHolder applicationKeyPair = RsaKeyPairFactory.createKeyPair();
+ final RsaKeyPairFactory.KeyPairHolder identityManagerKeyPair = RsaKeyPairFactory.createKeyPair();
+ final Signature applicationSignature = new Signature(applicationKeyPair.getPublicKeyMod(), applicationKeyPair.getPublicKeyExp());
+ final Signature identityManagerSignature = new Signature(identityManagerKeyPair.getPublicKeyMod(), identityManagerKeyPair.getPublicKeyExp());
+
+ final ApplicationSignatureSet applicationSignatureSet = new ApplicationSignatureSet(identityManagerKeyPair.getTimestamp(), applicationSignature, identityManagerSignature);
+
+ this.specialTenantSignatureRepository.addSignatureSet(applicationSignatureSet);
+ initialized = true;
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @RequestMapping(value = "/initialize", method = RequestMethod.GET)
+ @Permittable(AcceptedTokenType.GUEST)
+ public ResponseEntity<Boolean> isInitialized()
+ {
+ return new ResponseEntity<>(initialized, HttpStatus.OK);
+ }
+
+ @RequestMapping(value = "/initialize", method = RequestMethod.DELETE)
+ @Permittable(AcceptedTokenType.GUEST)
+ public ResponseEntity<Void> uninitialize()
+ {
+ initialized = false;
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java
new file mode 100644
index 0000000..c74903f
--- /dev/null
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.example.nokeystorage;
+
+import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
+import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.anubis.config.TenantSignatureRepository;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @author Myrle Krantz
+ */
+@Component
+public class SpecialTenantSignatureRepository implements TenantSignatureRepository {
+ private final Map<String, ApplicationSignatureSet> applicationSignatureSetMap = new HashMap<>();
+
+ void addSignatureSet(final ApplicationSignatureSet applicationSignatureSet) {
+ applicationSignatureSetMap.put(applicationSignatureSet.getTimestamp(), applicationSignatureSet);
+ }
+
+ @Override
+ public Optional<Signature> getIdentityManagerSignature(final String timestamp) throws IllegalArgumentException {
+ final Optional<ApplicationSignatureSet> sigset = Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ return sigset.map(ApplicationSignatureSet::getIdentityManagerSignature);
+ }
+
+ @Override
+ public List<String> getAllSignatureSetKeyTimestamps() {
+ return applicationSignatureSetMap.keySet().stream().collect(Collectors.toList());
+ }
+
+ @Override
+ public Optional<ApplicationSignatureSet> getSignatureSet(final String timestamp) {
+ return Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ }
+
+ @Override
+ public void deleteSignatureSet(final String timestamp) {
+ applicationSignatureSetMap.remove(timestamp);
+ }
+
+ @Override
+ public Optional<Signature> getApplicationSignature(final String timestamp) {
+ final Optional<ApplicationSignatureSet> sigset = Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ return sigset.map(ApplicationSignatureSet::getApplicationSignature);
+ }
+}
diff --git a/library/src/main/java/io/mifos/anubis/config/AnubisImportSelector.java b/library/src/main/java/io/mifos/anubis/config/AnubisImportSelector.java
index 290da98..7da83f3 100644
--- a/library/src/main/java/io/mifos/anubis/config/AnubisImportSelector.java
+++ b/library/src/main/java/io/mifos/anubis/config/AnubisImportSelector.java
@@ -16,6 +16,7 @@
package io.mifos.anubis.config;
import io.mifos.anubis.controller.EmptyInitializeResourcesRestController;
+import io.mifos.anubis.controller.SignatureCreatorRestController;
import io.mifos.anubis.controller.SignatureRestController;
import io.mifos.anubis.controller.PermittableRestController;
import io.mifos.anubis.provider.SystemRsaKeyProvider;
@@ -54,6 +55,7 @@
classesToImport.add(GuestAuthenticator.class);
classesToImport.add(PermittableRestController.class);
+ classesToImport.add(SignatureRestController.class);
classesToImport.add(PermittableService.class);
final boolean storeTenantKeysAtInitialization = (boolean)importingClassMetadata
@@ -61,7 +63,7 @@
.get("storeTenantKeysAtInitialization");
if (storeTenantKeysAtInitialization) {
- classesToImport.add(SignatureRestController.class);
+ classesToImport.add(SignatureCreatorRestController.class);
classesToImport.add(TenantAuthorizationDataRepository.class);
final boolean generateEmptyInitializeEndpoint = (boolean)importingClassMetadata
diff --git a/library/src/main/java/io/mifos/anubis/config/TenantSignatureProvider.java b/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
similarity index 73%
rename from library/src/main/java/io/mifos/anubis/config/TenantSignatureProvider.java
rename to library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
index 037fc10..de6b1eb 100644
--- a/library/src/main/java/io/mifos/anubis/config/TenantSignatureProvider.java
+++ b/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
@@ -16,11 +16,13 @@
package io.mifos.anubis.config;
+import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
+import java.util.List;
import java.util.Optional;
-public interface TenantSignatureProvider {
+public interface TenantSignatureRepository {
/**
*
* @param timestamp The timestamp of the signature to get.
@@ -28,4 +30,12 @@
* @throws IllegalArgumentException if the tenant context is not set.
*/
Optional<Signature> getIdentityManagerSignature(String timestamp) throws IllegalArgumentException;
+
+ List<String> getAllSignatureSetKeyTimestamps();
+
+ Optional<ApplicationSignatureSet> getSignatureSet(String timestamp);
+
+ void deleteSignatureSet(String timestamp);
+
+ Optional<Signature> getApplicationSignature(String timestamp);
}
diff --git a/library/src/main/java/io/mifos/anubis/controller/SignatureCreatorRestController.java b/library/src/main/java/io/mifos/anubis/controller/SignatureCreatorRestController.java
new file mode 100644
index 0000000..f149a3f
--- /dev/null
+++ b/library/src/main/java/io/mifos/anubis/controller/SignatureCreatorRestController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.controller;
+
+import io.mifos.anubis.annotation.AcceptedTokenType;
+import io.mifos.anubis.annotation.Permittable;
+import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
+import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.anubis.api.v1.validation.ValidKeyTimestamp;
+import io.mifos.anubis.repository.TenantAuthorizationDataRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * @author Myrle Krantz
+ */
+@RestController
+@RequestMapping()
+public class SignatureCreatorRestController {
+
+ private final TenantAuthorizationDataRepository tenantAuthorizationDataRepository;
+
+ @Autowired
+ public SignatureCreatorRestController(final TenantAuthorizationDataRepository tenantAuthorizationDataRepository) {
+ this.tenantAuthorizationDataRepository = tenantAuthorizationDataRepository;
+ }
+
+ @Permittable(AcceptedTokenType.SYSTEM)
+ @RequestMapping(
+ value = "/signatures/{timestamp}",
+ method = RequestMethod.POST,
+ consumes = {MediaType.ALL_VALUE},
+ produces = {MediaType.APPLICATION_JSON_VALUE})
+ public
+ @ResponseBody
+ ResponseEntity<ApplicationSignatureSet> createSignatureSet(
+ @PathVariable("timestamp") @ValidKeyTimestamp final String timestamp,
+ @RequestBody @Valid final Signature identityManagerSignature) {
+ return ResponseEntity.ok(
+ new ApplicationSignatureSet(
+ timestamp,
+ tenantAuthorizationDataRepository.createSignatureSet(timestamp, identityManagerSignature),
+ identityManagerSignature));
+ }
+}
diff --git a/library/src/main/java/io/mifos/anubis/controller/SignatureRestController.java b/library/src/main/java/io/mifos/anubis/controller/SignatureRestController.java
index 53b0d83..af81fe2 100644
--- a/library/src/main/java/io/mifos/anubis/controller/SignatureRestController.java
+++ b/library/src/main/java/io/mifos/anubis/controller/SignatureRestController.java
@@ -19,79 +19,71 @@
import io.mifos.anubis.annotation.Permittable;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
-import io.mifos.anubis.api.v1.validation.ValidKeyTimestamp;
-import io.mifos.anubis.repository.TenantAuthorizationDataRepository;
+import io.mifos.anubis.config.TenantSignatureRepository;
import io.mifos.core.lang.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
-import javax.validation.Valid;
+import java.util.List;
/**
* @author Myrle Krantz
*/
@RestController
-@RequestMapping("/signatures")
+@RequestMapping()
public class SignatureRestController {
- final private TenantAuthorizationDataRepository tenantAuthorizationDataRepository;
+ final private TenantSignatureRepository tenantSignatureRepository;
@Autowired
- public SignatureRestController(
- final TenantAuthorizationDataRepository tenantAuthorizationDataRepository) {
- this.tenantAuthorizationDataRepository = tenantAuthorizationDataRepository;
+ public SignatureRestController(final TenantSignatureRepository tenantSignatureRepository) {
+ this.tenantSignatureRepository = tenantSignatureRepository;
}
@Permittable(AcceptedTokenType.SYSTEM)
@RequestMapping(
- value = "/{timestamp}",
- method = RequestMethod.POST,
+ value = "/signatures",
+ method = RequestMethod.GET,
consumes = {MediaType.ALL_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public
- @ResponseBody ResponseEntity<ApplicationSignatureSet> createSignatureSet(
- @PathVariable("timestamp") @ValidKeyTimestamp final String timestamp,
- @RequestBody @Valid final Signature identityManagerSignature) {
- return ResponseEntity.ok(
- new ApplicationSignatureSet(
- timestamp,
- tenantAuthorizationDataRepository.createSignatureSet(timestamp, identityManagerSignature),
- identityManagerSignature));
+ @ResponseBody ResponseEntity<List<String>> getAllSignatureSets() {
+ return ResponseEntity.ok(tenantSignatureRepository.getAllSignatureSetKeyTimestamps());
}
@Permittable(AcceptedTokenType.SYSTEM)
- @RequestMapping(value = "/{timestamp}", method = RequestMethod.GET,
+ @RequestMapping(value = "/signatures/{timestamp}", method = RequestMethod.GET,
consumes = {MediaType.ALL_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public
@ResponseBody ResponseEntity<ApplicationSignatureSet> getSignatureSet(@PathVariable("timestamp") final String timestamp)
{
- return tenantAuthorizationDataRepository.getSignatureSet(timestamp)
+ return tenantSignatureRepository.getSignatureSet(timestamp)
.map(ResponseEntity::ok)
.orElseThrow(() -> ServiceException.notFound("Signature for timestamp '" + timestamp + "' not found."));
}
@Permittable(AcceptedTokenType.SYSTEM)
- @RequestMapping(value = "/{timestamp}", method = RequestMethod.DELETE,
+ @RequestMapping(value = "/signatures/{timestamp}", method = RequestMethod.DELETE,
consumes = {MediaType.ALL_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public
@ResponseBody ResponseEntity<Void> deleteSignatureSet(@PathVariable("timestamp") final String timestamp)
{
- tenantAuthorizationDataRepository.deleteSignatureSet(timestamp);
+ tenantSignatureRepository.deleteSignatureSet(timestamp);
return ResponseEntity.accepted().build();
}
@Permittable(AcceptedTokenType.SYSTEM)
- @RequestMapping(value = "/{timestamp}/application", method = RequestMethod.GET,
+ @RequestMapping(value = "/signatures/{timestamp}/application", method = RequestMethod.GET,
consumes = {MediaType.ALL_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public
@ResponseBody ResponseEntity<Signature> getApplicationSignature(@PathVariable("timestamp") final String timestamp)
{
- return tenantAuthorizationDataRepository.getApplicationSignature(timestamp)
+ return tenantSignatureRepository.getApplicationSignature(timestamp)
.map(ResponseEntity::ok)
.orElseThrow(() -> ServiceException.notFound("Signature for timestamp '" + timestamp + "' not found."));
}
-}
+}
\ No newline at end of file
diff --git a/library/src/main/java/io/mifos/anubis/provider/TenantRsaKeyProvider.java b/library/src/main/java/io/mifos/anubis/provider/TenantRsaKeyProvider.java
index fda5925..aa3d38f 100644
--- a/library/src/main/java/io/mifos/anubis/provider/TenantRsaKeyProvider.java
+++ b/library/src/main/java/io/mifos/anubis/provider/TenantRsaKeyProvider.java
@@ -16,7 +16,7 @@
package io.mifos.anubis.provider;
import io.mifos.anubis.api.v1.domain.Signature;
-import io.mifos.anubis.config.TenantSignatureProvider;
+import io.mifos.anubis.config.TenantSignatureRepository;
import io.mifos.core.lang.security.RsaPublicKeyBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -30,17 +30,17 @@
@Component
public class TenantRsaKeyProvider {
- private final TenantSignatureProvider tenantSignatureProvider;
+ private final TenantSignatureRepository tenantSignatureRepository;
@Autowired
- public TenantRsaKeyProvider(final TenantSignatureProvider tenantSignatureProvider)
+ public TenantRsaKeyProvider(final TenantSignatureRepository tenantSignatureRepository)
{
- this.tenantSignatureProvider = tenantSignatureProvider;
+ this.tenantSignatureRepository = tenantSignatureRepository;
}
public PublicKey getPublicKey(final String keyTimestamp) throws InvalidKeyTimestampException {
final Optional<Signature> tenantAuthorizationData =
- tenantSignatureProvider.getIdentityManagerSignature(keyTimestamp);
+ tenantSignatureRepository.getIdentityManagerSignature(keyTimestamp);
return
tenantAuthorizationData.map(x -> new RsaPublicKeyBuilder()
diff --git a/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java b/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
index a59a42c..c6e5c9e 100644
--- a/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
+++ b/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
@@ -18,11 +18,12 @@
import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
+import com.datastax.driver.core.querybuilder.Update;
import com.datastax.driver.core.schemabuilder.SchemaBuilder;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.config.AnubisConstants;
-import io.mifos.anubis.config.TenantSignatureProvider;
+import io.mifos.anubis.config.TenantSignatureRepository;
import io.mifos.core.cassandra.core.CassandraSessionProvider;
import io.mifos.core.lang.ApplicationName;
import io.mifos.core.lang.security.RsaKeyPairFactory;
@@ -32,17 +33,22 @@
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
+import javax.annotation.Nonnull;
import java.math.BigInteger;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
/**
* @author Myrle Krantz
*/
@Component
-public class TenantAuthorizationDataRepository implements TenantSignatureProvider {
+public class TenantAuthorizationDataRepository implements TenantSignatureRepository {
private static final String AUTHORIZATION_TABLE_SUFFIX = "_authorization_v1_data";
+ private static final String AUTHORIZATION_INDEX_SUFFIX = "_authorization_v1_valid_index";
private static final String TIMESTAMP_COLUMN = "timestamp";
private static final String VALID_COLUMN = "valid";
private static final String IDENTITY_MANAGER_PUBLIC_KEY_MOD_COLUMN = "identity_manager_public_key_mod";
@@ -53,6 +59,7 @@
private static final String APPLICATION_PUBLIC_KEY_EXP_COLUMN = "application_public_key_exp";
private final String tableName;
+ private final String indexName;
private final CassandraSessionProvider cassandraSessionProvider;
//So that the query only has to be prepared once and the Cassandra driver stops writing warnings into my logfiles.
@@ -66,6 +73,7 @@
final @Qualifier(AnubisConstants.LOGGER_NAME) Logger logger)
{
tableName = applicationName.getServiceName() + AUTHORIZATION_TABLE_SUFFIX;
+ indexName = applicationName.getServiceName() + AUTHORIZATION_INDEX_SUFFIX;
this.cassandraSessionProvider = cassandraSessionProvider;
this.logger = logger;
}
@@ -82,6 +90,9 @@
* for the identity manager.
*/
public Signature createSignatureSet(final String timestamp, final Signature identityManagerSignature) {
+ Assert.notNull(timestamp);
+ Assert.notNull(identityManagerSignature);
+
//TODO: add validation to make sure this timestamp is more recent than any already stored.
final RsaKeyPairFactory.KeyPairHolder applicationSignature = RsaKeyPairFactory.createKeyPair();
@@ -101,10 +112,12 @@
}
public Optional<ApplicationSignatureSet> getSignatureSet(final String timestamp) {
+ Assert.notNull(timestamp);
return getRow(timestamp).map(TenantAuthorizationDataRepository::mapRowToSignatureSet);
}
public void deleteSignatureSet(final String timestamp) {
+ Assert.notNull(timestamp);
//Don't actually delete, just invalidate, so that if someone starts coming at me with an older keyset, I'll
//know what's happening.
final Session session = cassandraSessionProvider.getTenantSession();
@@ -112,15 +125,17 @@
}
public Optional<Signature> getApplicationSignature(final String timestamp) {
+ Assert.notNull(timestamp);
+
return getRow(timestamp).map(TenantAuthorizationDataRepository::mapRowToApplicationSignature);
}
- private void createTable(final Session tenantSession) {
+ private void createTable(final @Nonnull Session tenantSession) {
final String createTenantsTable = SchemaBuilder
- .createTable(tableName)
- .ifNotExists()
- .addPartitionKey(TIMESTAMP_COLUMN, DataType.text())
+ .createTable(tableName)
+ .ifNotExists()
+ .addPartitionKey(TIMESTAMP_COLUMN, DataType.text())
.addColumn(VALID_COLUMN, DataType.cboolean())
.addColumn(IDENTITY_MANAGER_PUBLIC_KEY_MOD_COLUMN, DataType.varint())
.addColumn(IDENTITY_MANAGER_PUBLIC_KEY_EXP_COLUMN, DataType.varint())
@@ -128,19 +143,27 @@
.addColumn(APPLICATION_PRIVATE_KEY_EXP_COLUMN, DataType.varint())
.addColumn(APPLICATION_PUBLIC_KEY_MOD_COLUMN, DataType.varint())
.addColumn(APPLICATION_PUBLIC_KEY_EXP_COLUMN, DataType.varint())
- .buildInternal();
+ .buildInternal();
tenantSession.execute(createTenantsTable);
+
+ final String createValidIndex = SchemaBuilder.createIndex(indexName)
+ .ifNotExists()
+ .onTable(tableName)
+ .andColumn(VALID_COLUMN)
+ .toString();
+
+ tenantSession.execute(createValidIndex);
}
- private void createEntry(final Session tenantSession,
- final String timestamp,
- final BigInteger identityManagerPublicKeyModulus,
- final BigInteger identityManagerPublicKeyExponent,
- final BigInteger applicationPrivateKeyModulus,
- final BigInteger applicationPrivateKeyExponent,
- final BigInteger applicationPublicKeyModulus,
- final BigInteger applicationPublicKeyExponent)
+ private void createEntry(final @Nonnull Session tenantSession,
+ final @Nonnull String timestamp,
+ final @Nonnull BigInteger identityManagerPublicKeyModulus,
+ final @Nonnull BigInteger identityManagerPublicKeyExponent,
+ final @Nonnull BigInteger applicationPrivateKeyModulus,
+ final @Nonnull BigInteger applicationPrivateKeyExponent,
+ final @Nonnull BigInteger applicationPublicKeyModulus,
+ final @Nonnull BigInteger applicationPublicKeyExponent)
{
final ResultSet timestampCount =
@@ -196,28 +219,21 @@
}
}
- private void invalidateEntry(final Session tenantSession, final String timestamp) {
- final BoundStatement tenantUpdateStatement =
- tenantSession.prepare("UPDATE " + tableName + " SET "
- + VALID_COLUMN + " = ?, "
- + "WHERE " + TIMESTAMP_COLUMN + " = ?").bind();
-
- tenantUpdateStatement.setString(TIMESTAMP_COLUMN, timestamp);
- tenantUpdateStatement.setBool(VALID_COLUMN, false);
-
- tenantSession.execute(tenantUpdateStatement);
+ private void invalidateEntry(final @Nonnull Session tenantSession, final @Nonnull String timestamp) {
+ final Update.Assignments updateQuery = QueryBuilder.update(tableName).where(QueryBuilder.eq(TIMESTAMP_COLUMN, timestamp)).with(QueryBuilder.set(VALID_COLUMN, false));
+ tenantSession.execute(updateQuery);
}
private void completeBoundStatement(
- final BoundStatement boundStatement,
- final String timestamp,
+ final @Nonnull BoundStatement boundStatement,
+ final @Nonnull String timestamp,
final boolean valid,
- final BigInteger identityManagerPublicKeyModulus,
- final BigInteger identityManagerPublicKeyExponent,
- final BigInteger applicationPrivateKeyModulus,
- final BigInteger applicationPrivateKeyExponent,
- final BigInteger applicationPublicKeyModulus,
- final BigInteger applicationPublicKeyExponent) {
+ final @Nonnull BigInteger identityManagerPublicKeyModulus,
+ final @Nonnull BigInteger identityManagerPublicKeyExponent,
+ final @Nonnull BigInteger applicationPrivateKeyModulus,
+ final @Nonnull BigInteger applicationPrivateKeyExponent,
+ final @Nonnull BigInteger applicationPublicKeyModulus,
+ final @Nonnull BigInteger applicationPublicKeyExponent) {
boundStatement.setString(TIMESTAMP_COLUMN, timestamp);
boundStatement.setBool(VALID_COLUMN, valid);
boundStatement.setVarint(IDENTITY_MANAGER_PUBLIC_KEY_MOD_COLUMN, identityManagerPublicKeyModulus);
@@ -231,10 +247,11 @@
@Override
public Optional<Signature> getIdentityManagerSignature(final String timestamp)
{
+ Assert.notNull(timestamp);
return getRow(timestamp).map(TenantAuthorizationDataRepository::mapRowToIdentityManagerSignature);
}
- private Optional<Row> getRow(final String timestamp) {
+ private Optional<Row> getRow(final @Nonnull String timestamp) {
final Session tenantSession = cassandraSessionProvider.getTenantSession();
final Select.Where query = timestampToSignatureQueryMap.computeIfAbsent(timestamp, timestampKey ->
QueryBuilder.select().from(tableName).where(QueryBuilder.eq(TIMESTAMP_COLUMN, timestampKey)));
@@ -247,11 +264,13 @@
return ret.filter(TenantAuthorizationDataRepository::mapRowToValid);
}
- private static Boolean mapRowToValid(final Row row) {
+ private static Boolean mapRowToValid(final @Nonnull Row row) {
return row.get(VALID_COLUMN, Boolean.class);
}
- private static Signature getSignature(Row row, String publicKeyModColumnName, String publicKeyExpColumnName) {
+ private static Signature getSignature(final @Nonnull Row row,
+ final @Nonnull String publicKeyModColumnName,
+ final @Nonnull String publicKeyExpColumnName) {
final BigInteger publicKeyModulus = row.get(publicKeyModColumnName, BigInteger.class);
final BigInteger publicKeyExponent = row.get(publicKeyExpColumnName, BigInteger.class);
@@ -261,19 +280,27 @@
return new Signature(publicKeyModulus, publicKeyExponent);
}
- private static Signature mapRowToIdentityManagerSignature(final Row row) {
+ private static Signature mapRowToIdentityManagerSignature(final @Nonnull Row row) {
return getSignature(row, IDENTITY_MANAGER_PUBLIC_KEY_MOD_COLUMN, IDENTITY_MANAGER_PUBLIC_KEY_EXP_COLUMN);
}
- private static Signature mapRowToApplicationSignature(final Row row) {
+ private static Signature mapRowToApplicationSignature(final @Nonnull Row row) {
return getSignature(row, APPLICATION_PUBLIC_KEY_MOD_COLUMN, APPLICATION_PUBLIC_KEY_EXP_COLUMN);
}
- private static ApplicationSignatureSet mapRowToSignatureSet(final Row row) {
+ private static ApplicationSignatureSet mapRowToSignatureSet(final @Nonnull Row row) {
final String timestamp = row.get(TIMESTAMP_COLUMN, String.class);
final Signature identityManagerSignature = mapRowToIdentityManagerSignature(row);
final Signature applicationSignature = mapRowToApplicationSignature(row);
return new ApplicationSignatureSet(timestamp, applicationSignature, identityManagerSignature);
}
-}
+
+ public List<String> getAllSignatureSetKeyTimestamps() {
+ final Select.Where selectValid = QueryBuilder.select(TIMESTAMP_COLUMN).from(tableName).where(QueryBuilder.eq(VALID_COLUMN, true));
+ final ResultSet result = cassandraSessionProvider.getTenantSession().execute(selectValid);
+ return StreamSupport.stream(result.spliterator(), false)
+ .map(x -> x.get(TIMESTAMP_COLUMN, String.class))
+ .collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java b/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java
index 32342f1..38363c6 100644
--- a/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java
+++ b/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java
@@ -57,7 +57,7 @@
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class SystemSecurityEnvironment {
- private final static String LOGGER_NAME = "anubis-test-logger";
+ final static String LOGGER_NAME = "anubis-test-logger";
private final TenantAccessTokenSerializer tenantAccessTokenSerializer;
private final SystemAccessTokenSerializer systemAccessTokenSerializer;
diff --git a/test/src/main/java/io/mifos/anubis/test/v1/TenantApplicationSecurityEnvironmentTestRule.java b/test/src/main/java/io/mifos/anubis/test/v1/TenantApplicationSecurityEnvironmentTestRule.java
index 338f382..ea1c1b8 100644
--- a/test/src/main/java/io/mifos/anubis/test/v1/TenantApplicationSecurityEnvironmentTestRule.java
+++ b/test/src/main/java/io/mifos/anubis/test/v1/TenantApplicationSecurityEnvironmentTestRule.java
@@ -25,6 +25,8 @@
import io.mifos.core.lang.TenantContextHolder;
import io.mifos.core.test.env.TestEnvironment;
import org.junit.rules.ExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.security.interfaces.RSAPublicKey;
import java.util.Collections;
@@ -46,6 +48,7 @@
private final SystemSecurityEnvironment systemSecurityEnvironment;
private final BooleanSupplier waitForInitialize;
+ private final Logger logger;
public TenantApplicationSecurityEnvironmentTestRule(final TestEnvironment testEnvironment) {
this(testEnvironment, () -> true);
@@ -72,6 +75,7 @@
this.applicationUri = applicationUri;
this.systemSecurityEnvironment = systemSecurityEnvironment;
this.waitForInitialize = waitForInitialize;
+ this.logger = LoggerFactory.getLogger(SystemSecurityEnvironment.LOGGER_NAME);
}
@Override
@@ -83,7 +87,7 @@
public void initializeTenantInApplication()
{
- final Anubis anubis = AnubisApiFactory.create(applicationUri);
+ final Anubis anubis = getAnubis();
final String systemToken = systemSecurityEnvironment.systemToken(applicationName);
@@ -97,6 +101,10 @@
}}
}
+ public Anubis getAnubis() {
+ return AnubisApiFactory.create(applicationUri, logger);
+ }
+
public SystemSecurityEnvironment getSystemSecurityEnvironment()
{
return systemSecurityEnvironment;