diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml index 0f8186c..dfac57a 100644 --- a/.github/workflows/pre-release.yaml +++ b/.github/workflows/pre-release.yaml @@ -64,8 +64,8 @@ jobs: mvn clean package -Dquarkus.package.type=native -Dquarkus.container-image.build=true -Dquarkus.container-image.tag=${{ needs.generate-version.outputs.version }} -Dquarkus.container-image.registry="${{ env.REGISTRY }}" -Dquarkus.container-image.group="${{ env.REPOSITORY }}" - name: Test image - run: | - mvn integration-test -Pk8s-it -DskipTests -Dquarkus.container-image.tag=${{ needs.generate-version.outputs.version }} -Dquarkus.container-image.registry="${{ env.REGISTRY }}" -Dquarkus.container-image.group="${{ env.REPOSITORY }}" + run: | + mvn integration-test -Pk8s-it -DskipTests -Dquarkus.container-image.tag=${{ needs.generate-version.outputs.version }} -Dquarkus.container-image.registry="${{ env.REGISTRY }}" -Dquarkus.container-image.group="${{ env.REPOSITORY }}" - name: Push run: | diff --git a/api/odrl.yaml b/api/odrl.yaml index 3834287..7c9ea1a 100644 --- a/api/odrl.yaml +++ b/api/odrl.yaml @@ -26,6 +26,21 @@ paths: schema: type: string description: location of the created policy + get: + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + operationId: getPolicies + summary: Get the policies in the ODRL-Format + responses: + '200': + description: Successfully retrieved the policis. + content: + application/json: + schema: + $ref: '#/components/schemas/PolicyList' + '404': + description: No such policy exists /policy/{id}: put: parameters: @@ -86,6 +101,20 @@ components: required: true schema: $ref: '#/components/schemas/Uid' + Page: + name: page + in: query + required: false + schema: + type: integer + default: 0 + PageSize: + name: pageSize + in: query + required: false + schema: + type: integer + default: 25 schemas: Policy: type: object @@ -96,6 +125,10 @@ components: type: string rego: type: string + PolicyList: + type: array + items: + $ref: '#/components/schemas/Policy' Uid: type: string format: https://datatracker.ietf.org/doc/html/rfc3987 diff --git a/doc/REGO.md b/doc/REGO.md index e114461..3214ba1 100644 --- a/doc/REGO.md +++ b/doc/REGO.md @@ -67,6 +67,7 @@ | --- | --- | --- | --- | | leftOperand | vc:role | role(verifiable_credential,organization_id) | retrieves the roles from the credential, that target the current organization | | leftOperand | vc:currentParty | current_party(credential) | the current (organization)party, | +| leftOperand | vc:type | types(verifiable_credential) | the type(s) of the current credential | | assignee | odrl:any | is_any | allows for any user | ## ngsild @@ -78,6 +79,7 @@ | leftOperand | ngsi-ld:_observedAt | # | retrieves the observedAt of the property The method should be concretized in the mapping.json, to match a concrete property. | | leftOperand | ngsi-ld:_modifiedAt | # | retrieves the modifiedAt of the property The method should be concretized in the mapping.json, to match a concrete property. | | leftOperand | ngsi-ld: | # | retrieves the object of the relationship, only applies to properties of type "Relationship". The method should be concretized in the mapping.json, to match a concrete property. | +| action | ngsild:create | is_creation(request) | Check if the given request is a creation | ## tmf @@ -85,3 +87,4 @@ | --- | --- | --- | --- | | leftOperand | tmf:lifecycleStatus | life_cycle_status(entity) | return the lifeCycleStatus of a given entity | | leftOperand | tmf:resource | resource_type(http_part) | retrieves the type of the resource from the path | +| action | tmf:create | is_creation(request) | Check if the given request is a creation | diff --git a/pom.xml b/pom.xml index 76f926b..a4f85bd 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,13 @@ 6.2.8.Final 7.4.0 1.19.6 + 2.4.1 + 1.18.30 + 33.1.0-jre + 1.26.1 + 24.0.2 + 4.2.1 + 5.15.0 @@ -79,30 +86,25 @@ io.quarkus.qute qute-core - - io.quarkiverse.openapi.generator - quarkus-openapi-generator-server - 2.4.1 - io.quarkiverse.openapi.generator quarkus-openapi-generator - 2.4.1 + ${version.io.quarkiverse.openapi.generator} org.projectlombok lombok - 1.18.30 + ${version.org.projectlombok.lombok} com.google.guava guava - 33.1.0-jre + ${version.com.google.guava} org.apache.commons commons-compress - 1.26.1 + ${version.org.apache.commons.commons-compress} io.quarkus @@ -135,7 +137,7 @@ org.awaitility awaitility - 4.2.1 + ${version.org.awaitility} test @@ -153,19 +155,19 @@ org.keycloak keycloak-core - 24.0.2 + ${version.org.keycloak} test org.keycloak keycloak-services - 24.0.2 + ${version.org.keycloak} test org.mock-server mockserver-client-java - 5.15.0 + ${version.org.mock-server.mockserver-client-java} test diff --git a/src/main/java/org/fiware/odrl/BundleResource.java b/src/main/java/org/fiware/odrl/BundleResource.java index 8372f4d..782d8ed 100644 --- a/src/main/java/org/fiware/odrl/BundleResource.java +++ b/src/main/java/org/fiware/odrl/BundleResource.java @@ -7,6 +7,7 @@ import io.quarkus.runtime.StartupEvent; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; diff --git a/src/main/java/org/fiware/odrl/PolicyResource.java b/src/main/java/org/fiware/odrl/PolicyResource.java index c57be44..fa089c7 100644 --- a/src/main/java/org/fiware/odrl/PolicyResource.java +++ b/src/main/java/org/fiware/odrl/PolicyResource.java @@ -16,7 +16,9 @@ import org.fiware.odrl.rego.PolicyWrapper; import org.fiware.odrl.rego.RegoPolicy; +import java.util.List; import java.util.Map; +import java.util.Optional; /** * @author Stefan Wiedemann @@ -65,6 +67,23 @@ public Response deletePolicyById(String id) { return Response.noContent().build(); } + + @Override + public Response getPolicies(Integer page, Integer pageSize) { + List policyList = policyRepository + .getPolicies(Optional.ofNullable(page).orElse(0), Optional.ofNullable(pageSize).orElse(25)) + .entrySet() + .stream() + .map(policyEntry -> new Policy() + .id(policyEntry.getKey()) + .odrl(policyEntry.getValue().odrl().policy()) + .rego(policyEntry.getValue().rego().policy())) + .toList(); + + return Response.ok(policyList).build(); + } + + @Override public Response getPolicyById(String id) { return policyRepository diff --git a/src/main/java/org/fiware/odrl/mapping/EntityMapper.java b/src/main/java/org/fiware/odrl/mapping/EntityMapper.java index 30d666e..cfa7b9d 100644 --- a/src/main/java/org/fiware/odrl/mapping/EntityMapper.java +++ b/src/main/java/org/fiware/odrl/mapping/EntityMapper.java @@ -1,6 +1,11 @@ package org.fiware.odrl.mapping; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.fiware.odrl.persistence.Policy; import org.fiware.odrl.persistence.PolicyEntity; import org.fiware.odrl.rego.OdrlPolicy; import org.fiware.odrl.rego.PolicyWrapper; @@ -10,17 +15,32 @@ * @author Stefan Wiedemann */ @ApplicationScoped +@Slf4j public class EntityMapper { + @Inject + private ObjectMapper objectMapper; + public PolicyEntity map(String id, PolicyWrapper policyWrapper) { + Policy rego = new Policy(); + rego.setPolicy(policyWrapper.rego().policy()); + Policy odrl = new Policy(); + odrl.setPolicy(policyWrapper.odrl().policy()); PolicyEntity policy = new PolicyEntity(); policy.setPolicyId(id); - policy.setRego(policyWrapper.rego().policy()); - policy.setOdrl(policyWrapper.odrl().policy()); + policy.setRego(rego); + policy.setOdrl(odrl); return policy; } public PolicyWrapper map(PolicyEntity policyEntity) { - return new PolicyWrapper(new OdrlPolicy(policyEntity.getOdrl()), new RegoPolicy(policyEntity.getRego())); + try { + log.warn("The entity {}", objectMapper.writeValueAsString(policyEntity)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + return new PolicyWrapper( + new OdrlPolicy(policyEntity.getOdrl().getPolicy()), + new RegoPolicy(policyEntity.getRego().getPolicy())); } } diff --git a/src/main/java/org/fiware/odrl/mapping/PolicyReflectionConfiguration.java b/src/main/java/org/fiware/odrl/mapping/PolicyReflectionConfiguration.java new file mode 100644 index 0000000..cc76fcb --- /dev/null +++ b/src/main/java/org/fiware/odrl/mapping/PolicyReflectionConfiguration.java @@ -0,0 +1,11 @@ +package org.fiware.odrl.mapping; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import org.fiware.odrl.model.Policy; + +/** + * @author Stefan Wiedemann + */ +@RegisterForReflection(targets = {Policy.class}) +public class PolicyReflectionConfiguration { +} diff --git a/src/main/java/org/fiware/odrl/persistence/Policy.java b/src/main/java/org/fiware/odrl/persistence/Policy.java new file mode 100644 index 0000000..256bb12 --- /dev/null +++ b/src/main/java/org/fiware/odrl/persistence/Policy.java @@ -0,0 +1,13 @@ +package org.fiware.odrl.persistence; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Data; + +/** + * @author Stefan Wiedemann + */ +@RegisterForReflection +@Data +public class Policy { + private String policy; +} diff --git a/src/main/java/org/fiware/odrl/persistence/PolicyEntity.java b/src/main/java/org/fiware/odrl/persistence/PolicyEntity.java index 25b382a..b1c9fdd 100644 --- a/src/main/java/org/fiware/odrl/persistence/PolicyEntity.java +++ b/src/main/java/org/fiware/odrl/persistence/PolicyEntity.java @@ -1,9 +1,13 @@ package org.fiware.odrl.persistence; import io.quarkus.hibernate.orm.panache.PanacheEntity; +import jakarta.persistence.Basic; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.Lob; import lombok.Data; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import java.util.Optional; @@ -17,11 +21,12 @@ public class PolicyEntity extends PanacheEntity { public static final String TABLE_NAME = "policy_entity"; private String policyId; - @Lob - private String odrl; - @Lob - private String rego; + @JdbcTypeCode(SqlTypes.JSON) + private Policy odrl; + + @JdbcTypeCode(SqlTypes.JSON) + private Policy rego; public static Optional findByPolicyId(String policyId) { return Optional.ofNullable(find("policyId", policyId).firstResult()); diff --git a/src/main/java/org/fiware/odrl/rego/PersistentPolicyRepository.java b/src/main/java/org/fiware/odrl/rego/PersistentPolicyRepository.java index a73e606..d8ff2ef 100644 --- a/src/main/java/org/fiware/odrl/rego/PersistentPolicyRepository.java +++ b/src/main/java/org/fiware/odrl/rego/PersistentPolicyRepository.java @@ -1,6 +1,9 @@ package org.fiware.odrl.rego; import com.google.common.collect.ImmutableMap; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.hibernate.orm.panache.PanacheQuery; +import io.quarkus.panache.common.Page; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; @@ -12,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; /** * @author Stefan Wiedemann @@ -54,7 +58,6 @@ private String getUniqueId() { return generatedId; } - @Transactional public Map getPolicies() { Map policies = new HashMap<>(); @@ -64,8 +67,18 @@ public Map getPolicies() { return ImmutableMap.copyOf(policies); } + public Map getPolicies(int page, int pageSize) { + PanacheQuery policyEntities = PolicyEntity.findAll(); + List policyEntityList = policyEntities.page(Page.of(page, pageSize)).list(); + + return policyEntityList.stream().collect(Collectors.toMap(PolicyEntity::getPolicyId, e -> entityMapper.map(e), (e1, e2) -> e1)); + } + @Override + @Transactional public void deletePolicy(String id) { - PolicyEntity.findByPolicyId(id).map(policyEntity -> policyEntity.id).ifPresent(PolicyEntity::deleteById); + log.warn("Try to delete {}", id); + PolicyEntity.findByPolicyId(id) + .ifPresent(PanacheEntityBase::delete); } } diff --git a/src/main/java/org/fiware/odrl/rego/PolicyRepository.java b/src/main/java/org/fiware/odrl/rego/PolicyRepository.java index 823496b..2a6ca6e 100644 --- a/src/main/java/org/fiware/odrl/rego/PolicyRepository.java +++ b/src/main/java/org/fiware/odrl/rego/PolicyRepository.java @@ -1,5 +1,7 @@ package org.fiware.odrl.rego; +import jakarta.transaction.Transactional; + import java.util.Map; import java.util.Optional; import java.util.Random; @@ -29,5 +31,7 @@ default String generatePolicyId() { Map getPolicies(); + Map getPolicies(int page, int pageSize); + void deletePolicy(String id); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e639d32..1da426a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,7 +6,6 @@ quarkus.datasource.db-kind=postgresql quarkus.datasource.username=postgres quarkus.datasource.password=postgres quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/pap -quarkus.datasource.jdbc.max-size=13 quarkus.hibernate-orm.database.generation=update quarkus.hibernate-orm.database.generation.create-schemas=true diff --git a/src/main/resources/mapping.json b/src/main/resources/mapping.json index 0aa4318..1044ed0 100644 --- a/src/main/resources/mapping.json +++ b/src/main/resources/mapping.json @@ -39,6 +39,18 @@ "regoPackage": "dome.action as dome_action", "regoMethod": "dome_action.is_set_published(helper.http_part)" } + }, + "tmf": { + "create": { + "regoPackage": "tmf.action as tmf_action", + "regoMethod": "tmf_action.is_creation(helper.http_part)" + } + }, + "ngsild": { + "create": { + "regoPackage": "ngsild.action as ngsild_action", + "regoMethod": "ngsild_action.is_creation(helper.http_part)" + } } }, "operator": { @@ -132,6 +144,10 @@ "currentParty": { "regoPackage": "vc.leftOperand as vc_lo", "regoMethod": "vc_lo.current_party(helper.verifiable_credential)" + }, + "type": { + "regoPackage": "vc.leftOperand as vc_lo", + "regoMethod": "vc_lo.types(helper.verifiable_credential)" } }, "ngsi-ld": { diff --git a/src/main/resources/rego-resources.txt b/src/main/resources/rego-resources.txt index c66edae..daabaa2 100644 --- a/src/main/resources/rego-resources.txt +++ b/src/main/resources/rego-resources.txt @@ -87,3 +87,52 @@ rego/vc/leftOperand.rego rego/vc/assignee.rego rego/ngsi-ld/leftOperand.rego rego/tmf/leftOperand.rego +rego/dome/leftOperand.rego +rego/dome/action.rego +rego/odrl/operand.rego +rego/odrl/rightOperand.rego +rego/odrl/operator.rego +rego/odrl/leftOperand.rego +rego/odrl/target.rego +rego/odrl/action.rego +rego/odrl/assignee.rego +rego/utils/kong.rego +rego/utils/apisix.rego +rego/vc/leftOperand.rego +rego/vc/assignee.rego +rego/ngsi-ld/leftOperand.rego +rego/tmf/leftOperand.rego +rego/tmf/action.rego +rego/dome/leftOperand.rego +rego/dome/action.rego +rego/odrl/operand.rego +rego/odrl/rightOperand.rego +rego/odrl/operator.rego +rego/odrl/leftOperand.rego +rego/odrl/target.rego +rego/odrl/action.rego +rego/odrl/assignee.rego +rego/utils/kong.rego +rego/utils/apisix.rego +rego/vc/leftOperand.rego +rego/vc/assignee.rego +rego/ngsi-ld/leftOperand.rego +rego/tmf/leftOperand.rego +rego/tmf/action.rego +rego/dome/leftOperand.rego +rego/dome/action.rego +rego/odrl/operand.rego +rego/odrl/rightOperand.rego +rego/odrl/operator.rego +rego/odrl/leftOperand.rego +rego/odrl/target.rego +rego/odrl/action.rego +rego/odrl/assignee.rego +rego/utils/kong.rego +rego/utils/apisix.rego +rego/vc/leftOperand.rego +rego/vc/assignee.rego +rego/ngsi-ld/leftOperand.rego +rego/ngsi-ld/action.rego +rego/tmf/leftOperand.rego +rego/tmf/action.rego diff --git a/src/main/resources/rego/ngsi-ld/action.rego b/src/main/resources/rego/ngsi-ld/action.rego new file mode 100644 index 0000000..aaf5718 --- /dev/null +++ b/src/main/resources/rego/ngsi-ld/action.rego @@ -0,0 +1,8 @@ +package ngsild.action + +import rego.v1 + +## ngsild:create +# Check if the given request is a creation +is_creation(request) if request.method == "POST" + diff --git a/src/main/resources/rego/ngsi-ld/leftOperand.rego b/src/main/resources/rego/ngsi-ld/leftOperand.rego index 3fb774b..cc2a697 100644 --- a/src/main/resources/rego/ngsi-ld/leftOperand.rego +++ b/src/main/resources/rego/ngsi-ld/leftOperand.rego @@ -22,9 +22,9 @@ type_from_body(body) := body.type ## ngsi-ld:entityType # retrieves the type from an entity, either from the request path or from the body entity_type(http_part) := tfp if { - tfp = type_from_path(http_part.path) + tfp = type_from_path(http_part.path) } else := tfb if { - tfb = type_from_body(http_part.body) + tfb = type_from_body(http_part.body) } ## ngsi-ld: diff --git a/src/main/resources/rego/odrl/operand.rego b/src/main/resources/rego/odrl/operand.rego index 92a4ba9..534d95b 100644 --- a/src/main/resources/rego/odrl/operand.rego +++ b/src/main/resources/rego/odrl/operand.rego @@ -5,13 +5,14 @@ import rego.v1 ## odrl:and # checks if all given constraints are true and_operand(constraints) if { - some constraint in constraints + true_constraints := [constraint | some constraint in constraints; constraint == true] + count(true_constraints) == count(constraints) } ## odrl:andSequence # checks if all given constraints are true and_sequence_operand(constraints) if { - some constraint in constraints + and_operand(constraints) } ## odrl:or diff --git a/src/main/resources/rego/odrl/operator.rego b/src/main/resources/rego/odrl/operator.rego index ca4362e..4c072f1 100644 --- a/src/main/resources/rego/odrl/operator.rego +++ b/src/main/resources/rego/odrl/operator.rego @@ -4,7 +4,7 @@ import rego.v1 ## odrl:eq # check that both operands are equal -eq_operator(leftOperand, rightOperand) := leftOperand == rightOperand +eq_operator(leftOperand, rightOperand) if leftOperand == rightOperand ## odrl:hasPart # check that the rightOperand is in the leftOperand diff --git a/src/main/resources/rego/tmf/action.rego b/src/main/resources/rego/tmf/action.rego new file mode 100644 index 0000000..c2f9a18 --- /dev/null +++ b/src/main/resources/rego/tmf/action.rego @@ -0,0 +1,7 @@ +package tmf.action + +import rego.v1 + +## tmf:create +# Check if the given request is a creation +is_creation(request) if request.method == "POST" \ No newline at end of file diff --git a/src/main/resources/rego/tmf/leftOperand.rego b/src/main/resources/rego/tmf/leftOperand.rego index 4f15cb9..c91ee69 100644 --- a/src/main/resources/rego/tmf/leftOperand.rego +++ b/src/main/resources/rego/tmf/leftOperand.rego @@ -17,4 +17,4 @@ resource_type(http_part) := resource if { non_id_parts := [path_element | some path_element in reversed; not contains(path_element, "ngsi-ld")] # after removal of the id, the resource is the first one to be retrieved resource = non_id_parts[0] -} +} \ No newline at end of file diff --git a/src/main/resources/rego/vc/leftOperand.rego b/src/main/resources/rego/vc/leftOperand.rego index c088b03..2fa604a 100644 --- a/src/main/resources/rego/vc/leftOperand.rego +++ b/src/main/resources/rego/vc/leftOperand.rego @@ -12,4 +12,8 @@ role(verifiable_credential,organization_id) := r if { ## vc:currentParty # the current (organization)party, -current_party(credential) := credential.issuer \ No newline at end of file +current_party(credential) := credential.issuer + +## vc:type +# the type(s) of the current credential +types(verifiable_credential) := verifiable_credential.type \ No newline at end of file