Skip to content
Permalink
Browse files
It's now possible to configure a service to accept guest access to th…
…ose endpoints which do not have a permission-level specified. This is intended to make it possible to access spring actuators inside of a test system when trying to determine the appropriate resource level for a service.
  • Loading branch information
myrle-krantz committed Jul 31, 2017
1 parent aef0c51 commit c59befddd17c621b5ff540c98af8fd7cd002fd97
Showing 6 changed files with 143 additions and 27 deletions.
@@ -15,10 +15,10 @@
*/
package io.mifos.anubis;

import io.mifos.anubis.example.nokeystorage.ExampleConfiguration;
import io.mifos.anubis.example.nokeystorage.Example;
import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
import org.junit.Rule;
import io.mifos.anubis.example.nokeystorage.ExampleConfiguration;
import io.mifos.anubis.example.simple.MetricsFeignClient;
import io.mifos.anubis.suites.SuiteTestEnvironment;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,19 +31,25 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import io.mifos.anubis.suites.SuiteTestEnvironment;

/**
* @author Myrle Krantz
*
* Adding the property anubis.acceptGuestTokensForSystemEndpoints=true to this is a bit of a hack,
* that saves me from creating an entire test suite for just this one property. See
* TestAnubisInitializeWithSpecialTenantSignatureRepository.shouldBeAbleToContactSpringEndpointWithGuestTokenWhenSoConfigured
* for its use.
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
classes = AbstractNoKeyStorageTest.TestConfiguration.class)
classes = AbstractNoKeyStorageTest.TestConfiguration.class,
properties = {"anubis.acceptGuestTokensForSystemEndpoints=true"})

public class AbstractNoKeyStorageTest extends SuiteTestEnvironment {
private static final String LOGGER_QUALIFIER = "test-logger";

@Configuration
@EnableFeignClients(basePackages = {"io.mifos.anubis.example.nokeystorage"})
@EnableFeignClients(clients = {io.mifos.anubis.example.nokeystorage.Example.class, MetricsFeignClient.class})
@RibbonClient(name = APP_NAME)
@Import({ExampleConfiguration.class})
static public class TestConfiguration {
@@ -15,16 +15,26 @@
*/
package io.mifos.anubis;

import io.mifos.anubis.example.simple.Metrics;
import io.mifos.anubis.example.simple.MetricsFeignClient;
import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
import io.mifos.core.api.context.AutoGuest;
import io.mifos.core.api.context.AutoSeshat;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.lang.AutoTenantContext;
import io.mifos.core.lang.TenantContextHolder;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
* @author Myrle Krantz
*/
public class TestAnubisInitializeWithSpecialTenantSignatureRepository extends AbstractNoKeyStorageTest {
@SuppressWarnings({"SpringAutowiredFieldsWarningInspection", "SpringJavaAutowiringInspection"})
@Autowired
private MetricsFeignClient metricsFeignClient;

@Test
public void test()
{
@@ -40,4 +50,12 @@ public void test()
example.initialize();
}}
}

