Skip to content

Commit

Permalink
Consumer API: Password-protected Tokens (#909)
Browse files Browse the repository at this point in the history
* feat: implement password protected tokens

* chore: remove unused methods

* chore: remove empty line

* test: add tests for anonymous user fetching tokens

* fix: add check for existing allocations to relationship template CanBeCollectedWithPassword expression

* chore: add explaining comments to password checks in Token.cs

* refactor: don't use queryable extensions; instead use expressions in entity

* refactor: remove redundant "when not null" check from validator

* refactor: rename things

* fix: re-add check for "password is not null" to validators

* test: add can be collected with password tests

* test: check for status code in get multiple templates feature file

* test: check for status code in get multiple tokens feature file

* fix: don't throw ArgumentNullException in ReplaceExpressionVisitor

* test: fix naming

* fix: use correct error message in validator tests

* fix: use correct error message in CreateToken validator tests

* test: add tests for anonymous user

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Timo Notheisen <[email protected]>
  • Loading branch information
3 people authored Oct 16, 2024
1 parent de93171 commit ddcf3e1
Show file tree
Hide file tree
Showing 50 changed files with 748 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ User requests Relationship Templates
| rt12 | i2 | i3 | - |
| rt13 | i2 | i3 | password |
| rt14 | i2 | i3 | password |
When <activeIdentity> sends a GET request to the /RelationshipTemplate endpoint with the following payloads
When <activeIdentity> sends a GET request to the /RelationshipTemplates endpoint with the following payloads
| templateName | passwordOnGet |
| rt1 | - |
| rt2 | - |
Expand All @@ -37,7 +37,8 @@ User requests Relationship Templates
| rt12 | - |
| rt13 | password |
| rt14 | wordpass |
Then the response contains Relationship Template(s) <retreivedTemplates>
Then the response status code is 200 (OK)
And the response contains Relationship Template(s) <retreivedTemplates>

Examples:
| activeIdentity | retreivedTemplates |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ User creates a Relationship Template

Scenario: Create a personalized Relationship Template with a password
Given Identities i1 and i2
When i1 sends a POST request to the /RelationshipTemplate endpoint with password "my-password" and forIdentity i2
When i1 sends a POST request to the /RelationshipTemplates endpoint with password "my-password" and forIdentity i2
Then the response status code is 201 (Created)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ User requests a Relationship Template
Scenario Outline: Requesting a Relationship Template in a variety of scenarios
Given Identities <givenIdentities>
And Relationship Template rt created by <templateOwner> with password "<password>" and forIdentity <forIdentity>
When <activeIdentity> sends a GET request to the /RelationshipTemplate/rt.Id endpoint with password "<passwordOnGet>"
When <activeIdentity> sends a GET request to the /RelationshipTemplates/rt.Id endpoint with password "<passwordOnGet>"
Then the response status code is <responseStatusCode>

Examples:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,46 @@ Feature: GET /Tokens

User requests multiple Tokens

Scenario: Requesting a list of own Tokens
Given Identity i
And Tokens t1 and t2 belonging to i
When i sends a GET request to the /Tokens endpoint with the ids of t1 and t2
Scenario Outline: Requesting a list of Tokens in a variety of scenarios
Given Identities i1, i2, i3 and i4
And the following Tokens
| tokenName | tokenOwner | forIdentity | password |
| rt1 | i1 | - | - |
| rt2 | i2 | - | - |
| rt3 | i1 | - | - |
| rt4 | i2 | - | - |
| rt5 | i1 | - | password |
| rt6 | i1 | - | password |
| rt7 | i2 | - | password |
| rt8 | i2 | - | password |
| rt9 | i1 | i1 | - |
| rt10 | i2 | i3 | - |
| rt11 | i2 | i2 | - |
| rt12 | i2 | i3 | - |
| rt13 | i2 | i3 | password |
| rt14 | i2 | i3 | password |
When <activeIdentity> sends a GET request to the /Tokens endpoint with the following payloads
| tokenName | passwordOnGet |
| rt1 | - |
| rt2 | - |
| rt3 | password |
| rt4 | password |
| rt5 | password |
| rt6 | - |
| rt7 | password |
| rt8 | - |
| rt9 | - |
| rt10 | - |
| rt11 | - |
| rt12 | - |
| rt13 | password |
| rt14 | wordpass |
Then the response status code is 200 (OK)
And the response contains the Tokens t1 and t2
And the response contains Token(s) <retreivedTokens>

Scenario: Requesting an own Token and a Token belonging to another identity
Given Identities i1 and i2
And Token t1 belonging to i1
And Token t2 belonging to i2
When i1 sends a GET request to the /Tokens endpoint with the ids of t1 and t2
Then the response status code is 200 (OK)
And the response contains the Tokens t1 and t2

Scenario: Requesting a list of Tokens contains tokens with ForIdentity which were created by me
Given Identities i1 and i2
And Token t belonging to i1 where ForIdentity is the address of i2
When i1 sends a GET request to the /Tokens endpoint with the ids of t
Then the response status code is 200 (Ok)
And the response contains the Token t

Scenario: Requesting a list of Tokens contains tokens with ForIdentity which were created for me
Given Identities i1 and i2
And Token t belonging to i1 where ForIdentity is the address of i2
When i2 sends a GET request to the /Tokens endpoint with the ids of t
Then the response status code is 200 (Ok)
And the response contains the Token t

Scenario: Requesting a list of Tokens does not contain tokens with ForIdentity which were created for someone else
Given Identities i1, i2 and i3
And Token t belonging to i1 where ForIdentity is the address of i2
When i3 sends a GET request to the /Tokens endpoint with the ids of t
Then the response does not contain the Token t
Examples:
| activeIdentity | retreivedTokens |
| i1 | rt1, rt2, rt3, rt4, rt5, rt6, rt7, rt9 |
| i2 | rt1, rt2, rt3, rt4, rt5, rt7, rt8, rt10, rt11, rt12, rt13, rt14 |
| i3 | rt1, rt2, rt3, rt4, rt5, rt7, rt10, rt12, rt13 |
| i4 | rt1, rt2, rt3, rt4, rt5, rt7 |
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,15 @@ User creates a Token
Scenario: Creating a Token as an anonymous user
When an anonymous user sends a POST request to the /Tokens endpoint
Then the response status code is 401 (Unauthorized)

Scenario: Creating a Token with a password
Given Identity i
When i sends a POST request to the /Tokens endpoint with the password "password"
Then the response status code is 201 (Created)
And the response contains a CreateTokenResponse

Scenario: Create a personalized Token with a password
Given Identities i1 and i2
When i1 sends a POST request to the /Tokens endpoint with password "password" and forIdentity i2
Then the response status code is 201 (Created)
And the response contains a CreateTokenResponse
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,33 @@ Feature: GET /Tokens/{id}

User requests a Token

Scenario: Requesting an own Token as an authenticated user
Given Identity i
And Token t belonging to i
When i sends a GET request to the /Tokens/{id} endpoint with t.Id
Then the response status code is 200 (OK)
And the response contains a Token
Scenario Outline: Requesting a Token in a variety of scenarios
Given Identities <givenIdentities>
And Token t created by <tokenOwner> with password "<password>" and forIdentity <forIdentity>
When <activeIdentity> sends a GET request to the /Tokens/t.Id endpoint with password "<passwordOnGet>"
Then the response status code is <responseStatusCode>

Scenario: Requesting an own Token as an anonymous user
Given Identity i
And Token t belonging to i
When an anonymous user sends a GET request to the /Tokens/{id} endpoint with t.Id
Then the response status code is 200 (OK)
And the response contains a Token

Scenario: Requesting a Token of another Identity as an authenticated user
Given Identities i1 and i2
And Token t belonging to i2
When i1 sends a GET request to the /Tokens/{id} endpoint with t.Id
Then the response status code is 200 (OK)
And the response contains a Token

Scenario: Requesting a nonexistent Token
Given Identity i
When i sends a GET request to the /Tokens/{id} endpoint with "TOKthisisnonexisting"
Then the response status code is 404 (Not Found)
And the response content contains an error with the error code "error.platform.recordNotFound"
Examples:
| givenIdentities | tokenOwner | forIdentity | password | activeIdentity | passwordOnGet | responseStatusCode | description |
| i | i | - | - | i | - | 200 (OK) | owner tries to get |
| i1 and i2 | i1 | - | - | i2 | - | 200 (OK) | non-owner tries to get |
| i | i | - | - | - | - | 200 (OK) | anonymous user tries to get |
| i | i | - | - | i | password | 200 (OK) | owner passes password even though none is set |
| i1 and i2 | i1 | - | - | i2 | password | 200 (OK) | non-owner identity passes password even though none is set |
| i | i | - | - | - | password | 200 (OK) | anonymous user passes password even though none is set |
| i | i | - | password | i | password | 200 (OK) | owner passes correct password |
| i | i | - | password | i | - | 200 (OK) | owner doesn't pass password, even though one is set |
| i1 and i2 | i1 | - | password | i2 | password | 200 (OK) | non-owner identity passes correct password |
| i1 and i2 | i1 | - | password | i2 | - | 404 (Not Found) | non-owner identity passes no password even though one is set |
| i | i | - | password | - | password | 200 (OK) | anonymous user passes correct password |
| i | i | - | password | - | - | 404 (Not Found) | anonymous user doesn't pass password, even though one is set |
| i | i | i | - | i | - | 200 (OK) | owner is forIdentity and tries to get |
| i1 and i2 | i1 | i2 | - | i1 | - | 200 (OK) | non-owner is forIdentity, creator tries to get |
| i1 and i2 | i1 | i1 | - | i2 | - | 404 (Not Found) | owner is forIdentity and non-owner tries to get |
| i | i | i | - | - | - | 404 (Not Found) | owner is forIdentity and anonymous user tries to get |
| i1 and i2 | i1 | i2 | - | i2 | - | 200 (OK) | non-owner is forIdentity and tries to get |
| i | i | i | - | - | - | 404 (Not Found) | forIdentity is set and anonymous user tries to get |
| i1 and i2 | i1 | i2 | password | i2 | password | 200 (OK) | non-owner is forIdentity and tries to get with correct password |
| i1 and i2 | i1 | i2 | password | i2 | wordpass | 404 (Not Found) | non-owner is forIdentity and tries to get with incorrect password |
| i1, i2 and i3 | i1 | i2 | password | i3 | password | 404 (Not Found) | non-owner is forIdentity, and thirdParty tries to get |
| i1 and i2 | i1 | i2 | password | - | password | 404 (Not Found) | non-owner is forIdentity, and anonymous user tries to get |
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public async Task GivenRelationshipTemplateCreatedByTokenOwnerWithPasswordAndFor
}

