Skip to content

Commit

Permalink
feat: introduce auth configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood committed Jul 5, 2024
1 parent 2cf9bf2 commit 2723ca2
Show file tree
Hide file tree
Showing 16 changed files with 658 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

package org.eclipse.edc.api;

import org.eclipse.edc.api.auth.ApiAuthenticationProviderRegistryImpl;
import org.eclipse.edc.api.auth.ApiAuthenticationRegistryImpl;
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.Provider;
Expand All @@ -37,5 +39,9 @@ public String name() {
public ApiAuthenticationRegistry apiAuthenticationRegistry() {
return new ApiAuthenticationRegistryImpl();
}


@Provider
public ApiAuthenticationProviderRegistry apiAuthenticationProviderRegistry() {
return new ApiAuthenticationProviderRegistryImpl();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.api.auth;

import org.eclipse.edc.api.auth.spi.ApiAuthenticationProvider;
import org.eclipse.edc.api.auth.spi.registry.ApiAuthenticationProviderRegistry;

import java.util.HashMap;
import java.util.Map;

public class ApiAuthenticationProviderRegistryImpl implements ApiAuthenticationProviderRegistry {
private final Map<String, ApiAuthenticationProvider> providers = new HashMap<>();

@Override
public void register(String type, ApiAuthenticationProvider provider) {
providers.put(type, provider);
}

@Override
public ApiAuthenticationProvider resolve(String type) {
return providers.get(type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.api.auth;

import org.eclipse.edc.api.auth.spi.ApiAuthenticationProvider;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

class ApiAuthenticationProviderRegistryImplTest {

private final ApiAuthenticationProviderRegistryImpl registry = new ApiAuthenticationProviderRegistryImpl();

@Test
void shouldResolveRegisteredProvider() {
ApiAuthenticationProvider provider = mock();
registry.register("authType", provider);

var actual = registry.resolve("authType");

assertThat(actual).isSameAs(provider);
}

}
25 changes: 25 additions & 0 deletions extensions/common/auth/auth-configuration/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

plugins {
`java-library`
}

dependencies {
api(project(":spi:common:auth-spi"))

testImplementation(project(":core:common:junit"))
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

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

import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter;
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.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.configuration.Config;
import org.eclipse.edc.web.spi.WebService;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.eclipse.edc.api.auth.configuration.ApiAuthenticationConfigurationExtension.NAME;

@Extension(NAME)
public class ApiAuthenticationConfigurationExtension implements ServiceExtension {

public static final String NAME = "Api Authentication Configuration Extension";

public static final String WEB_HTTP_PREFIX = "web.http";

public static final String AUTH_KEY = "auth";
public static final String CONFIG_ALIAS = WEB_HTTP_PREFIX + ".<context>." + AUTH_KEY + ".";

@Setting(context = CONFIG_ALIAS, value = "The type of the authentication provider.", required = true)
public static final String TYPE_KEY = "type";

@Setting(context = CONFIG_ALIAS, value = "The api context where to apply the authentication. Default to the web <context>")
public static final String CONTEXT_KEY = "context";

private Map<String, Config> authConfiguration = new HashMap<>();

@Inject
private ApiAuthenticationProviderRegistry providerRegistry;

@Inject
private ApiAuthenticationRegistry authenticationRegistry;

@Inject
private WebService webService;


@Override
public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
authConfiguration = context.getConfig(WEB_HTTP_PREFIX)
.partition().filter(config -> config.getString(AUTH_KEY + "." + TYPE_KEY, null) != null)
.map(cfg -> Map.entry(cfg.currentNode(), cfg.getConfig(AUTH_KEY)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

@Override
public void prepare() {
for (var entry : authConfiguration.entrySet()) {
var webContext = entry.getKey();
var apiContext = Optional.ofNullable(entry.getValue().getString(CONTEXT_KEY, null)).orElse(webContext);
var serviceResult = configureService(entry.getValue());
if (serviceResult.failed()) {
throw new EdcException("Failed to configure authentication for context %s: %s".formatted(entry.getKey(), serviceResult.getFailureDetail()));
}
authenticationRegistry.register(apiContext, serviceResult.getContent());
var authenticationFilter = new AuthenticationRequestFilter(authenticationRegistry, apiContext);
webService.registerResource(entry.getKey(), authenticationFilter);
}
}

private Result<AuthenticationService> configureService(Config config) {
var type = config.getString(TYPE_KEY);
return Optional.ofNullable(providerRegistry.resolve(type))
.map(provider -> provider.provide(config))
.orElseGet(() -> Result.failure("Authentication provider for type %s not found".formatted(type)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#
# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
#
# Contributors:
# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
#
#

org.eclipse.edc.api.auth.configuration.ApiAuthenticationConfigurationExtension
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

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

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.junit.extensions.DependencyInjectionExtension;
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.ServiceExtensionContext;
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 org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.edc.api.auth.configuration.ApiAuthenticationConfigurationExtension.WEB_HTTP_PREFIX;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

@ExtendWith(DependencyInjectionExtension.class)
class ApiAuthenticationConfigurationExtensionTest {

private final Monitor monitor = mock(Monitor.class);
private final ApiAuthenticationRegistry authenticationRegistry = mock();
private final ApiAuthenticationProviderRegistry providerRegistry = mock();

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

@Test
void prepare(ApiAuthenticationConfigurationExtension extension, ServiceExtensionContext context) {
var authType = "testAuth";
var config = ConfigFactory.fromMap(Map.of("test.auth.type", authType, "test.auth.custom", "custom"));
var provider = mock(ApiAuthenticationProvider.class);
var authentication = mock(AuthenticationService.class);
when(context.getConfig(WEB_HTTP_PREFIX)).thenReturn(config);
when(providerRegistry.resolve(authType)).thenReturn(provider);
when(provider.provide(any())).thenReturn(Result.success(authentication));

extension.initialize(context);
extension.prepare();

verify(providerRegistry).resolve(authType);
verify(provider).provide(any());
verify(authenticationRegistry).register("test", authentication);

}

@Test
void prepare_whenEmptyConfig(ApiAuthenticationConfigurationExtension extension, ServiceExtensionContext context) {
var authType = "testAuth";
var config = ConfigFactory.fromMap(Map.of());
var provider = mock(ApiAuthenticationProvider.class);
var authentication = mock(AuthenticationService.class);
when(context.getConfig(WEB_HTTP_PREFIX)).thenReturn(config);
when(providerRegistry.resolve(authType)).thenReturn(provider);
when(provider.provide(any())).thenReturn(Result.success(authentication));

extension.initialize(context);
extension.prepare();

verifyNoMoreInteractions(providerRegistry);
verifyNoMoreInteractions(provider);
verifyNoMoreInteractions(authenticationRegistry);

}

@Test
void prepare_whenNoProvider(ApiAuthenticationConfigurationExtension extension, ServiceExtensionContext context) {
var authType = "testAuth";
var config = ConfigFactory.fromMap(Map.of("test.auth.type", authType, "test.auth.custom", "custom"));
var provider = mock(ApiAuthenticationProvider.class);
when(context.getConfig(WEB_HTTP_PREFIX)).thenReturn(config);
when(providerRegistry.resolve(authType)).thenReturn(null);

extension.initialize(context);

assertThatThrownBy(extension::prepare).isInstanceOf(EdcException.class).hasMessageContaining(authType);

verify(providerRegistry).resolve(authType);
verifyNoMoreInteractions(provider);
verifyNoMoreInteractions(authenticationRegistry);

}

@Test
void prepare_whenProviderFails(ApiAuthenticationConfigurationExtension extension, ServiceExtensionContext context) {
var authType = "testAuth";
var config = ConfigFactory.fromMap(Map.of("test.auth.type", authType, "test.auth.custom", "custom"));
var provider = mock(ApiAuthenticationProvider.class);
when(context.getConfig(WEB_HTTP_PREFIX)).thenReturn(config);
when(providerRegistry.resolve(authType)).thenReturn(provider);
when(provider.provide(any())).thenReturn(Result.failure("Auth failure"));

extension.initialize(context);

assertThatThrownBy(extension::prepare).isInstanceOf(EdcException.class).hasMessageContaining("Auth failure");

verify(providerRegistry).resolve(authType);
verify(provider).provide(any());
verifyNoMoreInteractions(authenticationRegistry);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#
# Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
#
# Contributors:
# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
#
#

org.eclipse.edc.api.auth.configuration.ApiAuthenticationConfigurationExtension
Loading

0 comments on commit 2723ca2

Please sign in to comment.