Skip to content

Commit

Permalink
Merge branch 'feat/planned-production-edc-assets' of https://github.c…
Browse files Browse the repository at this point in the history
…om/Achtzig20-GmbH/puris into feat/planned-production-edc-assets
  • Loading branch information
ReneSchroederLJ committed Apr 25, 2024
2 parents 5f38e88 + 9ac0c45 commit 076a9a5
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.SubmodelType;
Expand Down Expand Up @@ -122,13 +123,8 @@ private Response sendPostRequest(JsonNode requestBody, List<String> pathSegments
* @return true if all registrations were successful, otherwise false
*/
public boolean registerAssetsInitially() {
boolean result = true;
if (variablesService.isUseFrameworkPolicy()) {
log.info("Registration of framework agreement policy successful {}", (result = createFrameWorkPolicy()));
if (!result) return false;
} else {
log.info("Skipping registration of framework agreement policy");
}
boolean result;
log.info("Registration of framework agreement policy successful {}", (result = createContractPolicy()));
boolean assetRegistration;
log.info("Registration of DTR Asset successful {}", (assetRegistration = registerDtrAsset()));
result &= assetRegistration;
Expand Down Expand Up @@ -167,14 +163,13 @@ public boolean registerAssetsInitially() {
* @return true, if all registrations ran successfully
*/
public boolean createPolicyAndContractDefForPartner(Partner partner) {
boolean result = createPolicyDefinitionForPartner(partner);
boolean result = createBpnlAndMembershipPolicyDefinitionForPartner(partner);
result &= createSubmodelContractDefinitionForPartner(SubmodelType.ITEM_STOCK.URN_SEMANTIC_ID, variablesService.getItemStockSubmodelApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(SubmodelType.PRODUCTION.URN_SEMANTIC_ID, variablesService.getProductionSubmodelApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(SubmodelType.DEMAND.URN_SEMANTIC_ID, variablesService.getDemandSubmodelApiAssetId(), partner);
result &= createSubmodelContractDefinitionForPartner(SubmodelType.DELIVERY.URN_SEMANTIC_ID, variablesService.getDeliverySubmodelApiAssetId(), partner);
result &= createDtrContractDefinitionForPartner(partner);
return createSubmodelContractDefinitionForPartner(SubmodelType.PART_TYPE_INFORMATION.URN_SEMANTIC_ID, variablesService.getPartTypeSubmodelApiAssetId(), partner) && result;

}

private boolean createSubmodelContractDefinitionForPartner(String semanticId, String assetId, Partner partner) {
Expand Down Expand Up @@ -209,14 +204,15 @@ private boolean createDtrContractDefinitionForPartner(Partner partner) {
}

/**
* Registers a policy definition that allows only the given partner's
* BPNL.
* Registers a policy definition that evaluates to true in case all the following conditions apply:
* 1. The BPNL of the requesting connector is equal to the BPNL of the partner
* 2. There's a CX membership credential present
*
* @param partner the partner
* @param partner the partner to create the policy for
* @return true, if registration ran successfully
*/
private boolean createPolicyDefinitionForPartner(Partner partner) {
var body = edcRequestBodyBuilder.buildBpnRestrictedPolicy(partner);
private boolean createBpnlAndMembershipPolicyDefinitionForPartner(Partner partner) {
var body = edcRequestBodyBuilder.buildBpnAndMembershipRestrictedPolicy(partner);
try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) {
if (!response.isSuccessful()) {
log.warn("Policy Registration failed");
Expand All @@ -227,7 +223,7 @@ private boolean createPolicyDefinitionForPartner(Partner partner) {
}
return true;
} catch (Exception e) {
log.error("Failed to register policy definition for partner " + partner.getBpnl(), e);
log.error("Failed to register bpnl and membership policy definition for partner " + partner.getBpnl(), e);
return false;
}
}
Expand All @@ -237,8 +233,8 @@ private boolean createPolicyDefinitionForPartner(Partner partner) {
*
* @return true, if registration ran successfully
*/
private boolean createFrameWorkPolicy() {
var body = edcRequestBodyBuilder.buildFrameworkAgreementPolicy();
private boolean createContractPolicy() {
var body = edcRequestBodyBuilder.buildFrameworkPolicy();
try (var response = sendPostRequest(body, List.of("v2", "policydefinitions"))) {
if (!response.isSuccessful()) {
log.warn("Framework Policy Registration failed");
Expand Down Expand Up @@ -825,18 +821,14 @@ private boolean negotiateForSubmodel(MaterialPartnerRelation mpr, SubmodelType t
}
if (type.URN_SEMANTIC_ID.equals(idString) && submodelData.assetId.equals(entry.get("edc:id").asText())) {
if (targetCatalogEntry == null) {
if (variablesService.isUseFrameworkPolicy()) {
if (testFrameworkAgreementConstraint(entry)) {
targetCatalogEntry = entry;
} else {
log.error("Contract Negotiation for " + type + " Submodel asset with partner " + partner.getBpnl() + " has " +
"been aborted. This partner's contract policy does not match the policy " +
"supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() +
"\n Received offer from Partner: \n" + entry.toPrettyString());
break;
}
} else {
if (testContractPolicyConstraints(entry)) {
targetCatalogEntry = entry;
} else {
log.error("Contract Negotiation for " + type + " Submodel asset with partner " + partner.getBpnl() + " has " +
"been aborted. This partner's contract policy does not match the policy " +
"supported by this application. \n Supported Policy: " + variablesService.getPurisFrameworkAgreement() +
"\n Received offer from Partner: \n" + entry.toPrettyString());
break;
}
} else {
log.warn("Ambiguous catalog entries found! \n" + catalogArray.toPrettyString());
Expand Down Expand Up @@ -898,36 +890,24 @@ public String getCxIdFromPartTypeInformation(MaterialPartnerRelation mpr) {
* @param catalogEntry the catalog item containing the desired api asset
* @return true, if the policy matches yours, otherwise false
*/
private boolean testFrameworkAgreementConstraint(JsonNode catalogEntry) {
try {
var policyObject = catalogEntry.get("odrl:hasPolicy");
if (policyObject == null) {
return false;
}
var permissionObject = policyObject.get("odrl:permission");
if (permissionObject == null) {
return false;
}
var constraintObject = permissionObject.get("odrl:constraint");
if (constraintObject == null) {
return false;
}
var leftOperandObject = constraintObject.get("odrl:leftOperand");
if (!variablesService.getPurisFrameworkAgreement().equals(leftOperandObject.asText())) {
return false;
}
var operatorObject = constraintObject.get("odrl:operator");
if (!"odrl:eq".equals(operatorObject.get("@id").asText())) {
return false;
}
var rightOperandObject = constraintObject.get("odrl:rightOperand");
if (!"active".equals(rightOperandObject.asText())) {
return false;
}
} catch (Exception e) {
log.error("Failed in Framework Agreement Test ", e);
return false;
}
private boolean testContractPolicyConstraints(JsonNode catalogEntry) {
var constraint = Optional.ofNullable(catalogEntry.get("odrl:hasPolicy"))
.map(policy -> policy.get("odrl:permission"))
.map(permission -> permission.get("odrl:constraint"));
if (constraint.isEmpty()) return false;

var leftOperand = constraint.map(cons -> cons.get("odrl:leftOperand"))
.filter(operand -> variablesService.getPurisFrameworkAgreement().equals(operand.asText()));

var operator = constraint.map(cons -> cons.get("odrl:operator"))
.map(op -> op.get("@id"))
.filter(operand -> "odrl:eq".equals(operand.asText()));

var rightOperand = constraint.map(cons -> cons.get("odrl:rightOperand"))
.filter(operand -> "active".equals(operand.asText()));

if (leftOperand.isEmpty() || operator.isEmpty() || rightOperand.isEmpty()) return false;

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class EdcRequestBodyBuilder {
private final String CX_TAXO_NAMESPACE = "https://w3id.org/catenax/taxonomy#";
private final String CX_COMMON_NAMESPACE = "https://w3id.org/catenax/ontology/common#";
private final String DCT_NAMESPACE = "https://purl.org/dc/terms/";
private final String FRAMEWORK_POLICY_ID = "Framework_Agreement_Policy";
private final String CONTRACT_POLICY_ID = "Contract_Policy";

/**
* Creates a request body for requesting a catalog in DSP protocol.
Expand Down Expand Up @@ -79,35 +79,61 @@ public ObjectNode buildBasicCatalogRequestBody(String counterPartyDspUrl, Map<St
}

/**
* Creates a request body in order to register a policy that
* allows only the BPNL of the given partner.
* Creates a request body that demands all of the following conditions:
* 1. The BPNL of the requesting connector is equal to the BPNL of the partner
* 2. There's a valid CX membership credential present
*
* @param partner the partner
* @return the request body
* @param partner the partner to create the policy for
* @return the request body as a {@link JsonNode}
*/
public JsonNode buildBpnRestrictedPolicy(Partner partner) {
public JsonNode buildBpnAndMembershipRestrictedPolicy(Partner partner) {
var body = MAPPER.createObjectNode();
var context = MAPPER.createObjectNode();
context.put("odrl", ODRL_NAMESPACE);
body.set("@context", context);
body.put("@type", "PolicyDefinitionRequestDto");
body.put("@id", getBpnPolicyId(partner));

var policy = MAPPER.createObjectNode();
body.set("policy", policy);
policy.put("@type", "Policy");

var permissionsArray = MAPPER.createArrayNode();
policy.set("odrl:permission", permissionsArray);

var permissionsObject = MAPPER.createObjectNode();
permissionsArray.add(permissionsObject);
permissionsObject.put("odrl:action", "USE");

var constraintObject = MAPPER.createObjectNode();
permissionsObject.set("odrl:constraint", constraintObject);
constraintObject.put("@type", "LogicalConstraint");
constraintObject.put("odrl:leftOperand", "BusinessPartnerNumber");
var operatorObject = MAPPER.createObjectNode();
constraintObject.set("odrl:operator", operatorObject);
operatorObject.put("@id", "odrl:eq");
constraintObject.put("odrl:rightOperand", partner.getBpnl());

var andArray = MAPPER.createArrayNode();
constraintObject.set("odrl:and", andArray);

var bpnlpConstraint = MAPPER.createObjectNode();
andArray.add(bpnlpConstraint);
bpnlpConstraint.put("@type", "Constraint");
bpnlpConstraint.put("odrl:leftOperand", "BusinessPartnerNumber");

var bpnlOperator = MAPPER.createObjectNode();
bpnlpConstraint.set("odrl:operator", bpnlOperator);
bpnlOperator.put("@id", "odrl:eq");

bpnlpConstraint.put("odrl:rightOperand", partner.getBpnl());

var membershipConstraint = MAPPER.createObjectNode();
andArray.add(membershipConstraint);
membershipConstraint.put("@type", "Constraint");
membershipConstraint.put("odrl:leftOperand", "Membership");

var membershipOperator = MAPPER.createObjectNode();
membershipConstraint.set("odrl:operator", membershipOperator);
membershipOperator.put("@id", "odrl:eq");

membershipConstraint.put("odrl:rightOperand", "active");

return body;
}

Expand All @@ -117,41 +143,43 @@ public JsonNode buildBpnRestrictedPolicy(Partner partner) {
*
* @return the request body
*/
public JsonNode buildFrameworkAgreementPolicy() {
public JsonNode buildFrameworkPolicy() {
var body = MAPPER.createObjectNode();
var context = MAPPER.createObjectNode();
context.put("odrl", ODRL_NAMESPACE);
body.set("@context", context);
body.put("@type", "PolicyDefinitionRequestDto");
body.put("@id", FRAMEWORK_POLICY_ID);
body.put("@id", CONTRACT_POLICY_ID);

var policy = MAPPER.createObjectNode();
body.set("policy", policy);
policy.put("@type", "Policy");

var permissionsArray = MAPPER.createArrayNode();
policy.set("odrl:permission", permissionsArray);
var permissionsObject = MAPPER.createObjectNode();
permissionsArray.add(permissionsObject);
permissionsObject.put("odrl:action", "USE");

var permissionObject = MAPPER.createObjectNode();
permissionsArray.add(permissionObject);
permissionObject.put("odrl:action", "USE");

var constraintObject = MAPPER.createObjectNode();
permissionsObject.set("odrl:constraint", constraintObject);
permissionObject.set("odrl:constraint", constraintObject);
constraintObject.put("@type", "LogicalConstraint");
constraintObject.put("odrl:leftOperand", variablesService.getPurisFrameworkAgreement());

var operatorObject = MAPPER.createObjectNode();
constraintObject.set("odrl:operator", operatorObject);
operatorObject.put("@id", "odrl:eq");
constraintObject.put("odrl:rightOperand", "active");

return body;
}

public JsonNode buildSubmodelContractDefinitionWithBpnRestrictedPolicy(String assetId, Partner partner) {
var body = getEdcContextObject();
body.put("@id", partner.getBpnl() + "_contractdefinition_for_" + assetId);
body.put("accessPolicyId", getBpnPolicyId(partner));
if(variablesService.isUseFrameworkPolicy()) {
body.put("contractPolicyId", FRAMEWORK_POLICY_ID);
} else {
body.put("contractPolicyId", getBpnPolicyId(partner));
}
body.put("contractPolicyId", CONTRACT_POLICY_ID);
var assetsSelector = MAPPER.createObjectNode();
body.set("assetsSelector", assetsSelector);
assetsSelector.put("@type", "CriterionDto");
Expand Down Expand Up @@ -179,11 +207,7 @@ public JsonNode buildPartTypeInfoContractDefinitionForPartner(Partner partner) {
var body = getEdcContextObject();
body.put("@id", partner.getBpnl() +"_contractdefinition_for_PartTypeInfoAsset");
body.put("accessPolicyId", getBpnPolicyId(partner));
if(variablesService.isUseFrameworkPolicy()) {
body.put("contractPolicyId", FRAMEWORK_POLICY_ID);
} else {
body.put("contractPolicyId", getBpnPolicyId(partner));
}
body.put("contractPolicyId", CONTRACT_POLICY_ID);
var assetsSelector = MAPPER.createObjectNode();
body.set("assetsSelector", assetsSelector);
assetsSelector.put("@type", "CriterionDto");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,6 @@ public class VariablesService {
*/
private String deliverySubmodelAssetId;

@Value("${puris.frameworkagreement.use}")
/**
* A flag that signals whether a framework policy
* shall be used as contract policy for your api assets.
*/
private boolean useFrameworkPolicy;

@Value("${puris.frameworkagreement.credential}")
/**
* The name of the framework agreement to be used.
Expand Down
2 changes: 1 addition & 1 deletion charts/puris/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ dependencies:
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 2.0.2
version: 2.2.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
Expand Down
1 change: 0 additions & 1 deletion charts/puris/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ $ helm install puris --namespace puris --create-namespace .
| backend.puris.edr.deletiontimer | int | `2` | Number of minutes before received authentication data of a consumer pull is removed from memory |
| backend.puris.existingSecret | string | `"secret-backend-puris"` | Secret for backend passwords. For more information look into 'backend-secrets.yaml' file. |
| backend.puris.frameworkagreement.credential | string | `"FrameworkAgreement.traceability"` | The name of the framework agreement |
| backend.puris.frameworkagreement.use | bool | `false` | Flag to determine whether to use a framework agreement in puris |
| backend.puris.generatematerialcatenaxid | bool | `true` | Flag that decides whether the auto-generation feature of the puris backend is enabled. Since all Material entities are required to have a CatenaX-Id, you must enter any pre-existing CatenaX-Id via the materials-API of the backend, when you are inserting a new Material entity to the backend's database. If a CatenaX-Id was not assigned to your Material so far, then this feature can auto-generate one randomly. In a real-world-scenario, you must then use this randomly generated CatenaX-Id for the lifetime of that Material entity. |
| backend.puris.itemstocksubmodel.apiassetid | string | `"itemstocksubmodel-api-asset"` | Asset ID for ItemStockSubmodel API |
| backend.puris.jpa.hibernate.ddl-auto | string | `"create"` | Initialises SQL database with Hibernate property "create" to allow Hibernate to first drop all tables and then create new ones |
Expand Down
2 changes: 0 additions & 2 deletions charts/puris/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,6 @@ backend:
# -- Asset ID for DeliverySubmodel API
apiassetid: deliverysubmodel-api-asset
frameworkagreement:
# -- Flag to determine whether to use a framework agreement in puris
use: false
# -- The name of the framework agreement
credential: FrameworkAgreement.traceability
edr:
Expand Down
3 changes: 1 addition & 2 deletions docs/adminGuide/Admin_Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ _Note: The application does NOT make use of the `Client Authentication` (private
## Configure Framework Agreement Credential Usage

To configure the usage of a framework agreement credential, that is automatically enforced by the EDC during contracting
(see further details in [ARC42 - Chapter 8](../arc42/08_concepts.md)), the following properties need to be configures:
(see further details in [ARC42 - Chapter 8](../arc42/08_concepts.md)), the following property needs to be configured:

- `backend.frameworkagreement.use` (docker `PURIS_FRAMEWORKAGREEMENT_USE`) = true
- `backend.frameworkagreement.credential` (docker `PURIS_FRAMEWORKAGREEMENT_CREDENTIAL`) = 'puris' (NOTE: not available
for R24.03)

Expand Down
Loading

0 comments on commit 076a9a5

Please sign in to comment.