-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: backport of issue upstream#3607
- Loading branch information
Showing
12 changed files
with
342 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Token Based Authentication Service | ||
|
||
The token based authentication service extension is used to secure connector APIs. These APIs are not protected by the `AuthenticationService` by default. To find out how a specific API is protected please consult its documentation. | ||
|
||
APIs, protected by this extension, require a client to authenticate by adding a authentication key to the request header. | ||
|
||
Authentication Header Example: | ||
``` | ||
curl <url> --header "X-API-Key: <key>" | ||
``` | ||
|
||
## Configuration | ||
|
||
| Key | Description | Required | | ||
|:-----------------------|:-------------------------------------------------------------|:---------| | ||
| edc.api.auth.key | API Key Header Value | false | | ||
| edc.api.auth.key.alias | Secret name of the API Key Header Value, stored in the vault | false | | ||
|
||
- If the API key is stored in the Vault _and_ in the configuration, the extension will take the key from the vault. | ||
|
||
- If no API key is defined, a random value is generated and printed out into the logs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright (c) 2020 - 2022 Microsoft Corporation | ||
* | ||
* 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: | ||
* Microsoft Corporation - initial API and implementation | ||
* | ||
*/ | ||
|
||
plugins { | ||
`java-library` | ||
} | ||
|
||
dependencies { | ||
implementation(libs.edc.spi.auth) | ||
implementation(libs.jakarta.rsApi) | ||
|
||
testImplementation(testFixtures(libs.edc.junit)) | ||
} | ||
|
||
|
65 changes: 65 additions & 0 deletions
65
.../main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright (c) 2020 - 2022 Microsoft Corporation | ||
* | ||
* 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: | ||
* Microsoft Corporation - initial API and implementation | ||
* Mercedes-Benz Tech Innovation GmbH - add README.md; authentication key can be retrieved from vault | ||
* Fraunhofer Institute for Software and Systems Engineering - update monitor info | ||
* | ||
*/ | ||
|
||
package org.eclipse.tractusx.edc.api.auth.token; | ||
|
||
import org.eclipse.edc.api.auth.spi.AuthenticationService; | ||
import org.eclipse.edc.runtime.metamodel.annotation.Extension; | ||
import org.eclipse.edc.runtime.metamodel.annotation.Inject; | ||
import org.eclipse.edc.runtime.metamodel.annotation.Provides; | ||
import org.eclipse.edc.runtime.metamodel.annotation.Setting; | ||
import org.eclipse.edc.spi.security.Vault; | ||
import org.eclipse.edc.spi.system.ServiceExtension; | ||
import org.eclipse.edc.spi.system.ServiceExtensionContext; | ||
|
||
import java.util.UUID; | ||
|
||
/** | ||
* Extension that registers an AuthenticationService that uses API Keys | ||
*/ | ||
@Provides(AuthenticationService.class) | ||
@Extension(value = TokenBasedAuthenticationExtension.NAME) | ||
public class TokenBasedAuthenticationExtension implements ServiceExtension { | ||
|
||
public static final String NAME = "Static token API Authentication"; | ||
@Setting | ||
private static final String AUTH_SETTING_APIKEY = "edc.api.auth.key"; | ||
@Setting | ||
private static final String AUTH_SETTING_APIKEY_ALIAS = "edc.api.auth.key.alias"; | ||
@Inject | ||
private Vault vault; | ||
|
||
@Override | ||
public String name() { | ||
return NAME; | ||
} | ||
|
||
@Override | ||
public void initialize(ServiceExtensionContext context) { | ||
String apiKey = null; | ||
|
||
var apiKeyAlias = context.getSetting(AUTH_SETTING_APIKEY_ALIAS, null); | ||
if (apiKeyAlias != null) { | ||
apiKey = vault.resolveSecret(apiKeyAlias); | ||
} | ||
|
||
if (apiKey == null) { | ||
apiKey = context.getSetting(AUTH_SETTING_APIKEY, UUID.randomUUID().toString()); | ||
} | ||
|
||
context.registerService(AuthenticationService.class, new TokenBasedAuthenticationService(apiKey)); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
...rc/main/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright (c) 2020 - 2022 Microsoft Corporation | ||
* | ||
* 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: | ||
* Microsoft Corporation - initial API and implementation | ||
* | ||
*/ | ||
|
||
package org.eclipse.tractusx.edc.api.auth.token; | ||
|
||
import org.eclipse.edc.api.auth.spi.AuthenticationService; | ||
import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
public class TokenBasedAuthenticationService implements AuthenticationService { | ||
|
||
private static final String API_KEY_HEADER_NAME = "x-api-key"; | ||
private final String hardCodedApiKey; //todo: have a list of API keys? | ||
|
||
public TokenBasedAuthenticationService(String hardCodedApiKey) { | ||
this.hardCodedApiKey = hardCodedApiKey; | ||
} | ||
|
||
/** | ||
* Checks whether a particular request is authorized based on the "X-Api-Key" header. | ||
* | ||
* @param headers The headers, that have to contain the "X-Api-Key" header. | ||
* @throws IllegalArgumentException The map of headers did not contain the "X-Api-Key" header | ||
*/ | ||
@Override | ||
public boolean isAuthenticated(Map<String, List<String>> headers) { | ||
|
||
Objects.requireNonNull(headers, "headers"); | ||
|
||
var apiKey = headers.keySet().stream() | ||
.filter(k -> k.equalsIgnoreCase(API_KEY_HEADER_NAME)) | ||
.map(headers::get) | ||
.findFirst(); | ||
|
||
return apiKey.map(this::checkApiKeyValid).orElseThrow(() -> new AuthenticationFailedException(API_KEY_HEADER_NAME + " not found")); | ||
} | ||
|
||
private boolean checkApiKeyValid(List<String> apiKeys) { | ||
return apiKeys.size() == 1 && apiKeys.stream().allMatch(hardCodedApiKey::equalsIgnoreCase); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...kenbased/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# | ||
# Copyright (c) 2020 - 2022 Microsoft Corporation | ||
# | ||
# 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: | ||
# Microsoft Corporation - initial API and implementation | ||
# | ||
# | ||
|
||
org.eclipse.tractusx.edc.api.auth.token.TokenBasedAuthenticationExtension |
80 changes: 80 additions & 0 deletions
80
...t/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationExtensionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH | ||
* | ||
* 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: | ||
* Mercedes-Benz Tech Innovation GmbH - initial implementation | ||
* | ||
*/ | ||
|
||
package org.eclipse.tractusx.edc.api.auth.token; | ||
|
||
import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; | ||
import org.eclipse.edc.spi.security.Vault; | ||
import org.eclipse.edc.spi.system.ServiceExtensionContext; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
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.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(); | ||
|
||
@BeforeEach | ||
void setup(ServiceExtensionContext context) { | ||
context.registerService(Vault.class, vault); | ||
|
||
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); | ||
} | ||
|
||
@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()); | ||
} | ||
|
||
} |
76 changes: 76 additions & 0 deletions
76
...est/java/org/eclipse/tractusx/edc/api/auth/token/TokenBasedAuthenticationServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright (c) 2020 - 2022 Microsoft Corporation | ||
* | ||
* 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: | ||
* Microsoft Corporation - initial API and implementation | ||
* | ||
*/ | ||
|
||
package org.eclipse.tractusx.edc.api.auth.token; | ||
|
||
import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
class TokenBasedAuthenticationServiceTest { | ||
|
||
private static final String TEST_API_KEY = "test-key"; | ||
private TokenBasedAuthenticationService service; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
service = new TokenBasedAuthenticationService(TEST_API_KEY); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(strings = { "x-api-key", "X-API-KEY", "X-Api-Key" }) | ||
void isAuthorized(String validKey) { | ||
var map = Map.of(validKey, List.of(TEST_API_KEY)); | ||
assertThat(service.isAuthenticated(map)).isTrue(); | ||
} | ||
|
||
@Test | ||
void isAuthorized_headerNotPresent_throwsException() { | ||
var map = Map.of("header1", List.of("val1, val2"), | ||
"header2", List.of("anotherval1", "anotherval2")); | ||
assertThatThrownBy(() -> service.isAuthenticated(map)).isInstanceOf(AuthenticationFailedException.class).hasMessage("x-api-key not found"); | ||
} | ||
|
||
@Test | ||
void isAuthorized_headersEmpty_throwsException() { | ||
Map<String, List<String>> map = Collections.emptyMap(); | ||
assertThatThrownBy(() -> service.isAuthenticated(map)).isInstanceOf(AuthenticationFailedException.class).hasMessage("x-api-key not found"); | ||
} | ||
|
||
@Test | ||
void isAuthorized_headersNull_throwsException() { | ||
assertThatThrownBy(() -> service.isAuthenticated(null)).isInstanceOf(NullPointerException.class); | ||
} | ||
|
||
@Test | ||
void isAuthorized_notAuthorized() { | ||
var map = Map.of("x-api-key", List.of("invalid_api_key")); | ||
assertThat(service.isAuthenticated(map)).isFalse(); | ||
} | ||
|
||
@Test | ||
void isAuthorized_multipleValues_oneAuthorized_shouldReturnFalse() { | ||
var map = Map.of("x-api-key", List.of("invalid_api_key", TEST_API_KEY)); | ||
assertThat(service.isAuthenticated(map)).isFalse(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.