Skip to content
Permalink
Browse files
Merge pull request #3 from myrle-krantz/develop
Preparation for key rotation in the provisioner
  • Loading branch information
myrle-krantz committed Apr 13, 2017
2 parents 7689ccc + 4675a76 commit 2bbcadd52fe07e8093d24eefe81fd809f5eec291
Showing 10 changed files with 172 additions and 69 deletions.
@@ -17,6 +17,7 @@

import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import java.util.Objects;

@SuppressWarnings({"unused", "WeakerAccess"})
public final class DatabaseConnectionInfo {
@@ -90,4 +91,34 @@ public String getPassword() {
public void setPassword(@Nonnull final String password) {
this.password = password;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DatabaseConnectionInfo that = (DatabaseConnectionInfo) o;
return Objects.equals(driverClass, that.driverClass) &&
Objects.equals(databaseName, that.databaseName) &&
Objects.equals(host, that.host) &&
Objects.equals(port, that.port) &&
Objects.equals(user, that.user) &&
Objects.equals(password, that.password);
}

@Override
public int hashCode() {
return Objects.hash(driverClass, databaseName, host, port, user, password);
}

@Override
public String toString() {
return "DatabaseConnectionInfo{" +
"driverClass='" + driverClass + '\'' +
", databaseName='" + databaseName + '\'' +
", host='" + host + '\'' +
", port='" + port + '\'' +
", user='" + user + '\'' +
", password='" + password + '\'' +
'}';
}
}
@@ -16,6 +16,7 @@
package io.mifos.provisioner.api.v1.domain;

import javax.validation.constraints.NotNull;
import java.util.Objects;

@SuppressWarnings("unused")
public final class Tenant {
@@ -73,4 +74,32 @@ public DatabaseConnectionInfo getDatabaseConnectionInfo() {
public void setDatabaseConnectionInfo(DatabaseConnectionInfo databaseConnectionInfo) {
this.databaseConnectionInfo = databaseConnectionInfo;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tenant tenant = (Tenant) o;
return Objects.equals(identifier, tenant.identifier) &&
Objects.equals(name, tenant.name) &&
Objects.equals(description, tenant.description) &&
Objects.equals(cassandraConnectionInfo, tenant.cassandraConnectionInfo) &&
Objects.equals(databaseConnectionInfo, tenant.databaseConnectionInfo);
}

@Override
public int hashCode() {
return Objects.hash(identifier, name, description, cassandraConnectionInfo, databaseConnectionInfo);
}

@Override
public String toString() {
return "Tenant{" +
"identifier='" + identifier + '\'' +
", name='" + name + '\'' +
", description='" + description + '\'' +
", cassandraConnectionInfo=" + cassandraConnectionInfo +
", databaseConnectionInfo=" + databaseConnectionInfo +
'}';
}
}
@@ -16,6 +16,7 @@
package io.mifos.provisioner.tenant;

import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.PermittableEndpoint;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.provider.SystemRsaKeyProvider;
@@ -25,6 +26,7 @@
import io.mifos.core.api.util.ApiConstants;
import io.mifos.core.api.util.ApiFactory;
import io.mifos.core.lang.AutoTenantContext;
import io.mifos.core.lang.security.RsaKeyPairFactory;
import io.mifos.core.test.env.TestEnvironment;
import io.mifos.identity.api.v1.client.IdentityManager;
import io.mifos.identity.api.v1.domain.PermittableGroup;
@@ -107,7 +109,7 @@ public TokenProvider tokenProviderSpy(final @Qualifier("tokenProvider") TokenPro
private static ProvisionerMariaDBInitializer mariaDBInitializer = new ProvisionerMariaDBInitializer();
private static ProvisionerCassandraInitializer cassandraInitializer = new ProvisionerCassandraInitializer();
private static SystemSecurityEnvironment systemSecurityEnvironment
= new SystemSecurityEnvironment(testEnvironment.getSystemPublicKey(), testEnvironment.getSystemPrivateKey());
= new SystemSecurityEnvironment(testEnvironment.getSystemKeyTimestamp(), testEnvironment.getSystemPublicKey(), testEnvironment.getSystemPrivateKey());

@ClassRule
public static TestRule orderClassRules = RuleChain
@@ -168,27 +170,34 @@ public TokenSerializationResult answer(final InvocationOnMock invocation) throws
}
}

private class VerifyIsisInitializeContext implements Answer<Signature> {
private class VerifyIsisInitializeContext implements Answer<ApplicationSignatureSet> {

private final String keyTimestamp;
private final BigInteger modulus;
private final BigInteger exponent;

private boolean validSecurityContext = false;

VerifyIsisInitializeContext(final BigInteger modulus, final BigInteger exponent) {
VerifyIsisInitializeContext(final String keyTimestamp, final BigInteger modulus, final BigInteger exponent) {
this.keyTimestamp = keyTimestamp;
this.modulus = modulus;
this.exponent = exponent;
}

@Override
public Signature answer(final InvocationOnMock invocation) throws Throwable {
public ApplicationSignatureSet answer(final InvocationOnMock invocation) throws Throwable {
validSecurityContext = systemSecurityEnvironment.isValidSystemSecurityContext("identity", "1", Fixture.TENANT_IDENTIFIER);

final Signature fakeSignature = new Signature();
fakeSignature.setPublicKeyMod(modulus);
fakeSignature.setPublicKeyExp(exponent);

return fakeSignature;
final ApplicationSignatureSet ret = new ApplicationSignatureSet();
ret.setTimestamp(keyTimestamp);
ret.setApplicationSignature(fakeSignature);
ret.setIdentityManagerSignature(fakeSignature);

return ret;
}

boolean isValidSecurityContext() {
@@ -216,6 +225,32 @@ boolean isValidSecurityContext() {
}
}

private class VerifyCreateSignatureSetContext implements Answer<ApplicationSignatureSet> {

private boolean validSecurityContext = false;
final private String target;

private VerifyCreateSignatureSetContext(final String target) {
this.target = target;
}

@Override
public ApplicationSignatureSet answer(final InvocationOnMock invocation) throws Throwable {
final String timestamp = invocation.getArgumentAt(0, String.class);
final Signature identityManagerSignature = invocation.getArgumentAt(1, Signature.class);
validSecurityContext = systemSecurityEnvironment.isValidSystemSecurityContext(target, "1", Fixture.TENANT_IDENTIFIER);
final RsaKeyPairFactory.KeyPairHolder keys = RsaKeyPairFactory.createKeyPair();
return new ApplicationSignatureSet(
timestamp,
new Signature(keys.getPublicKeyMod(), keys.getPublicKeyExp()),
identityManagerSignature);
}

boolean isValidSecurityContext() {
return validSecurityContext;
}
}


private class VerifyAnubisPermittablesContext implements Answer<List<PermittableEndpoint>> {

@@ -265,6 +300,7 @@ public void testTenantApplicationAssignment() throws InterruptedException {
final VerifyIsisInitializeContext verifyInitializeContextAndReturnSignature;
try (final AutoTenantContext ignored = new AutoTenantContext(Fixture.TENANT_IDENTIFIER)) {
verifyInitializeContextAndReturnSignature = new VerifyIsisInitializeContext(
systemSecurityEnvironment.tenantKeyTimestamp(),
systemSecurityEnvironment.tenantPublicKey().getModulus(),
systemSecurityEnvironment.tenantPublicKey().getPublicExponent());
}
@@ -282,7 +318,7 @@ public void testTenantApplicationAssignment() throws InterruptedException {
Assert.assertNotNull(identityServiceAdminInitialization.getAdminPassword());
}

verify(applicationCallContextProviderSpy).getApplicationCallContext(Fixture.TENANT_IDENTIFIER, "identity-v1");
verify(applicationCallContextProviderSpy, atMost(2)).getApplicationCallContext(Fixture.TENANT_IDENTIFIER, "identity-v1");


//Create horus application.
@@ -309,12 +345,15 @@ public void testTenantApplicationAssignment() throws InterruptedException {
final PermittableEndpoint mPermittableEndpoint = new PermittableEndpoint("/m/n", "GET", "m");

final VerifyAnubisInitializeContext verifyAnubisInitializeContext;
final VerifyCreateSignatureSetContext verifyCreateSignatureSetContext;
final VerifyAnubisPermittablesContext verifyAnubisPermittablesContext;
try (final AutoTenantContext ignored = new AutoTenantContext(Fixture.TENANT_IDENTIFIER)) {
verifyAnubisInitializeContext = new VerifyAnubisInitializeContext("office");
verifyCreateSignatureSetContext = new VerifyCreateSignatureSetContext("office");
verifyAnubisPermittablesContext = new VerifyAnubisPermittablesContext(Arrays.asList(xxPermittableEndpoint, xxPermittableEndpoint, xyPermittableEndpoint, xyGetPermittableEndpoint, mPermittableEndpoint));
}
doAnswer(verifyAnubisInitializeContext).when(anubisMock).initialize(anyObject(), anyObject());
doAnswer(verifyAnubisInitializeContext).when(anubisMock).initializeResources();
doAnswer(verifyCreateSignatureSetContext).when(anubisMock).createSignatureSet(anyString(), anyObject());
doAnswer(verifyAnubisPermittablesContext).when(anubisMock).getPermittableEndpoints();

{
@@ -330,6 +369,7 @@ public void testTenantApplicationAssignment() throws InterruptedException {
verify(identityServiceMock).createPermittableGroup(new PermittableGroup("m", Collections.singletonList(mPermittableEndpoint)));

Assert.assertTrue(verifyAnubisInitializeContext.isValidSecurityContext());
Assert.assertTrue(verifyCreateSignatureSetContext.isValidSecurityContext());
Assert.assertTrue(verifyAnubisPermittablesContext.isValidSecurityContext());
}
}
@@ -79,6 +79,7 @@ public void shouldFindTenant() {
provisioner.createTenant(tenant);
final Tenant foundTenant = provisioner.getTenant(tenant.getIdentifier());
Assert.assertNotNull(foundTenant);
Assert.assertEquals(tenant, foundTenant);
}

@Test(expected = NotFoundException.class)
@@ -15,15 +15,12 @@
*/
package io.mifos.provisioner.config;

import io.mifos.anubis.config.AnubisConstants;
import io.mifos.anubis.config.EnableAnubis;
import io.mifos.anubis.config.TenantSignatureProvider;
import io.mifos.anubis.repository.TenantAuthorizationDataRepository;
import io.mifos.anubis.token.SystemAccessTokenSerializer;
import io.mifos.core.api.util.ApiFactory;
import io.mifos.core.async.config.EnableAsync;
import io.mifos.core.cassandra.config.EnableCassandra;
import io.mifos.core.cassandra.core.CassandraSessionProvider;
import io.mifos.core.lang.ApplicationName;
import io.mifos.core.lang.config.EnableApplicationName;
import io.mifos.core.lang.config.EnableServiceException;
import io.mifos.core.mariadb.config.EnableMariaDB;
@@ -50,7 +47,7 @@
})
@EnableCrypto
@EnableAsync
@EnableAnubis(storeTenantKeysAtInitialization = false)
@EnableAnubis(provideSignatureRestController = false)
@EnableMariaDB
@EnableCassandra
@EnableServiceException
@@ -68,8 +65,12 @@ public Logger logger() {

@Bean(name = "tokenProvider")
public TokenProvider tokenProvider(final Environment environment,
@SuppressWarnings("SpringJavaAutowiringInspection") final SystemAccessTokenSerializer tokenSerializer) {
return new TokenProvider(
@SuppressWarnings("SpringJavaAutowiringInspection") final SystemAccessTokenSerializer tokenSerializer,
@Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger) {
final String timestamp = environment.getProperty(AnubisConstants.PUBLIC_KEY_TIMESTAMP_PROPERTY);
logger.info("Provisioner key timestamp: " + timestamp);

return new TokenProvider( timestamp,
new BigInteger(environment.getProperty("system.privateKey.modulus")),
new BigInteger(environment.getProperty("system.privateKey.exponent")), tokenSerializer);
}
@@ -78,20 +79,4 @@ public TokenProvider tokenProvider(final Environment environment,
public ApiFactory apiFactory(@Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger) {
return new ApiFactory(logger);
}

@Bean
public TenantSignatureProvider tenantSignatureProvider()
{
return tenant -> {
throw new IllegalArgumentException("no io.mifos.provisioner.tenant signatures here.");
};
}

@Bean
public TenantAuthorizationDataRepository tenantAuthorizationDataRepository(
final ApplicationName applicationName,
final CassandraSessionProvider cassandraSessionProvider)
{
return new TenantAuthorizationDataRepository(applicationName, cassandraSessionProvider);
}
}
@@ -18,18 +18,16 @@
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.Result;

import io.mifos.anubis.api.v1.TokenConstants;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.repository.TenantAuthorizationDataRepository;
import io.mifos.anubis.config.TenantSignatureRepository;
import io.mifos.core.cassandra.core.CassandraSessionProvider;
import io.mifos.core.lang.AutoTenantContext;
import io.mifos.core.lang.ServiceException;
import io.mifos.provisioner.internal.repository.ApplicationEntity;
import io.mifos.provisioner.internal.repository.TenantCassandraRepository;
import io.mifos.provisioner.internal.repository.TenantApplicationEntity;
import io.mifos.provisioner.internal.repository.TenantCassandraRepository;
import io.mifos.provisioner.internal.repository.TenantEntity;

import io.mifos.provisioner.internal.service.applications.AnubisInitializer;
import io.mifos.provisioner.internal.service.applications.IdentityServiceInitializer;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,7 +36,10 @@
import org.springframework.util.Assert;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@@ -47,20 +48,20 @@ public class TenantApplicationService {
private final CassandraSessionProvider cassandraSessionProvider;
private final AnubisInitializer anubisInitializer;
private final IdentityServiceInitializer identityServiceInitializer;
private final TenantAuthorizationDataRepository tenantAuthorizationDataRepository;
private final TenantSignatureRepository tenantSignatureRepository;
private final TenantCassandraRepository tenantCassandraRepository;

@Autowired
public TenantApplicationService(final CassandraSessionProvider cassandraSessionProvider,
final AnubisInitializer anubisInitializer,
final IdentityServiceInitializer identityServiceInitializer,
final TenantAuthorizationDataRepository tenantAuthorizationDataRepository,
@SuppressWarnings("SpringJavaAutowiringInspection") final TenantSignatureRepository tenantSignatureRepository,
final TenantCassandraRepository tenantCassandraRepository) {
super();
this.cassandraSessionProvider = cassandraSessionProvider;
this.anubisInitializer = anubisInitializer;
this.identityServiceInitializer = identityServiceInitializer;
this.tenantAuthorizationDataRepository = tenantAuthorizationDataRepository;
this.tenantSignatureRepository = tenantSignatureRepository;
this.tenantCassandraRepository = tenantCassandraRepository;
}

@@ -80,7 +81,7 @@ public void assign(final @Nonnull TenantApplicationEntity tenantApplicationEntit

initializeIsis(x, applicationNameToUriPairs);

getIsisSignature(x).ifPresent(y -> initializeAnubis(x, y, applicationNameToUriPairs));
getLatestIdentityManagerSignatureSet(x).ifPresent(y -> initializeAnubis(x, y.getTimestamp(), y.getIdentityManagerSignature(), applicationNameToUriPairs));
});

tenantEntity.orElseThrow(
@@ -127,9 +128,9 @@ public int hashCode() {
}
}

private Optional<Signature> getIsisSignature(final @Nonnull TenantEntity tenantEntity) {
private Optional<ApplicationSignatureSet> getLatestIdentityManagerSignatureSet(final @Nonnull TenantEntity tenantEntity) {
try (final AutoTenantContext ignored = new AutoTenantContext(tenantEntity.getIdentifier())) {
return tenantAuthorizationDataRepository.getSignature(TokenConstants.VERSION);
return tenantSignatureRepository.getLatestSignatureSet();
}
}

@@ -146,13 +147,15 @@ private void initializeIsis(

private void initializeAnubis(
final @Nonnull TenantEntity tenantEntity,
final @Nonnull String keyTimestamp,
final @Nonnull Signature identityServiceTenantSignature,
final @Nonnull Set<ApplicationNameToUriPair> applicationNameToUriPairs) {
applicationNameToUriPairs.forEach(applicationNameUriPair ->
anubisInitializer.initializeAnubis(
tenantEntity.getIdentifier(),
applicationNameUriPair.name,
applicationNameUriPair.uri,
keyTimestamp,
identityServiceTenantSignature)
);
}

0 comments on commit 2bbcadd

Please sign in to comment.