diff --git a/src/main/java/core/JsonUtils.java b/src/main/java/core/JsonUtils.java index 2bc24bde1aea336ed99ea2abf37f700241a11908..2b63d39188e43990999974d9a72a0d674f4f213d 100644 --- a/src/main/java/core/JsonUtils.java +++ b/src/main/java/core/JsonUtils.java @@ -244,6 +244,14 @@ public class JsonUtils { return System.getProperty("baseUrl") + ":8083/v1/cache"; } + /** + * Get the url for "TSA Task" + * @return the uri + */ + public static String getTSATask() { + return System.getProperty("baseUrl") + ":8082/v1"; + } + /** * UTF8 encodes a string * diff --git a/src/main/resources/REST/schemas/Policy_EvaluateDID_negative_schema.json b/src/main/resources/REST/schemas/Policy_EvaluateDID_negative_schema.json index f8694063f96579419c3537036404d7c1f2c375f3..25fe0ec626a05ef9d7af9205348bd12541f4d408 100644 --- a/src/main/resources/REST/schemas/Policy_EvaluateDID_negative_schema.json +++ b/src/main/resources/REST/schemas/Policy_EvaluateDID_negative_schema.json @@ -2,47 +2,59 @@ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { - "data": { + "evaluationID": { + "type": "string" + }, + "result": { "type": "object", "properties": { - "@context": { - "type": "string" - }, - "didDocument": { - "type": "null" - }, - "didDocumentMetadata": { - "type": "object" - }, - "didResolutionMetadata": { + "data": { "type": "object", "properties": { - "contentType": { + "@context": { "type": "string" }, - "error": { - "type": "string" + "didDocument": { + "type": "null" }, - "errorMessage": { - "type": "string" + "didDocumentMetadata": { + "type": "object" + }, + "didResolutionMetadata": { + "type": "object", + "properties": { + "contentType": { + "type": "string" + }, + "error": { + "type": "string" + }, + "errorMessage": { + "type": "string" + } + }, + "required": [ + "contentType", + "error", + "errorMessage" + ] } }, "required": [ - "contentType", - "error", - "errorMessage" + "@context", + "didDocument", + "didDocumentMetadata", + "didResolutionMetadata" ] } }, "required": [ - "@context", - "didDocument", - "didDocumentMetadata", - "didResolutionMetadata" + "data" ] } }, "required": [ - "data" + "evaluationID", + "result" ] } \ No newline at end of file diff --git a/src/main/resources/REST/schemas/Policy_EvaluateDID_schema.json b/src/main/resources/REST/schemas/Policy_EvaluateDID_schema.json index 2e695fe995638b17a726ffc452e94e096f9f6a48..5366333a3446c34403ca7acc6f4c7545e518c2d3 100644 --- a/src/main/resources/REST/schemas/Policy_EvaluateDID_schema.json +++ b/src/main/resources/REST/schemas/Policy_EvaluateDID_schema.json @@ -1,80 +1,110 @@ { - "definitions": {}, - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://example.com/object1655293265.json", - "title": "Root", + "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "required": [ - "data" - ], "properties": { - "data": { - "$id": "#root/data", - "title": "Data", + "evaluationID": { + "type": "string" + }, + "result": { "type": "object", - "required": [ - "@context", - "didDocument", - "didResolutionMetadata" - ], "properties": { - "@context": { - "$id": "#root/data/@context", - "title": "@context", - "type": "string", - "default": "", - "pattern": "^.*$" - }, - "didDocument": { - "$id": "#root/data/didDocument", - "title": "Diddocument", + "data": { "type": "object", - "required": [ - "authentication", - "id", - "verificationMethod" - ], "properties": { - "authentication": { - "$id": "#root/data/didDocument/authentication", - "title": "Authentication", - "type": "array", - "default": [], - "items":{ - "$id": "#root/data/didDocument/authentication/items", - "title": "Items", - "type": "string", - "default": "", - "pattern": "^.*$" - } + "@context": { + "type": "string" }, - "id": { - "$id": "#root/data/didDocument/id", - "title": "Id", - "type": "string", - "default": "", - "pattern": "^.*$" + "didDocument": { + "type": "object", + "properties": { + "authentication": { + "type": "array", + "items": [ + { + "type": "string" + } + ] + }, + "id": { + "type": "string" + }, + "verificationMethod": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "controller": { + "type": "string" + }, + "id": { + "type": "string" + }, + "publicKeyBase58": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "controller", + "id", + "publicKeyBase58", + "type" + ] + } + ] + } + }, + "required": [ + "authentication", + "id", + "verificationMethod" + ] }, - "verificationMethod": { - "$id": "#root/data/didDocument/verificationMethod", - "title": "Verificationmethod", - "type": "array", - "default": [], - "items":{ - "$id": "#root/data/didDocument/verificationMethod/items", - "title": "Items", - "type": "object" - } + "didResolutionMetadata": { + "type": "object", + "properties": { + "did": { + "type": "object", + "properties": { + "didString": { + "type": "string" + }, + "method": { + "type": "string" + }, + "methodSpecificId": { + "type": "string" + } + }, + "required": [ + "didString", + "method", + "methodSpecificId" + ] + } + }, + "required": [ + "did" + ] } - } - } - , - "didResolutionMetadata": { - "$id": "#root/data/didResolutionMetadata", - "title": "Didresolutionmetadata", - "type": "object" + }, + "required": [ + "@context", + "didDocument", + "didResolutionMetadata" + ] } - } + }, + "required": [ + "data" + ] } - } -} + }, + "required": [ + "evaluationID", + "result" + ] +} \ No newline at end of file diff --git a/src/main/resources/REST/schemas/Policy_Evaluate_schema.json b/src/main/resources/REST/schemas/Policy_Evaluate_schema.json index 2ba6dde47d0952c54ea726cc177b2d18ef3157b2..3acde7b9d7b5926fdcd44cdc043980c67725ca8b 100644 --- a/src/main/resources/REST/schemas/Policy_Evaluate_schema.json +++ b/src/main/resources/REST/schemas/Policy_Evaluate_schema.json @@ -1,16 +1,24 @@ { -"$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { - "allow": { - "type": "boolean" - }, - "taskID": { + "evaluationID": { "type": "string" + }, + "result": { + "type": "object", + "properties": { + "allow": { + "type": "boolean" + } + }, + "required": [ + "allow" + ] } }, "required": [ - "allow", - "taskID" + "evaluationID", + "result" ] } \ No newline at end of file diff --git a/src/main/resources/REST/schemas/Task_ExecuteDID_schema.json b/src/main/resources/REST/schemas/Task_ExecuteDID_schema.json new file mode 100644 index 0000000000000000000000000000000000000000..f0ce19fcd55525710d5acbe66d05262ab9efe1dc --- /dev/null +++ b/src/main/resources/REST/schemas/Task_ExecuteDID_schema.json @@ -0,0 +1,33 @@ +{ + "evaluationID": "1f5ba887-8360-46a5-a8af-7a4a8175582d", + "result": { + "doc": { + "@context": "https://w3id.org/did-resolution/v1", + "didDocument": { + "authentication": [ + "did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE#verkey" + ], + "id": "did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE", + "verificationMethod": [ + { + "controller": "did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE", + "id": "did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE#verkey", + "publicKeyBase58": "2HHAKDS8EZK5ZfgZTT7jJFjE5bqkZuFoUgHYAZLjFMbY", + "type": "Ed25519VerificationKey2018" + } + ] + }, + "didResolutionMetadata": { + "contentType": "application/did+ld+json", + "did": { + "didString": "did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE", + "method": "indy", + "methodSpecificId": "idunion:BDrEcHc8Tb4Lb2VyQZWEDE" + }, + "driverUrl": "http://indy-did-driver:8080/1.0/identifiers/$1", + "duration": 708, + "pattern": "^(did:indy:.+)$" + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/REST/schemas/Task_Execute_schema.json b/src/main/resources/REST/schemas/Task_Execute_schema.json new file mode 100644 index 0000000000000000000000000000000000000000..74efa26decc8f1ee385e628ffa75216a0d8dd44f --- /dev/null +++ b/src/main/resources/REST/schemas/Task_Execute_schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "taskID": { + "type": "string" + } + }, + "required": [ + "taskID" + ] +} \ No newline at end of file diff --git a/src/test/java/api/test/rest/tsa/cache/CacheStepDefinitions.java b/src/test/java/api/test/rest/tsa/cache/CacheStepDefinitions.java index b83261988ec1e571b31f41bf0e5690b7bf7a2029..7f854a018d177aa6331668d1bec26f2f575b680f 100644 --- a/src/test/java/api/test/rest/tsa/cache/CacheStepDefinitions.java +++ b/src/test/java/api/test/rest/tsa/cache/CacheStepDefinitions.java @@ -8,7 +8,6 @@ import core.JsonUtils; import core.Request; import core.RestClient; import cucumber.api.java.en.Given; -import cucumber.api.java.en.Then; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/test/java/api/test/rest/tsa/policy/PolicyStepDefinitions.java b/src/test/java/api/test/rest/tsa/policy/PolicyStepDefinitions.java index 866469f94077a5b56647587468fc3f0a577382d5..9886298c38c9c5b14f8f93b29b2b911e0b74497b 100644 --- a/src/test/java/api/test/rest/tsa/policy/PolicyStepDefinitions.java +++ b/src/test/java/api/test/rest/tsa/policy/PolicyStepDefinitions.java @@ -70,8 +70,7 @@ public class PolicyStepDefinitions extends BaseStepDefinitions { .append("locked", false) .append("rego", "package testable."+ policy +"\n" + "default allow = false\n" + - "allow {input.message == \"hello world\"}\n" + - "taskID := \"0123456\""); + "allow {input.message == \"hello world\"}"); policies.insertOne(test); } @@ -87,8 +86,7 @@ public class PolicyStepDefinitions extends BaseStepDefinitions { .append("version", "1.0") .append("locked", false) .append("rego", "package example.resolve\n" + - "data = did.resolve(input.did)\n" + - "taskID := \"01234567\""); + "data = did.resolve(input.did)"); policies.insertOne(test); } diff --git a/src/test/java/api/test/rest/tsa/task/TaskStepDefinitions.java b/src/test/java/api/test/rest/tsa/task/TaskStepDefinitions.java new file mode 100644 index 0000000000000000000000000000000000000000..cd0362daa4230e6f2e2acd66c2b03631f19b2629 --- /dev/null +++ b/src/test/java/api/test/rest/tsa/task/TaskStepDefinitions.java @@ -0,0 +1,78 @@ +package api.test.rest.tsa.task; + +import api.test.core.BaseStepDefinitions; +import api.test.rest.RestGeneralStepDefinitions; +import api.test.rest.RestSessionContainer; +import com.mongodb.MongoException; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.result.DeleteResult; +import core.*; +import cucumber.api.java.en.And; +import cucumber.api.java.en.Given; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bson.Document; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; + +import static api.test.rest.tsa.policy.PolicyStepDefinitions.mongoConnection; +import static com.mongodb.client.model.Filters.eq; + +public class TaskStepDefinitions extends BaseStepDefinitions{ + private static final Logger logger = LogManager.getLogger(RestGeneralStepDefinitions.class.getSimpleName()); + RestSessionContainer restSessionContainer; + Request currentRequest; + + public TaskStepDefinitions(RestSessionContainer restSessionContainer, Request currentRequest, DataContainer dataContainer) { + super(dataContainer); + this.restSessionContainer = restSessionContainer; + this.currentRequest = currentRequest; + } + + @Given("we are testing the TSA Task Api") + public void weAreTestingTheTSATaskApi() { + RestClient.setDefaultEncoding("UTF8"); + RestClient.setBaseURI(JsonUtils.getTSATask()); + RestClient.appendDefaultContentCharsetToContentTypeIfUndefined(false); + currentRequest.clear(); + currentRequest.getHeaders().put("X-Client-UserAgent", "test framework"); + currentRequest.setContentType("application/json"); + } + + @Given("^I upload a task template \\{(.*?)\\} to repository$") + public void iUploadATaskTemplateToRepository(String task) { + MongoClient client = MongoClients.create(mongoConnection); + MongoDatabase database = client.getDatabase("task"); + MongoCollection<Document> policies = database.getCollection("taskTemplates"); + Document test = new Document("_id", new ObjectId()) + .append("name", task) + .append("requestPolicy", "example/resolve/1.0"); + policies.insertOne(test); + } + + @And("^I send the current request to endpoint \\{(.*?)\\} with container value\\{(.*?)\\}$") + public void I_send_the_current_request_to_endpoint_with_container_value(String endpoint, String suffix) throws Throwable { + currentRequest.setPath(endpoint +"/"+ getDataContainer().getObject(suffix)); + + Response response = RestClient.get(currentRequest); + addRequest(currentRequest); + addResponse(response); + } + + @And("^delete task \\{(.*)\\} from repository$") + public void deletePolicyTestFromRepository(String task) { + MongoClient client = MongoClients.create(mongoConnection); + MongoDatabase database = client.getDatabase("task"); + MongoCollection<Document> policies = database.getCollection("taskTemplates"); + Bson query = eq("name", task); + try { + DeleteResult result = policies.deleteOne(query); + System.out.println("Deleted document count: " + result.getDeletedCount()); + } catch (MongoException me) { + System.err.println("Unable to delete due to an error: " + me); + } + } +} diff --git a/src/test/resources/features/tsa/policy/{group}/{name}/{version}/evaluation/POST.feature b/src/test/resources/features/tsa/policy/{group}/{name}/{version}/evaluation/POST.feature index b0c336b88e13245d8a4809fae80d74da16651e1c..d021432d54b5940ee5a8ed8b7483f3a1bbf2dbc1 100644 --- a/src/test/resources/features/tsa/policy/{group}/{name}/{version}/evaluation/POST.feature +++ b/src/test/resources/features/tsa/policy/{group}/{name}/{version}/evaluation/POST.feature @@ -36,8 +36,7 @@ Feature: API -TSA - Policy - :group/:name/:version/evaluation POST And I execute the policy {/testable/test1/2.0/evaluation} Then the status code should be {200} And the response is valid according to the {Policy_Evaluate_schema.json} REST schema - And the field {allow} has the value {true} - And the field {taskID} has the value {0123456} + And the field {result.allow} has the value {true} And delete policy {test1} from repository Scenario: TSA - DID resolution - Positive @@ -46,8 +45,7 @@ Feature: API -TSA - Policy - :group/:name/:version/evaluation POST And I execute the policy {/example/resolve/1.0/evaluation} Then the status code should be {200} And the response is valid according to the {Policy_EvaluateDID_schema.json} REST schema - And the field {data.didDocument.id} has the value {did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE} - And the field {taskID} has the value {01234567} + And the field {result.data.didDocument.id} has the value {did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE} And delete policy {resolve} from repository @negative @@ -57,8 +55,7 @@ Feature: API -TSA - Policy - :group/:name/:version/evaluation POST And I execute the policy {/testable/test6/2.0/evaluation} Then the status code should be {200} And the response is valid according to the {Policy_Evaluate_schema.json} REST schema - And the field {allow} has the value {false} - And the field {taskID} has the value {0123456} + And the field {result.allow} has the value {false} And delete policy {test6} from repository @negative @@ -85,5 +82,5 @@ Feature: API -TSA - Policy - :group/:name/:version/evaluation POST And I execute the policy {/example/resolve/1.0/evaluation} Then the status code should be {200} And the response is valid according to the {Policy_EvaluateDID_negative_schema.json} REST schema - And the field {data.didResolutionMetadata.error} has the value {notFound} + And the field {result.data.didResolutionMetadata.error} has the value {notFound} And delete policy {resolve} from repository diff --git a/src/test/resources/features/tsa/v1/cache/POST.feature b/src/test/resources/features/tsa/v1/cache/POST.feature index 0189919fcf2c17e62a8f2b9e7ab67fd525e5d62a..1b3995ea93447c6b9fe65723a94bc412178a3873 100644 --- a/src/test/resources/features/tsa/v1/cache/POST.feature +++ b/src/test/resources/features/tsa/v1/cache/POST.feature @@ -43,7 +43,7 @@ Feature: API -TSA - Cache - v1/cache POST @negative Scenario: TSA - Access non existing Cache - Negative - And I store key {key_negative} with value {test} in the data container + And I store key {key_negative} with value {NEGATIVE} in the data container And I load object with key {key_negative} from DataContainer into currentRequest HEADER {x-cache-key} Given I send the current request as GET to endpoint {} Then the status code should be {404} diff --git a/src/test/resources/features/tsa/v1/task/{taskName}/POST.feature b/src/test/resources/features/tsa/v1/task/{taskName}/POST.feature new file mode 100644 index 0000000000000000000000000000000000000000..b7117ed2cc73cd5b104245d3e1762e1e644d52ae --- /dev/null +++ b/src/test/resources/features/tsa/v1/task/{taskName}/POST.feature @@ -0,0 +1,48 @@ +#Copyright (c) 2018 Vereign AG [https://www.vereign.com] +# +#This is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as +#published by the Free Software Foundation, either version 3 of the +#License, or (at your option) any later version. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU Affero General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see <http://www.gnu.org/licenses/>. + +#http://localhost:8082/v1/task +#Author: Georgi Michev georgi.michev@vereign.com + +@rest @all @tsa @task +Feature: API -TSA - Task - v1/task POST + As user + I want to evaluate the policy asynchronously + So I am able to execute the developed Rego code in the future non-blocking + + Acceptance criteria: + - HTTP endpoints to evaluate the policy asynchronously and get the result + - example of long-running policy committed to Git repo + - Green test based on example committed to the system + + Background: + Given we are testing the TSA Task Api + + Scenario: TSA - Executing Task with DID resolver - Positive + Given I upload a DID resolver policy to repository + And I upload a task template {didResolve} to repository + Then I load the REST request {Policy.json} with profile {didResolve_evaluate} + And I send the current request as POST to endpoint {task/resolve} + Then the status code should be {200} + And the response is valid according to the {Task_Execute_schema.json} REST schema + Then I get the value of {taskID} from the last response and store it in the DataContainer with key {taskID} + Then I clear the request body + And I wait for {3000} mseconds + And I send the current request to endpoint {taskResult} with container value{taskID} + Then the status code should be {200} + And the response is valid according to the {Task_ExecuteDID_schema.json} REST schema + And the field {result.doc.didDocument.id} has the value {did:indy:idunion:BDrEcHc8Tb4Lb2VyQZWEDE} + Then delete task {didResolve} from repository + And delete policy {resolve} from repository