Skip to content

Commit

Permalink
refactor: hard requirement to switch to the new way to configure api …
Browse files Browse the repository at this point in the history
…authentication (#4735)
  • Loading branch information
ndr-brt authored Jan 20, 2025
1 parent fb80c76 commit 989b2dd
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 210 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
import org.eclipse.edc.api.auth.spi.ApiAuthenticationProvider;
import org.eclipse.edc.api.auth.spi.AuthenticationService;
import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationProviderRegistry;
import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry;
import org.eclipse.edc.keys.spi.KeyParserRegistry;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.system.ServiceExtension;
Expand All @@ -45,29 +45,29 @@
@Extension(value = DelegatedAuthenticationExtension.NAME)
public class DelegatedAuthenticationExtension implements ServiceExtension {

public static final int DEFAULT_VALIDATION_TOLERANCE = 5_000;
public static final String NAME = "Delegating Authentication Service Extension";
private static final int DEFAULT_VALIDATION_TOLERANCE = 5_000;
private static final String AUTH_KEY = "auth";
private static final String CONFIG_ALIAS = WEB_HTTP_PREFIX + ".<context>." + AUTH_KEY + ".";
private static final String DELEGATED_TYPE = "delegated";
@Deprecated(since = "0.12.0", forRemoval = true)
private static final String KEY_URL_PROPERTY = "edc.api.auth.dac.key.url";
@Deprecated(since = "0.12.0", forRemoval = true)
private static final String DEPRECATED_AUTH_CACHE_VALIDITY = "edc.api.auth.dac.cache.validity";

@Deprecated(since = "0.7.1")
@Setting(description = "Duration (in ms) that the internal key cache is valid", defaultValue = "" + DEFAULT_CACHE_TIME_TO_LIVE, key = "edc.api.auth.dac.cache.validity", required = false)
private long cacheValidityMs;

@Deprecated(since = "0.7.1")
@Setting(description = "URL where the third-party IdP's public key(s) can be resolved", key = KEY_URL_PROPERTY, required = false, warnOnMissingConfig = true)
private String keyUrl;

public static final String AUTH_KEY = "auth";
public static final String CONFIG_ALIAS = WEB_HTTP_PREFIX + ".<context>." + AUTH_KEY + ".";
@Setting(context = CONFIG_ALIAS, description = "URL where the third-party IdP's public key(s) can be resolved for the configured <context>")
public static final String AUTH_KEY_URL = "dac.key.url";
@Setting(context = CONFIG_ALIAS, description = "Duration (in ms) that the internal key cache is valid for the configured <context>", type = "Long", defaultValue = "" + DEFAULT_CACHE_TIME_TO_LIVE)
public static final String AUTH_CACHE_VALIDITY_MS = "dac.cache.validity";
public static final String DELEGATED_TYPE = "delegated";
@Setting(description = "Default token validation time tolerance (in ms), e.g. for nbf or exp claims", defaultValue = "" + DEFAULT_VALIDATION_TOLERANCE, key = "edc.api.auth.dac.validation.tolerance")
private int validationTolerance;
@Inject
private ApiAuthenticationRegistry authenticationRegistry;
@Deprecated(since = "0.12.0", forRemoval = true)
@Setting(description = "Duration (in ms) that the internal key cache is valid", defaultValue = "" + DEFAULT_CACHE_TIME_TO_LIVE, key = DEPRECATED_AUTH_CACHE_VALIDITY, required = false)
private long cacheValidityMs;
@Deprecated(since = "0.12.0", forRemoval = true)
@Setting(description = "URL where the third-party IdP's public key(s) can be resolved", key = KEY_URL_PROPERTY, required = false)
private String keyUrl;

@Inject
private ApiAuthenticationProviderRegistry providerRegistry;
@Inject
Expand All @@ -88,20 +88,17 @@ public String name() {
public void initialize(ServiceExtensionContext context) {
var monitor = context.getMonitor().withPrefix("Delegated API Authentication");

if (keyUrl == null) {
monitor.warning("The '%s' setting was not provided, so the DelegatedAuthenticationService will NOT be registered. In this case, the TokenBasedAuthenticationService usually acts as fallback.".formatted(KEY_URL_PROPERTY));
return;
if (keyUrl != null) {
var message = "Settings %s and %s have been removed".formatted(KEY_URL_PROPERTY, DEPRECATED_AUTH_CACHE_VALIDITY) +
", to configure delegated authentication for management api please configure it properly through the " +
"`web.http.management.auth.%s` and `web.http.management.auth.%s` settings".formatted(AUTH_KEY_URL, AUTH_CACHE_VALIDITY_MS);
context.getMonitor().severe(message);
throw new EdcException(message);
}

//todo: currently, only JWKS urls are supported
var resolver = JwksPublicKeyResolver.create(keyParserRegistry, keyUrl, monitor, cacheValidityMs);

tokenValidationRulesRegistry.addRule(MANAGEMENT_API_CONTEXT, new NotBeforeValidationRule(clock, validationTolerance, true));
tokenValidationRulesRegistry.addRule(MANAGEMENT_API_CONTEXT, new ExpirationIssuedAtValidationRule(clock, validationTolerance, true));

// always register - this would potentially overwrite other services
authenticationRegistry.register("management-api", new DelegatedAuthenticationService(resolver, monitor, tokenValidationService, tokenValidationRulesRegistry));

providerRegistry.register(DELEGATED_TYPE, (cfg) -> delegatedProvider(monitor, cfg));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,64 +14,32 @@

package org.eclipse.edc.api.auth.delegated;

import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry;
import org.eclipse.edc.boot.system.injection.ObjectFactory;
import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.configuration.Config;
import org.eclipse.edc.spi.system.configuration.ConfigFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import java.util.Map;

import static com.nimbusds.jose.jwk.source.JWKSourceBuilder.DEFAULT_CACHE_TIME_TO_LIVE;
import static org.eclipse.edc.api.auth.delegated.DelegatedAuthenticationExtension.AUTH_CACHE_VALIDITY_MS;
import static org.eclipse.edc.api.auth.delegated.DelegatedAuthenticationExtension.AUTH_KEY_URL;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.endsWith;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(DependencyInjectionExtension.class)
class DelegatedAuthenticationExtensionTest {

private final Monitor monitor = mock(Monitor.class);
private final ApiAuthenticationRegistry registry = mock();
private final Monitor monitor = mock();

@BeforeEach
void setUp(ServiceExtensionContext context) {
when(monitor.withPrefix(anyString())).thenReturn(monitor);
when(context.getMonitor()).thenReturn(monitor);
context.registerService(ApiAuthenticationRegistry.class, registry);
}

@Test
void initialize(ServiceExtensionContext context, ObjectFactory factory) {

var configMock = ConfigFactory.fromMap(Map.of("edc.api.auth.dac.key.url", "http://foo.bar/.well-known/jwks.json"));
when(context.getConfig()).thenReturn(configMock);

var extension = factory.constructInstance(DelegatedAuthenticationExtension.class);
extension.initialize(context);

verify(registry).register(eq("management-api"), isA(DelegatedAuthenticationService.class));
}

@Test
void initialize_noUrlGiven_shouldNotRegister(DelegatedAuthenticationExtension extension, ServiceExtensionContext context) {

extension.initialize(context);

verify(monitor).warning(endsWith("setting was not provided, so the DelegatedAuthenticationService will NOT be registered. In this case, the TokenBasedAuthenticationService usually acts as fallback."));
verify(registry, never()).register(eq("management-api"), isA(DelegatedAuthenticationService.class));
}

@Test
Expand All @@ -89,4 +57,4 @@ public void delegatedProvider(DelegatedAuthenticationExtension extension) {
verify(config).getLong(AUTH_CACHE_VALIDITY_MS, DEFAULT_CACHE_TIME_TO_LIVE);

}
}
}
21 changes: 0 additions & 21 deletions extensions/common/auth/auth-tokenbased/README.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@
import org.eclipse.edc.api.auth.spi.ApiAuthenticationProvider;
import org.eclipse.edc.api.auth.spi.AuthenticationService;
import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationProviderRegistry;
import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationRegistry;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.security.Vault;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.configuration.Config;

import java.util.Optional;
import java.util.UUID;

import static org.eclipse.edc.web.spi.configuration.WebServiceConfigurer.WEB_HTTP_PREFIX;

Expand All @@ -42,25 +41,27 @@
public class TokenBasedAuthenticationExtension implements ServiceExtension {

public static final String NAME = "Static token API Authentication";
public static final String AUTH_KEY = "auth";
private static final String AUTH_KEY = "auth";
private static final String CONFIG_ALIAS = WEB_HTTP_PREFIX + ".<context>." + AUTH_KEY + ".";
private static final String TOKENBASED_TYPE = "tokenbased";
@Deprecated(since = "0.12.0", forRemoval = true)
private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key";
@Deprecated(since = "0.12.0", forRemoval = true)
private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias";

public static final String CONFIG_ALIAS = WEB_HTTP_PREFIX + ".<context>." + AUTH_KEY + ".";
@Setting(context = CONFIG_ALIAS, value = "The api key to use for the <context>")
@Setting(context = CONFIG_ALIAS, description = "The api key to use for the <context>")
public static final String AUTH_API_KEY = "key";
@Setting(context = CONFIG_ALIAS, value = "The vault api key alias to use for the <context>")
@Setting(context = CONFIG_ALIAS, description = "The vault api key alias to use for the <context>")
public static final String AUTH_API_KEY_ALIAS = "key.alias";
public static final String TOKENBASED_TYPE = "tokenbased";
@Setting
@Deprecated(since = "0.7.1")
private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key";
@Setting
@Deprecated(since = "0.7.1")
private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias";
@Setting(description = "DEPRECATED: auth key", key = AUTH_SETTING_APIKEY, required = false)
@Deprecated(since = "0.12.0", forRemoval = true)
private String deprecatedApiKey;
@Setting(description = "DEPRECATED: auth key alias", key = AUTH_SETTING_APIKEY_ALIAS, required = false)
@Deprecated(since = "0.12.0", forRemoval = true)
private String deprecatedApiKeyAlias;

@Inject
private Vault vault;
@Inject
private ApiAuthenticationRegistry authenticationRegistry;

@Inject
private ApiAuthenticationProviderRegistry providerRegistry;

Expand All @@ -71,13 +72,12 @@ public String name() {

@Override
public void initialize(ServiceExtensionContext context) {
var apiKey = Optional.ofNullable(context.getSetting(AUTH_SETTING_APIKEY_ALIAS, null))
.map(alias -> vault.resolveSecret(alias))
.orElseGet(() -> context.getSetting(AUTH_SETTING_APIKEY, UUID.randomUUID().toString()));

// only register as fallback, if no other has been registered
if (!authenticationRegistry.hasService("management-api")) {
authenticationRegistry.register("management-api", new TokenBasedAuthenticationService(apiKey));
if (deprecatedApiKey != null || deprecatedApiKeyAlias != null) {
var message = "Settings %s and %s have been removed".formatted(AUTH_SETTING_APIKEY, AUTH_SETTING_APIKEY_ALIAS) +
", to configure token based authentication for management api please configure it properly through the " +
"`web.http.management.auth.%s` or `web.http.management.auth.%s` settings".formatted(AUTH_API_KEY, AUTH_API_KEY_ALIAS);
context.getMonitor().severe(message);
throw new EdcException(message);
}

providerRegistry.register(TOKENBASED_TYPE, this::tokenBasedProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,14 @@
import static org.eclipse.edc.api.auth.token.TokenBasedAuthenticationExtension.AUTH_API_KEY;
import static org.eclipse.edc.api.auth.token.TokenBasedAuthenticationExtension.AUTH_API_KEY_ALIAS;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

@ExtendWith(DependencyInjectionExtension.class)
public class TokenBasedAuthenticationExtensionTest {

private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key";
private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias";
private static final String VAULT_KEY = "foo";

private final Vault vault = mock();
Expand All @@ -54,40 +47,6 @@ void setup(ServiceExtensionContext context) {
when(vault.resolveSecret(VAULT_KEY)).thenReturn("foo");
}

@Test
public void testPrimaryMethod_loadKeyFromVault(ServiceExtensionContext context, TokenBasedAuthenticationExtension extension) {
when(context.getSetting(eq(AUTH_SETTING_APIKEY_ALIAS), isNull())).thenReturn(VAULT_KEY);
when(context.getSetting(eq(AUTH_SETTING_APIKEY), anyString())).thenReturn("bar");

extension.initialize(context);

verify(context, never())
.getSetting(eq(AUTH_SETTING_APIKEY), anyString());

verify(context)
.getSetting(AUTH_SETTING_APIKEY_ALIAS, null);

verify(vault).resolveSecret(VAULT_KEY);
verify(apiAuthenticationRegistry).register(eq("management-api"), isA(TokenBasedAuthenticationService.class));
}

@Test
public void testSecondaryMethod_loadKeyFromConfig(ServiceExtensionContext context, TokenBasedAuthenticationExtension extension) {
when(context.getSetting(eq(AUTH_SETTING_APIKEY_ALIAS), isNull())).thenReturn(null);
when(context.getSetting(eq(AUTH_SETTING_APIKEY), anyString())).thenReturn("bar");

extension.initialize(context);

verify(context)
.getSetting(eq(AUTH_SETTING_APIKEY), anyString());

verify(context)
.getSetting(AUTH_SETTING_APIKEY_ALIAS, null);

verify(vault, never()).resolveSecret(anyString());
verify(apiAuthenticationRegistry).register(eq("management-api"), isA(TokenBasedAuthenticationService.class));
}

@Test
public void tokenBasedProvider(TokenBasedAuthenticationExtension extension) {
var config = mock(Config.class);
Expand All @@ -97,7 +56,6 @@ public void tokenBasedProvider(TokenBasedAuthenticationExtension extension) {
.isSucceeded().isInstanceOf(TokenBasedAuthenticationService.class);

verifyNoMoreInteractions(vault);

}

@Test
Expand Down
Loading

0 comments on commit 989b2dd

Please sign in to comment.