@Test
public void shouldBeAbleToContactSpringEndpointWithGuestTokenWhenSoConfigured() throws Exception {
try (final AutoUserContext ignored = new AutoGuest()) {
final Metrics metrics = metricsFeignClient.getMetrics();
Assert.assertTrue(metrics.getThreads() > 0);
}
}
}
@@ -19,6 +19,7 @@
import io.mifos.anubis.example.simple.MetricsFeignClient;
import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
import io.mifos.core.api.context.AutoGuest;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.NotFoundException;
import org.junit.Assert;
@@ -33,9 +34,14 @@ public class TestSystemToken extends AbstractSimpleTest {
@Autowired
private MetricsFeignClient metricsFeignClient;


@Test
public void shouldBeAbleToGetContactSpringEndpoint() throws Exception {
public void shouldNotBeAbleToContactSpringEndpointWithGuestTokenWhenNotSoConfigured() throws Exception {
try (final AutoUserContext ignored = new AutoGuest()) {
metricsFeignClient.getMetrics();
Assert.fail("Should not be able to get metrics with guest token unless system is so configured.");
}
catch (final NotFoundException ignore) { }

try (final AutoUserContext ignored = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
final Metrics metrics = metricsFeignClient.getMetrics();
Assert.assertTrue(metrics.getThreads() > 0);
@@ -21,6 +21,7 @@
import io.mifos.core.lang.config.EnableApplicationName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@@ -32,6 +33,7 @@
@Configuration
@EnableApplicationName
@EnableCassandra
@EnableConfigurationProperties(AnubisProperties.class)
public class AnubisConfiguration {

@Bean(name = LOGGER_NAME)
@@ -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.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

/**
* @author Myrle Krantz
*/
@SuppressWarnings("unused")
@ConfigurationProperties(prefix="anubis")
@Validated
public class AnubisProperties {
private Boolean acceptGuestTokensForSystemEndpoints = false;

public Boolean getAcceptGuestTokensForSystemEndpoints() {
return acceptGuestTokensForSystemEndpoints;
}

public void setAcceptGuestTokensForSystemEndpoints(Boolean acceptGuestTokensForSystemEndpoints) {
this.acceptGuestTokensForSystemEndpoints = acceptGuestTokensForSystemEndpoints;
}
}
@@ -20,9 +20,12 @@
import io.mifos.anubis.annotation.Permittable;
import io.mifos.anubis.api.v1.domain.AllowedOperation;
import io.mifos.anubis.api.v1.domain.PermittableEndpoint;
import io.mifos.anubis.config.AnubisProperties;
import io.mifos.anubis.security.ApplicationPermission;
import io.mifos.core.lang.ApplicationName;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
@@ -38,6 +41,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.mifos.anubis.config.AnubisConstants.LOGGER_NAME;

/**
* @author Myrle Krantz
*/
@@ -46,14 +51,25 @@ public class PermittableService {
private final RequestMappingHandlerMapping requestMappingHandlerMapping;
private final EndpointHandlerMapping endpointHandlerMapping;
private final ApplicationName applicationName;
private final Permittable defaultPermittable;

@Autowired
public PermittableService(final RequestMappingHandlerMapping requestMappingHandlerMapping,
final EndpointHandlerMapping endpointHandlerMapping,
final ApplicationName applicationName) {
final ApplicationName applicationName,
final AnubisProperties anubisProperties,
final @Qualifier(LOGGER_NAME) Logger logger) {
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
this.endpointHandlerMapping = endpointHandlerMapping;
this.applicationName = applicationName;
if (anubisProperties.getAcceptGuestTokensForSystemEndpoints()) {
logger.error("The service property anubis.tokenTypeRequiredForSystemEndpoints is set to GUEST. This " +
"feature is intended for use only in test environments. Turning it on in a production environment " +
"could be a serious security vulnerability.");
this.defaultPermittable = guestPermittable();}
else {
this.defaultPermittable = systemPermittable();
}
}

public Set<ApplicationPermission> getPermittableEndpointsAsPermissions(
@@ -136,24 +152,23 @@ private void fillPermittableEndpointsFromHandlerMethods(
final Map<RequestMappingInfo, HandlerMethod> handlerMethods,
final @Nonnull Set<PermittableEndpoint> permittableEndpoints) {
handlerMethods.entrySet()
.stream().flatMap(PermittableService::whatINeedToBuildAPermittableEndpoint)
.stream().flatMap(handlerMethod -> PermittableService.whatINeedToBuildAPermittableEndpoint(handlerMethod, defaultPermittable))
.filter(whatINeedToBuildAPermittableEndpoint -> acceptedTokenTypes.contains(getAcceptedTokenType(whatINeedToBuildAPermittableEndpoint)))
.forEachOrdered(whatINeedToBuildAPermittableEndpoint ->
whatINeedToBuildAPermittableEndpoint.patterns.stream()
.forEachOrdered(pattern -> whatINeedToBuildAPermittableEndpoint.methods
.stream()
.forEachOrdered(method -> {
final PermittableEndpoint permittableEndpoint = new PermittableEndpoint();
permittableEndpoint.setPath(getPath(
whatINeedToBuildAPermittableEndpoint.patterns
.forEach(pattern -> whatINeedToBuildAPermittableEndpoint.methods
.forEach(method -> {
final PermittableEndpoint permittableEndpoint = new PermittableEndpoint();
permittableEndpoint.setPath(getPath(
withAppName ? applicationName.toString() : "",
pattern,
whatINeedToBuildAPermittableEndpoint));
permittableEndpoint.setMethod(method.name());
permittableEndpoint.setGroupId(whatINeedToBuildAPermittableEndpoint.annotation.groupId());
permittableEndpoint.setAcceptTokenIntendedForForeignApplication(whatINeedToBuildAPermittableEndpoint.annotation.acceptTokenIntendedForForeignApplication());
permittableEndpoints.add(permittableEndpoint);
})
));
permittableEndpoint.setMethod(method.name());
permittableEndpoint.setGroupId(whatINeedToBuildAPermittableEndpoint.annotation.groupId());
permittableEndpoint.setAcceptTokenIntendedForForeignApplication(whatINeedToBuildAPermittableEndpoint.annotation.acceptTokenIntendedForForeignApplication());
permittableEndpoints.add(permittableEndpoint);
})
));
}

static private AcceptedTokenType getAcceptedTokenType(final @Nonnull WhatINeedToBuildAPermittableEndpoint whatINeedToBuildAPermittableEndpoint) {
@@ -178,24 +193,56 @@ static private String getPath(final @Nonnull String applicationName,
}

static private Stream<WhatINeedToBuildAPermittableEndpoint> whatINeedToBuildAPermittableEndpoint(
final Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethod) {
final Set<Permittable> annotations = getPermittableAnnotations(handlerMethod);
final Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethod,
final Permittable defaultPermittable) {
final Set<Permittable> annotations = getPermittableAnnotations(handlerMethod, defaultPermittable);
final Set<String> patterns = handlerMethod.getKey().getPatternsCondition().getPatterns();
final Set<RequestMethod> methods = handlerMethod.getKey().getMethodsCondition().getMethods();
return annotations.stream()
.map(annotation -> new WhatINeedToBuildAPermittableEndpoint(annotation, patterns, methods));
}

static private Set<Permittable> getPermittableAnnotations(Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethod) {
static private Set<Permittable> getPermittableAnnotations(
final Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethod,
final Permittable defaultPermittable) {
final Method method = handlerMethod.getValue().getMethod();
final Set<Permittable> ret = AnnotationUtils.getRepeatableAnnotations(method, Permittable.class);
if (ret.isEmpty())
return Collections.singleton(defaultPermittable());
return Collections.singleton(defaultPermittable);
else
return ret;
}

static private Permittable defaultPermittable() {
static private Permittable guestPermittable() {
return new Permittable() {
@Override
public Class<? extends Annotation> annotationType() {
return Permittable.class;
}

@Override
public AcceptedTokenType value() {
return AcceptedTokenType.GUEST;
}

@Override
public String groupId() {
return "";
}

@Override
public String permittedEndpoint() {
return "";
}

@Override
public boolean acceptTokenIntendedForForeignApplication() {
return false;
}
};
}

static private Permittable systemPermittable() {
return new Permittable() {
@Override
public Class<? extends Annotation> annotationType() {

0 comments on commit c59befd

Please sign in to comment.