Skip to content

Commit

Permalink
feat(IATP): pluggable constraint to scope mapping
Browse files Browse the repository at this point in the history
wip
  • Loading branch information
wolf4ood committed Nov 7, 2023
1 parent 7ca9375 commit c3f5ca1
Show file tree
Hide file tree
Showing 16 changed files with 766 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@

package org.eclipse.edc.iam.identitytrust.core;

import org.eclipse.edc.iam.identitytrust.core.defaults.DefaultTrustedIssuerRegistry;
import org.eclipse.edc.iam.identitytrust.core.defaults.InMemorySignatureSuiteRegistry;
import org.eclipse.edc.iam.identitytrust.core.scope.IatpScopeExtractorRegistry;
import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService;
import org.eclipse.edc.identitytrust.SecureTokenService;
import org.eclipse.edc.identitytrust.TrustedIssuerRegistry;
import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry;
import org.eclipse.edc.identitytrust.verification.SignatureSuiteRegistry;
import org.eclipse.edc.jwt.TokenGenerationServiceImpl;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
Expand Down Expand Up @@ -78,6 +82,11 @@ public SignatureSuiteRegistry createSignatureSuiteRegistry() {
return new InMemorySignatureSuiteRegistry();
}

@Provider(isDefault = true)
public ScopeExtractorRegistry scopeExtractorRegistry() {
return new IatpScopeExtractorRegistry();
}

