Skip to content

Commit

Permalink
Merge pull request #143 from ember-nexus/github-issue/121
Browse files Browse the repository at this point in the history
GitHub issue/121
  • Loading branch information
Syndesi authored Sep 29, 2023
2 parents 8a60ed9 + 5859ed8 commit 13bb783
Show file tree
Hide file tree
Showing 18 changed files with 730 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ APP_SECRET=f762ce98c98b7579d2d6a6c0e5eb1d39
###< symfony/framework-bundle ###

VERSION=dev
ANONYMOUS_USER_UUID=1f875943-5a12-42bb-b093-1826a8e1a34d
ANONYMOUS_USER_UUID=2d376349-c5e2-42c8-8ce0-d6f525256cf7
CYPHER_AUTH=bolt://neo4j:password@ember-nexus-neo4j
MONGO_AUTH=mongodb://mongodb:password@ember-nexus-mongodb:27017
ELASTIC_AUTH=ember-nexus-elasticsearch:9200
REDIS_AUTH=tcp://ember-nexus-redis?password=redis-password
RABBITMQ_AUTH=amqp://user:password@ember-nexus-rabbitmq:5672

REFERENCE_DATASET_VERSION=0.0.10
REFERENCE_DATASET_VERSION=0.0.12
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ CYPHER_AUTH=bolt://neo4j:password@ember-nexus-neo4j
ELASTIC_AUTH=ember-nexus-elasticsearch:9200
MONGO_AUTH=mongodb://mongodb:password@ember-nexus-mongodb:27017
REDIS_AUTH=tcp://ember-nexus-redis?password=redis-password
ANONYMOUS_USER_UUID=0c891ecf-46e2-4e1b-b030-2bc48a744f8b
ANONYMOUS_USER_UUID=2d376349-c5e2-42c8-8ce0-d6f525256cf7
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- Add POST /change-password endpoint, including tests and documentation. Closes #121.
### Changed
- Increase reference dataset version to 0.0.11, skipped 0.0.10.
- Change default anonymous user to `2d376349-c5e2-42c8-8ce0-d6f525256cf7`.
### Removed
- Test command, closes #138.
### Fixed
- Backup will only load JSON files, before that every file was tried to be loaded.