[Given(@"the following Relationship Templates")]
public async Task GivenRelationshipTemplatesWithTheFollowingProperties(Table table)
public async Task GivenTheFollowingRelationshipTemplates(Table table)
{
var relationshipTemplatePropertiesSet = table.CreateSet<RelationshipTemplateProperties>();

Expand Down Expand Up @@ -85,7 +85,7 @@ public async Task WhenIdentitySendsAPostRequestToTheRelationshipTemplatesEndpoin
new CreateRelationshipTemplateRequest { Content = TestData.SOME_BYTES, Password = password });
}

[When($@"{RegexFor.SINGLE_THING} sends a POST request to the /RelationshipTemplate endpoint with password ""(.*)"" and forIdentity {RegexFor.SINGLE_THING}")]
[When($@"{RegexFor.SINGLE_THING} sends a POST request to the /RelationshipTemplates endpoint with password ""(.*)"" and forIdentity {RegexFor.SINGLE_THING}")]
public async Task WhenIdentitySendsAPostRequestToTheRelationshipTemplatesEndpointWithPasswordAndForIdentity(string identityName, string passwordString, string forIdentityName)
{
var client = _clientPool.FirstForIdentityName(identityName);
Expand All @@ -96,7 +96,7 @@ public async Task WhenIdentitySendsAPostRequestToTheRelationshipTemplatesEndpoin
new CreateRelationshipTemplateRequest { Content = TestData.SOME_BYTES, ForIdentity = forClient.IdentityData!.Address, Password = password });
}