private KeyPair keyPairFromConfig(ServiceExtensionContext context) {
var pubKeyAlias = context.getSetting(STS_PUBLIC_KEY_ALIAS, null);
var privKeyAlias = context.getSetting(STS_PRIVATE_KEY_ALIAS, null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 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.iam.identitytrust.core;

import org.eclipse.edc.iam.identitytrust.core.scope.IatpScopeExtractorFunction;
import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

import static org.eclipse.edc.iam.identitytrust.core.IatpScopeExtractorExtension.NAME;

@Extension(NAME)
public class IatpScopeExtractorExtension implements ServiceExtension {

public static final String NAME = "IATP scope extractor extension";

public static final String CATALOG_REQUEST_SCOPE = "request.catalog";
public static final String NEGOTIATION_REQUEST_SCOPE = "request.contract.negotiation";
public static final String TRANSFER_PROCESS_REQUEST_SCOPE = "request.transfer.process";

@Inject
private PolicyEngine policyEngine;

@Inject
private ScopeExtractorRegistry scopeMapperRegistry;

@Inject
private Monitor monitor;

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

@Override
public void initialize(ServiceExtensionContext context) {
var contextMappingFunction = new IatpScopeExtractorFunction(scopeMapperRegistry, monitor);
policyEngine.registerPreValidator(CATALOG_REQUEST_SCOPE, contextMappingFunction);
policyEngine.registerPreValidator(NEGOTIATION_REQUEST_SCOPE, contextMappingFunction);
policyEngine.registerPreValidator(TRANSFER_PROCESS_REQUEST_SCOPE, contextMappingFunction);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
*/

package org.eclipse.edc.iam.identitytrust.core;
package org.eclipse.edc.iam.identitytrust.core.defaults;

import org.eclipse.edc.identitytrust.TrustedIssuerRegistry;
import org.eclipse.edc.identitytrust.model.Issuer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
*/

package org.eclipse.edc.iam.identitytrust.core;
package org.eclipse.edc.iam.identitytrust.core.defaults;

import com.apicatalog.ld.signature.SignatureSuite;
import org.eclipse.edc.identitytrust.verification.SignatureSuiteRegistry;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 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.iam.identitytrust.core.scope;

import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.iam.TokenParameters;
import org.eclipse.edc.spi.monitor.Monitor;

import java.util.function.BiFunction;

import static java.lang.String.format;

public class IatpScopeExtractorFunction implements BiFunction<Policy, PolicyContext, Boolean> {

private final ScopeExtractorRegistry registry;
private final Monitor monitor;

public IatpScopeExtractorFunction(ScopeExtractorRegistry registry, Monitor monitor) {
this.registry = registry;
this.monitor = monitor;
}

@Override
public Boolean apply(Policy policy, PolicyContext context) {
var params = context.getContextData(TokenParameters.Builder.class);
if (params == null) {
throw new EdcException(format("%s not set in policy context", TokenParameters.Builder.class.getName()));
}
var results = registry.extractScopes(policy, context).map(scopes -> String.join(" ", scopes));

if (results.succeeded()) {
params.scope(results.getContent());
return true;
} else {
monitor.warning("Failed to map credentials to scopes for policy");
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 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.iam.identitytrust.core.scope;

import org.eclipse.edc.identitytrust.scope.ScopeExtractor;
import org.eclipse.edc.identitytrust.scope.ScopeExtractorRegistry;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.spi.result.Result;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class IatpScopeExtractorRegistry implements ScopeExtractorRegistry {
private final List<ScopeExtractor> extractors = new ArrayList<>();

@Override
public void registerScopeExtractor(ScopeExtractor extractor) {
extractors.add(extractor);
}

@Override
public Result<Set<String>> extractScopes(Policy policy, PolicyContext policyContext) {
var visitor = new IatpScopeExtractorVisitor(extractors, policyContext);
var policies = policy.accept(visitor);
if (policyContext.hasProblems()) {
return Result.failure(policyContext.getProblems());
}
return Result.success(policies);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2023 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.iam.identitytrust.core.scope;

import org.eclipse.edc.identitytrust.scope.ScopeExtractor;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.model.AndConstraint;
import org.eclipse.edc.policy.model.AtomicConstraint;
import org.eclipse.edc.policy.model.Constraint;
import org.eclipse.edc.policy.model.Duty;
import org.eclipse.edc.policy.model.Expression;
import org.eclipse.edc.policy.model.LiteralExpression;
import org.eclipse.edc.policy.model.MultiplicityConstraint;
import org.eclipse.edc.policy.model.OrConstraint;
import org.eclipse.edc.policy.model.Permission;
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.policy.model.Prohibition;
import org.eclipse.edc.policy.model.Rule;
import org.eclipse.edc.policy.model.XoneConstraint;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class IatpScopeExtractorVisitor implements Policy.Visitor<Set<String>>, Rule.Visitor<Set<String>>, Constraint.Visitor<Set<String>>, Expression.Visitor<Object> {
private final List<ScopeExtractor> mappers;
private final PolicyContext policyContext;

public IatpScopeExtractorVisitor(List<ScopeExtractor> mappers, PolicyContext policyContext) {
this.mappers = mappers;
this.policyContext = policyContext;
}

@Override
public Set<String> visitAndConstraint(AndConstraint andConstraint) {
return visitMultiplicityConstraint(andConstraint);
}

@Override
public Set<String> visitOrConstraint(OrConstraint orConstraint) {
return visitMultiplicityConstraint(orConstraint);
}

@Override
public Set<String> visitXoneConstraint(XoneConstraint constraint) {
return visitMultiplicityConstraint(constraint);
}

@Override
public Set<String> visitAtomicConstraint(AtomicConstraint constraint) {
var rightValue = constraint.getRightExpression().accept(this);
var leftRawValue = constraint.getLeftExpression().accept(this);

return mappers.stream()
.map(mapper -> mapper.extractScopes(leftRawValue, constraint.getOperator(), rightValue, policyContext))
.flatMap(Collection::stream)
.collect(Collectors.toSet());

}

@Override
public Object visitLiteralExpression(LiteralExpression expression) {
return expression.getValue();
}

@Override
public Set<String> visitPolicy(Policy policy) {
var scopes = new HashSet<String>();
policy.getPermissions().forEach(permission -> scopes.addAll(permission.accept(this)));
policy.getProhibitions().forEach(prohibition -> scopes.addAll(prohibition.accept(this)));
policy.getObligations().forEach(duty -> scopes.addAll(duty.accept(this)));
return scopes;
}

@Override
public Set<String> visitPermission(Permission policy) {
var scopes = policy.getDuties().stream()
.map(duty -> duty.accept(this))
.flatMap(Collection::stream)
.collect(Collectors.toSet());

scopes.addAll(visitRule(policy));
return scopes;
}

@Override
public Set<String> visitProhibition(Prohibition policy) {
return visitRule(policy);
}

@Override
public Set<String> visitDuty(Duty policy) {
return visitRule(policy);
}

private Set<String> visitRule(Rule rule) {
return rule.getConstraints().stream()
.map(constraint -> constraint.accept(this))
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}

private Set<String> visitMultiplicityConstraint(MultiplicityConstraint multiplicityConstraint) {
return multiplicityConstraint.getConstraints().stream()
.map(constraint -> constraint.accept(this))
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import org.eclipse.edc.iam.identitytrust.core.defaults.DefaultTrustedIssuerRegistry;
import org.eclipse.edc.iam.identitytrust.core.scope.IatpScopeExtractorRegistry;
import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService;
import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
import org.eclipse.edc.spi.monitor.Monitor;
Expand Down Expand Up @@ -103,4 +105,12 @@ void verify_defaultIssuerRegistry(ServiceExtensionContext context, ObjectFactory

assertThat(ext.createInMemoryIssuerRegistry()).isInstanceOf(DefaultTrustedIssuerRegistry.class);
}

@Test
void verify_defaultCredentialMapperRegistry(ServiceExtensionContext context, IatpDefaultServicesExtension ext) {
Monitor mockedMonitor = mock();
context.registerService(Monitor.class, mockedMonitor);

assertThat(ext.scopeExtractorRegistry()).isInstanceOf(IatpScopeExtractorRegistry.class);
}
}
Loading

0 comments on commit c3f5ca1

Please sign in to comment.