Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "extensions/super-user-seed-extension"]
path = extensions/super-user-seed-extension
url = https://github.com/FraunhoferISST/super-user-seed-extension.git
2 changes: 2 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The project maintains the following source code repository:

Please refer to the [DEPENDENCIES](https://eclipse-edc.github.io/IdentityHub/DEPENDENCIES) file for a detailed report.

Beyond that, the construct-x wallet repository also includes the super-user-seed-extension as provided [here](https://github.com/FraunhoferISST/super-user-seed-extension) as a submodule.

## Cryptography

Content may contain encryption software. The country in which you are currently may have restrictions on the import,
Expand Down
100 changes: 100 additions & 0 deletions extensions/con-x/dev-attestation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
## How to use

This extension allows you to freely define the contents of a credentialSubject field for a Verifiable Credential.

It is not intended to be used in a productive scenario. It's intended purpose is to help the construct-x project to
easily define new types of Verifiable Credentials and test them without any hard-coded restrictions.

A possible request body for creating an attestation on your wallet may look like this:

```json
{
"attestationType": "dev",
"id": "dev-def-1",
"configuration": {
"did:web:consumer-idhub:user:consumer": {
"isConsumer": true,
"isProvider": false,
"foo": {
"bar": 123
}
},
"did:web:provider-idhub:user:provider": {
"isConsumer": false,
"isProvider": true,
"foo": {
"bar": 789
}
},
"default": {
"isConsumer": false,
"isProvider": false,
"foo": {
"bar": 0
}
},
"blackList": []
}
}
```

The key `attestationType` field must be set to `dev` (or else this extension will not be called at runtime).

The key `id` must be a unique string, that you can set freely. You will need to reference to this `id` later (see below).

The `configuration` key must be a JSON non-empty object, that may hold several other key-value pairs.

That `configuration`-object may contain a `blackList` key, which (if it exists) must be of type list. This list must
contain nothing else than string-typed entries. All of these entries must be valid did:web ids.

The `configuration`-object may contain a `default` key, which (if it exists) must be of type object. That object must not
be empty. It neeeds to contain at least one key-value-pair (not counting an `id` key-value-pair) Beyond that, the
content of that object can be freely designed by you. It will be used to define the credentialSubject of Verifiable Credentials.

Beyond that, the `configuration`-object may contain an arbitrary number of additional keys. These keys must be strings
in the form of did:web id's. The value must be a non-empty object. The same restrictions as described for the `default`
apply here.

Note: the credentialSubject will always contain an `id` key, whose value will be automatically set to the did:web id of
the requesting holder. So you don't need (and normally should not) set an `id` key manually when you are designing a
credentialSubject using the above-mentioned means.

The attestations that are created in this way, can be referenced in a subsequent request to create a Credential Type definition.
Please also see this [documentation](./../../../docs/developer/architecture/issuer/issuance/issuance.process.md).

A possible request body may look like this:

```json
{
"attestations": [
"dev-def-1"
],
"credentialType": "MembershipCredential",
"id": "dev-mem-def",
"jsonSchema": "{}",
"jsonSchemaUrl": "https://example.com/schema/dev-credential.json",
"rules": [],
"format": "VC1_0_JWT",
"mappings": [
{
"input": "content",
"output": "credentialSubject",
"required": true
}
],
"validity": 15552000
}
```

In the array of the `attestations` field, you need to use the same `id` that you used during the attestation creation request (see above).

The `credentialType` can be set to your required credential type, like e.g. `FooCredential`.

The `jsonSchema`, `jsonSchemaUrl`, `rules` and `format` fields should remain as shown above. (Change them at your own risk)

The `mappings` field must remain exactly as shown above, or otherwise this extension will be unable to work.

The `validity` field defines the timespan, after which a credential of this type will expire in seconds (15552000 seconds = 180 days). You
can change this to any positive value you like.


40 changes: 40 additions & 0 deletions extensions/con-x/dev-attestation/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2026. Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST)
*
* 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.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

plugins {
id("java")
id("application")
}

repositories { mavenCentral() }

val edcVersion = project.property("con-x-edcVersion") as String

dependencies {
implementation("org.eclipse.edc:core-spi:${edcVersion}")
implementation("org.eclipse.edc:issuerservice-issuance-spi:${edcVersion}")

testImplementation("org.eclipse.edc:junit:${edcVersion}") {
exclude(group = "org.junit.jupiter")
exclude(group = "org.junit.platform")
exclude(group = "org.junit")
}
testImplementation("org.eclipse.edc:dcp-issuer-core:${edcVersion}")
testImplementation(platform("org.junit:junit-bom:${property("junitVersion")}"))
testImplementation("org.junit.jupiter:junit-jupiter-api:${property("junitVersion")}")
testImplementation("com.fasterxml.jackson.core:jackson-databind:${property("jacksonVersion")}")
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2026. Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST)
*
* 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.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

package de.fraunhofer.isst.edc.extension.attestation.dev;

import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationDefinitionValidatorRegistry;
import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationSourceFactoryRegistry;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

@Extension("Dev Attestation Extension")
public class DevAttestationExtension implements ServiceExtension {

@Inject
private AttestationSourceFactoryRegistry registry;

@Inject
private AttestationDefinitionValidatorRegistry validatorRegistry;

@Override
public String name() {
return "Dev Attestation Extension";
}

@Override
public void initialize(ServiceExtensionContext context) {
registry.registerFactory("dev", new DevAttestationSourceFactory(context.getMonitor()));
validatorRegistry.registerValidator("dev", new DevAttestationSourceValidator());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026. Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST)
*
* 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.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

package de.fraunhofer.isst.edc.extension.attestation.dev;

import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationContext;
import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationSource;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.result.Result;

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

public class DevAttestationSource implements AttestationSource {
public final static String BLACKLIST = "blackList";
public final static String DEFAULT = "default";

private final Map<String, Object> config;
private final Monitor monitor;

public DevAttestationSource(Map<String, Object> configuration, Monitor monitor) {
this.config = configuration;
this.monitor = monitor.withPrefix(this.getClass().getSimpleName());
}

@Override
public Result<Map<String, Object>> execute(AttestationContext context) {
if (config.get(BLACKLIST) instanceof List<?> blackList) {
if (blackList.contains(context.holder().getDid())) {
String message = "Rejecting blacklisted holder " + context.holder().getDid();
monitor.warning(message);
return Result.failure(message);
}
}
var content = config.getOrDefault(context.holder().getDid(),
config.getOrDefault(DEFAULT, new HashMap<>()));
if (content instanceof Map<?, ?> contentMap && !contentMap.containsKey("id")) {
((Map<String, Object>) contentMap).put("id", context.holder().getDid());
}
return Result.success(Map.of("content", content));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2026. Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST)
*
* 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.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

package de.fraunhofer.isst.edc.extension.attestation.dev;

import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationSource;
import org.eclipse.edc.issuerservice.spi.issuance.attestation.AttestationSourceFactory;
import org.eclipse.edc.issuerservice.spi.issuance.model.AttestationDefinition;
import org.eclipse.edc.spi.monitor.Monitor;

public class DevAttestationSourceFactory implements AttestationSourceFactory {

private final Monitor monitor;

public DevAttestationSourceFactory(Monitor monitor) {
this.monitor = monitor;
}

@Override
public AttestationSource createSource(AttestationDefinition definition) {
return new DevAttestationSource(definition.getConfiguration(), monitor);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2026. Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. (represented by Fraunhofer ISST)
*
* 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.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

package de.fraunhofer.isst.edc.extension.attestation.dev;

import org.eclipse.edc.issuerservice.spi.issuance.model.AttestationDefinition;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;
import org.eclipse.edc.validator.spi.Violation;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static de.fraunhofer.isst.edc.extension.attestation.dev.DevAttestationSource.BLACKLIST;
import static de.fraunhofer.isst.edc.extension.attestation.dev.DevAttestationSource.DEFAULT;

public class DevAttestationSourceValidator implements Validator<AttestationDefinition> {

private static final Pattern DID_WEB_REGEX = Pattern.compile(
"^did:web:" +
"(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)(?:\\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*)" +
"(?:%3[Aa][0-9]{1,5})?" +
"(?::(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+)*$");

@Override
public ValidationResult validate(AttestationDefinition input) {
Map<String, Object> config = input.getConfiguration();
List<Violation> violations = new ArrayList<>();
if (config.isEmpty()) {
violations.add(new Violation("-configuration must not be empty-", "", ""));
}
if (config.get(BLACKLIST) != null) {
if (!(config.get(BLACKLIST) instanceof List<?> blackList)) {
violations.add(new Violation("-configuration.blackList must be of type list-",
"configuration.blackList", config.get(BLACKLIST)));
} else {
if (!blackList.stream().allMatch(it -> it instanceof String)) {
violations.add(new Violation("-configuration.blackList must contain only string-typed objects-",
"configuration.blackList", blackList));
}
for (var id : blackList) {
if (config.containsKey(id.toString())) {
violations.add(new Violation("-" + id + " is blacklisted, but at the same time given an " +
"individual credential subject content-", "", ""));
}
}
}
}
for (String key : config.keySet()) {
if (!BLACKLIST.equals(key)) {
if (!DEFAULT.equals(key) && !DID_WEB_REGEX.matcher(key).matches()) {
violations.add(new Violation("-" + key + " is not a valid did:web id", "", ""));
}
if (!(config.get(key) instanceof Map<?, ?>)) {
violations.add(new Violation("-" + key + " must be of type object", "configuration." + key,
config.get(key)));
}
}
}
if (!violations.isEmpty()) {
return ValidationResult.failure(violations);
}
return ValidationResult.success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
de.fraunhofer.isst.edc.extension.attestation.dev.DevAttestationExtension
Loading