[When($@"{RegexFor.SINGLE_THING} sends a GET request to the /RelationshipTemplate/{RegexFor.SINGLE_THING}.Id endpoint with password ""([^""]*)""")]
[When($@"{RegexFor.SINGLE_THING} sends a GET request to the /RelationshipTemplates/{RegexFor.SINGLE_THING}.Id endpoint with password ""([^""]*)""")]
public async Task WhenIdentitySendsAGetRequestToTheRelationshipTemplatesIdEndpointWithPassword(string identityName, string relationshipTemplateName, string password)
{
var client = _clientPool.FirstForIdentityName(identityName);
Expand All @@ -108,8 +108,8 @@ public async Task WhenIdentitySendsAGetRequestToTheRelationshipTemplatesIdEndpoi
: await client.RelationshipTemplates.GetTemplate(relationshipTemplateId);
}

[When($@"{RegexFor.SINGLE_THING} sends a GET request to the /RelationshipTemplate endpoint with the following payloads")]
public async Task WhenISendsAGETRequestToTheRelationshipTemplateEndpointWithTheFollowingPayloads(string identityName, Table table)
[When($@"{RegexFor.SINGLE_THING} sends a GET request to the /RelationshipTemplates endpoint with the following payloads")]
public async Task WhenISendsAGETRequestToTheRelationshipTemplatesEndpointWithTheFollowingPayloads(string identityName, Table table)
{
var client = _clientPool.FirstForIdentityName(identityName);

Expand All @@ -120,7 +120,7 @@ public async Task WhenISendsAGETRequestToTheRelationshipTemplateEndpointWithTheF
var relationshipTemplateId = _relationshipTemplatesContext.CreateRelationshipTemplatesResponses[payload.TemplateName].Id;
var password = payload.PasswordOnGet == "-" ? null : Convert.FromBase64String(payload.PasswordOnGet.Trim());

return new RelationshipTemplateQueryItem { Id = relationshipTemplateId, Password = password };
return new ListRelationshipTemplatesQueryItem { Id = relationshipTemplateId, Password = password };
}).ToList();

_responseContext.WhenResponse = _listRelationshipTemplatesResponse = await client.RelationshipTemplates.ListTemplates(queryItems);
Expand Down
Loading

0 comments on commit ddcf3e1

Please sign in to comment.