From e3a804216aba369fd1eab506002972f39a507122 Mon Sep 17 00:00:00 2001 From: Tomas Langer Date: Fri, 13 Nov 2020 17:36:31 +0100 Subject: [PATCH] Support for overriding configuration of security providers using simple properties. (#2511) Signed-off-by: Tomas Langer --- docs/se/security/01_introduction.adoc | 15 +++++ .../java/io/helidon/security/Security.java | 54 ++++++++++----- tests/integration/security/gh2455/pom.xml | 67 +++++++++++++++++++ .../src/main/resources/application.yaml | 25 +++++++ .../gh2455/TestProviderOverrides.java | 43 ++++++++++++ tests/integration/security/pom.xml | 1 + 6 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 tests/integration/security/gh2455/pom.xml create mode 100644 tests/integration/security/gh2455/src/main/resources/application.yaml create mode 100644 tests/integration/security/gh2455/src/test/java/io/helidon/tests/integration/security/gh2455/TestProviderOverrides.java diff --git a/docs/se/security/01_introduction.adoc b/docs/se/security/01_introduction.adoc index 27430c6f2..e1031f252 100644 --- a/docs/se/security/01_introduction.adoc +++ b/docs/se/security/01_introduction.adoc @@ -138,6 +138,21 @@ security: roles: ["user"] ---- +==== Overriding configuration +When a configuration needs to be overridden, we may have problems with the list +type of the `providers` configuration. To simplify overrides using properties, +you can explicitly setup a type of provider using a `type` key. + +Example: +[source,properties] +---- +security.providers.1.type=header-atn +security.providers.1.header-atn.authenticate=false +---- + +Would explicitly override the second provider (`http-basic-auth` in example above) with +`header-atn` provider. Note that the `type` and the key of the provider must match. + === Hybrid pattern (Builder & Configuration) [source,java] diff --git a/security/security/src/main/java/io/helidon/security/Security.java b/security/security/src/main/java/io/helidon/security/Security.java index f1e50161e..c402855d7 100644 --- a/security/security/src/main/java/io/helidon/security/Security.java +++ b/security/security/src/main/java/io/helidon/security/Security.java @@ -44,6 +44,7 @@ import java.util.stream.Collectors; import io.helidon.common.configurable.ThreadPoolSupplier; import io.helidon.common.serviceloader.HelidonServiceLoader; import io.helidon.config.Config; +import io.helidon.config.ConfigValue; import io.helidon.security.internal.SecurityAuditEvent; import io.helidon.security.spi.AuditProvider; import io.helidon.security.spi.AuthenticationProvider; @@ -80,6 +81,7 @@ public class Security { private static final Set RESERVED_PROVIDER_KEYS = Set.of( "name", + "type", "class", "is-authentication-provider", "is-authorization-provider", @@ -951,7 +953,9 @@ public class Security { } private void providerFromConfig(Map configKeyToService, - Map classNameToService, String knownKeys, Config pConf) { + Map classNameToService, + String knownKeys, + Config pConf) { AtomicReference service = new AtomicReference<>(); AtomicReference providerSpecific = new AtomicReference<>(); @@ -1053,24 +1057,38 @@ public class Security { Config pConf, AtomicReference service, AtomicReference providerSpecific) { - // everything else is based on provider specific configuration - pConf.asNodeList().get().stream().filter(this::notReservedProviderKey).forEach(providerSpecificConf -> { - if (!providerSpecific.compareAndSet(null, providerSpecificConf)) { - throw new SecurityException("More than one provider configurations found, each provider can only" - + " have one provider specific config. Conflict: " - + providerSpecific.get().key() - + " and " + providerSpecificConf.key()); - } - String keyName = providerSpecificConf.name(); - if (configKeyToService.containsKey(keyName)) { - service.set(configKeyToService.get(keyName)); - } else { - throw new SecurityException("Configuration key " + providerSpecificConf.key() - + " is not a valid provider configuration. Supported keys: " - + knownKeys); - } - }); + ConfigValue type = pConf.get("type").asString(); + if (type.isPresent()) { + // explicit type, ignore search below + findProviderService(service, configKeyToService, type.get(), knownKeys); + providerSpecific.set(pConf.get(type.get())); + } else { + // everything else is based on provider specific configuration + pConf.asNodeList().get().stream().filter(this::notReservedProviderKey).forEach(providerSpecificConf -> { + if (!providerSpecific.compareAndSet(null, providerSpecificConf)) { + throw new SecurityException("More than one provider configurations found, each provider can only" + + " have one provider specific config. Conflict: " + + providerSpecific.get().key() + + " and " + providerSpecificConf.key()); + } + + findProviderService(service, configKeyToService, providerSpecificConf.name(), knownKeys); + }); + } + } + + private void findProviderService(AtomicReference service, + Map configKeyToService, + String name, + String knownKeys) { + if (configKeyToService.containsKey(name)) { + service.set(configKeyToService.get(name)); + } else { + throw new SecurityException("Configuration key " + name + + " is not a valid provider configuration. Supported keys: " + + knownKeys); + } } private String loadProviderServices(Map configKeyToService, diff --git a/tests/integration/security/gh2455/pom.xml b/tests/integration/security/gh2455/pom.xml new file mode 100644 index 000000000..76f32c7a6 --- /dev/null +++ b/tests/integration/security/gh2455/pom.xml @@ -0,0 +1,67 @@ + + + + + + helidon-tests-integration-security + io.helidon.tests.integration + 2.1.1-SNAPSHOT + + 4.0.0 + + helidon-tests-integration-security-gh2455 + Helidon Tests Integration Security GH-2455 + + + Integration test for issue https://github.com/oracle/helidon/issues/2455 + + + + + io.helidon.security + helidon-security + + + io.helidon.security.providers + helidon-security-providers-http-auth + + + io.helidon.security.providers + helidon-security-providers-header + + + io.helidon.security.providers + helidon-security-providers-abac + + + io.helidon.config + helidon-config-yaml + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + \ No newline at end of file diff --git a/tests/integration/security/gh2455/src/main/resources/application.yaml b/tests/integration/security/gh2455/src/main/resources/application.yaml new file mode 100644 index 000000000..dea525fb4 --- /dev/null +++ b/tests/integration/security/gh2455/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2020 Oracle and/or its affiliates. +# +# 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. +# + +security: + providers: + - abac: + - http-basic-auth: + realm: "helidon" + users: + - login: "tomas" + password: "password" + roles: ["admin"] \ No newline at end of file diff --git a/tests/integration/security/gh2455/src/test/java/io/helidon/tests/integration/security/gh2455/TestProviderOverrides.java b/tests/integration/security/gh2455/src/test/java/io/helidon/tests/integration/security/gh2455/TestProviderOverrides.java new file mode 100644 index 000000000..cd930d377 --- /dev/null +++ b/tests/integration/security/gh2455/src/test/java/io/helidon/tests/integration/security/gh2455/TestProviderOverrides.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Oracle and/or its affiliates. + * + * 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.helidon.tests.integration.security.gh2455; + +import java.util.Map; + +import io.helidon.config.Config; +import io.helidon.config.ConfigSources; +import io.helidon.security.Security; + +import org.junit.jupiter.api.Test; + +class TestProviderOverrides { + @Test + void testOverride() { + Map map = Map.of( + "security.providers.1.type", "header-atn", + "security.providers.1.header-atn.authenticate", "false" + ); + Config config = Config.builder() + .addSource(ConfigSources.create(map)) + .addSource(ConfigSources.classpath("application.yaml")) + .disableEnvironmentVariablesSource() + .disableSystemPropertiesSource() + .build(); + + Security security = Security.create(config.get("security")); + } +} diff --git a/tests/integration/security/pom.xml b/tests/integration/security/pom.xml index b46f8ff7e..7423eb5f4 100644 --- a/tests/integration/security/pom.xml +++ b/tests/integration/security/pom.xml @@ -37,5 +37,6 @@ gh1487 gh2297 + gh2455 \ No newline at end of file