## 0.0.31 - 2023-09-26
### Added
- Add healthcheck command.
- Add healthcheck command in Docker image, closes [#106].
- Add feature test to test that tokens not owned by any user are invalid, closes [#118].
- Add feature test to test that tokens owned by two or more users are invalid as well, closes [#118].
### Changed
- Increase reference dataset version to 0.0.9.

### Changed
- Upgrade MongoDB version from 6.x to 7.x in Docker Compose examples.
Expand Down
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

- **User Endpoints**
- [<span class="method-post">POST</span>` /register -` Register New Account](/api-endpoints/user/post-register)
- [<span class="method-post">POST</span>` /change-password -` Change Password](/api-endpoints/user/post-change-password)
- [<span class="method-post">POST</span>` /token -` Create Token](/api-endpoints/user/post-token)
- [<span class="method-get">GET</span>` /token -` Get Token](/api-endpoints/user/get-token)
- [<span class="method-delete">DELETE</span>` /token -` Delete Token](/api-endpoints/user/delete-token)
Expand Down
198 changes: 198 additions & 0 deletions docs/api-endpoints/user/post-change-password.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# <span class="method-post">POST</span>` /change-password -` Change Password

<!-- panels:start -->
<!-- div:left-panel -->

Endpoint allows changing the user's current password.

!> This endpoint requires knowing the currently used password.

!> Using auth tokens for this endpoint is optional; they are just used for rate limiting here.

## Request Body

The posted request must be a valid JSON document.

The request must contain the following attributes:

- `type`: Contains the value `ActionChangePassword`. No other types are currently allowed.
- `currentPassword`: The user's currently used password in plain text.
- `newPassword`: The user's new password in plain text.
- `data.<identifier>`: By default, `data.email` must contain a new unique string. While not required, keeping the
content within 256 bytes is encouraged. Optional limits might be added at a later time.
The required identifier name is returned by the
[instance configuration endpoint](/api-endpoints/get-instance-configuration) and in error messages.

!> **Notice regarding passwords**: They can contain any string and will be hashed internally. Whitespace at the start or
end of the password will **not** be removed, though it is discouraged. No password complexity checks are performed.

```json
{
"type": "ActionChangePassword",
"currentPassword": "1234",
"newPassword": "9876",
"data": {
"<identifier>": "[email protected]"
}
}
```

## Request Example

```bash
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"type": "ActionChangePassword", "currentPassword": "1234", "newPassword": "9876", "data": {"email": "[email protected]"}}' \
https://api.localhost/change-password
```

<!-- tabs:start -->

### **🟢 Success 204**

<div class="code-title auto-refresh">Response Headers</div>

[Response Body](./post-change-password/204-response-header.txt ':include :type=code')

Success response does not have a return body. The password is successfully changed, and creating new tokens require the
new password.

### **🔴 Error 400**

<div class="code-title auto-refresh">Response Headers</div>

[Response Body](./post-change-password/400-response-header.txt ':include :type=code')

<div class="code-title auto-refresh">Response Body</div>

[Response Body](./post-change-password/400-response-body.json ':include :type=code problem+json')

### **🔴 Error 401**

<div class="code-title auto-refresh">Response Headers</div>

[Response Body](./post-change-password/401-response-header.txt ':include :type=code')

<div class="code-title auto-refresh">Response Body</div>

[Response Body](./post-change-password/401-response-body.json ':include :type=code problem+json')

### **🔴 Error 403**

<div class="code-title auto-refresh">Response Headers</div>

[Response Body](./post-change-password/403-response-header.txt ':include :type=code')

<div class="code-title auto-refresh">Response Body</div>

[Response Body](./post-change-password/403-response-body.json ':include :type=code problem+json')

### **🔴 Error 429**

<div class="code-title">Response Headers</div>

[Response Body](./post-change-password/429-response-header.txt ':include :type=code')

<div class="code-title">Response Body</div>

[Response Body](./post-change-password/429-response-body.json ':include :type=code problem+json')

<!-- tabs:end -->

<!-- div:right-panel -->

## Internal Workflow

Once the server receives such a request, it checks several things internally:

<div id="graph-container-1" class="graph-container" style="height:1400px"></div>

<!-- panels:end -->

<script>
G6.registerEdge('polyline-edge', {
draw(cfg, group) {
const { startPoint, endPoint } = cfg;
const hgap = Math.abs(endPoint.x - startPoint.x);

const path = [
['M', startPoint.x, startPoint.y],
[
'C',
startPoint.x + hgap / 4,
startPoint.y,
endPoint.x - hgap / 2,
endPoint.y,
endPoint.x,
endPoint.y,
],
];
const shape = group.addShape('path', {
attrs: {
stroke: '#AAB7C4',
path,
},
name: 'path-shape',
});
const midPoint = {
x: (startPoint.x + endPoint.x) / 2,
y: (startPoint.y + endPoint.y) / 2,
};
const label = group.addShape('text', {
attrs: {
text: cfg.label + '###########',
x: midPoint.x,
y: midPoint.y,
textAlign: 'center',
textBaseline: 'middle',
fill: '#000',
fontSize: 14,
},
name: 'label-shape',
});
return shape;
},
});
renderWorkflow(document.getElementById('graph-container-1'), {
nodes: [
{ id: 'init', ...workflowStart, label: 'server receives POST-request' },
{ id: 'checkType', ...workflowDecision, label: 'is type given?' },
{ id: 'checkTypeContent', ...workflowDecision, label: "is type equal to\n\"ActionChangePassword\"?" },
{ id: 'checkCurrentPassword', ...workflowDecision, label: "is currentPassword given?" },
{ id: 'checkNewPassword', ...workflowDecision, label: 'is newPassword given?' },
{ id: 'checkUniqueIdentifier', ...workflowDecision, label: 'is unique identifier given?' },
{ id: 'checkNewPasswordDifferentToCurrentPassword', ...workflowDecision, label: "is new password different\nto old password?" },
{ id: 'checkUser', ...workflowDecision, label: 'does user exist?' },
{ id: 'checkAnonymousUser', ...workflowDecision, label: 'is anonymous user?' },
{ id: 'checkCurrentPasswordMatch', ...workflowDecision, label: 'does current password match?' },
{ id: 'changePassword', ...workflowStep, label: "change password" },
{ id: 'error400', ...workflowEndError, label: "return 400" },
{ id: 'error401', ...workflowEndError, label: "return 401" },
{ id: 'error403', ...workflowEndError, label: 'return 403' },
{ id: 'success204', ...workflowEndSuccess , label: "return 204"},
],
edges: [
{ source: 'init', target: 'checkType', label: '' },
{ source: 'checkType', target: 'checkTypeContent', label: 'yes' },
{ source: 'checkType', target: 'error400', label: 'no' },
{ source: 'checkTypeContent', target: 'checkCurrentPassword', label: 'yes' },
{ source: 'checkTypeContent', target: 'error400', label: 'no' },
{ source: 'checkCurrentPassword', target: 'checkNewPassword', label: 'yes' },
{ source: 'checkCurrentPassword', target: 'error400', label: 'no' },
{ source: 'checkNewPassword', target: 'checkUniqueIdentifier', label: 'yes' },
{ source: 'checkNewPassword', target: 'error400', label: 'no' },
{ source: 'checkUniqueIdentifier', target: 'checkNewPasswordDifferentToCurrentPassword', label: 'yes' },
{ source: 'checkUniqueIdentifier', target: 'error400', label: 'no' },
{ source: 'checkNewPasswordDifferentToCurrentPassword', target: 'checkUser', label: 'yes' },
{ source: 'checkNewPasswordDifferentToCurrentPassword', target: 'error400', label: 'no' },
{ source: 'checkUser', target: 'checkAnonymousUser', label: 'yes' },
{ source: 'checkUser', target: 'error401', label: 'no' },
{ source: 'checkAnonymousUser', target: 'checkCurrentPasswordMatch', label: 'no' },
{ source: 'checkAnonymousUser', target: 'error403', label: 'yes' },
{ source: 'checkCurrentPasswordMatch', target: 'changePassword', label: 'yes' },
{ source: 'checkCurrentPasswordMatch', target: 'error401', label: 'no' },
{ source: 'changePassword', target: 'success204', label: '' },
],
}, 'TB');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
Date: Fri, 29 Sep 2023 10:05:24 GMT
Server: Unit
X-Powered-By: Ember-Nexus-API
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "http://ember-nexus-api/error/400/bad-content",
"title": "Bad content",
"status": 400,
"detail": "Endpoint expects property 'type' to be ActionChangePassword, got 'NotActionChangePassword'."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
Content-Type: application/problem+json; charset=utf-8
Date: Fri, 29 Sep 2023 10:11:29 GMT
Server: Unit
Transfer-Encoding: chunked
X-Powered-By: Ember-Nexus-API
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "http://ember-nexus-api/error/401/unauthorized",
"title": "Request does not contain valid token, or anonymous user is disabled.",
"status": 401
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
Content-Type: application/problem+json; charset=utf-8
Date: Fri, 29 Sep 2023 10:14:53 GMT
Server: Unit
Transfer-Encoding: chunked
X-Powered-By: Ember-Nexus-API
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "http://ember-nexus-api/error/403/forbidden",
"title": "Requested endpoint, element or action is forbidden.",
"status": 403
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
Content-Type: application/problem+json; charset=utf-8
Date: Fri, 29 Sep 2023 17:33:00 GMT
Server: Unit
Transfer-Encoding: chunked
X-Powered-By: Ember-Nexus-API
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "401-unauthorized",
"title": "Request does not contain valid token, or anonymous user is disabled.",
"status": 401,
"detail": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE
Allow: GET, POST, OPTIONS, PUT, PATCH, DELETE
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache, private
Date: Fri, 15 Sep 2023 08:03:41 GMT
Server: Unit
Transfer-Encoding: chunked
6 changes: 6 additions & 0 deletions src/Command/BackupLoadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ private function loadNodes(): void
if (!$nodeFile->isFile()) {
continue;
}
if (!str_ends_with($nodeFile->path(), '.json')) {
continue;
}
$data = \Safe\json_decode($this->backupStorage->read($nodeFile->path()), true);
$nodeElement = $this->rawToElementService->rawToElement($data);
unset($data);
Expand Down Expand Up @@ -127,6 +130,9 @@ private function loadRelations(): void
if (!$relationFile->isFile()) {
continue;
}
if (!str_ends_with($relationFile->path(), '.json')) {
continue;
}
$data = \Safe\json_decode($this->backupStorage->read($relationFile->path()), true);
$relationElement = $this->rawToElementService->rawToElement($data);
unset($data);
Expand Down
Loading

0 comments on commit 13bb783

Please sign in to comment.