Skip to content
Permalink
Browse files
Improved configuration validation, and configuration defaults by addi…
…ng two @ConfigurationProperties annotated classes to assist in reading configuration properties and programming validation for those classes. After this change the provisioner will not start unless there are public and private keys set and those keys match each other.
  • Loading branch information
myrle-krantz committed Jul 19, 2017
1 parent eb94569 commit eb369d5e586eabdf78eefa155f3e8b9e17a2f50c
Showing 16 changed files with 409 additions and 86 deletions.
@@ -17,7 +17,6 @@

import io.mifos.core.test.env.TestEnvironment;
import io.mifos.provisioner.api.v1.client.Provisioner;
import io.mifos.provisioner.config.ProvisionerActiveMQProperties;
import io.mifos.provisioner.config.ProvisionerServiceConfig;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -37,11 +36,7 @@

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
classes = {AbstractServiceTest.TestConfiguration.class},
properties = {
ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_DEFAULT,
ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_DEFAULT}
)
classes = {AbstractServiceTest.TestConfiguration.class})
public class AbstractServiceTest {
private static final String APP_NAME = "provisioner-v1";
private static final String CLIENT_ID = "sillyRabbit";
@@ -72,6 +67,7 @@ public Logger logger() {
.around(mariaDBInitializer)
.around(cassandraInitializer);

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
protected Provisioner provisioner;

@@ -21,7 +21,6 @@
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;
import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
import io.mifos.core.api.context.AutoSeshat;
import io.mifos.core.api.util.ApiConstants;
@@ -40,7 +39,6 @@
import io.mifos.provisioner.ProvisionerMariaDBInitializer;
import io.mifos.provisioner.api.v1.client.Provisioner;
import io.mifos.provisioner.api.v1.domain.*;
import io.mifos.provisioner.config.ProvisionerActiveMQProperties;
import io.mifos.provisioner.config.ProvisionerConstants;
import io.mifos.provisioner.config.ProvisionerServiceConfig;
import io.mifos.provisioner.internal.listener.IdentityListener;
@@ -77,11 +75,7 @@
* @author Myrle Krantz
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = {
ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_BROKER_URL_DEFAULT,
ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_PROP + "=" + ProvisionerActiveMQProperties.ACTIVEMQ_CONCURRENCY_DEFAULT}
)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestTenantApplicationAssignment {
private static final String APP_NAME = "provisioner-v1";
private static final String CLIENT_ID = "sillyRabbit";
@@ -128,24 +122,21 @@ public TokenProvider tokenProviderSpy(final @Qualifier("tokenProvider") TokenPro
.around(mariaDBInitializer)
.around(cassandraInitializer);

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
private Provisioner provisioner;

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
@Qualifier("tokenProviderSpy")
protected TokenProvider tokenProviderSpy;
private ApplicationCallContextProvider applicationCallContextProviderSpy;

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
protected ApplicationCallContextProvider applicationCallContextProviderSpy;
private IdentityListener identityListener;

@SuppressWarnings("SpringAutowiredFieldsWarningInspection")
@Autowired
protected SystemRsaKeyProvider systemRsaKeyProvider;

@Autowired
protected IdentityListener identityListener;

@Autowired
protected Gson gson;
private Gson gson;

private AutoSeshat autoSeshat;

@@ -0,0 +1,26 @@
#
# 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.
#

system:
domain: mifos.io
dataStoreOption: ALL # possible values ALL, CASSANDRA, RDBMS
token:
ttl: 60


activemq:
brokerUrl: vm://localhost?broker.persistent=false
concurrency: 3-10
@@ -47,6 +47,10 @@ dependencies {
[group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator],
[group: 'io.mifos.tools', name: 'crypto', version: versions.frameworkcrypto],
)

testCompile(
[group: 'io.mifos.core', name: 'test', version: versions.frameworktest],
)
}

publishToMavenLocal.dependsOn bootRepackage
@@ -0,0 +1,60 @@
/*
* 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.provisioner.config;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

/**
* @author Myrle Krantz
*/
public class CheckKeysValid implements ConstraintValidator<KeysValid, SystemProperties> {

@Override
public void initialize(KeysValid constraintAnnotation) {
}

@Override
public boolean isValid(final SystemProperties value, final ConstraintValidatorContext context) {
if (value.getPrivateKey().getModulus() == null || value.getPrivateKey().getExponent() == null ||
value.getPublicKey().getModulus() == null ||value.getPublicKey().getExponent() == null)
return false;

try {
final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
final RSAPrivateKeySpec rsaPrivateKeySpec
= new RSAPrivateKeySpec(value.getPrivateKey().getModulus(), value.getPrivateKey().getExponent());
final PrivateKey privateKey = keyFactory.generatePrivate(rsaPrivateKeySpec);

final RSAPublicKeySpec rsaPublicKeySpec
= new RSAPublicKeySpec(value.getPublicKey().getModulus(), value.getPublicKey().getExponent());
final PublicKey publicKey = keyFactory.generatePublic(rsaPublicKeySpec);

final Signature signature = Signature.getInstance("NONEwithRSA");
signature.initSign(privateKey);
final byte[] signed = signature.sign();

signature.initVerify(publicKey);
return signature.verify(signed);
} catch (final NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException | SignatureException e) {
return false;
}
}
}
@@ -0,0 +1,38 @@
/*
* 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.provisioner.config;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
* @author Myrle Krantz
*/
@SuppressWarnings("unused")
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {CheckKeysValid.class}
)
public @interface KeysValid {
String message() default "Public and private keys must be valid and matching.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,38 @@
/*
* 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.provisioner.config;

import io.mifos.provisioner.internal.util.DataStoreOption;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.validation.Valid;

/**
* @author Myrle Krantz
*/
@ConfigurationProperties(prefix = "provisioner")
public class ProvisionerProperties {
@Valid
private DataStoreOption dataStoreOption = DataStoreOption.ALL;

public DataStoreOption getDataStoreOption() {
return dataStoreOption;
}

public void setDataStoreOption(DataStoreOption dataStoreOption) {
this.dataStoreOption = dataStoreOption;
}
}
@@ -15,7 +15,6 @@
*/
package io.mifos.provisioner.config;

import io.mifos.anubis.config.AnubisConstants;
import io.mifos.anubis.config.EnableAnubis;
import io.mifos.anubis.token.SystemAccessTokenSerializer;
import io.mifos.core.api.util.ApiFactory;
@@ -36,14 +35,11 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.math.BigInteger;

@Configuration
@EnableAutoConfiguration
@ComponentScan({
@@ -60,7 +56,7 @@
@EnableCassandra
@EnableServiceException
@EnableApplicationName
@EnableConfigurationProperties({ProvisionerActiveMQProperties.class})
@EnableConfigurationProperties({ProvisionerActiveMQProperties.class, ProvisionerProperties.class, SystemProperties.class})
public class ProvisionerServiceConfig extends WebMvcConfigurerAdapter {

public ProvisionerServiceConfig() {
@@ -73,15 +69,15 @@ public Logger logger() {
}

@Bean(name = "tokenProvider")
public TokenProvider tokenProvider(final Environment environment,
public TokenProvider tokenProvider(final SystemProperties systemProperties,
@SuppressWarnings("SpringJavaAutowiringInspection") final SystemAccessTokenSerializer tokenSerializer,
@Qualifier(ProvisionerConstants.LOGGER_NAME) final Logger logger) {
final String timestamp = environment.getProperty(AnubisConstants.PUBLIC_KEY_TIMESTAMP_PROPERTY);
final String timestamp = systemProperties.getPublicKey().getTimestamp();
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);
systemProperties.getPrivateKey().getModulus(),
systemProperties.getPrivateKey().getExponent(), tokenSerializer);
}

@Bean

0 comments on commit eb369d5

Please sign in to comment.