Skip to content
Permalink
Browse files
Merge pull request #20 from myrle-krantz/develop
Making it possible to call initialize multiple times.
  • Loading branch information
myrle-krantz committed Jul 12, 2017
2 parents fb5077f + 6700e6f commit 5de793c10582db7317fef382129fb03d7906c3d2
Showing 11 changed files with 130 additions and 133 deletions.
@@ -226,7 +226,6 @@ boolean getApplicationPermissionEnabledForUser(@PathVariable("applicationidentif
@RequestMapping(value = "/initialize", method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
@ThrowsException(status = HttpStatus.CONFLICT, exception = TenantAlreadyInitializedException.class)
ApplicationSignatureSet initialize(@RequestParam("password") String password);

@RequestMapping(value = "/signatures", method = RequestMethod.POST,
@@ -24,7 +24,6 @@
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 io.mifos.core.test.listener.EnableEventRecording;
import io.mifos.core.test.listener.EventRecorder;
import io.mifos.identity.api.v1.PermittableGroupIds;
@@ -38,8 +37,6 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -60,8 +57,7 @@
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
classes = {AbstractComponentTest.TestConfiguration.class})
@TestPropertySource(properties = {"cassandra.cl.read = LOCAL_QUORUM", "cassandra.cl.write = LOCAL_QUORUM", "cassandra.cl.delete = LOCAL_QUORUM", "identity.token.refresh.secureCookie = false", "identity.passwordExpiresInDays = 93"})
public class AbstractComponentTest {
static final String APP_NAME = "identity-v1";
public class AbstractComponentTest extends SuiteTestEnvironment {
@Configuration
@EnableApiFactory
@EnableEventRecording
@@ -80,16 +76,8 @@ public TestConfiguration() {
static final String AHMES_PASSWORD = "fractions";
static final String AHMES_FRIENDS_PASSWORD = "sekhem";

final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer);
private static boolean alreadyInitialized = false;

@ClassRule
public static TestRule orderClassRules = RuleChain
.outerRule(testEnvironment)
.around(cassandraInitializer)
.around(tenantDataStoreContext);
public final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer);

//Not using this as a rule because initialize in identityManager is different.
static final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);
@@ -108,12 +96,9 @@ public TestConfiguration() {
public void provision() throws Exception {
identityManager = apiFactory.create(IdentityManager.class, testEnvironment.serverURI());

if (!alreadyInitialized) {
try (final AutoUserContext ignored
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
identityManager.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
alreadyInitialized = true;
try (final AutoUserContext ignored
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
identityManager.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
}

@@ -0,0 +1,36 @@
/*
* 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.core.test.env.TestEnvironment;
import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
import org.junit.ClassRule;
import org.junit.rules.RuleChain;
import org.junit.rules.RunExternalResourceOnce;
import org.junit.rules.TestRule;

/**
* @author Myrle Krantz
*/
public class SuiteTestEnvironment {
static final String APP_NAME = "identity-v1";

final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
final static CassandraInitializer cassandraInitializer = new CassandraInitializer();

@ClassRule
public static TestRule orderClassRules = RuleChain
.outerRule(new RunExternalResourceOnce(testEnvironment))
.around(new RunExternalResourceOnce(cassandraInitializer));
}
@@ -17,34 +17,16 @@
import io.mifos.anubis.api.v1.RoleConstants;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
import io.mifos.anubis.token.SystemAccessTokenSerializer;
import io.mifos.core.api.config.EnableApiFactory;
import io.mifos.core.api.context.AutoSeshat;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.ApiFactory;
import io.mifos.core.api.util.InvalidTokenException;
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.TenantDataStoreTestContext;
import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
import io.mifos.identity.api.v1.client.IdentityManager;
import io.mifos.identity.api.v1.client.TenantAlreadyInitializedException;
import io.mifos.identity.config.IdentityServiceConfig;
import org.junit.Assert;
import org.junit.ClassRule;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigInteger;
import java.security.KeyFactory;
@@ -57,48 +39,29 @@
/**
* @author Myrle Krantz
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestProvisioning {
private static final String APP_NAME = "identity-v1";
@Configuration
@EnableApiFactory
@Import({IdentityServiceConfig.class})
public static class TestConfiguration {
public TestConfiguration() {
super();
}

@Bean()
public Logger logger() {
return LoggerFactory.getLogger("login-test-logger");
}
}

private static final String ADMIN_PASSWORD = "golden_osiris";
private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();

//Not using this as a rule because initialize in idnetity manager is different.
private static final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);

@ClassRule
public static TestRule orderClassRules = RuleChain
.outerRule(testEnvironment)
.around(cassandraInitializer);

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired ApiFactory apiFactory;

public class TestProvisioning extends AbstractComponentTest {

@Test
public void testBoundaryInitializeCases() throws InterruptedException {
final IdentityManager testSubject = getTestSubject();


final ApplicationSignatureSet firstTenantSignatureSet;
Signature firstTenantIdentityManagerSignature = null;
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
final Signature firstTenantIdentityManagerSignature;

//Create tenant keyspaces.
final String tenant1 = TestEnvironment.getRandomTenantName();
final String tenant2 = TestEnvironment.getRandomTenantName();
cassandraInitializer.initializeTenant(tenant1);
cassandraInitializer.initializeTenant(tenant2);
TimeUnit.SECONDS.sleep(1);
// This gives cassandra a chance to complete saving the new keyspaces.
// Theoretically, the creation of keyspaces is synchronous, but I've
// found that the cassandra driver needs just a little bit longer
// To show up in the request for metadata for that keyspace.


try (final AutoTenantContext ignored = new AutoTenantContext(tenant1)) {

final String invalidSeshatToken = "notBearer";
try (final AutoSeshat ignored2 = new AutoSeshat(invalidSeshatToken)){
@@ -130,27 +93,22 @@ public void testBoundaryInitializeCases() throws InterruptedException {
Assert.assertTrue("The exception should be 'not found'", (e instanceof InvalidTokenException));
}

// The second otherwise valid call to initialize for the same tenant should
// not fail even though the tenant is now already initialized.
try (final AutoUserContext ignored2 = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
firstTenantSignatureSet = testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));

final Signature applicationSignature = tenantApplicationSecurityEnvironment.getAnubis().getApplicationSignature(firstTenantSignatureSet.getTimestamp());
firstTenantIdentityManagerSignature = tenantApplicationSecurityEnvironment.getAnubis().getSignatureSet(firstTenantSignatureSet.getTimestamp()).getIdentityManagerSignature();
Assert.assertEquals(applicationSignature, firstTenantIdentityManagerSignature);


testSubject.initialize("golden_osiris");
Assert.fail("The second otherwise valid call to initialize for the same tenant should "
+ "fail because the tenant is now already initialized.");
}
catch (final TenantAlreadyInitializedException e)
{
//All is well.
}
}


final ApplicationSignatureSet secondTenantSignatureSet;
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
try (final AutoTenantContext ignored = new AutoTenantContext(tenant2)) {
try (final AutoUserContext ignored2
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
secondTenantSignatureSet = testSubject.initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
@@ -171,11 +129,6 @@ public void testBoundaryInitializeCases() throws InterruptedException {
TenantContextHolder.clear();
}


private IdentityManager getTestSubject() {
return apiFactory.create(IdentityManager.class, testEnvironment.serverURI());
}

private String systemTokenFromWrongKey()
{
final SystemAccessTokenSerializer.Specification tokenSpecification
@@ -13,11 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.mifos.identity.api.v1.client;


import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
* @author Myrle Krantz
*/
@SuppressWarnings("WeakerAccess")
public class TenantAlreadyInitializedException extends RuntimeException{
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestApplications.class,
TestAuthentication.class,
TestKeyRotation.class,
//TestPasswords.class,
TestPermittableGroups.class,
TestProvisioning.class,
//TestRefreshToken.class,
TestRoles.class,
TestUsers.class,
})
public class TestSuite extends SuiteTestEnvironment {
//TODO: Add TestPasswords and TestRefreshToken back in.
// For some reason, they fail in the test suite even though
// they succeed when run individually.
}
@@ -18,6 +18,7 @@
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
@@ -32,6 +33,7 @@
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
@@ -126,14 +128,14 @@ public Optional<SignatureEntity> getSignature(final String keyTimestamp) {
*/
public Optional<PrivateSignatureEntity> getPrivateSignature()
{
final Select.Where query = QueryBuilder.select(KEY_TIMESTAMP_COLUMN).from(TABLE_NAME).where(QueryBuilder.eq(VALID_COLUMN, Boolean.TRUE));
final ResultSet result = cassandraSessionProvider.getTenantSession().execute(query);
final Optional<String> maximumKeyTimestamp =
StreamSupport.stream(result.spliterator(), false)
.map(x -> x.get(KEY_TIMESTAMP_COLUMN, String.class))
.max(String::compareTo);

return maximumKeyTimestamp.flatMap(this::getPrivateSignatureEntity);
try {
final Optional<String> maximumKeyTimestamp = streamValidKeyTimestamps().max(String::compareTo);

return maximumKeyTimestamp.flatMap(this::getPrivateSignatureEntity);
}
catch (final InvalidQueryException e) {
return Optional.empty();
}
}

private Optional<PrivateSignatureEntity> getPrivateSignatureEntity(final String keyTimestamp) {
@@ -146,11 +148,21 @@ private Optional<PrivateSignatureEntity> getPrivateSignatureEntity(final String
}

public List<String> getAllKeyTimestamps() {
final Select.Where selectValid = QueryBuilder.select(KEY_TIMESTAMP_COLUMN).from(TABLE_NAME).where(QueryBuilder.eq(VALID_COLUMN, true));
final ResultSet result = cassandraSessionProvider.getTenantSession().execute(selectValid);
return StreamSupport.stream(result.spliterator(), false)
.map(x -> x.get(KEY_TIMESTAMP_COLUMN, String.class))
.collect(Collectors.toList());
return streamValidKeyTimestamps().collect(Collectors.toList());
}

private Stream<String> streamValidKeyTimestamps() {
try {
final Select.Where selectValid = QueryBuilder.select(KEY_TIMESTAMP_COLUMN)
.from(TABLE_NAME)
.where(QueryBuilder.eq(VALID_COLUMN, true));
final ResultSet result = cassandraSessionProvider.getTenantSession().execute(selectValid);
return StreamSupport.stream(result.spliterator(), false)
.map(x -> x.get(KEY_TIMESTAMP_COLUMN, String.class));
}
catch (final InvalidQueryException e) {
return Stream.empty();
}
}

public void invalidateEntry(final String keyTimestamp) {
@@ -94,9 +94,10 @@ public Optional<PrivateTenantInfoEntity> getPrivateTenantInfo()

public boolean currentTenantAlreadyProvisioned() {
final String keyspace = cassandraSessionProvider.getTenantSession().getLoggedKeyspace();

final KeyspaceMetadata keyspaceMetadata = cassandraSessionProvider.getTenantSession()
.getCluster().getMetadata().getKeyspace(keyspace);
.getCluster().getMetadata().getKeyspace(keyspace);

return keyspaceMetadata.getTable(TABLE_NAME) != null;
return keyspaceMetadata != null && keyspaceMetadata.getTable(TABLE_NAME) != null;
}
}

0 comments on commit 5de793c

Please sign in to comment.