-
Notifications
You must be signed in to change notification settings - Fork 245
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add JTI validation feature (#4560)
* add JTI validation store (wip) * specify web contexts explicitly * add inmem implementation * add SQL store for JTI validation entries * add test for the JTI validation rule * add JtiValidationStore to SQL BOM * add reaper thread to JTI Validation store * move reaper thread to DCP extension * STS uses JTI Validation Service, records JTI * record JTI when creating access tokens * simplify JTI validation rule * Update extensions/common/store/sql/jti-validation-store-sql/src/test/java/org/eclipse/edc/edr/store/index/sql/SqlJtiValidationStoreExtensionTest.java Co-authored-by: Enrico Risa <enrico.risa@gmail.com> --------- Co-authored-by: Enrico Risa <enrico.risa@gmail.com>
- Loading branch information
1 parent
d04ee6e
commit 7f83a70
Showing
37 changed files
with
1,113 additions
and
39 deletions.
There are no files selected for viewing
53 changes: 53 additions & 0 deletions
53
core/common/token-core/src/main/java/org/eclipse/edc/token/InMemoryJtiValidationStore.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,53 @@ | ||
/* | ||
* 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.token; | ||
|
||
import org.eclipse.edc.jwt.validation.jti.JtiValidationEntry; | ||
import org.eclipse.edc.jwt.validation.jti.JtiValidationStore; | ||
import org.eclipse.edc.spi.result.StoreResult; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class InMemoryJtiValidationStore implements JtiValidationStore { | ||
private final Map<String, JtiValidationEntry> jtiValidationEntries = new ConcurrentHashMap<>(); | ||
|
||
@Override | ||
public StoreResult<Void> storeEntry(JtiValidationEntry entry) { | ||
if (jtiValidationEntries.containsKey(entry.tokenId())) { | ||
return StoreResult.alreadyExists("JTI Validation Entry with ID '%s' already exists".formatted(entry.tokenId())); | ||
} | ||
jtiValidationEntries.put(entry.tokenId(), entry); | ||
return StoreResult.success(); | ||
} | ||
|
||
@Override | ||
public JtiValidationEntry findById(String id, boolean autoRemove) { | ||
return autoRemove ? jtiValidationEntries.remove(id) : jtiValidationEntries.get(id); | ||
} | ||
|
||
@Override | ||
public StoreResult<Void> deleteById(String id) { | ||
return jtiValidationEntries.remove(id) == null ? | ||
StoreResult.notFound("JTI Validation Entry with ID '%s' not found".formatted(id)) : StoreResult.success(); | ||
} | ||
|
||
@Override | ||
public StoreResult<Integer> deleteExpired() { | ||
var count = jtiValidationEntries.values().stream().filter(JtiValidationEntry::isExpired).count(); | ||
jtiValidationEntries.values().removeIf(JtiValidationEntry::isExpired); | ||
return StoreResult.success((int) count); | ||
} | ||
} |
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
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
29 changes: 29 additions & 0 deletions
29
...c/test/java/org/eclipse/edc/verifiablecredentials/jwt/InMemoryJtiValidationStoreTest.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,29 @@ | ||
/* | ||
* 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.verifiablecredentials.jwt; | ||
|
||
import org.eclipse.edc.jwt.validation.jti.JtiValidationStore; | ||
import org.eclipse.edc.jwt.validation.jti.JtiValidationStoreTestBase; | ||
import org.eclipse.edc.token.InMemoryJtiValidationStore; | ||
|
||
class InMemoryJtiValidationStoreTest extends JtiValidationStoreTestBase { | ||
|
||
private final InMemoryJtiValidationStore store = new InMemoryJtiValidationStore(); | ||
|
||
@Override | ||
protected JtiValidationStore getStore() { | ||
return store; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
.../src/test/java/org/eclipse/edc/verifiablecredentials/jwt/rules/JtiValidationRuleTest.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,59 @@ | ||
/* | ||
* 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.verifiablecredentials.jwt.rules; | ||
|
||
import org.eclipse.edc.jwt.validation.jti.JtiValidationEntry; | ||
import org.eclipse.edc.jwt.validation.jti.JtiValidationStore; | ||
import org.eclipse.edc.spi.iam.ClaimToken; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.time.Instant; | ||
import java.util.Map; | ||
|
||
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
class JtiValidationRuleTest { | ||
|
||
private final JtiValidationStore store = mock(); | ||
private final JtiValidationRule rule = new JtiValidationRule(store, mock()); | ||
|
||
@Test | ||
void checkRule_noExpiration_success() { | ||
when(store.findById(eq("test-id"))).thenReturn(new JtiValidationEntry("test-id")); | ||
assertThat(rule.checkRule(ClaimToken.Builder.newInstance().claim("jti", "test-id").build(), Map.of())).isSucceeded(); | ||
} | ||
|
||
@Test | ||
void checkRule_withExpiration_success() { | ||
when(store.findById(eq("test-id"))).thenReturn(new JtiValidationEntry("test-id", Instant.now().plusSeconds(3600).toEpochMilli())); | ||
assertThat(rule.checkRule(ClaimToken.Builder.newInstance().claim("jti", "test-id").build(), Map.of())).isSucceeded(); | ||
} | ||
|
||
@Test | ||
void checkRule_withExpiration_alreadyExpired() { | ||
when(store.findById(eq("test-id"))).thenReturn(new JtiValidationEntry("test-id", Instant.now().minusSeconds(3600).toEpochMilli())); | ||
assertThat(rule.checkRule(ClaimToken.Builder.newInstance().claim("jti", "test-id").build(), Map.of())).isSucceeded(); | ||
} | ||
|
||
@Test | ||
void checkRule_entryNotFound_success() { | ||
when(store.findById(eq("test-id"))).thenReturn(null); | ||
assertThat(rule.checkRule(ClaimToken.Builder.newInstance().claim("jti", "test-id").build(), Map.of())).isFailed() | ||
.detail().isEqualTo("The JWT id 'test-id' was not found"); | ||
} | ||
} |
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
Oops, something went wrong.