Skip to content
Snippets Groups Projects
Commit 56d1316f authored by Georgi Michev's avatar Georgi Michev
Browse files

Merge branch 'credential-proof-tests' into 'main'

Add credential verify proof tests

See merge request !31
parents 8ea93bc3 b9384b2c
No related branches found
No related tags found
1 merge request!31Add credential verify proof tests
Pipeline #53185 passed with stage
in 4 minutes and 9 seconds
Showing
with 567 additions and 100 deletions
......@@ -2,26 +2,78 @@
"for_proof":{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/jws-2020/v1"
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"type": "VerifiablePresentation",
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/jws-2020/v1",
"https://schema.org"
],
"credentialSubject": {
"allow": true,
"id": "https://gaiax.vereign.com/tsa/policy/example/examplePolicy/1.0"
},
"issuanceDate": "2022-07-01T11:07:57.975459274Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"type": "VerifiableCredential"
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"issuanceDate": "2010-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"allow": true,
"num": 123,
"array": {
"txt1": "1",
"txt2": "2"
}
]
}
},
"for_proof_alumni": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"issuanceDate": "2010-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science and Arts"
}
}
},
"for_proof_without_ID": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"issuanceDate": "2010-01-01T00:00:00Z",
"credentialSubject": {
"allow": true,
"num": 123,
"array": {
"txt1": "1",
"txt2": "2"
}
}
},
"for_proof_incorrect_ID_space":{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"id": "http://example.edu/credentials/3732",
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"issuanceDate": "2010-01-01T00:00:00Z",
"credentialSubject": {
"id": " ",
"allow": true,
"num": 123,
"array": {
"txt1": "1",
"txt2": "2"
}
}
},
"incorrect_id":{
"@context": [
......@@ -46,5 +98,148 @@
"type": "VerifiableCredential"
}
]
},
"missing_proof": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"credentialSubject": {
"allow": true,
"array": {
"txt1": "1",
"txt2": "2"
},
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"num": 123
},
"id": "http://example.edu/credentials/3732",
"issuanceDate": "2010-01-01T00:00:00Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
},
"modified_ID": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"credentialSubject": {
"allow": true,
"array": {
"txt1": "1",
"txt2": "2"
},
"id": "did:web:gaiax.XXXX.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"num": 123
},
"id": "http://example.edu/credentials/3732",
"issuanceDate": "2010-01-01T00:00:00Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"proof": {
"created": "2022-08-04T15:26:30.176376203Z",
"jws": "eyJhbGciOiJKc29uV2ViU2lnbmF0dXJlMjAyMCIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MEQCIDq0c_zVTJeQbBLEPENCiJYQ1YTm4qI4mFVNhi_t5c1bAiABJacTO6fYXfNA4N0WqHhyEAl5F3BqwfPoc3R7iX2c9Q",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1"
},
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
},
"modified_issuanceDate": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"credentialSubject": {
"allow": true,
"array": {
"txt1": "1",
"txt2": "2"
},
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"num": 123
},
"id": "http://example.edu/credentials/3732",
"issuanceDate": "2020-01-01T00:00:00Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"proof": {
"created": "2022-08-04T15:26:30.176376203Z",
"jws": "eyJhbGciOiJKc29uV2ViU2lnbmF0dXJlMjAyMCIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MEQCIDq0c_zVTJeQbBLEPENCiJYQ1YTm4qI4mFVNhi_t5c1bAiABJacTO6fYXfNA4N0WqHhyEAl5F3BqwfPoc3R7iX2c9Q",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1"
},
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
},
"modified_credentialSubject_allow": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"credentialSubject": {
"allow": false,
"array": {
"txt1": "1",
"txt2": "2"
},
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"num": 123
},
"id": "http://example.edu/credentials/3732",
"issuanceDate": "2010-01-01T00:00:00Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"proof": {
"created": "2022-08-04T15:26:30.176376203Z",
"jws": "eyJhbGciOiJKc29uV2ViU2lnbmF0dXJlMjAyMCIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MEQCIDq0c_zVTJeQbBLEPENCiJYQ1YTm4qI4mFVNhi_t5c1bAiABJacTO6fYXfNA4N0WqHhyEAl5F3BqwfPoc3R7iX2c9Q",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1"
},
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
},
"modified_credentialSubject_array": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://www.schema.org"
],
"credentialSubject": {
"allow": true,
"array": {
"txt1": "I",
"txt2": "2"
},
"id": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"num": 123
},
"id": "http://example.edu/credentials/3732",
"issuanceDate": "2010-01-01T00:00:00Z",
"issuer": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation",
"proof": {
"created": "2022-08-04T15:26:30.176376203Z",
"jws": "eyJhbGciOiJKc29uV2ViU2lnbmF0dXJlMjAyMCIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MEQCIDq0c_zVTJeQbBLEPENCiJYQ1YTm4qI4mFVNhi_t5c1bAiABJacTO6fYXfNA4N0WqHhyEAl5F3BqwfPoc3R7iX2c9Q",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1"
},
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}
}
\ No newline at end of file
......@@ -69,7 +69,7 @@
}
}]
},
"for_proof_missing_ID": {
"for_proof_without_ID": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://w3id.org/security/suites/jws-2020/v1"
......
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"@context": {
"type": "array",
"items": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"credentialSubject": {
"type": "object",
"properties": {
"degree": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": [
"name",
"type"
]
},
"id": {
"type": "string"
}
},
"required": [
"degree",
"id"
]
},
"id": {
"type": "string"
},
"issuanceDate": {
"type": "string"
},
"issuer": {
"type": "string"
},
"proof": {
"type": "object",
"properties": {
"created": {
"type": "string"
},
"jws": {
"type": "string"
},
"proofPurpose": {
"type": "string"
},
"type": {
"type": "string"
},
"verificationMethod": {
"type": "string"
}
},
"required": [
"created",
"jws",
"proofPurpose",
"type",
"verificationMethod"
]
},
"type": {
"type": "array",
"items": [
{
"type": "string"
},
{
"type": "string"
}
]
}
},
"required": [
"@context",
"credentialSubject",
"id",
"issuanceDate",
"issuer",
"proof",
"type"
]
}
\ No newline at end of file
......@@ -8,17 +8,55 @@
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
}
]
},
"credentialSubject": {
"type": "null"
"type": "object",
"properties": {
"allow": {
"type": "boolean"
},
"array": {
"type": "object",
"properties": {
"txt1": {
"type": "string"
},
"txt2": {
"type": "string"
}
},
"required": [
"txt1",
"txt2"
]
},
"id": {
"type": "string"
},
"num": {
"type": "integer"
}
},
"required": [
"allow",
"array",
"id",
"num"
]
},
"id": {
"type": "string"
},
"issuanceDate": {
"type": "string"
},
"issuer": {
"type": "string"
},
......@@ -50,60 +88,13 @@
]
},
"type": {
"type": "string"
},
"verifiableCredential": {
"type": "array",
"items": [
{
"type": "object",
"properties": {
"@context": {
"type": "array",
"items": [
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
}
]
},
"credentialSubject": {
"type": "object",
"properties": {
"allow": {
"type": "boolean"
},
"id": {
"type": "string"
}
},
"required": [
"allow",
"id"
]
},
"issuanceDate": {
"type": "string"
},
"issuer": {
"type": "string"
},
"type": {
"type": "string"
}
},
"required": [
"@context",
"credentialSubject",
"issuanceDate",
"issuer",
"type"
]
"type": "string"
},
{
"type": "string"
}
]
}
......@@ -112,9 +103,9 @@
"@context",
"credentialSubject",
"id",
"issuanceDate",
"issuer",
"proof",
"type",
"verifiableCredential"
"type"
]
}
\ No newline at end of file
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"@context": {
"type": "array",
"items": [
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
}
]
},
"id": {
"type": "string"
},
"type": {
"type": "array",
"items": [
{
"type": "string"
},
{
"type": "string"
}
]
},
"issuer": {
"type": "string"
},
"issuanceDate": {
"type": "string"
},
"credentialSubject": {
"type": "object",
"properties": {
"allow": {
"type": "boolean"
},
"num": {
"type": "integer"
},
"array": {
"type": "object",
"properties": {
"txt1": {
"type": "string"
},
"txt2": {
"type": "string"
}
},
"required": [
"txt1",
"txt2"
]
}
},
"required": [
"allow",
"num",
"array"
]
}
},
"required": [
"@context",
"id",
"type",
"issuer",
"issuanceDate",
"credentialSubject"
]
}
\ No newline at end of file
......@@ -67,4 +67,12 @@ public class SignerStepDefinitions extends BaseStepDefinitions {
addRequest(currentRequest);
addResponse(response);
}
@When("I verify credential proof via TSA Signer API")
public void iVerifyCredentialProofViaTSASignerAPI() {
currentRequest.setPath("/v1/credential/verify");
Response response = RestClient.post(currentRequest);
addRequest(currentRequest);
addResponse(response);
}
}
......@@ -27,14 +27,35 @@ Feature: API - TSA - Signer credential proof - v1/credential/proof POST
Scenario: TSA - create credential proof - Positive
When I load the REST request {Credential.json} with profile {for_proof}
When I create credential proof via TSA Signer API
And the status code should be {200}
And I create credential proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_CredentialProof_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
Scenario: TSA - create credential proof alumni of - Positive
When I load the REST request {Credential.json} with profile {for_proof_alumni}
And I create credential proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_CredentialProof_alumni_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
Scenario: TSA - create credential proof without ID - Positive
When I load the REST request {Credential.json} with profile {for_proof_without_ID}
And I create credential proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_CredentialProof_without_ID_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
@negative
Scenario: TSA - validate credential proof with empty body - Negative
Scenario: TSA - create credential proof with empty body - Negative
When I set the following request body {{}}
When I create credential proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {build new credential: fill credential types from raw: credential type of unknown structure}
And I create credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {credential type of unknown structure}
@negative
Scenario: TSA - create credential proof with incorrect ID (space)- Negative
When I load the REST request {Credential.json} with profile {for_proof_incorrect_ID_space}
And I create credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} has the value {invalid format of subject id}
#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/>.
#https://gaiax.vereign.com/tsa/signer/v1/credential/verify
#Author: Georgi Michev georgi.michev@vereign.com
@rest @all @tsa @signer
Feature: API - TSA - Signer credential verify - v1/credential/verify POST
As user
I want to verify a credential
So I know it is valid
Background:
Given we are testing the TSA Signer Api
Scenario: TSA - verify credential proof - Positive
When I load the REST request {Credential.json} with profile {for_proof}
And I create credential proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_CredentialProof_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
Then I get the last response body and load it to the current request body
And I verify credential proof via TSA Signer API
And the status code should be {200}
And the field {valid} has the value {true}
@negative
Scenario: TSA - verify credential proof with missing proof - Negative
When I load the REST request {Credential.json} with profile {missing_proof}
And I verify credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {verifiable credential must have proof section}
@negative
Scenario: TSA - verify credential proof with missing proof - Negative
When I load the REST request {Credential.json} with profile {modified_ID}
And I verify credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify credential proof with modified issuanceDate field - Negative
When I load the REST request {Credential.json} with profile {modified_issuanceDate}
And I verify credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify credential proof with modified issuanceDate field - Negative
When I load the REST request {Credential.json} with profile {modified_credentialSubject_allow}
And I verify credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify credential proof with modified issuanceDate field - Negative
When I load the REST request {Credential.json} with profile {modified_credentialSubject_array}
And I verify credential proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {invalid signature}
......@@ -27,40 +27,41 @@ Feature: API - TSA - Signer presentation proof - v1/presentation/proof POST
Scenario: TSA - create presentation proof - Positive
When I load the REST request {Presentation.json} with profile {for_proof}
When I create presentation proof via TSA Signer API
And the status code should be {200}
And I create presentation proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_PresentationProof_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
Scenario: TSA - create presentation proof - Positive
Scenario: TSA - create presentation proof alumni of - Positive
When I load the REST request {Presentation.json} with profile {for_proof_alumni}
When I create presentation proof via TSA Signer API
And the status code should be {200}
And I create presentation proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_PresentationProof_schema_alumni.json} REST schema
Scenario: TSA - create presentation proof missing ID - Positive
When I load the REST request {Presentation.json} with profile {for_proof_missing_ID}
When I create presentation proof via TSA Signer API
And the status code should be {200}
Scenario: TSA - create presentation proof without ID - Positive
When I load the REST request {Presentation.json} with profile {for_proof_without_ID}
And I create presentation proof via TSA Signer API
Then the status code should be {200}
And the response is valid according to the {Signer_PresentationProof_schema.json} REST schema
And the field {proof.verificationMethod} has the value {did:web:gaiax.vereign.com:tsa:policy:policy:example:returnDID:1.0:evaluation#key1}
@negative
Scenario: TSA - validate presentation proof with empty body - Negative
When I set the following request body {{}}
When I create presentation proof via TSA Signer API
And the status code should be {400}
And I create presentation proof via TSA Signer API
Then the status code should be {400}
And the field {message} contains the value {verifiable presentation is not valid}
@negative
Scenario: TSA - create presentation proof with incorrect ID (space) - Negative
When I load the REST request {Presentation.json} with profile {for_proof_incorrect_ID_space}
When I create presentation proof via TSA Signer API
And the status code should be {400}
And I create presentation proof via TSA Signer API
Then the status code should be {400}
And the field {message} has the value {invalid format of subject id}
@negative
Scenario: TSA - create presentation proof with incorrect ID (symbols) - Negative
When I load the REST request {Presentation.json} with profile {for_proof_incorrect_ID_symbols}
When I create presentation proof via TSA Signer API
And the status code should be {400}
And I create presentation proof via TSA Signer API
Then the status code should be {400}
And the field {message} has the value {invalid format of subject id}
......@@ -48,39 +48,39 @@ Feature: API - TSA - Signer presentation verify - v1/presentation/verify POST
When I load the REST request {Presentation.json} with profile {modified_ID}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify presentation proof with modified issuanceDate field - Negative
When I load the REST request {Presentation.json} with profile {modified_issuanceDate}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify presentation proof with modified first credentialSubject.allow field - Negative
When I load the REST request {Presentation.json} with profile {modified_credentialSubject_1_allow}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify presentation proof with modified second credentialSubject.allow field - Negative
When I load the REST request {Presentation.json} with profile {modified_credentialSubject_2_allow}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify presentation proof with modified first credentialSubject.ID field - Negative
When I load the REST request {Presentation.json} with profile {modified_credentialSubject_1_ID}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
@negative
Scenario: TSA - verify presentation proof with modified second credentialSubject.ID field - Negative
When I load the REST request {Presentation.json} with profile {modified_credentialSubject_2_ID}
And I verify presentation proof via TSA Signer API
And the status code should be {400}
And the field {message} has the value {check embedded proof: check linked data proof: ecdsa: invalid signature}
And the field {message} contains the value {invalid signature}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment