mirror of
https://github.com/standardnotes/server
synced 2026-05-08 03:57:31 -04:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e98393452b | |||
| 302b624504 | |||
| e00d9d2ca0 | |||
| 9ab4601c8d | |||
| 19e43bdb1a | |||
| 49832e7944 | |||
| 916e98936a | |||
| 31d1eef7f7 | |||
| 2648d9a813 | |||
| b24b576209 | |||
| faee38bffd | |||
| 65f3503fe8 | |||
| 054023b791 | |||
| 383c3a68fa | |||
| 7d22b1c15c | |||
| c71e7cd926 | |||
| 83ad069c5d | |||
| 081108d9ba | |||
| 8f3df56a2b | |||
| d02124f4e5 | |||
| 09e351fedb | |||
| ad4b85b095 | |||
| 0bf7d8beae | |||
| 1ae7cca394 | |||
| bc1c7a8ae1 | |||
| c22c5e4584 | |||
| ac3646836c | |||
| 7a31ab75d6 | |||
| c49dc35ab5 | |||
| 06cedd11d8 | |||
| f496376fb3 | |||
| 091e2a57e8 | |||
| 0d40ef6796 | |||
| 1be33ba4c3 | |||
| aaeb311928 | |||
| a7a38c07ac | |||
| 56f49752b4 | |||
| 892d8b6fe2 | |||
| cec2005436 | |||
| 0eb86c0096 | |||
| b8e39d76c1 | |||
| 1c3ff526b7 | |||
| 373767248c | |||
| d7965b2748 | |||
| cbcd2ec87a | |||
| c74d37fc48 | |||
| 66f9352a06 | |||
| e5eef3aba0 | |||
| d261c81cd0 | |||
| 634e3bbb67 | |||
| f8c9e67063 | |||
| 18eddea6f8 | |||
| c6d655c5f5 | |||
| 46867c1a4d | |||
| d29903bab6 | |||
| 3415cae093 | |||
| 408fd5a0c6 | |||
| 0a16ee64fe | |||
| 22b00479b4 | |||
| 5311e74266 | |||
| 5be7db7788 | |||
| 3bd1547ce3 | |||
| a1fe15f7a9 | |||
| 19b8921f28 | |||
| 6b7879ba15 | |||
| bd5f492a73 | |||
| 67311cc002 | |||
| f39d3aca5b | |||
| 8e47491e3c | |||
| 0036d527bd | |||
| f565f1d950 | |||
| 8e35dfa4b7 | |||
| f911473be9 | |||
| 71624f1897 | |||
| 17de6ea7e1 | |||
| 6aad7cd207 | |||
| 63af335877 | |||
| 8cd7a138ab | |||
| f69cdc7b03 | |||
| 2ca649cf31 | |||
| f2ada08201 | |||
| 54ba1f69e5 | |||
| f13a99f5fd | |||
| e9bba6fd3a | |||
| f0d1a70c87 | |||
| 56f0aef21d | |||
| 75e266cb9e | |||
| b9bb83c0ce | |||
| da645c5ab3 | |||
| 318af5757d | |||
| b1cc156a25 | |||
| 79d71ca161 | |||
| cedd50b366 | |||
| 0d5dcdd8ec | |||
| d2b0fb144b | |||
| 053852b46c | |||
| 6ad349d379 | |||
| f7d33c7164 | |||
| b53b67328f | |||
| 573ffbfcf3 | |||
| 501ac0e99f | |||
| 959a11293a | |||
| fee1f1a3a7 | |||
| b0fbe0bb58 | |||
| 0087c70007 | |||
| 36e496dd7c | |||
| f2e2030e85 | |||
| 0c3737dc19 | |||
| f7471119e1 | |||
| 9bd97b95e9 | |||
| b7400c198f | |||
| f87036e3a8 | |||
| a43e5ef724 | |||
| 913ced70b0 | |||
| 6ffce30a36 | |||
| f5a57d886c | |||
| e8ba49ecca | |||
| c79a5dc94b | |||
| 4db83ae678 | |||
| 84ceb7ffd2 | |||
| e215ac4343 | |||
| bc8048790f | |||
| 886ccf84c1 | |||
| c067cb9fe4 | |||
| 6b2389cdc3 | |||
| d93916b159 | |||
| c34f548e45 | |||
| 6fcd56cc86 | |||
| 8f88a87c93 | |||
| f8c2f84322 | |||
| 46c4947871 | |||
| 64759ec2da | |||
| 5f7e768e64 | |||
| 4bc189f1c5 | |||
| 71721ab198 | |||
| 5536a48966 | |||
| f77e29d3c9 | |||
| 4b1fc718a2 | |||
| 1708c3f8a0 |
+7
-1
@@ -10,7 +10,7 @@ REDIS_HOST=cache
|
|||||||
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
||||||
AUTH_SERVER_REFRESH_TOKEN_AGE=10
|
AUTH_SERVER_REFRESH_TOKEN_AGE=10
|
||||||
AUTH_SERVER_EPHEMERAL_SESSION_AGE=300
|
AUTH_SERVER_EPHEMERAL_SESSION_AGE=300
|
||||||
SYNCING_SERVER_REVISIONS_FREQUENCY=5
|
SYNCING_SERVER_REVISIONS_FREQUENCY=2
|
||||||
AUTH_SERVER_LOG_LEVEL=debug
|
AUTH_SERVER_LOG_LEVEL=debug
|
||||||
SYNCING_SERVER_LOG_LEVEL=debug
|
SYNCING_SERVER_LOG_LEVEL=debug
|
||||||
FILES_SERVER_LOG_LEVEL=debug
|
FILES_SERVER_LOG_LEVEL=debug
|
||||||
@@ -22,6 +22,12 @@ MYSQL_USER=std_notes_user
|
|||||||
MYSQL_PASSWORD=changeme123
|
MYSQL_PASSWORD=changeme123
|
||||||
MYSQL_ROOT_PASSWORD=changeme123
|
MYSQL_ROOT_PASSWORD=changeme123
|
||||||
|
|
||||||
|
MONGO_HOST=secondary_db
|
||||||
|
MONGO_PORT=27017
|
||||||
|
MONGO_USERNAME=standardnotes
|
||||||
|
MONGO_PASSWORD=standardnotes
|
||||||
|
MONGO_DATABASE=standardnotes
|
||||||
|
|
||||||
AUTH_JWT_SECRET=f95259c5e441f5a4646d76422cfb3df4c4488842901aa50b6c51b8be2e0040e9
|
AUTH_JWT_SECRET=f95259c5e441f5a4646d76422cfb3df4c4488842901aa50b6c51b8be2e0040e9
|
||||||
AUTH_SERVER_ENCRYPTION_SERVER_KEY=1087415dfde3093797f9a7ca93a49e7d7aa1861735eb0d32aae9c303b8c3d060
|
AUTH_SERVER_ENCRYPTION_SERVER_KEY=1087415dfde3093797f9a7ca93a49e7d7aa1861735eb0d32aae9c303b8c3d060
|
||||||
VALET_TOKEN_SECRET=4b886819ebe1e908077c6cae96311b48a8416bd60cc91c03060e15bdf6b30d1f
|
VALET_TOKEN_SECRET=4b886819ebe1e908077c6cae96311b48a8416bd60cc91c03060e15bdf6b30d1f
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
e2e:
|
e2e:
|
||||||
name: (Docker) E2E Test Suite
|
name: (Docker) E2E Test Suite
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
secondary_db_enabled: [true, false]
|
||||||
|
transition_mode_enabled: [true, false]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -45,19 +50,41 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DB_TYPE: mysql
|
DB_TYPE: mysql
|
||||||
CACHE_TYPE: redis
|
CACHE_TYPE: redis
|
||||||
|
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
|
||||||
|
TRANSITION_MODE_ENABLED: ${{ matrix.transition_mode_enabled }}
|
||||||
|
|
||||||
- name: Wait for server to start
|
- name: Wait for server to start
|
||||||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||||
|
|
||||||
|
- name: Define if vault tests are enabled
|
||||||
|
id: vaults
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.secondary_db_enabled }}" = "true" ] && [ "${{ matrix.transition_mode_enabled }}" = "true" ]; then
|
||||||
|
echo "vault-tests=enabled" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "vault-tests=disabled" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Run E2E Test Suite
|
- name: Run E2E Test Suite
|
||||||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
|
run: yarn dlx mocha-headless-chrome --timeout 1800000 -f http://localhost:9001/mocha/test.html?vaults=${{ steps.vaults.outputs.vault-tests }}
|
||||||
|
|
||||||
|
- name: Show logs on failure
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: |
|
||||||
|
echo "# Errors:"
|
||||||
|
tail -n 100 logs/*.err
|
||||||
|
echo "# Logs:"
|
||||||
|
tail -n 100 logs/*.log
|
||||||
|
|
||||||
e2e-home-server:
|
e2e-home-server:
|
||||||
name: (Home Server) E2E Test Suite
|
name: (Home Server) E2E Test Suite
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
db_type: [mysql, sqlite]
|
db_type: [mysql, sqlite]
|
||||||
cache_type: [redis, memory]
|
cache_type: [redis, memory]
|
||||||
|
secondary_db_enabled: [true, false]
|
||||||
|
transition_mode_enabled: [true, false]
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
@@ -79,6 +106,14 @@ jobs:
|
|||||||
MYSQL_DATABASE: standardnotes
|
MYSQL_DATABASE: standardnotes
|
||||||
MYSQL_USER: standardnotes
|
MYSQL_USER: standardnotes
|
||||||
MYSQL_PASSWORD: standardnotes
|
MYSQL_PASSWORD: standardnotes
|
||||||
|
secondary_db:
|
||||||
|
image: mongo:5.0
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
env:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: standardnotes
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: standardnotes
|
||||||
|
MONGO_INITDB_DATABASE: standardnotes
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -106,25 +141,47 @@ jobs:
|
|||||||
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
|
sed -i "s/PSEUDO_KEY_PARAMS_KEY=/PSEUDO_KEY_PARAMS_KEY=$(openssl rand -hex 32)/g" packages/home-server/.env
|
||||||
sed -i "s/VALET_TOKEN_SECRET=/VALET_TOKEN_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
|
sed -i "s/VALET_TOKEN_SECRET=/VALET_TOKEN_SECRET=$(openssl rand -hex 32)/g" packages/home-server/.env
|
||||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||||
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
|
||||||
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
|
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
|
||||||
echo "DB_HOST=db" >> packages/home-server/.env
|
echo "DB_HOST=localhost" >> packages/home-server/.env
|
||||||
echo "DB_PORT=3306" >> packages/home-server/.env
|
echo "DB_PORT=3306" >> packages/home-server/.env
|
||||||
|
echo "DB_DATABASE=standardnotes" >> packages/home-server/.env
|
||||||
|
echo "DB_SQLITE_DATABASE_PATH=homeserver.db" >> packages/home-server/.env
|
||||||
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
|
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
|
||||||
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
|
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
|
||||||
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
|
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
|
||||||
echo "REDIS_URL=redis://cache" >> packages/home-server/.env
|
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
|
||||||
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
|
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
|
||||||
|
echo "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
|
||||||
|
echo "TRANSITION_MODE_ENABLED=${{ matrix.transition_mode_enabled }}" >> packages/home-server/.env
|
||||||
|
echo "MONGO_HOST=localhost" >> packages/home-server/.env
|
||||||
|
echo "MONGO_PORT=27017" >> packages/home-server/.env
|
||||||
|
echo "MONGO_DATABASE=standardnotes" >> packages/home-server/.env
|
||||||
|
echo "MONGO_USERNAME=standardnotes" >> packages/home-server/.env
|
||||||
|
echo "MONGO_PASSWORD=standardnotes" >> packages/home-server/.env
|
||||||
echo "FILES_SERVER_URL=http://localhost:3123" >> packages/home-server/.env
|
echo "FILES_SERVER_URL=http://localhost:3123" >> packages/home-server/.env
|
||||||
echo "E2E_TESTING=true" >> packages/home-server/.env
|
echo "E2E_TESTING=true" >> packages/home-server/.env
|
||||||
|
|
||||||
- name: Run Server
|
- name: Run Server
|
||||||
run: nohup yarn workspace @standardnotes/home-server start &
|
run: nohup yarn workspace @standardnotes/home-server start > logs/output.log 2>&1 &
|
||||||
env:
|
env:
|
||||||
PORT: 3123
|
PORT: 3123
|
||||||
|
|
||||||
- name: Wait for server to start
|
- name: Wait for server to start
|
||||||
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
||||||
|
|
||||||
|
- name: Define if vault tests are enabled
|
||||||
|
id: vaults
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.secondary_db_enabled }}" = "true" ] && [ "${{ matrix.transition_mode_enabled }}" = "true" ]; then
|
||||||
|
echo "vault-tests=enabled" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "vault-tests=disabled" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Run E2E Test Suite
|
- name: Run E2E Test Suite
|
||||||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
|
run: yarn dlx mocha-headless-chrome --timeout 1800000 -f http://localhost:9001/mocha/test.html?vaults=${{ steps.vaults.outputs.vault-tests }}
|
||||||
|
|
||||||
|
- name: Show logs on failure
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: tail -n 500 logs/output.log
|
||||||
|
|||||||
@@ -5191,6 +5191,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["inversify-express-utils", "npm:6.4.3"],\
|
["inversify-express-utils", "npm:6.4.3"],\
|
||||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||||
["jsonwebtoken", "npm:9.0.0"],\
|
["jsonwebtoken", "npm:9.0.0"],\
|
||||||
|
["mongodb", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:5.7.0"],\
|
||||||
["mysql2", "npm:3.3.3"],\
|
["mysql2", "npm:3.3.3"],\
|
||||||
["newrelic", "npm:10.1.2"],\
|
["newrelic", "npm:10.1.2"],\
|
||||||
["nodemon", "npm:2.0.22"],\
|
["nodemon", "npm:2.0.22"],\
|
||||||
@@ -5201,7 +5202,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["semver", "npm:7.5.1"],\
|
["semver", "npm:7.5.1"],\
|
||||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\
|
["typeorm", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.16"],\
|
||||||
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
||||||
["ua-parser-js", "npm:1.0.35"],\
|
["ua-parser-js", "npm:1.0.35"],\
|
||||||
["uuid", "npm:9.0.0"],\
|
["uuid", "npm:9.0.0"],\
|
||||||
@@ -5259,7 +5260,6 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/responses", "npm:1.13.27"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/utils", "npm:1.17.5"],\
|
|
||||||
["@types/cors", "npm:2.8.13"],\
|
["@types/cors", "npm:2.8.13"],\
|
||||||
["@types/express", "npm:4.17.17"],\
|
["@types/express", "npm:4.17.17"],\
|
||||||
["@types/ioredis", "npm:5.0.0"],\
|
["@types/ioredis", "npm:5.0.0"],\
|
||||||
@@ -5870,6 +5870,26 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["@types/webidl-conversions", [\
|
||||||
|
["npm:7.0.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-86c337dc1e.zip/node_modules/@types/webidl-conversions/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["@types/webidl-conversions", "npm:7.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
|
["@types/whatwg-url", [\
|
||||||
|
["npm:8.2.2", {\
|
||||||
|
"packageLocation": "./.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-25f20f5649.zip/node_modules/@types/whatwg-url/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["@types/whatwg-url", "npm:8.2.2"],\
|
||||||
|
["@types/node", "npm:20.2.5"],\
|
||||||
|
["@types/webidl-conversions", "npm:7.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["@types/yargs", [\
|
["@types/yargs", [\
|
||||||
["npm:17.0.24", {\
|
["npm:17.0.24", {\
|
||||||
"packageLocation": "./.yarn/cache/@types-yargs-npm-17.0.24-b034cf1d8b-f7811cc0b9.zip/node_modules/@types/yargs/",\
|
"packageLocation": "./.yarn/cache/@types-yargs-npm-17.0.24-b034cf1d8b-f7811cc0b9.zip/node_modules/@types/yargs/",\
|
||||||
@@ -7075,6 +7095,15 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["bson", [\
|
||||||
|
["npm:5.4.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/bson-npm-5.4.0-2f854c8216-2c913a45c0.zip/node_modules/bson/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["bson", "npm:5.4.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["buffer", [\
|
["buffer", [\
|
||||||
["npm:5.7.1", {\
|
["npm:5.7.1", {\
|
||||||
"packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-8e611bed4d.zip/node_modules/buffer/",\
|
"packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-8e611bed4d.zip/node_modules/buffer/",\
|
||||||
@@ -11933,6 +11962,15 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["memory-pager", [\
|
||||||
|
["npm:1.5.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-6b00ff499b.zip/node_modules/memory-pager/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["memory-pager", "npm:1.5.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["meow", [\
|
["meow", [\
|
||||||
["npm:8.1.2", {\
|
["npm:8.1.2", {\
|
||||||
"packageLocation": "./.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-e36c879078.zip/node_modules/meow/",\
|
"packageLocation": "./.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-e36c879078.zip/node_modules/meow/",\
|
||||||
@@ -12291,6 +12329,59 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["mongodb", [\
|
||||||
|
["npm:5.7.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/mongodb-npm-5.7.0-c5e415a2e7-23a291ffe7.zip/node_modules/mongodb/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["mongodb", "npm:5.7.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "SOFT"\
|
||||||
|
}],\
|
||||||
|
["virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:5.7.0", {\
|
||||||
|
"packageLocation": "./.yarn/__virtual__/mongodb-virtual-eb0cd47e23/0/cache/mongodb-npm-5.7.0-c5e415a2e7-23a291ffe7.zip/node_modules/mongodb/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["mongodb", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:5.7.0"],\
|
||||||
|
["@aws-sdk/credential-providers", null],\
|
||||||
|
["@mongodb-js/zstd", null],\
|
||||||
|
["@types/aws-sdk__credential-providers", null],\
|
||||||
|
["@types/kerberos", null],\
|
||||||
|
["@types/mongodb-client-encryption", null],\
|
||||||
|
["@types/mongodb-js__zstd", null],\
|
||||||
|
["@types/snappy", null],\
|
||||||
|
["bson", "npm:5.4.0"],\
|
||||||
|
["kerberos", null],\
|
||||||
|
["mongodb-client-encryption", null],\
|
||||||
|
["mongodb-connection-string-url", "npm:2.6.0"],\
|
||||||
|
["saslprep", "npm:1.0.3"],\
|
||||||
|
["snappy", null],\
|
||||||
|
["socks", "npm:2.7.1"]\
|
||||||
|
],\
|
||||||
|
"packagePeers": [\
|
||||||
|
"@aws-sdk/credential-providers",\
|
||||||
|
"@mongodb-js/zstd",\
|
||||||
|
"@types/aws-sdk__credential-providers",\
|
||||||
|
"@types/kerberos",\
|
||||||
|
"@types/mongodb-client-encryption",\
|
||||||
|
"@types/mongodb-js__zstd",\
|
||||||
|
"@types/snappy",\
|
||||||
|
"kerberos",\
|
||||||
|
"mongodb-client-encryption",\
|
||||||
|
"snappy"\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
|
["mongodb-connection-string-url", [\
|
||||||
|
["npm:2.6.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-8a9186dd1b.zip/node_modules/mongodb-connection-string-url/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["mongodb-connection-string-url", "npm:2.6.0"],\
|
||||||
|
["@types/whatwg-url", "npm:8.2.2"],\
|
||||||
|
["whatwg-url", "npm:11.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["ms", [\
|
["ms", [\
|
||||||
["npm:2.0.0", {\
|
["npm:2.0.0", {\
|
||||||
"packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-de027828fc.zip/node_modules/ms/",\
|
"packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-de027828fc.zip/node_modules/ms/",\
|
||||||
@@ -14250,6 +14341,16 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["saslprep", [\
|
||||||
|
["npm:1.0.3", {\
|
||||||
|
"packageLocation": "./.yarn/cache/saslprep-npm-1.0.3-8db649c346-23ebcda091.zip/node_modules/saslprep/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["saslprep", "npm:1.0.3"],\
|
||||||
|
["sparse-bitfield", "npm:3.0.3"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["schema-utils", [\
|
["schema-utils", [\
|
||||||
["npm:3.1.2", {\
|
["npm:3.1.2", {\
|
||||||
"packageLocation": "./.yarn/cache/schema-utils-npm-3.1.2-d97c6dc247-11d35f997e.zip/node_modules/schema-utils/",\
|
"packageLocation": "./.yarn/cache/schema-utils-npm-3.1.2-d97c6dc247-11d35f997e.zip/node_modules/schema-utils/",\
|
||||||
@@ -14605,6 +14706,16 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
|
["sparse-bitfield", [\
|
||||||
|
["npm:3.0.3", {\
|
||||||
|
"packageLocation": "./.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-625ecdf6f4.zip/node_modules/sparse-bitfield/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["sparse-bitfield", "npm:3.0.3"],\
|
||||||
|
["memory-pager", "npm:1.5.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}]\
|
||||||
|
]],\
|
||||||
["spawn-please", [\
|
["spawn-please", [\
|
||||||
["npm:2.0.1", {\
|
["npm:2.0.1", {\
|
||||||
"packageLocation": "./.yarn/cache/spawn-please-npm-2.0.1-265b6b5432-fe19a7ceb5.zip/node_modules/spawn-please/",\
|
"packageLocation": "./.yarn/cache/spawn-please-npm-2.0.1-265b6b5432-fe19a7ceb5.zip/node_modules/spawn-please/",\
|
||||||
@@ -15247,6 +15358,14 @@ const RAW_RUNTIME_STATE =
|
|||||||
["tr46", "npm:0.0.3"]\
|
["tr46", "npm:0.0.3"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
|
["npm:3.0.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-3a481676bf.zip/node_modules/tr46/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["tr46", "npm:3.0.0"],\
|
||||||
|
["punycode", "npm:2.3.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["treeverse", [\
|
["treeverse", [\
|
||||||
@@ -15758,6 +15877,98 @@ const RAW_RUNTIME_STATE =
|
|||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}],\
|
}],\
|
||||||
|
["virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.16", {\
|
||||||
|
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-13b6364fde/0/cache/typeorm-npm-0.3.16-5ac12a7afc-19803f935e.zip/node_modules/typeorm/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["typeorm", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:0.3.16"],\
|
||||||
|
["@google-cloud/spanner", null],\
|
||||||
|
["@sap/hana-client", null],\
|
||||||
|
["@sqltools/formatter", "npm:1.2.5"],\
|
||||||
|
["@types/better-sqlite3", null],\
|
||||||
|
["@types/google-cloud__spanner", null],\
|
||||||
|
["@types/hdb-pool", null],\
|
||||||
|
["@types/ioredis", null],\
|
||||||
|
["@types/mongodb", null],\
|
||||||
|
["@types/mssql", null],\
|
||||||
|
["@types/mysql2", null],\
|
||||||
|
["@types/oracledb", null],\
|
||||||
|
["@types/pg", null],\
|
||||||
|
["@types/pg-native", null],\
|
||||||
|
["@types/pg-query-stream", null],\
|
||||||
|
["@types/redis", null],\
|
||||||
|
["@types/sap__hana-client", null],\
|
||||||
|
["@types/sql.js", null],\
|
||||||
|
["@types/sqlite3", null],\
|
||||||
|
["@types/ts-node", null],\
|
||||||
|
["@types/typeorm-aurora-data-api-driver", null],\
|
||||||
|
["app-root-path", "npm:3.1.0"],\
|
||||||
|
["better-sqlite3", null],\
|
||||||
|
["buffer", "npm:6.0.3"],\
|
||||||
|
["chalk", "npm:4.1.2"],\
|
||||||
|
["cli-highlight", "npm:2.1.11"],\
|
||||||
|
["date-fns", "npm:2.30.0"],\
|
||||||
|
["debug", "virtual:ac3d8e680759ce54399273724d44e041d6c9b73454d191d411a8c44bb27e22f02aaf6ed9d3ad0ac1c298eac4833cff369c9c7b84c573016112c4f84be2cd8543#npm:4.3.4"],\
|
||||||
|
["dotenv", "npm:16.1.3"],\
|
||||||
|
["glob", "npm:8.1.0"],\
|
||||||
|
["hdb-pool", null],\
|
||||||
|
["ioredis", null],\
|
||||||
|
["mkdirp", "npm:2.1.6"],\
|
||||||
|
["mongodb", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:5.7.0"],\
|
||||||
|
["mssql", null],\
|
||||||
|
["mysql2", "npm:3.3.3"],\
|
||||||
|
["oracledb", null],\
|
||||||
|
["pg", null],\
|
||||||
|
["pg-native", null],\
|
||||||
|
["pg-query-stream", null],\
|
||||||
|
["redis", null],\
|
||||||
|
["reflect-metadata", "npm:0.1.13"],\
|
||||||
|
["sha.js", "npm:2.4.11"],\
|
||||||
|
["sql.js", null],\
|
||||||
|
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||||
|
["ts-node", null],\
|
||||||
|
["tslib", "npm:2.5.2"],\
|
||||||
|
["typeorm-aurora-data-api-driver", null],\
|
||||||
|
["uuid", "npm:9.0.0"],\
|
||||||
|
["yargs", "npm:17.7.2"]\
|
||||||
|
],\
|
||||||
|
"packagePeers": [\
|
||||||
|
"@google-cloud/spanner",\
|
||||||
|
"@sap/hana-client",\
|
||||||
|
"@types/better-sqlite3",\
|
||||||
|
"@types/google-cloud__spanner",\
|
||||||
|
"@types/hdb-pool",\
|
||||||
|
"@types/ioredis",\
|
||||||
|
"@types/mongodb",\
|
||||||
|
"@types/mssql",\
|
||||||
|
"@types/mysql2",\
|
||||||
|
"@types/oracledb",\
|
||||||
|
"@types/pg-native",\
|
||||||
|
"@types/pg-query-stream",\
|
||||||
|
"@types/pg",\
|
||||||
|
"@types/redis",\
|
||||||
|
"@types/sap__hana-client",\
|
||||||
|
"@types/sql.js",\
|
||||||
|
"@types/sqlite3",\
|
||||||
|
"@types/ts-node",\
|
||||||
|
"@types/typeorm-aurora-data-api-driver",\
|
||||||
|
"better-sqlite3",\
|
||||||
|
"hdb-pool",\
|
||||||
|
"ioredis",\
|
||||||
|
"mongodb",\
|
||||||
|
"mssql",\
|
||||||
|
"mysql2",\
|
||||||
|
"oracledb",\
|
||||||
|
"pg-native",\
|
||||||
|
"pg-query-stream",\
|
||||||
|
"pg",\
|
||||||
|
"redis",\
|
||||||
|
"sql.js",\
|
||||||
|
"sqlite3",\
|
||||||
|
"ts-node",\
|
||||||
|
"typeorm-aurora-data-api-driver"\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
["virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:0.3.16", {\
|
["virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:0.3.16", {\
|
||||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-fc9b7b780b/0/cache/typeorm-npm-0.3.16-5ac12a7afc-19803f935e.zip/node_modules/typeorm/",\
|
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-fc9b7b780b/0/cache/typeorm-npm-0.3.16-5ac12a7afc-19803f935e.zip/node_modules/typeorm/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
@@ -16192,6 +16403,13 @@ const RAW_RUNTIME_STATE =
|
|||||||
["webidl-conversions", "npm:3.0.1"]\
|
["webidl-conversions", "npm:3.0.1"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
|
["npm:7.0.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-bdbe11c68c.zip/node_modules/webidl-conversions/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["webidl-conversions", "npm:7.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["webpack", [\
|
["webpack", [\
|
||||||
@@ -16250,6 +16468,15 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["whatwg-url", [\
|
["whatwg-url", [\
|
||||||
|
["npm:11.0.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-ee3a532bfb.zip/node_modules/whatwg-url/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["whatwg-url", "npm:11.0.0"],\
|
||||||
|
["tr46", "npm:3.0.0"],\
|
||||||
|
["webidl-conversions", "npm:7.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
["npm:5.0.0", {\
|
["npm:5.0.0", {\
|
||||||
"packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-bd0cc6b75b.zip/node_modules/whatwg-url/",\
|
"packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-bd0cc6b75b.zip/node_modules/whatwg-url/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -23,6 +23,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
DB_TYPE: "${DB_TYPE}"
|
DB_TYPE: "${DB_TYPE}"
|
||||||
CACHE_TYPE: "${CACHE_TYPE}"
|
CACHE_TYPE: "${CACHE_TYPE}"
|
||||||
|
SECONDARY_DB_ENABLED: "${SECONDARY_DB_ENABLED}"
|
||||||
|
TRANSITION_MODE_ENABLED: "${TRANSITION_MODE_ENABLED}"
|
||||||
container_name: server-ci
|
container_name: server-ci
|
||||||
ports:
|
ports:
|
||||||
- 3123:3000
|
- 3123:3000
|
||||||
@@ -61,6 +63,21 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- standardnotes_self_hosted
|
- standardnotes_self_hosted
|
||||||
|
|
||||||
|
secondary_db:
|
||||||
|
image: mongo:5.0
|
||||||
|
container_name: secondary_db-ci
|
||||||
|
expose:
|
||||||
|
- 27017
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./data/mongo:/data/db
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: standardnotes
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: standardnotes
|
||||||
|
MONGO_INITDB_DATABASE: standardnotes
|
||||||
|
networks:
|
||||||
|
- standardnotes_self_hosted
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
image: redis:6.0-alpine
|
image: redis:6.0-alpine
|
||||||
container_name: cache-ci
|
container_name: cache-ci
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ services:
|
|||||||
image: standardnotes/server
|
image: standardnotes/server
|
||||||
env_file: .env
|
env_file: .env
|
||||||
container_name: server_self_hosted
|
container_name: server_self_hosted
|
||||||
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
- 3125:3104
|
- 3125:3104
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ fi
|
|||||||
if [ -z "$CACHE_TYPE" ]; then
|
if [ -z "$CACHE_TYPE" ]; then
|
||||||
export CACHE_TYPE="redis"
|
export CACHE_TYPE="redis"
|
||||||
fi
|
fi
|
||||||
|
if [ -z "$SECONDARY_DB_ENABLED" ]; then
|
||||||
|
export SECONDARY_DB_ENABLED=false
|
||||||
|
fi
|
||||||
|
if [ -z "$TRANSITION_MODE_ENABLED" ]; then
|
||||||
|
export TRANSITION_MODE_ENABLED=false
|
||||||
|
fi
|
||||||
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
|
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
|
||||||
|
|
||||||
#########
|
#########
|
||||||
|
|||||||
+4
-1
@@ -12,12 +12,15 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "yarn workspaces foreach -p -j 10 --verbose run lint",
|
"lint": "yarn workspaces foreach -p -j 10 --verbose run lint",
|
||||||
|
"lint:fix": "yarn workspaces foreach -p -j 10 --verbose run lint:fix",
|
||||||
"clean": "yarn workspaces foreach -p --verbose run clean",
|
"clean": "yarn workspaces foreach -p --verbose run clean",
|
||||||
"setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env",
|
"setup:env": "cp .env.sample .env && yarn workspaces foreach -p --verbose run setup:env",
|
||||||
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
|
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
|
||||||
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
|
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
|
||||||
"postversion": "./scripts/push-tags-one-by-one.sh",
|
"postversion": "./scripts/push-tags-one-by-one.sh",
|
||||||
"upgrade:snjs": "yarn workspaces foreach --verbose run upgrade:snjs"
|
"upgrade:snjs": "yarn workspaces foreach --verbose run upgrade:snjs",
|
||||||
|
"e2e": "yarn build packages/home-server && PORT=3123 yarn workspace @standardnotes/home-server start",
|
||||||
|
"start": "yarn build packages/home-server && yarn workspace @standardnotes/home-server start"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.0.2",
|
"@commitlint/cli": "^17.0.2",
|
||||||
|
|||||||
@@ -3,6 +3,54 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.25.14](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.13...@standardnotes/analytics@2.25.14) (2023-08-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.13](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.12...@standardnotes/analytics@2.25.13) (2023-08-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.12](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.11...@standardnotes/analytics@2.25.12) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.11](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.10...@standardnotes/analytics@2.25.11) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.10](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.9...@standardnotes/analytics@2.25.10) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.8...@standardnotes/analytics@2.25.9) (2023-08-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.7...@standardnotes/analytics@2.25.8) (2023-08-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.6...@standardnotes/analytics@2.25.7) (2023-08-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.5...@standardnotes/analytics@2.25.6) (2023-07-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.4...@standardnotes/analytics@2.25.5) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.3...@standardnotes/analytics@2.25.4) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.2...@standardnotes/analytics@2.25.3) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
## [2.25.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.1...@standardnotes/analytics@2.25.2) (2023-07-21)
|
## [2.25.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.1...@standardnotes/analytics@2.25.2) (2023-07-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/analytics
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/analytics",
|
"name": "@standardnotes/analytics",
|
||||||
"version": "2.25.2",
|
"version": "2.25.14",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,74 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.70.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.70.4...@standardnotes/api-gateway@1.70.5) (2023-08-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.70.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.70.3...@standardnotes/api-gateway@1.70.4) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.70.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.70.2...@standardnotes/api-gateway@1.70.3) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.70.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.70.1...@standardnotes/api-gateway@1.70.2) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.70.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.70.0...@standardnotes/api-gateway@1.70.1) (2023-08-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
# [1.70.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.69.3...@standardnotes/api-gateway@1.70.0) (2023-08-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** limit shared vaults creation based on role ([#687](https://github.com/standardnotes/api-gateway/issues/687)) ([19b8921](https://github.com/standardnotes/api-gateway/commit/19b8921f286ff8f88c427e8ddd4512a8d61edb4f))
|
||||||
|
|
||||||
|
## [1.69.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.69.2...@standardnotes/api-gateway@1.69.3) (2023-08-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.69.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.69.1...@standardnotes/api-gateway@1.69.2) (2023-08-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.69.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.69.0...@standardnotes/api-gateway@1.69.1) (2023-07-31)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **api-gateway:** remove duplicating req/res objects on return raw response from payments ([79d71ca](https://github.com/standardnotes/api-gateway/commit/79d71ca161cc18135fcd1a83b021662e189b3ddb))
|
||||||
|
|
||||||
|
# [1.69.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.68.1...@standardnotes/api-gateway@1.69.0) (2023-07-31)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* refactor deleting account ([#676](https://github.com/standardnotes/api-gateway/issues/676)) ([0d5dcdd](https://github.com/standardnotes/api-gateway/commit/0d5dcdd8ec2336e41e7604c4157f79a89163ed29))
|
||||||
|
|
||||||
|
## [1.68.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.68.0...@standardnotes/api-gateway@1.68.1) (2023-07-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
# [1.68.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.4...@standardnotes/api-gateway@1.68.0) (2023-07-27)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** add deleting outbound messages ([e8ba49e](https://github.com/standardnotes/api-gateway/commit/e8ba49ecca38ab10c0ea0e1f4cf4db9fb17366db))
|
||||||
|
|
||||||
|
## [1.67.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.3...@standardnotes/api-gateway@1.67.4) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.67.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.2...@standardnotes/api-gateway@1.67.3) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.67.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.1...@standardnotes/api-gateway@1.67.2) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
## [1.67.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.0...@standardnotes/api-gateway@1.67.1) (2023-07-21)
|
## [1.67.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.0...@standardnotes/api-gateway@1.67.1) (2023-07-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void container.load().then((container) => {
|
|||||||
|
|
||||||
server.setConfig((app) => {
|
server.setConfig((app) => {
|
||||||
app.use((_request: Request, response: Response, next: NextFunction) => {
|
app.use((_request: Request, response: Response, next: NextFunction) => {
|
||||||
response.setHeader('X-API-Gateway-Version', container.get(TYPES.VERSION))
|
response.setHeader('X-API-Gateway-Version', container.get(TYPES.ApiGateway_VERSION))
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
app.use(
|
app.use(
|
||||||
@@ -87,7 +87,7 @@ void container.load().then((container) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const logger: winston.Logger = container.get(TYPES.Logger)
|
const logger: winston.Logger = container.get(TYPES.ApiGateway_Logger)
|
||||||
|
|
||||||
server.setErrorConfig((app) => {
|
server.setErrorConfig((app) => {
|
||||||
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
|
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/api-gateway",
|
"name": "@standardnotes/api-gateway",
|
||||||
"version": "1.67.1",
|
"version": "1.70.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
"clean": "rm -fr dist",
|
"clean": "rm -fr dist",
|
||||||
"build": "tsc --build",
|
"build": "tsc --build",
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
|
"lint:fix": "eslint . --fix --ext .ts",
|
||||||
"setup:env": "cp .env.sample .env",
|
"setup:env": "cp .env.sample .env",
|
||||||
"start": "yarn node dist/bin/server.js",
|
"start": "yarn node dist/bin/server.js",
|
||||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class ContainerConfigLoader {
|
|||||||
defaultMeta: { service: 'api-gateway' },
|
defaultMeta: { service: 'api-gateway' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
container.bind<winston.Logger>(TYPES.ApiGateway_Logger).toConstantValue(logger)
|
||||||
|
|
||||||
if (!isConfiguredForInMemoryCache) {
|
if (!isConfiguredForInMemoryCache) {
|
||||||
const redisUrl = env.get('REDIS_URL')
|
const redisUrl = env.get('REDIS_URL')
|
||||||
@@ -68,36 +68,39 @@ export class ContainerConfigLoader {
|
|||||||
} else {
|
} else {
|
||||||
redis = new Redis(redisUrl)
|
redis = new Redis(redisUrl)
|
||||||
}
|
}
|
||||||
container.bind(TYPES.Redis).toConstantValue(redis)
|
container.bind(TYPES.ApiGateway_Redis).toConstantValue(redis)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
|
container.bind<AxiosInstance>(TYPES.ApiGateway_HTTPClient).toConstantValue(axios.create())
|
||||||
|
|
||||||
// env vars
|
// env vars
|
||||||
container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
|
container.bind(TYPES.ApiGateway_SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
|
||||||
container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
|
||||||
container.bind(TYPES.REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
|
||||||
container.bind(TYPES.EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
|
||||||
container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
||||||
container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
|
||||||
container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
|
container.bind(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
|
||||||
container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
container.bind(TYPES.ApiGateway_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||||
container
|
container
|
||||||
.bind(TYPES.HTTP_CALL_TIMEOUT)
|
.bind(TYPES.ApiGateway_HTTP_CALL_TIMEOUT)
|
||||||
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
|
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
|
||||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
|
container.bind(TYPES.ApiGateway_VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
|
||||||
container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
|
container
|
||||||
|
.bind(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL)
|
||||||
|
.toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
|
||||||
|
container.bind(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER).toConstantValue(isConfiguredForHomeServer)
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
container
|
container
|
||||||
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.RequiredCrossServiceTokenMiddleware)
|
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
.to(RequiredCrossServiceTokenMiddleware)
|
.to(RequiredCrossServiceTokenMiddleware)
|
||||||
container
|
container
|
||||||
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.OptionalCrossServiceTokenMiddleware)
|
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
|
||||||
.to(OptionalCrossServiceTokenMiddleware)
|
.to(OptionalCrossServiceTokenMiddleware)
|
||||||
container.bind<WebSocketAuthMiddleware>(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
|
container.bind<WebSocketAuthMiddleware>(TYPES.ApiGateway_WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
|
||||||
container
|
container
|
||||||
.bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
|
.bind<SubscriptionTokenAuthMiddleware>(TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
.to(SubscriptionTokenAuthMiddleware)
|
.to(SubscriptionTokenAuthMiddleware)
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
@@ -106,24 +109,26 @@ export class ContainerConfigLoader {
|
|||||||
throw new Error('Service container is required when configured for home server')
|
throw new Error('Service container is required when configured for home server')
|
||||||
}
|
}
|
||||||
container
|
container
|
||||||
.bind<ServiceProxyInterface>(TYPES.ServiceProxy)
|
.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.FILES_SERVER_URL)),
|
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.ApiGateway_FILES_SERVER_URL)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
container.bind<ServiceProxyInterface>(TYPES.ServiceProxy).to(HttpServiceProxy)
|
container.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy).to(HttpServiceProxy)
|
||||||
}
|
}
|
||||||
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
|
container.bind<TimerInterface>(TYPES.ApiGateway_Timer).toConstantValue(new Timer())
|
||||||
|
|
||||||
if (isConfiguredForHomeServer) {
|
if (isConfiguredForHomeServer) {
|
||||||
container
|
container
|
||||||
.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache)
|
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.Timer)))
|
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
|
||||||
} else {
|
} else {
|
||||||
container.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache)
|
container
|
||||||
|
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||||
|
.to(RedisCrossServiceTokenCache)
|
||||||
}
|
}
|
||||||
container
|
container
|
||||||
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
|
.bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
|
||||||
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
||||||
|
|
||||||
logger.debug('Configuration complete')
|
logger.debug('Configuration complete')
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
export const TYPES = {
|
export const TYPES = {
|
||||||
Logger: Symbol.for('Logger'),
|
ApiGateway_Logger: Symbol.for('ApiGateway_Logger'),
|
||||||
Redis: Symbol.for('Redis'),
|
ApiGateway_Redis: Symbol.for('ApiGateway_Redis'),
|
||||||
HTTPClient: Symbol.for('HTTPClient'),
|
ApiGateway_HTTPClient: Symbol.for('ApiGateway_HTTPClient'),
|
||||||
// env vars
|
// env vars
|
||||||
SYNCING_SERVER_JS_URL: Symbol.for('SYNCING_SERVER_JS_URL'),
|
ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
|
||||||
AUTH_SERVER_URL: Symbol.for('AUTH_SERVER_URL'),
|
ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
|
||||||
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
|
ApiGateway_PAYMENTS_SERVER_URL: Symbol.for('ApiGateway_PAYMENTS_SERVER_URL'),
|
||||||
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
|
ApiGateway_FILES_SERVER_URL: Symbol.for('ApiGateway_FILES_SERVER_URL'),
|
||||||
REVISIONS_SERVER_URL: Symbol.for('REVISIONS_SERVER_URL'),
|
ApiGateway_REVISIONS_SERVER_URL: Symbol.for('ApiGateway_REVISIONS_SERVER_URL'),
|
||||||
EMAIL_SERVER_URL: Symbol.for('EMAIL_SERVER_URL'),
|
ApiGateway_EMAIL_SERVER_URL: Symbol.for('ApiGateway_EMAIL_SERVER_URL'),
|
||||||
WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
|
ApiGateway_WEB_SOCKET_SERVER_URL: Symbol.for('ApiGateway_WEB_SOCKET_SERVER_URL'),
|
||||||
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
ApiGateway_AUTH_JWT_SECRET: Symbol.for('ApiGateway_AUTH_JWT_SECRET'),
|
||||||
HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
|
ApiGateway_HTTP_CALL_TIMEOUT: Symbol.for('ApiGateway_HTTP_CALL_TIMEOUT'),
|
||||||
VERSION: Symbol.for('VERSION'),
|
ApiGateway_VERSION: Symbol.for('ApiGateway_VERSION'),
|
||||||
CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'),
|
ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL'),
|
||||||
|
ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER: Symbol.for('ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER'),
|
||||||
// Middleware
|
// Middleware
|
||||||
RequiredCrossServiceTokenMiddleware: Symbol.for('RequiredCrossServiceTokenMiddleware'),
|
ApiGateway_RequiredCrossServiceTokenMiddleware: Symbol.for('ApiGateway_RequiredCrossServiceTokenMiddleware'),
|
||||||
OptionalCrossServiceTokenMiddleware: Symbol.for('OptionalCrossServiceTokenMiddleware'),
|
ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
|
||||||
WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'),
|
ApiGateway_WebSocketAuthMiddleware: Symbol.for('ApiGateway_WebSocketAuthMiddleware'),
|
||||||
SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'),
|
ApiGateway_SubscriptionTokenAuthMiddleware: Symbol.for('ApiGateway_SubscriptionTokenAuthMiddleware'),
|
||||||
// Services
|
// Services
|
||||||
ServiceProxy: Symbol.for('ServiceProxy'),
|
ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
|
||||||
CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'),
|
ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
|
||||||
Timer: Symbol.for('Timer'),
|
ApiGateway_Timer: Symbol.for('ApiGateway_Timer'),
|
||||||
EndpointResolver: Symbol.for('EndpointResolver'),
|
ApiGateway_EndpointResolver: Symbol.for('ApiGateway_EndpointResolver'),
|
||||||
}
|
}
|
||||||
|
|
||||||
// export default TYPES
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CrossServiceTokenData } from '@standardnotes/security'
|
import { CrossServiceTokenData } from '@standardnotes/security'
|
||||||
import { RoleName } from '@standardnotes/domain-core'
|
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
import { NextFunction, Request, Response } from 'express'
|
import { NextFunction, Request, Response } from 'express'
|
||||||
import { BaseMiddleware } from 'inversify-express-utils'
|
import { BaseMiddleware } from 'inversify-express-utils'
|
||||||
@@ -51,10 +50,6 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
|||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
response.locals.freeUser =
|
|
||||||
decodedToken.roles.length === 1 &&
|
|
||||||
decodedToken.roles.find((role) => role.name === RoleName.NAMES.CoreUser) !== undefined
|
|
||||||
|
|
||||||
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
||||||
await this.crossServiceTokenCache.set({
|
await this.crossServiceTokenCache.set({
|
||||||
authorizationHeaderValue: authHeaderValue,
|
authorizationHeaderValue: authHeaderValue,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class LegacyController extends BaseHttpController {
|
|||||||
private AUTH_ROUTES: Map<string, string>
|
private AUTH_ROUTES: Map<string, string>
|
||||||
private PARAMETRIZED_AUTH_ROUTES: Map<string, string>
|
private PARAMETRIZED_AUTH_ROUTES: Map<string, string>
|
||||||
|
|
||||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.AUTH_ROUTES = new Map([
|
this.AUTH_ROUTES = new Map([
|
||||||
@@ -29,17 +29,17 @@ export class LegacyController extends BaseHttpController {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/items/sync', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/items/sync', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async legacyItemsSync(request: Request, response: Response): Promise<void> {
|
async legacyItemsSync(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/items/:item_id/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async legacyGetRevisions(request: Request, response: Response): Promise<void> {
|
async legacyGetRevisions(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/items/:item_id/revisions/:id', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/items/:item_id/revisions/:id', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async legacyGetRevision(request: Request, response: Response): Promise<void> {
|
async legacyGetRevision(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware {
|
export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||||
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
|
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
|
||||||
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
@inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||||
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
@inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||||
@inject(TYPES.Timer) timer: TimerInterface,
|
@inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
|
||||||
@inject(TYPES.Logger) logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) logger: Logger,
|
||||||
) {
|
) {
|
||||||
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
|
export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||||
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
|
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
|
||||||
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
@inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||||
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
@inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||||
@inject(TYPES.Timer) timer: TimerInterface,
|
@inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
|
||||||
@inject(TYPES.Logger) logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) logger: Logger,
|
||||||
) {
|
) {
|
||||||
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||||
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
|
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CrossServiceTokenData } from '@standardnotes/security'
|
import { CrossServiceTokenData } from '@standardnotes/security'
|
||||||
import { RoleName } from '@standardnotes/domain-core'
|
|
||||||
import { NextFunction, Request, Response } from 'express'
|
import { NextFunction, Request, Response } from 'express'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { BaseMiddleware } from 'inversify-express-utils'
|
import { BaseMiddleware } from 'inversify-express-utils'
|
||||||
@@ -12,10 +11,10 @@ import { TYPES } from '../Bootstrap/Types'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||||
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
|
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -60,9 +59,6 @@ export class WebSocketAuthMiddleware extends BaseMiddleware {
|
|||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
response.locals.freeUser =
|
|
||||||
decodedToken.roles.length === 1 &&
|
|
||||||
decodedToken.roles.find((role) => role.name === RoleName.NAMES.CoreUser) !== undefined
|
|
||||||
response.locals.user = decodedToken.user
|
response.locals.user = decodedToken.user
|
||||||
response.locals.roles = decodedToken.roles
|
response.locals.roles = decodedToken.roles
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1')
|
@controller('/v1')
|
||||||
export class ActionsController extends BaseHttpController {
|
export class ActionsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ export class ActionsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
|
@httpGet('/login-params', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
|
||||||
async loginParams(request: Request, response: Response): Promise<void> {
|
async loginParams(request: Request, response: Response): Promise<void> {
|
||||||
await this.serviceProxy.callAuthServer(
|
await this.serviceProxy.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -34,7 +34,7 @@ export class ActionsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/logout', TYPES.OptionalCrossServiceTokenMiddleware)
|
@httpPost('/logout', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
|
||||||
async logout(request: Request, response: Response): Promise<void> {
|
async logout(request: Request, response: Response): Promise<void> {
|
||||||
await this.serviceProxy.callAuthServer(
|
await this.serviceProxy.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -54,7 +54,7 @@ export class ActionsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/recovery/codes', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/recovery/codes', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async recoveryCodes(request: Request, response: Response): Promise<void> {
|
async recoveryCodes(request: Request, response: Response): Promise<void> {
|
||||||
await this.serviceProxy.callAuthServer(
|
await this.serviceProxy.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/authenticators')
|
@controller('/v1/authenticators')
|
||||||
export class AuthenticatorsController extends BaseHttpController {
|
export class AuthenticatorsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:authenticatorId', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:authenticatorId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async delete(request: Request, response: Response): Promise<void> {
|
async delete(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -29,7 +29,7 @@ export class AuthenticatorsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async list(request: Request, response: Response): Promise<void> {
|
async list(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -39,7 +39,7 @@ export class AuthenticatorsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/generate-registration-options', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/generate-registration-options', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async generateRegistrationOptions(request: Request, response: Response): Promise<void> {
|
async generateRegistrationOptions(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -59,7 +59,7 @@ export class AuthenticatorsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/verify-registration', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/verify-registration', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async verifyRegistration(request: Request, response: Response): Promise<void> {
|
async verifyRegistration(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/files')
|
@controller('/v1/files')
|
||||||
export class FilesController extends BaseHttpController {
|
export class FilesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/valet-tokens', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/valet-tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async createToken(request: Request, response: Response): Promise<void> {
|
async createToken(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
|||||||
|
|
||||||
@controller('/v1')
|
@controller('/v1')
|
||||||
export class InvoicesController extends BaseHttpController {
|
export class InvoicesController extends BaseHttpController {
|
||||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/invoices/send-latest', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/invoices/send-latest', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async sendLatestInvoice(request: Request, response: Response): Promise<void> {
|
async sendLatestInvoice(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-invoice', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-invoice', request.body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v1/items', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/items', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class ItemsController extends BaseHttpController {
|
export class ItemsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v1/messages', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/messages', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class MessagesController extends BaseHttpController {
|
export class MessagesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/offline')
|
@controller('/v1/offline')
|
||||||
export class OfflineController extends BaseHttpController {
|
export class OfflineController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
|||||||
|
|
||||||
@controller('/v1')
|
@controller('/v1')
|
||||||
export class PaymentsController extends BaseHttpController {
|
export class PaymentsController extends BaseHttpController {
|
||||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,12 +40,12 @@ export class PaymentsController extends BaseHttpController {
|
|||||||
await this.httpService.callPaymentsServer(request, response, 'api/extensions', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/extensions', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/subscriptions/tiered', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/subscriptions/tiered', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async createTieredSubscription(request: Request, response: Response): Promise<void> {
|
async createTieredSubscription(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/tiered', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/tiered', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/subscriptions/apple_iap_confirm', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/subscriptions/apple_iap_confirm', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async appleIAPConfirm(request: Request, response: Response): Promise<void> {
|
async appleIAPConfirm(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/apple_iap_confirm', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/apple_iap_confirm', request.body)
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ export class PaymentsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/payments/stripe-setup-intent', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/payments/stripe-setup-intent', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async createStripeSetupIntent(request: Request, response: Response): Promise<void> {
|
async createStripeSetupIntent(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/stripe-setup-intent', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/stripe-setup-intent', request.body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils'
|
import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils'
|
||||||
import { TYPES } from '../../Bootstrap/Types'
|
import { TYPES } from '../../Bootstrap/Types'
|
||||||
|
|
||||||
@controller('/v1/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/items/:item_id/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class RevisionsController extends BaseHttpController {
|
export class RevisionsController extends BaseHttpController {
|
||||||
@httpGet('/')
|
@httpGet('/')
|
||||||
async getRevisions(): Promise<results.JsonResult> {
|
async getRevisions(): Promise<results.JsonResult> {
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/sessions')
|
@controller('/v1/sessions')
|
||||||
export class SessionsController extends BaseHttpController {
|
export class SessionsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async getSessions(request: Request, response: Response): Promise<void> {
|
async getSessions(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -23,7 +23,7 @@ export class SessionsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:uuid', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:uuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async deleteSession(request: Request, response: Response): Promise<void> {
|
async deleteSession(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -35,7 +35,7 @@ export class SessionsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async deleteSessions(request: Request, response: Response): Promise<void> {
|
async deleteSessions(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class SharedVaultInvitesController extends BaseHttpController {
|
export class SharedVaultInvitesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -83,6 +83,16 @@ export class SharedVaultInvitesController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@httpDelete('/invites/outbound')
|
||||||
|
async deleteOutboundUserInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'shared-vaults/invites/outbound'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@httpGet('/invites/outbound')
|
@httpGet('/invites/outbound')
|
||||||
async getOutboundUserInvites(request: Request, response: Response): Promise<void> {
|
async getOutboundUserInvites(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callSyncingServer(
|
await this.httpService.callSyncingServer(
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class SharedVaultUsersController extends BaseHttpController {
|
export class SharedVaultUsersController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class SharedVaultsController extends BaseHttpController {
|
export class SharedVaultsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/subscription-invites')
|
@controller('/v1/subscription-invites')
|
||||||
export class SubscriptionInvitesController extends BaseHttpController {
|
export class SubscriptionInvitesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -25,7 +25,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async listInvites(request: Request, response: Response): Promise<void> {
|
async listInvites(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -35,7 +35,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:inviteUuid', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:inviteUuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async cancelSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
async cancelSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -48,7 +48,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/:inviteUuid/accept', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/:inviteUuid/accept', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async acceptInvite(request: Request, response: Response): Promise<void> {
|
async acceptInvite(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/subscription-tokens')
|
@controller('/v1/subscription-tokens')
|
||||||
export class TokensController extends BaseHttpController {
|
export class TokensController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async createToken(request: Request, response: Response): Promise<void> {
|
async createToken(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/users')
|
@controller('/v1/users')
|
||||||
export class UsersController extends BaseHttpController {
|
export class UsersController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||||
|
@inject(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER) private isConfiguredForHomeServer: boolean,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -32,12 +33,12 @@ export class UsersController extends BaseHttpController {
|
|||||||
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/claim-account', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/claim-account', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/send-activation-code', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/send-activation-code', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async sendActivationCode(request: Request, response: Response): Promise<void> {
|
async sendActivationCode(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPatch('/:userId', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPatch('/:userId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async updateUser(request: Request, response: Response): Promise<void> {
|
async updateUser(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -47,7 +48,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPut('/:userUuid/password', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPut('/:userUuid/password', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async changePassword(request: Request, response: Response): Promise<void> {
|
async changePassword(request: Request, response: Response): Promise<void> {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password',
|
'[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password',
|
||||||
@@ -65,7 +66,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPut('/:userUuid/attributes/credentials', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPut('/:userUuid/attributes/credentials', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async changeCredentials(request: Request, response: Response): Promise<void> {
|
async changeCredentials(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -79,7 +80,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userId/params', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/:userId/params', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async getKeyParams(request: Request, response: Response): Promise<void> {
|
async getKeyParams(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -88,12 +89,12 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@all('/:userId/mfa', TYPES.RequiredCrossServiceTokenMiddleware)
|
@all('/:userId/mfa', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async blockMFA(): Promise<results.StatusCodeResult> {
|
async blockMFA(): Promise<results.StatusCodeResult> {
|
||||||
return this.statusCode(401)
|
return this.statusCode(401)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/:userUuid/integrations/listed', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/:userUuid/integrations/listed', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async createListedAccount(request: Request, response: Response): Promise<void> {
|
async createListedAccount(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -113,7 +114,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/:userUuid/settings', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async listSettings(request: Request, response: Response): Promise<void> {
|
async listSettings(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -126,7 +127,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPut('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPut('/:userUuid/settings', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async putSetting(request: Request, response: Response): Promise<void> {
|
async putSetting(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -140,7 +141,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/:userUuid/settings/:settingName', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async getSetting(request: Request, response: Response): Promise<void> {
|
async getSetting(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -154,7 +155,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:userUuid/settings/:settingName', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async deleteSetting(request: Request, response: Response): Promise<void> {
|
async deleteSetting(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -169,7 +170,10 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet(
|
||||||
|
'/:userUuid/subscription-settings/:subscriptionSettingName',
|
||||||
|
TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware,
|
||||||
|
)
|
||||||
async getSubscriptionSetting(request: Request, response: Response): Promise<void> {
|
async getSubscriptionSetting(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -183,7 +187,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userUuid/features', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/:userUuid/features', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async getFeatures(request: Request, response: Response): Promise<void> {
|
async getFeatures(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -196,7 +200,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:userUuid/subscription', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpGet('/:userUuid/subscription', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async getSubscription(request: Request, response: Response): Promise<void> {
|
async getSubscription(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
@@ -209,7 +213,7 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/subscription', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpGet('/subscription', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
|
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
|
||||||
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
@@ -232,12 +236,20 @@ export class UsersController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:userUuid', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:userUuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async deleteUser(request: Request, response: Response): Promise<void> {
|
async deleteUser(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/account', request.body)
|
if (!this.isConfiguredForHomeServer) {
|
||||||
|
await this.httpService.callPaymentsServer(request, response, 'api/account', request.body, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.httpService.callAuthServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'users/:userUuid', request.params.userUuid),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/:userUuid/requests', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/:userUuid/requests', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async submitRequest(request: Request, response: Response): Promise<void> {
|
async submitRequest(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v1/sockets')
|
@controller('/v1/sockets')
|
||||||
export class WebSocketsController extends BaseHttpController {
|
export class WebSocketsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/tokens', TYPES.RequiredCrossServiceTokenMiddleware)
|
@httpPost('/tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
async createWebSocketConnectionToken(request: Request, response: Response): Promise<void> {
|
async createWebSocketConnectionToken(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callWebSocketServer(
|
await this.httpService.callWebSocketServer(
|
||||||
request,
|
request,
|
||||||
@@ -27,7 +27,7 @@ export class WebSocketsController extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/connections', TYPES.WebSocketAuthMiddleware)
|
@httpPost('/connections', TYPES.ApiGateway_WebSocketAuthMiddleware)
|
||||||
async createWebSocketConnection(request: Request, response: Response): Promise<void> {
|
async createWebSocketConnection(request: Request, response: Response): Promise<void> {
|
||||||
if (!request.headers.connectionid) {
|
if (!request.headers.connectionid) {
|
||||||
this.logger.error('Could not create a websocket connection. Missing connection id header.')
|
this.logger.error('Could not create a websocket connection. Missing connection id header.')
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
|||||||
@controller('/v2')
|
@controller('/v2')
|
||||||
export class ActionsControllerV2 extends BaseHttpController {
|
export class ActionsControllerV2 extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ export class ActionsControllerV2 extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
|
@httpPost('/login-params', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
|
||||||
async loginParams(request: Request, response: Response): Promise<void> {
|
async loginParams(request: Request, response: Response): Promise<void> {
|
||||||
await this.serviceProxy.callAuthServer(
|
await this.serviceProxy.callAuthServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
|||||||
|
|
||||||
@controller('/v2')
|
@controller('/v2')
|
||||||
export class PaymentsControllerV2 extends BaseHttpController {
|
export class PaymentsControllerV2 extends BaseHttpController {
|
||||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,22 +15,22 @@ export class PaymentsControllerV2 extends BaseHttpController {
|
|||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/subscriptions/tailored', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpGet('/subscriptions/tailored', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async getTailoredSubscriptionsWithFeatures(request: Request, response: Response): Promise<void> {
|
async getTailoredSubscriptionsWithFeatures(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/subscriptions/deltas', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpGet('/subscriptions/deltas', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async getSubscriptionDeltasForChangingPlan(request: Request, response: Response): Promise<void> {
|
async getSubscriptionDeltasForChangingPlan(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/subscriptions/deltas/apply', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/subscriptions/deltas/apply', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async applySubscriptionDelta(request: Request, response: Response): Promise<void> {
|
async applySubscriptionDelta(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas/apply', request.body)
|
await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas/apply', request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPost('/subscriptions/change-payment-method', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPost('/subscriptions/change-payment-method', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async changePaymentMethod(request: Request, response: Response): Promise<void> {
|
async changePaymentMethod(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(
|
await this.httpService.callPaymentsServer(
|
||||||
request,
|
request,
|
||||||
@@ -40,7 +40,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpGet('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async getSubscription(request: Request, response: Response): Promise<void> {
|
async getSubscription(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(
|
await this.httpService.callPaymentsServer(
|
||||||
request,
|
request,
|
||||||
@@ -50,7 +50,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpDelete('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async cancelSubscription(request: Request, response: Response): Promise<void> {
|
async cancelSubscription(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(
|
await this.httpService.callPaymentsServer(
|
||||||
request,
|
request,
|
||||||
@@ -60,7 +60,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpPatch('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
|
@httpPatch('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async updateSubscription(request: Request, response: Response): Promise<void> {
|
async updateSubscription(request: Request, response: Response): Promise<void> {
|
||||||
await this.httpService.callPaymentsServer(
|
await this.httpService.callPaymentsServer(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
|||||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
@controller('/v2/items/:itemUuid/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
|
@controller('/v2/items/:itemUuid/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||||
export class RevisionsControllerV2 extends BaseHttpController {
|
export class RevisionsControllerV2 extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterf
|
|||||||
private readonly PREFIX = 'cst'
|
private readonly PREFIX = 'cst'
|
||||||
private readonly USER_CST_PREFIX = 'user-cst'
|
private readonly USER_CST_PREFIX = 'user-cst'
|
||||||
|
|
||||||
constructor(@inject(TYPES.Redis) private redisClient: IORedis.Redis) {}
|
constructor(@inject(TYPES.ApiGateway_Redis) private redisClient: IORedis.Redis) {}
|
||||||
|
|
||||||
async set(dto: {
|
async set(dto: {
|
||||||
authorizationHeaderValue: string
|
authorizationHeaderValue: string
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ import { ServiceProxyInterface } from './ServiceProxyInterface'
|
|||||||
@injectable()
|
@injectable()
|
||||||
export class HttpServiceProxy implements ServiceProxyInterface {
|
export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||||
@inject(TYPES.SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
|
@inject(TYPES.ApiGateway_SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
|
||||||
@inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
|
@inject(TYPES.ApiGateway_PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
|
||||||
@inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string,
|
@inject(TYPES.ApiGateway_FILES_SERVER_URL) private filesServerUrl: string,
|
||||||
@inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
@inject(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
||||||
@inject(TYPES.REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
@inject(TYPES.ApiGateway_REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
||||||
@inject(TYPES.EMAIL_SERVER_URL) private emailServerUrl: string,
|
@inject(TYPES.ApiGateway_EMAIL_SERVER_URL) private emailServerUrl: string,
|
||||||
@inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
@inject(TYPES.ApiGateway_HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
||||||
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
@inject(TYPES.ApiGateway_CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async validateSession(
|
async validateSession(
|
||||||
@@ -130,19 +130,26 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
|||||||
response: Response,
|
response: Response,
|
||||||
endpointOrMethodIdentifier: string,
|
endpointOrMethodIdentifier: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void> {
|
returnRawResponse?: boolean,
|
||||||
|
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||||
if (!this.paymentsServerUrl) {
|
if (!this.paymentsServerUrl) {
|
||||||
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
|
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await this.callServerWithLegacyFormat(
|
|
||||||
|
const rawResponse = await this.callServerWithLegacyFormat(
|
||||||
this.paymentsServerUrl,
|
this.paymentsServerUrl,
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
endpointOrMethodIdentifier,
|
endpointOrMethodIdentifier,
|
||||||
payload,
|
payload,
|
||||||
|
returnRawResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (returnRawResponse) {
|
||||||
|
return rawResponse
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async callAuthServerWithLegacyFormat(
|
async callAuthServerWithLegacyFormat(
|
||||||
@@ -279,7 +286,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
|||||||
response: Response,
|
response: Response,
|
||||||
endpointOrMethodIdentifier: string,
|
endpointOrMethodIdentifier: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void> {
|
returnRawResponse?: boolean,
|
||||||
|
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||||
const serviceResponse = await this.getServerResponse(
|
const serviceResponse = await this.getServerResponse(
|
||||||
serverUrl,
|
serverUrl,
|
||||||
request,
|
request,
|
||||||
@@ -295,9 +303,21 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
|||||||
this.applyResponseHeaders(serviceResponse, response)
|
this.applyResponseHeaders(serviceResponse, response)
|
||||||
|
|
||||||
if (serviceResponse.request._redirectable._redirectCount > 0) {
|
if (serviceResponse.request._redirectable._redirectCount > 0) {
|
||||||
response.status(302).redirect(serviceResponse.request.res.responseUrl)
|
response.status(302)
|
||||||
|
|
||||||
|
if (returnRawResponse) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
response.redirect(serviceResponse.request.res.responseUrl)
|
||||||
} else {
|
} else {
|
||||||
response.status(serviceResponse.status).send(serviceResponse.data)
|
response.status(serviceResponse.status)
|
||||||
|
|
||||||
|
if (returnRawResponse) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
response.send(serviceResponse.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ export interface ServiceProxyInterface {
|
|||||||
response: Response,
|
response: Response,
|
||||||
endpointOrMethodIdentifier: string,
|
endpointOrMethodIdentifier: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void>
|
returnRawResponse?: boolean,
|
||||||
|
): Promise<void | Response<unknown, Record<string, unknown>>>
|
||||||
callWebSocketServer(
|
callWebSocketServer(
|
||||||
request: Request,
|
request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export class EndpointResolver implements EndpointResolverInterface {
|
|||||||
['[PATCH]:users/:userId', 'auth.users.update'],
|
['[PATCH]:users/:userId', 'auth.users.update'],
|
||||||
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
||||||
['[PUT]:auth/params', 'auth.users.getKeyParams'],
|
['[PUT]:auth/params', 'auth.users.getKeyParams'],
|
||||||
|
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
||||||
['[POST]:listed', 'auth.users.createListedAccount'],
|
['[POST]:listed', 'auth.users.createListedAccount'],
|
||||||
['[POST]:auth', 'auth.users.register'],
|
['[POST]:auth', 'auth.users.register'],
|
||||||
['[GET]:users/:userUuid/settings', 'auth.users.getSettings'],
|
['[GET]:users/:userUuid/settings', 'auth.users.getSettings'],
|
||||||
@@ -79,6 +80,7 @@ export class EndpointResolver implements EndpointResolverInterface {
|
|||||||
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/accept', 'sync.shared-vault-invites.accept'],
|
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/accept', 'sync.shared-vault-invites.accept'],
|
||||||
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/decline', 'sync.shared-vault-invites.decline'],
|
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/decline', 'sync.shared-vault-invites.decline'],
|
||||||
['[DELETE]:shared-vaults/invites/inbound', 'sync.shared-vault-invites.delete-inbound'],
|
['[DELETE]:shared-vaults/invites/inbound', 'sync.shared-vault-invites.delete-inbound'],
|
||||||
|
['[DELETE]:shared-vaults/invites/outbound', 'sync.shared-vault-invites.delete-outbound'],
|
||||||
['[GET]:shared-vaults/invites/outbound', 'sync.shared-vault-invites.get-outbound'],
|
['[GET]:shared-vaults/invites/outbound', 'sync.shared-vault-invites.get-outbound'],
|
||||||
['[GET]:shared-vaults/invites', 'sync.shared-vault-invites.get-user-invites'],
|
['[GET]:shared-vaults/invites', 'sync.shared-vault-invites.get-user-invites'],
|
||||||
['[GET]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.get-vault-invites'],
|
['[GET]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.get-vault-invites'],
|
||||||
|
|||||||
@@ -3,6 +3,102 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [1.132.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.5...@standardnotes/auth-server@1.132.0) (2023-08-18)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add mechanism for determining if a user should use the primary or secondary items database ([#700](https://github.com/standardnotes/server/issues/700)) ([302b624](https://github.com/standardnotes/server/commit/302b624504f4c87fd7c3ddfee77cbdc14a61018b))
|
||||||
|
|
||||||
|
## [1.131.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.4...@standardnotes/auth-server@1.131.5) (2023-08-15)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** passing the invalidate cache header ([#697](https://github.com/standardnotes/server/issues/697)) ([83ad069](https://github.com/standardnotes/server/commit/83ad069c5dd9afa3a6db881f0d8a55a58d0642aa))
|
||||||
|
|
||||||
|
## [1.131.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.3...@standardnotes/auth-server@1.131.4) (2023-08-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.131.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.2...@standardnotes/auth-server@1.131.3) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.131.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.1...@standardnotes/auth-server@1.131.2) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.131.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.131.0...@standardnotes/auth-server@1.131.1) (2023-08-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [1.131.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.130.1...@standardnotes/auth-server@1.131.0) (2023-08-08)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* update storage quota used for user based on shared vault files ([#689](https://github.com/standardnotes/server/issues/689)) ([5311e74](https://github.com/standardnotes/server/commit/5311e7426617da6fc75593dd0fcbff589ca4fc22))
|
||||||
|
|
||||||
|
## [1.130.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.130.0...@standardnotes/auth-server@1.130.1) (2023-08-07)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** update user agent upon refreshing session token ([#685](https://github.com/standardnotes/server/issues/685)) ([bd5f492](https://github.com/standardnotes/server/commit/bd5f492a733f783c64fa4bc5840b4a9f5c913d3d))
|
||||||
|
|
||||||
|
# [1.130.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.129.0...@standardnotes/auth-server@1.130.0) (2023-08-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** invalidate other sessions for user if the email or password are changed ([#684](https://github.com/standardnotes/server/issues/684)) ([f39d3ac](https://github.com/standardnotes/server/commit/f39d3aca5b7bb9e5f9c1c24cbe2359f30dea835c))
|
||||||
|
|
||||||
|
# [1.129.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.128.1...@standardnotes/auth-server@1.129.0) (2023-08-03)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** add handling payments account deleted events STA-1769 ([#682](https://github.com/standardnotes/server/issues/682)) ([8e35dfa](https://github.com/standardnotes/server/commit/8e35dfa4b77256f4c0a3294b296a5526fd1020ad))
|
||||||
|
|
||||||
|
## [1.128.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.128.0...@standardnotes/auth-server@1.128.1) (2023-08-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [1.128.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.127.2...@standardnotes/auth-server@1.128.0) (2023-08-02)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* enable Write Ahead Log mode for SQLite ([#681](https://github.com/standardnotes/server/issues/681)) ([8cd7a13](https://github.com/standardnotes/server/commit/8cd7a138ab56f6a2b0d6c06ef6041ab9b85ae540))
|
||||||
|
|
||||||
|
## [1.127.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.127.1...@standardnotes/auth-server@1.127.2) (2023-08-01)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* controller naming ([#678](https://github.com/standardnotes/server/issues/678)) ([56f0aef](https://github.com/standardnotes/server/commit/56f0aef21d3fcec7ac7e968cb1c1b071becbbe26))
|
||||||
|
|
||||||
|
## [1.127.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.127.0...@standardnotes/auth-server@1.127.1) (2023-07-31)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** auth middleware on delete account ([318af57](https://github.com/standardnotes/server/commit/318af5757d6c42f580157647b22112a9936765e7))
|
||||||
|
|
||||||
|
# [1.127.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.5...@standardnotes/auth-server@1.127.0) (2023-07-31)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* refactor deleting account ([#676](https://github.com/standardnotes/server/issues/676)) ([0d5dcdd](https://github.com/standardnotes/server/commit/0d5dcdd8ec2336e41e7604c4157f79a89163ed29))
|
||||||
|
|
||||||
|
## [1.126.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.4...@standardnotes/auth-server@1.126.5) (2023-07-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.126.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.3...@standardnotes/auth-server@1.126.4) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.126.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.2...@standardnotes/auth-server@1.126.3) (2023-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.126.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.1...@standardnotes/auth-server@1.126.2) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
## [1.126.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.0...@standardnotes/auth-server@1.126.1) (2023-07-21)
|
## [1.126.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.0...@standardnotes/auth-server@1.126.1) (2023-07-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/auth-server
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|||||||
+18
-18
@@ -1,23 +1,23 @@
|
|||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAuthController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthenticatorsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSessionsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSessionsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionInvitesController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedUserRequestsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedWebSocketsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressUsersController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedUsersController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedValetTokenController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAdminController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedAdminController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionTokensController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSettingsController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSettingsController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSessionController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedSessionController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressOfflineController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedOfflineController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressListedController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedListedController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressInternalController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedInternalController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressHealthCheckController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
|
||||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController'
|
import '../src/Infra/InversifyExpressUtils/AnnotatedFeaturesController'
|
||||||
|
|
||||||
import * as cors from 'cors'
|
import * as cors from 'cors'
|
||||||
import { urlencoded, json, Request, Response, NextFunction } from 'express'
|
import { urlencoded, json, Request, Response, NextFunction } from 'express'
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
|
export class AddTransitionRole1692348191367 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
'INSERT INTO `roles` (uuid, name, version) VALUES ("e7381dc5-3d67-49e9-b7bd-f2407b2f726e", "TRANSITION_USER", 1)',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(): Promise<void> {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
|
export class AddTransitionRole1692348280258 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
'INSERT INTO `roles` (uuid, name, version) VALUES ("e7381dc5-3d67-49e9-b7bd-f2407b2f726e", "TRANSITION_USER", 1)',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(): Promise<void> {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/auth-server",
|
"name": "@standardnotes/auth-server",
|
||||||
"version": "1.126.1",
|
"version": "1.132.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { GetUserKeyParams } from '../Domain/UseCase/GetUserKeyParams/GetUserKeyP
|
|||||||
import { UpdateUser } from '../Domain/UseCase/UpdateUser'
|
import { UpdateUser } from '../Domain/UseCase/UpdateUser'
|
||||||
import { RedisEphemeralSessionRepository } from '../Infra/Redis/RedisEphemeralSessionRepository'
|
import { RedisEphemeralSessionRepository } from '../Infra/Redis/RedisEphemeralSessionRepository'
|
||||||
import { GetActiveSessionsForUser } from '../Domain/UseCase/GetActiveSessionsForUser'
|
import { GetActiveSessionsForUser } from '../Domain/UseCase/GetActiveSessionsForUser'
|
||||||
import { DeletePreviousSessionsForUser } from '../Domain/UseCase/DeletePreviousSessionsForUser'
|
import { DeleteOtherSessionsForUser } from '../Domain/UseCase/DeleteOtherSessionsForUser'
|
||||||
import { DeleteSessionForUser } from '../Domain/UseCase/DeleteSessionForUser'
|
import { DeleteSessionForUser } from '../Domain/UseCase/DeleteSessionForUser'
|
||||||
import { Register } from '../Domain/UseCase/Register'
|
import { Register } from '../Domain/UseCase/Register'
|
||||||
import { LockRepository } from '../Infra/Redis/LockRepository'
|
import { LockRepository } from '../Infra/Redis/LockRepository'
|
||||||
@@ -234,24 +234,28 @@ import { OfflineUserAuthMiddleware } from '../Infra/InversifyExpressUtils/Middle
|
|||||||
import { LockMiddleware } from '../Infra/InversifyExpressUtils/Middleware/LockMiddleware'
|
import { LockMiddleware } from '../Infra/InversifyExpressUtils/Middleware/LockMiddleware'
|
||||||
import { RequiredCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware'
|
import { RequiredCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware'
|
||||||
import { OptionalCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware'
|
import { OptionalCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware'
|
||||||
import { HomeServerSettingsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSettingsController'
|
import { BaseSettingsController } from '../Infra/InversifyExpressUtils/Base/BaseSettingsController'
|
||||||
import { HomeServerAdminController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAdminController'
|
import { BaseAdminController } from '../Infra/InversifyExpressUtils/Base/BaseAdminController'
|
||||||
import { HomeServerAuthController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAuthController'
|
import { BaseAuthController } from '../Infra/InversifyExpressUtils/Base/BaseAuthController'
|
||||||
import { HomeServerAuthenticatorsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAuthenticatorsController'
|
import { BaseAuthenticatorsController } from '../Infra/InversifyExpressUtils/Base/BaseAuthenticatorsController'
|
||||||
import { HomeServerFeaturesController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerFeaturesController'
|
import { BaseFeaturesController } from '../Infra/InversifyExpressUtils/Base/BaseFeaturesController'
|
||||||
import { HomeServerListedController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerListedController'
|
import { BaseListedController } from '../Infra/InversifyExpressUtils/Base/BaseListedController'
|
||||||
import { HomeServerOfflineController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerOfflineController'
|
import { BaseOfflineController } from '../Infra/InversifyExpressUtils/Base/BaseOfflineController'
|
||||||
import { HomeServerSessionController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionController'
|
import { BaseSessionController } from '../Infra/InversifyExpressUtils/Base/BaseSessionController'
|
||||||
import { HomeServerSubscriptionInvitesController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionInvitesController'
|
import { BaseSubscriptionInvitesController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionInvitesController'
|
||||||
import { HomeServerSubscriptionSettingsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionSettingsController'
|
import { BaseSubscriptionSettingsController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController'
|
||||||
import { HomeServerSubscriptionTokensController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionTokensController'
|
import { BaseSubscriptionTokensController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionTokensController'
|
||||||
import { HomeServerUserRequestsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerUserRequestsController'
|
import { BaseUserRequestsController } from '../Infra/InversifyExpressUtils/Base/BaseUserRequestsController'
|
||||||
import { HomeServerUsersController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerUsersController'
|
import { BaseUsersController } from '../Infra/InversifyExpressUtils/Base/BaseUsersController'
|
||||||
import { HomeServerValetTokenController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerValetTokenController'
|
import { BaseValetTokenController } from '../Infra/InversifyExpressUtils/Base/BaseValetTokenController'
|
||||||
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
|
import { BaseWebSocketsController } from '../Infra/InversifyExpressUtils/Base/BaseWebSocketsController'
|
||||||
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
|
import { BaseSessionsController } from '../Infra/InversifyExpressUtils/Base/BaseSessionsController'
|
||||||
import { Transform } from 'stream'
|
import { Transform } from 'stream'
|
||||||
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
||||||
|
import { PaymentsAccountDeletedEventHandler } from '../Domain/Handler/PaymentsAccountDeletedEventHandler'
|
||||||
|
import { UpdateStorageQuotaUsedForUser } from '../Domain/UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||||
|
import { SharedVaultFileUploadedEventHandler } from '../Domain/Handler/SharedVaultFileUploadedEventHandler'
|
||||||
|
import { SharedVaultFileRemovedEventHandler } from '../Domain/Handler/SharedVaultFileRemovedEventHandler'
|
||||||
|
|
||||||
export class ContainerConfigLoader {
|
export class ContainerConfigLoader {
|
||||||
async load(configuration?: {
|
async load(configuration?: {
|
||||||
@@ -556,6 +560,9 @@ export class ContainerConfigLoader {
|
|||||||
container
|
container
|
||||||
.bind(TYPES.Auth_READONLY_USERS)
|
.bind(TYPES.Auth_READONLY_USERS)
|
||||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
||||||
|
container
|
||||||
|
.bind(TYPES.Auth_TRANSITION_MODE_ENABLED)
|
||||||
|
.toConstantValue(env.get('TRANSITION_MODE_ENABLED', true) === 'true')
|
||||||
|
|
||||||
if (isConfiguredForInMemoryCache) {
|
if (isConfiguredForInMemoryCache) {
|
||||||
container
|
container
|
||||||
@@ -826,9 +833,7 @@ export class ContainerConfigLoader {
|
|||||||
container.bind<UpdateUser>(TYPES.Auth_UpdateUser).to(UpdateUser)
|
container.bind<UpdateUser>(TYPES.Auth_UpdateUser).to(UpdateUser)
|
||||||
container.bind<Register>(TYPES.Auth_Register).to(Register)
|
container.bind<Register>(TYPES.Auth_Register).to(Register)
|
||||||
container.bind<GetActiveSessionsForUser>(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser)
|
container.bind<GetActiveSessionsForUser>(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser)
|
||||||
container
|
container.bind<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser).to(DeleteOtherSessionsForUser)
|
||||||
.bind<DeletePreviousSessionsForUser>(TYPES.Auth_DeletePreviousSessionsForUser)
|
|
||||||
.to(DeletePreviousSessionsForUser)
|
|
||||||
container.bind<DeleteSessionForUser>(TYPES.Auth_DeleteSessionForUser).to(DeleteSessionForUser)
|
container.bind<DeleteSessionForUser>(TYPES.Auth_DeleteSessionForUser).to(DeleteSessionForUser)
|
||||||
container.bind<ChangeCredentials>(TYPES.Auth_ChangeCredentials).to(ChangeCredentials)
|
container.bind<ChangeCredentials>(TYPES.Auth_ChangeCredentials).to(ChangeCredentials)
|
||||||
container.bind<GetSettings>(TYPES.Auth_GetSettings).to(GetSettings)
|
container.bind<GetSettings>(TYPES.Auth_GetSettings).to(GetSettings)
|
||||||
@@ -883,6 +888,15 @@ export class ContainerConfigLoader {
|
|||||||
container.bind<VerifyPredicate>(TYPES.Auth_VerifyPredicate).to(VerifyPredicate)
|
container.bind<VerifyPredicate>(TYPES.Auth_VerifyPredicate).to(VerifyPredicate)
|
||||||
container.bind<CreateCrossServiceToken>(TYPES.Auth_CreateCrossServiceToken).to(CreateCrossServiceToken)
|
container.bind<CreateCrossServiceToken>(TYPES.Auth_CreateCrossServiceToken).to(CreateCrossServiceToken)
|
||||||
container.bind<ProcessUserRequest>(TYPES.Auth_ProcessUserRequest).to(ProcessUserRequest)
|
container.bind<ProcessUserRequest>(TYPES.Auth_ProcessUserRequest).to(ProcessUserRequest)
|
||||||
|
container
|
||||||
|
.bind<UpdateStorageQuotaUsedForUser>(TYPES.Auth_UpdateStorageQuotaUsedForUser)
|
||||||
|
.toConstantValue(
|
||||||
|
new UpdateStorageQuotaUsedForUser(
|
||||||
|
container.get(TYPES.Auth_UserRepository),
|
||||||
|
container.get(TYPES.Auth_UserSubscriptionService),
|
||||||
|
container.get(TYPES.Auth_SubscriptionSettingService),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Controller
|
// Controller
|
||||||
container
|
container
|
||||||
@@ -952,8 +966,38 @@ export class ContainerConfigLoader {
|
|||||||
container
|
container
|
||||||
.bind<UserEmailChangedEventHandler>(TYPES.Auth_UserEmailChangedEventHandler)
|
.bind<UserEmailChangedEventHandler>(TYPES.Auth_UserEmailChangedEventHandler)
|
||||||
.to(UserEmailChangedEventHandler)
|
.to(UserEmailChangedEventHandler)
|
||||||
container.bind<FileUploadedEventHandler>(TYPES.Auth_FileUploadedEventHandler).to(FileUploadedEventHandler)
|
container
|
||||||
container.bind<FileRemovedEventHandler>(TYPES.Auth_FileRemovedEventHandler).to(FileRemovedEventHandler)
|
.bind<FileUploadedEventHandler>(TYPES.Auth_FileUploadedEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new FileUploadedEventHandler(
|
||||||
|
container.get(TYPES.Auth_UpdateStorageQuotaUsedForUser),
|
||||||
|
container.get(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<SharedVaultFileUploadedEventHandler>(TYPES.Auth_SharedVaultFileUploadedEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new SharedVaultFileUploadedEventHandler(
|
||||||
|
container.get(TYPES.Auth_UpdateStorageQuotaUsedForUser),
|
||||||
|
container.get(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<FileRemovedEventHandler>(TYPES.Auth_FileRemovedEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new FileRemovedEventHandler(
|
||||||
|
container.get(TYPES.Auth_UpdateStorageQuotaUsedForUser),
|
||||||
|
container.get(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<SharedVaultFileRemovedEventHandler>(TYPES.Auth_SharedVaultFileRemovedEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new SharedVaultFileRemovedEventHandler(
|
||||||
|
container.get(TYPES.Auth_UpdateStorageQuotaUsedForUser),
|
||||||
|
container.get(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
container
|
container
|
||||||
.bind<ListedAccountCreatedEventHandler>(TYPES.Auth_ListedAccountCreatedEventHandler)
|
.bind<ListedAccountCreatedEventHandler>(TYPES.Auth_ListedAccountCreatedEventHandler)
|
||||||
.to(ListedAccountCreatedEventHandler)
|
.to(ListedAccountCreatedEventHandler)
|
||||||
@@ -978,6 +1022,14 @@ export class ContainerConfigLoader {
|
|||||||
container.get(TYPES.Auth_SettingService),
|
container.get(TYPES.Auth_SettingService),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
container
|
||||||
|
.bind<PaymentsAccountDeletedEventHandler>(TYPES.Auth_PaymentsAccountDeletedEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new PaymentsAccountDeletedEventHandler(
|
||||||
|
container.get(TYPES.Auth_DeleteAccount),
|
||||||
|
container.get(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||||
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
||||||
@@ -992,7 +1044,9 @@ export class ContainerConfigLoader {
|
|||||||
['SUBSCRIPTION_REASSIGNED', container.get(TYPES.Auth_SubscriptionReassignedEventHandler)],
|
['SUBSCRIPTION_REASSIGNED', container.get(TYPES.Auth_SubscriptionReassignedEventHandler)],
|
||||||
['USER_EMAIL_CHANGED', container.get(TYPES.Auth_UserEmailChangedEventHandler)],
|
['USER_EMAIL_CHANGED', container.get(TYPES.Auth_UserEmailChangedEventHandler)],
|
||||||
['FILE_UPLOADED', container.get(TYPES.Auth_FileUploadedEventHandler)],
|
['FILE_UPLOADED', container.get(TYPES.Auth_FileUploadedEventHandler)],
|
||||||
|
['SHARED_VAULT_FILE_UPLOADED', container.get(TYPES.Auth_SharedVaultFileUploadedEventHandler)],
|
||||||
['FILE_REMOVED', container.get(TYPES.Auth_FileRemovedEventHandler)],
|
['FILE_REMOVED', container.get(TYPES.Auth_FileRemovedEventHandler)],
|
||||||
|
['SHARED_VAULT_FILE_REMOVED', container.get(TYPES.Auth_SharedVaultFileRemovedEventHandler)],
|
||||||
['LISTED_ACCOUNT_CREATED', container.get(TYPES.Auth_ListedAccountCreatedEventHandler)],
|
['LISTED_ACCOUNT_CREATED', container.get(TYPES.Auth_ListedAccountCreatedEventHandler)],
|
||||||
['LISTED_ACCOUNT_DELETED', container.get(TYPES.Auth_ListedAccountDeletedEventHandler)],
|
['LISTED_ACCOUNT_DELETED', container.get(TYPES.Auth_ListedAccountDeletedEventHandler)],
|
||||||
[
|
[
|
||||||
@@ -1005,6 +1059,7 @@ export class ContainerConfigLoader {
|
|||||||
],
|
],
|
||||||
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.Auth_PredicateVerificationRequestedEventHandler)],
|
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.Auth_PredicateVerificationRequestedEventHandler)],
|
||||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
||||||
|
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
||||||
])
|
])
|
||||||
|
|
||||||
if (isConfiguredForHomeServer) {
|
if (isConfiguredForHomeServer) {
|
||||||
@@ -1037,9 +1092,9 @@ export class ContainerConfigLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container
|
container
|
||||||
.bind<HomeServerAuthController>(TYPES.Auth_HomeServerAuthController)
|
.bind<BaseAuthController>(TYPES.Auth_BaseAuthController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerAuthController(
|
new BaseAuthController(
|
||||||
container.get(TYPES.Auth_VerifyMFA),
|
container.get(TYPES.Auth_VerifyMFA),
|
||||||
container.get(TYPES.Auth_SignIn),
|
container.get(TYPES.Auth_SignIn),
|
||||||
container.get(TYPES.Auth_GetUserKeyParams),
|
container.get(TYPES.Auth_GetUserKeyParams),
|
||||||
@@ -1054,42 +1109,42 @@ export class ContainerConfigLoader {
|
|||||||
// Inversify Controllers
|
// Inversify Controllers
|
||||||
if (isConfiguredForHomeServer) {
|
if (isConfiguredForHomeServer) {
|
||||||
container
|
container
|
||||||
.bind<HomeServerAuthenticatorsController>(TYPES.Auth_HomeServerAuthenticatorsController)
|
.bind<BaseAuthenticatorsController>(TYPES.Auth_BaseAuthenticatorsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerAuthenticatorsController(
|
new BaseAuthenticatorsController(
|
||||||
container.get(TYPES.Auth_AuthenticatorsController),
|
container.get(TYPES.Auth_AuthenticatorsController),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSubscriptionInvitesController>(TYPES.Auth_HomeServerSubscriptionInvitesController)
|
.bind<BaseSubscriptionInvitesController>(TYPES.Auth_BaseSubscriptionInvitesController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSubscriptionInvitesController(
|
new BaseSubscriptionInvitesController(
|
||||||
container.get(TYPES.Auth_SubscriptionInvitesController),
|
container.get(TYPES.Auth_SubscriptionInvitesController),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerUserRequestsController>(TYPES.Auth_HomeServerUserRequestsController)
|
.bind<BaseUserRequestsController>(TYPES.Auth_BaseUserRequestsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerUserRequestsController(
|
new BaseUserRequestsController(
|
||||||
container.get(TYPES.Auth_UserRequestsController),
|
container.get(TYPES.Auth_UserRequestsController),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerWebSocketsController>(TYPES.Auth_HomeServerWebSocketsController)
|
.bind<BaseWebSocketsController>(TYPES.Auth_BaseWebSocketsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerWebSocketsController(
|
new BaseWebSocketsController(
|
||||||
container.get(TYPES.Auth_CreateCrossServiceToken),
|
container.get(TYPES.Auth_CreateCrossServiceToken),
|
||||||
container.get(TYPES.Auth_WebSocketConnectionTokenDecoder),
|
container.get(TYPES.Auth_WebSocketConnectionTokenDecoder),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSessionsController>(TYPES.Auth_HomeServerSessionsController)
|
.bind<BaseSessionsController>(TYPES.Auth_BaseSessionsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSessionsController(
|
new BaseSessionsController(
|
||||||
container.get(TYPES.Auth_GetActiveSessionsForUser),
|
container.get(TYPES.Auth_GetActiveSessionsForUser),
|
||||||
container.get(TYPES.Auth_AuthenticateRequest),
|
container.get(TYPES.Auth_AuthenticateRequest),
|
||||||
container.get(TYPES.Auth_SessionProjector),
|
container.get(TYPES.Auth_SessionProjector),
|
||||||
@@ -1098,17 +1153,17 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerValetTokenController>(TYPES.Auth_HomeServerValetTokenController)
|
.bind<BaseValetTokenController>(TYPES.Auth_BaseValetTokenController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerValetTokenController(
|
new BaseValetTokenController(
|
||||||
container.get(TYPES.Auth_CreateValetToken),
|
container.get(TYPES.Auth_CreateValetToken),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerUsersController>(TYPES.Auth_HomeServerUsersController)
|
.bind<BaseUsersController>(TYPES.Auth_BaseUsersController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerUsersController(
|
new BaseUsersController(
|
||||||
container.get(TYPES.Auth_UpdateUser),
|
container.get(TYPES.Auth_UpdateUser),
|
||||||
container.get(TYPES.Auth_GetUserKeyParams),
|
container.get(TYPES.Auth_GetUserKeyParams),
|
||||||
container.get(TYPES.Auth_DeleteAccount),
|
container.get(TYPES.Auth_DeleteAccount),
|
||||||
@@ -1120,9 +1175,9 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerAdminController>(TYPES.Auth_HomeServerAdminController)
|
.bind<BaseAdminController>(TYPES.Auth_BaseAdminController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerAdminController(
|
new BaseAdminController(
|
||||||
container.get(TYPES.Auth_DeleteSetting),
|
container.get(TYPES.Auth_DeleteSetting),
|
||||||
container.get(TYPES.Auth_UserRepository),
|
container.get(TYPES.Auth_UserRepository),
|
||||||
container.get(TYPES.Auth_CreateSubscriptionToken),
|
container.get(TYPES.Auth_CreateSubscriptionToken),
|
||||||
@@ -1131,9 +1186,9 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSubscriptionTokensController>(TYPES.Auth_HomeServerSubscriptionTokensController)
|
.bind<BaseSubscriptionTokensController>(TYPES.Auth_BaseSubscriptionTokensController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSubscriptionTokensController(
|
new BaseSubscriptionTokensController(
|
||||||
container.get(TYPES.Auth_CreateSubscriptionToken),
|
container.get(TYPES.Auth_CreateSubscriptionToken),
|
||||||
container.get(TYPES.Auth_AuthenticateSubscriptionToken),
|
container.get(TYPES.Auth_AuthenticateSubscriptionToken),
|
||||||
container.get(TYPES.Auth_SettingService),
|
container.get(TYPES.Auth_SettingService),
|
||||||
@@ -1145,17 +1200,17 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSubscriptionSettingsController>(TYPES.Auth_HomeServerSubscriptionSettingsController)
|
.bind<BaseSubscriptionSettingsController>(TYPES.Auth_BaseSubscriptionSettingsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSubscriptionSettingsController(
|
new BaseSubscriptionSettingsController(
|
||||||
container.get(TYPES.Auth_GetSetting),
|
container.get(TYPES.Auth_GetSetting),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSettingsController>(TYPES.Auth_HomeServerSettingsController)
|
.bind<BaseSettingsController>(TYPES.Auth_BaseSettingsController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSettingsController(
|
new BaseSettingsController(
|
||||||
container.get(TYPES.Auth_GetSettings),
|
container.get(TYPES.Auth_GetSettings),
|
||||||
container.get(TYPES.Auth_GetSetting),
|
container.get(TYPES.Auth_GetSetting),
|
||||||
container.get(TYPES.Auth_UpdateSetting),
|
container.get(TYPES.Auth_UpdateSetting),
|
||||||
@@ -1164,19 +1219,19 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerSessionController>(TYPES.Auth_HomeServerSessionController)
|
.bind<BaseSessionController>(TYPES.Auth_BaseSessionController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerSessionController(
|
new BaseSessionController(
|
||||||
container.get(TYPES.Auth_DeleteSessionForUser),
|
container.get(TYPES.Auth_DeleteSessionForUser),
|
||||||
container.get(TYPES.Auth_DeletePreviousSessionsForUser),
|
container.get(TYPES.Auth_DeleteOtherSessionsForUser),
|
||||||
container.get(TYPES.Auth_RefreshSessionToken),
|
container.get(TYPES.Auth_RefreshSessionToken),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerOfflineController>(TYPES.Auth_HomeServerOfflineController)
|
.bind<BaseOfflineController>(TYPES.Auth_BaseOfflineController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerOfflineController(
|
new BaseOfflineController(
|
||||||
container.get(TYPES.Auth_GetUserFeatures),
|
container.get(TYPES.Auth_GetUserFeatures),
|
||||||
container.get(TYPES.Auth_GetUserOfflineSubscription),
|
container.get(TYPES.Auth_GetUserOfflineSubscription),
|
||||||
container.get(TYPES.Auth_CreateOfflineSubscriptionToken),
|
container.get(TYPES.Auth_CreateOfflineSubscriptionToken),
|
||||||
@@ -1188,17 +1243,17 @@ export class ContainerConfigLoader {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerListedController>(TYPES.Auth_HomeServerListedController)
|
.bind<BaseListedController>(TYPES.Auth_BaseListedController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerListedController(
|
new BaseListedController(
|
||||||
container.get(TYPES.Auth_CreateListedAccount),
|
container.get(TYPES.Auth_CreateListedAccount),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
container
|
container
|
||||||
.bind<HomeServerFeaturesController>(TYPES.Auth_HomeServerFeaturesController)
|
.bind<BaseFeaturesController>(TYPES.Auth_BaseFeaturesController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new HomeServerFeaturesController(
|
new BaseFeaturesController(
|
||||||
container.get(TYPES.Auth_GetUserFeatures),
|
container.get(TYPES.Auth_GetUserFeatures),
|
||||||
container.get(TYPES.Auth_ControllerContainer),
|
container.get(TYPES.Auth_ControllerContainer),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ export class AppDataSource {
|
|||||||
...commonDataSourceOptions,
|
...commonDataSourceOptions,
|
||||||
type: 'sqlite',
|
type: 'sqlite',
|
||||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||||
|
enableWAL: true,
|
||||||
|
busyErrorRetry: 2000,
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ const TYPES = {
|
|||||||
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
|
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
|
||||||
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
|
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
|
||||||
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
|
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
|
||||||
|
Auth_TRANSITION_MODE_ENABLED: Symbol.for('Auth_TRANSITION_MODE_ENABLED'),
|
||||||
// use cases
|
// use cases
|
||||||
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
|
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
|
||||||
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
|
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
|
||||||
@@ -113,7 +114,7 @@ const TYPES = {
|
|||||||
Auth_UpdateUser: Symbol.for('Auth_UpdateUser'),
|
Auth_UpdateUser: Symbol.for('Auth_UpdateUser'),
|
||||||
Auth_Register: Symbol.for('Auth_Register'),
|
Auth_Register: Symbol.for('Auth_Register'),
|
||||||
Auth_GetActiveSessionsForUser: Symbol.for('Auth_GetActiveSessionsForUser'),
|
Auth_GetActiveSessionsForUser: Symbol.for('Auth_GetActiveSessionsForUser'),
|
||||||
Auth_DeletePreviousSessionsForUser: Symbol.for('Auth_DeletePreviousSessionsForUser'),
|
Auth_DeleteOtherSessionsForUser: Symbol.for('Auth_DeleteOtherSessionsForUser'),
|
||||||
Auth_DeleteSessionForUser: Symbol.for('Auth_DeleteSessionForUser'),
|
Auth_DeleteSessionForUser: Symbol.for('Auth_DeleteSessionForUser'),
|
||||||
Auth_ChangeCredentials: Symbol.for('Auth_ChangePassword'),
|
Auth_ChangeCredentials: Symbol.for('Auth_ChangePassword'),
|
||||||
Auth_GetSettings: Symbol.for('Auth_GetSettings'),
|
Auth_GetSettings: Symbol.for('Auth_GetSettings'),
|
||||||
@@ -152,6 +153,7 @@ const TYPES = {
|
|||||||
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
||||||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||||
|
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||||
// Handlers
|
// Handlers
|
||||||
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
||||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||||
@@ -165,7 +167,9 @@ const TYPES = {
|
|||||||
Auth_ExtensionKeyGrantedEventHandler: Symbol.for('Auth_ExtensionKeyGrantedEventHandler'),
|
Auth_ExtensionKeyGrantedEventHandler: Symbol.for('Auth_ExtensionKeyGrantedEventHandler'),
|
||||||
Auth_UserEmailChangedEventHandler: Symbol.for('Auth_UserEmailChangedEventHandler'),
|
Auth_UserEmailChangedEventHandler: Symbol.for('Auth_UserEmailChangedEventHandler'),
|
||||||
Auth_FileUploadedEventHandler: Symbol.for('Auth_FileUploadedEventHandler'),
|
Auth_FileUploadedEventHandler: Symbol.for('Auth_FileUploadedEventHandler'),
|
||||||
|
Auth_SharedVaultFileUploadedEventHandler: Symbol.for('Auth_SharedVaultFileUploadedEventHandler'),
|
||||||
Auth_FileRemovedEventHandler: Symbol.for('Auth_FileRemovedEventHandler'),
|
Auth_FileRemovedEventHandler: Symbol.for('Auth_FileRemovedEventHandler'),
|
||||||
|
Auth_SharedVaultFileRemovedEventHandler: Symbol.for('Auth_SharedVaultFileRemovedEventHandler'),
|
||||||
Auth_ListedAccountCreatedEventHandler: Symbol.for('Auth_ListedAccountCreatedEventHandler'),
|
Auth_ListedAccountCreatedEventHandler: Symbol.for('Auth_ListedAccountCreatedEventHandler'),
|
||||||
Auth_ListedAccountDeletedEventHandler: Symbol.for('Auth_ListedAccountDeletedEventHandler'),
|
Auth_ListedAccountDeletedEventHandler: Symbol.for('Auth_ListedAccountDeletedEventHandler'),
|
||||||
Auth_UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for(
|
Auth_UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for(
|
||||||
@@ -176,6 +180,7 @@ const TYPES = {
|
|||||||
),
|
),
|
||||||
Auth_PredicateVerificationRequestedEventHandler: Symbol.for('Auth_PredicateVerificationRequestedEventHandler'),
|
Auth_PredicateVerificationRequestedEventHandler: Symbol.for('Auth_PredicateVerificationRequestedEventHandler'),
|
||||||
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
||||||
|
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
||||||
// Services
|
// Services
|
||||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||||
@@ -217,22 +222,22 @@ const TYPES = {
|
|||||||
Auth_ProtocolVersionSelector: Symbol.for('Auth_ProtocolVersionSelector'),
|
Auth_ProtocolVersionSelector: Symbol.for('Auth_ProtocolVersionSelector'),
|
||||||
Auth_BooleanSelector: Symbol.for('Auth_BooleanSelector'),
|
Auth_BooleanSelector: Symbol.for('Auth_BooleanSelector'),
|
||||||
Auth_UserSubscriptionService: Symbol.for('Auth_UserSubscriptionService'),
|
Auth_UserSubscriptionService: Symbol.for('Auth_UserSubscriptionService'),
|
||||||
Auth_HomeServerAuthController: Symbol.for('Auth_HomeServerAuthController'),
|
Auth_BaseAuthController: Symbol.for('Auth_BaseAuthController'),
|
||||||
Auth_HomeServerAuthenticatorsController: Symbol.for('Auth_HomeServerAuthenticatorsController'),
|
Auth_BaseAuthenticatorsController: Symbol.for('Auth_BaseAuthenticatorsController'),
|
||||||
Auth_HomeServerSubscriptionInvitesController: Symbol.for('Auth_HomeServerSubscriptionInvitesController'),
|
Auth_BaseSubscriptionInvitesController: Symbol.for('Auth_BaseSubscriptionInvitesController'),
|
||||||
Auth_HomeServerUserRequestsController: Symbol.for('Auth_HomeServerUserRequestsController'),
|
Auth_BaseUserRequestsController: Symbol.for('Auth_BaseUserRequestsController'),
|
||||||
Auth_HomeServerWebSocketsController: Symbol.for('Auth_HomeServerWebSocketsController'),
|
Auth_BaseWebSocketsController: Symbol.for('Auth_BaseWebSocketsController'),
|
||||||
Auth_HomeServerSessionsController: Symbol.for('Auth_HomeServerSessionsController'),
|
Auth_BaseSessionsController: Symbol.for('Auth_BaseSessionsController'),
|
||||||
Auth_HomeServerValetTokenController: Symbol.for('Auth_HomeServerValetTokenController'),
|
Auth_BaseValetTokenController: Symbol.for('Auth_BaseValetTokenController'),
|
||||||
Auth_HomeServerUsersController: Symbol.for('Auth_HomeServerUsersController'),
|
Auth_BaseUsersController: Symbol.for('Auth_BaseUsersController'),
|
||||||
Auth_HomeServerAdminController: Symbol.for('Auth_HomeServerAdminController'),
|
Auth_BaseAdminController: Symbol.for('Auth_BaseAdminController'),
|
||||||
Auth_HomeServerSubscriptionTokensController: Symbol.for('Auth_HomeServerSubscriptionTokensController'),
|
Auth_BaseSubscriptionTokensController: Symbol.for('Auth_BaseSubscriptionTokensController'),
|
||||||
Auth_HomeServerSubscriptionSettingsController: Symbol.for('Auth_HomeServerSubscriptionSettingsController'),
|
Auth_BaseSubscriptionSettingsController: Symbol.for('Auth_BaseSubscriptionSettingsController'),
|
||||||
Auth_HomeServerSettingsController: Symbol.for('Auth_HomeServerSettingsController'),
|
Auth_BaseSettingsController: Symbol.for('Auth_BaseSettingsController'),
|
||||||
Auth_HomeServerSessionController: Symbol.for('Auth_HomeServerSessionController'),
|
Auth_BaseSessionController: Symbol.for('Auth_BaseSessionController'),
|
||||||
Auth_HomeServerOfflineController: Symbol.for('Auth_HomeServerOfflineController'),
|
Auth_BaseOfflineController: Symbol.for('Auth_BaseOfflineController'),
|
||||||
Auth_HomeServerListedController: Symbol.for('Auth_HomeServerListedController'),
|
Auth_BaseListedController: Symbol.for('Auth_BaseListedController'),
|
||||||
Auth_HomeServerFeaturesController: Symbol.for('Auth_HomeServerFeaturesController'),
|
Auth_BaseFeaturesController: Symbol.for('Auth_BaseFeaturesController'),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TYPES
|
export default TYPES
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ describe('AuthResponseFactory20161215', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create a 20161215 auth response', async () => {
|
it('should create a 20161215 auth response', async () => {
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20161215',
|
apiVersion: '20161215',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -38,7 +38,7 @@ describe('AuthResponseFactory20161215', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
user: { foo: 'bar' },
|
user: { foo: 'bar' },
|
||||||
token: 'foobar',
|
token: 'foobar',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { User } from '../User/User'
|
|||||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||||
import { AuthResponseFactoryInterface } from './AuthResponseFactoryInterface'
|
import { AuthResponseFactoryInterface } from './AuthResponseFactoryInterface'
|
||||||
|
import { Session } from '../Session/Session'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface {
|
export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface {
|
||||||
@@ -26,7 +27,7 @@ export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface
|
|||||||
userAgent: string
|
userAgent: string
|
||||||
ephemeralSession: boolean
|
ephemeralSession: boolean
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<AuthResponse20161215 | AuthResponse20200115> {
|
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }> {
|
||||||
this.logger.debug(`Creating JWT auth response for user ${dto.user.uuid}`)
|
this.logger.debug(`Creating JWT auth response for user ${dto.user.uuid}`)
|
||||||
|
|
||||||
const data: SessionTokenData = {
|
const data: SessionTokenData = {
|
||||||
@@ -39,12 +40,14 @@ export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface
|
|||||||
this.logger.debug(`Created JWT token for user ${dto.user.uuid}: ${token}`)
|
this.logger.debug(`Created JWT token for user ${dto.user.uuid}: ${token}`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: this.userProjector.projectSimple(dto.user) as {
|
response: {
|
||||||
uuid: string
|
user: this.userProjector.projectSimple(dto.user) as {
|
||||||
email: string
|
uuid: string
|
||||||
protocolVersion: ProtocolVersion
|
email: string
|
||||||
|
protocolVersion: ProtocolVersion
|
||||||
|
},
|
||||||
|
token,
|
||||||
},
|
},
|
||||||
token,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe('AuthResponseFactory20190520', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create a 20161215 auth response', async () => {
|
it('should create a 20161215 auth response', async () => {
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20161215',
|
apiVersion: '20161215',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -37,7 +37,7 @@ describe('AuthResponseFactory20190520', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
user: { foo: 'bar' },
|
user: { foo: 'bar' },
|
||||||
token: 'foobar',
|
token: 'foobar',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { User } from '../User/User'
|
|||||||
import { AuthResponseFactory20200115 } from './AuthResponseFactory20200115'
|
import { AuthResponseFactory20200115 } from './AuthResponseFactory20200115'
|
||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||||
|
import { Session } from '../Session/Session'
|
||||||
|
|
||||||
describe('AuthResponseFactory20200115', () => {
|
describe('AuthResponseFactory20200115', () => {
|
||||||
let sessionService: SessionServiceInterface
|
let sessionService: SessionServiceInterface
|
||||||
@@ -48,8 +49,12 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
||||||
sessionService.createNewSessionForUser = jest.fn().mockReturnValue(sessionPayload)
|
sessionService.createNewSessionForUser = jest
|
||||||
sessionService.createNewEphemeralSessionForUser = jest.fn().mockReturnValue(sessionPayload)
|
.fn()
|
||||||
|
.mockReturnValue({ sessionHttpRepresentation: sessionPayload, session: {} as jest.Mocked<Session> })
|
||||||
|
sessionService.createNewEphemeralSessionForUser = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ sessionHttpRepresentation: sessionPayload, session: {} as jest.Mocked<Session> })
|
||||||
|
|
||||||
keyParamsFactory = {} as jest.Mocked<KeyParamsFactoryInterface>
|
keyParamsFactory = {} as jest.Mocked<KeyParamsFactoryInterface>
|
||||||
keyParamsFactory.create = jest.fn().mockReturnValue({
|
keyParamsFactory.create = jest.fn().mockReturnValue({
|
||||||
@@ -76,7 +81,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
it('should create a 20161215 auth response if user does not support sessions', async () => {
|
it('should create a 20161215 auth response if user does not support sessions', async () => {
|
||||||
user.supportsSessions = jest.fn().mockReturnValue(false)
|
user.supportsSessions = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20161215',
|
apiVersion: '20161215',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -84,7 +89,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
user: { foo: 'bar' },
|
user: { foo: 'bar' },
|
||||||
token: expect.any(String),
|
token: expect.any(String),
|
||||||
})
|
})
|
||||||
@@ -93,7 +98,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
it('should create a 20200115 auth response', async () => {
|
it('should create a 20200115 auth response', async () => {
|
||||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||||
|
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20200115',
|
apiVersion: '20200115',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -101,7 +106,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
key_params: {
|
key_params: {
|
||||||
key1: 'value1',
|
key1: 'value1',
|
||||||
key2: 'value2',
|
key2: 'value2',
|
||||||
@@ -124,7 +129,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
domainEventPublisher.publish = jest.fn().mockRejectedValue(new Error('test'))
|
domainEventPublisher.publish = jest.fn().mockRejectedValue(new Error('test'))
|
||||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||||
|
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20200115',
|
apiVersion: '20200115',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -132,7 +137,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
key_params: {
|
key_params: {
|
||||||
key1: 'value1',
|
key1: 'value1',
|
||||||
key2: 'value2',
|
key2: 'value2',
|
||||||
@@ -153,7 +158,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
it('should create a 20200115 auth response with an ephemeral session', async () => {
|
it('should create a 20200115 auth response with an ephemeral session', async () => {
|
||||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||||
|
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20200115',
|
apiVersion: '20200115',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -161,7 +166,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
key_params: {
|
key_params: {
|
||||||
key1: 'value1',
|
key1: 'value1',
|
||||||
key2: 'value2',
|
key2: 'value2',
|
||||||
@@ -183,11 +188,14 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||||
|
|
||||||
sessionService.createNewSessionForUser = jest.fn().mockReturnValue({
|
sessionService.createNewSessionForUser = jest.fn().mockReturnValue({
|
||||||
...sessionPayload,
|
sessionHttpRepresentation: {
|
||||||
readonly_access: true,
|
...sessionPayload,
|
||||||
|
readonly_access: true,
|
||||||
|
},
|
||||||
|
session: {} as jest.Mocked<Session>,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await createFactory().createResponse({
|
const result = await createFactory().createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: '20200115',
|
apiVersion: '20200115',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -195,7 +203,7 @@ describe('AuthResponseFactory20200115', () => {
|
|||||||
readonlyAccess: true,
|
readonlyAccess: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(result.response).toEqual({
|
||||||
key_params: {
|
key_params: {
|
||||||
key1: 'value1',
|
key1: 'value1',
|
||||||
key2: 'value2',
|
key2: 'value2',
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterfac
|
|||||||
|
|
||||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||||
|
import { Session } from '../Session/Session'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||||
@@ -40,21 +41,28 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
|||||||
userAgent: string
|
userAgent: string
|
||||||
ephemeralSession: boolean
|
ephemeralSession: boolean
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<AuthResponse20161215 | AuthResponse20200115> {
|
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }> {
|
||||||
if (!dto.user.supportsSessions()) {
|
if (!dto.user.supportsSessions()) {
|
||||||
this.logger.debug(`User ${dto.user.uuid} does not support sessions. Falling back to JWT auth response`)
|
this.logger.debug(`User ${dto.user.uuid} does not support sessions. Falling back to JWT auth response`)
|
||||||
|
|
||||||
return super.createResponse(dto)
|
return super.createResponse(dto)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionPayload = await this.createSession(dto)
|
const sessionCreationResult = await this.createSession(dto)
|
||||||
|
|
||||||
this.logger.debug('Created session payload for user %s: %O', dto.user.uuid, sessionPayload)
|
this.logger.debug(
|
||||||
|
'Created session payload for user %s: %O',
|
||||||
|
dto.user.uuid,
|
||||||
|
sessionCreationResult.sessionHttpRepresentation,
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
session: sessionPayload,
|
response: {
|
||||||
key_params: this.keyParamsFactory.create(dto.user, true),
|
session: sessionCreationResult.sessionHttpRepresentation,
|
||||||
user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection,
|
key_params: this.keyParamsFactory.create(dto.user, true),
|
||||||
|
user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection,
|
||||||
|
},
|
||||||
|
session: sessionCreationResult.session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,12 +72,12 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
|||||||
userAgent: string
|
userAgent: string
|
||||||
ephemeralSession: boolean
|
ephemeralSession: boolean
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<SessionBody> {
|
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||||
if (dto.ephemeralSession) {
|
if (dto.ephemeralSession) {
|
||||||
return this.sessionService.createNewEphemeralSessionForUser(dto)
|
return this.sessionService.createNewEphemeralSessionForUser(dto)
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = this.sessionService.createNewSessionForUser(dto)
|
const sessionCreationResult = await this.sessionService.createNewSessionForUser(dto)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.domainEventPublisher.publish(
|
await this.domainEventPublisher.publish(
|
||||||
@@ -79,6 +87,6 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
|||||||
this.logger.error(`Failed to publish session created event: ${(error as Error).message}`)
|
this.logger.error(`Failed to publish session created event: ${(error as Error).message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
return sessionCreationResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Session } from '../Session/Session'
|
||||||
import { User } from '../User/User'
|
import { User } from '../User/User'
|
||||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||||
@@ -9,5 +10,5 @@ export interface AuthResponseFactoryInterface {
|
|||||||
userAgent: string
|
userAgent: string
|
||||||
ephemeralSession: boolean
|
ephemeralSession: boolean
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<AuthResponse20161215 | AuthResponse20200115>
|
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ describe('AuthenticationMethodResolver', () => {
|
|||||||
|
|
||||||
user = {} as jest.Mocked<User>
|
user = {} as jest.Mocked<User>
|
||||||
|
|
||||||
session = {} as jest.Mocked<Session>
|
session = {
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
} as jest.Mocked<Session>
|
||||||
|
|
||||||
revokedSession = {} as jest.Mocked<RevokedSession>
|
revokedSession = {} as jest.Mocked<RevokedSession>
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ describe('AuthenticationMethodResolver', () => {
|
|||||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||||
|
|
||||||
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
||||||
sessionService.getSessionFromToken = jest.fn()
|
sessionService.getSessionFromToken = jest.fn().mockReturnValue({ session: undefined, isEphemeral: false })
|
||||||
sessionService.getRevokedSessionFromToken = jest.fn()
|
sessionService.getRevokedSessionFromToken = jest.fn()
|
||||||
sessionService.markRevokedSessionAsReceived = jest.fn().mockReturnValue(revokedSession)
|
sessionService.markRevokedSessionAsReceived = jest.fn().mockReturnValue(revokedSession)
|
||||||
|
|
||||||
@@ -50,19 +52,25 @@ describe('AuthenticationMethodResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should resolve jwt authentication method', async () => {
|
it('should resolve jwt authentication method', async () => {
|
||||||
sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: '123' })
|
sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: '00000000-0000-0000-0000-000000000000' })
|
||||||
|
|
||||||
expect(await createResolver().resolve('test')).toEqual({
|
expect(await createResolver().resolve('test')).toEqual({
|
||||||
claims: {
|
claims: {
|
||||||
user_uuid: '123',
|
user_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
type: 'jwt',
|
type: 'jwt',
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not resolve jwt authentication method with invalid user uuid', async () => {
|
||||||
|
sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: 'invalid' })
|
||||||
|
|
||||||
|
expect(await createResolver().resolve('test')).toBeUndefined
|
||||||
|
})
|
||||||
|
|
||||||
it('should resolve session authentication method', async () => {
|
it('should resolve session authentication method', async () => {
|
||||||
sessionService.getSessionFromToken = jest.fn().mockReturnValue(session)
|
sessionService.getSessionFromToken = jest.fn().mockReturnValue({ session, isEphemeral: false })
|
||||||
|
|
||||||
expect(await createResolver().resolve('test')).toEqual({
|
expect(await createResolver().resolve('test')).toEqual({
|
||||||
session,
|
session,
|
||||||
@@ -71,6 +79,14 @@ describe('AuthenticationMethodResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not resolve session authentication method with invalid user uuid on session', async () => {
|
||||||
|
sessionService.getSessionFromToken = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ session: { userUuid: 'invalid' }, isEphemeral: false })
|
||||||
|
|
||||||
|
expect(await createResolver().resolve('test')).toBeUndefined
|
||||||
|
})
|
||||||
|
|
||||||
it('should resolve archvied session authentication method', async () => {
|
it('should resolve archvied session authentication method', async () => {
|
||||||
sessionService.getRevokedSessionFromToken = jest.fn().mockReturnValue(revokedSession)
|
sessionService.getRevokedSessionFromToken = jest.fn().mockReturnValue(revokedSession)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
|||||||
import { AuthenticationMethod } from './AuthenticationMethod'
|
import { AuthenticationMethod } from './AuthenticationMethod'
|
||||||
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
|
export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
|
||||||
@@ -29,20 +30,32 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
|||||||
if (decodedToken) {
|
if (decodedToken) {
|
||||||
this.logger.debug('Token decoded successfully. User found.')
|
this.logger.debug('Token decoded successfully. User found.')
|
||||||
|
|
||||||
|
const userUuidOrError = Uuid.create(decodedToken.user_uuid as string)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'jwt',
|
type: 'jwt',
|
||||||
user: await this.userRepository.findOneByUuid(<string>decodedToken.user_uuid),
|
user: await this.userRepository.findOneByUuid(userUuid),
|
||||||
claims: decodedToken,
|
claims: decodedToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await this.sessionService.getSessionFromToken(token)
|
const { session } = await this.sessionService.getSessionFromToken(token)
|
||||||
if (session) {
|
if (session) {
|
||||||
this.logger.debug('Token decoded successfully. Session found.')
|
this.logger.debug('Token decoded successfully. Session found.')
|
||||||
|
|
||||||
|
const userUuidOrError = Uuid.create(session.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'session_token',
|
type: 'session_token',
|
||||||
user: await this.userRepository.findOneByUuid(session.userUuid),
|
user: await this.userRepository.findOneByUuid(userUuid),
|
||||||
session: session,
|
session: session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||||||
|
|
||||||
ephemeralSession = {
|
ephemeralSession = {
|
||||||
uuid: '2-3-4',
|
uuid: '2-3-4',
|
||||||
userUuid: '1-2-3',
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
} as jest.Mocked<EphemeralSession>
|
} as jest.Mocked<EphemeralSession>
|
||||||
|
|
||||||
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
||||||
@@ -68,7 +68,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||||
event.createdAt = new Date(1)
|
event.createdAt = new Date(1)
|
||||||
event.payload = {
|
event.payload = {
|
||||||
userUuid: '1-2-3',
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
userCreatedAtTimestamp: 1,
|
userCreatedAtTimestamp: 1,
|
||||||
regularSubscriptionUuid: '2-3-4',
|
regularSubscriptionUuid: '2-3-4',
|
||||||
}
|
}
|
||||||
@@ -84,6 +84,14 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||||||
expect(userRepository.remove).toHaveBeenCalledWith(user)
|
expect(userRepository.remove).toHaveBeenCalledWith(user)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not remove a user with invalid uuid', async () => {
|
||||||
|
event.payload.userUuid = 'invalid'
|
||||||
|
|
||||||
|
await createHandler().handle(event)
|
||||||
|
|
||||||
|
expect(userRepository.remove).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('should not remove a user if one does not exist', async () => {
|
it('should not remove a user if one does not exist', async () => {
|
||||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
@@ -100,6 +108,6 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||||||
|
|
||||||
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
|
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
|
||||||
expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
|
expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
|
||||||
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '1-2-3')
|
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '00000000-0000-0000-0000-000000000000')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSession
|
|||||||
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
||||||
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||||
@@ -19,19 +20,27 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
||||||
const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
|
const userUuidOrError = Uuid.create(event.payload.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
if (user === null) {
|
|
||||||
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
|
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
await this.removeSessions(event.payload.userUuid)
|
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||||
|
|
||||||
|
if (user === null) {
|
||||||
|
this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.removeSessions(userUuid.value)
|
||||||
|
|
||||||
await this.userRepository.remove(user)
|
await this.userRepository.remove(user)
|
||||||
|
|
||||||
this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`)
|
this.logger.info(`Finished account cleanup for user: ${userUuid.value}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeSessions(userUuid: string): Promise<void> {
|
private async removeSessions(userUuid: string): Promise<void> {
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
import 'reflect-metadata'
|
|
||||||
|
|
||||||
import { FileRemovedEvent } from '@standardnotes/domain-events'
|
|
||||||
import { Logger } from 'winston'
|
|
||||||
|
|
||||||
import { User } from '../User/User'
|
|
||||||
import { FileRemovedEventHandler } from './FileRemovedEventHandler'
|
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
|
||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
|
||||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
|
||||||
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
|
||||||
|
|
||||||
describe('FileRemovedEventHandler', () => {
|
|
||||||
let userSubscriptionService: UserSubscriptionServiceInterface
|
|
||||||
let logger: Logger
|
|
||||||
let regularUser: User
|
|
||||||
let sharedUser: User
|
|
||||||
let event: FileRemovedEvent
|
|
||||||
let subscriptionSettingService: SubscriptionSettingServiceInterface
|
|
||||||
let regularSubscription: UserSubscription
|
|
||||||
let sharedSubscription: UserSubscription
|
|
||||||
|
|
||||||
const createHandler = () => new FileRemovedEventHandler(userSubscriptionService, subscriptionSettingService, logger)
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
regularUser = {
|
|
||||||
uuid: '123',
|
|
||||||
} as jest.Mocked<User>
|
|
||||||
|
|
||||||
sharedUser = {
|
|
||||||
uuid: '234',
|
|
||||||
} as jest.Mocked<User>
|
|
||||||
|
|
||||||
regularSubscription = {
|
|
||||||
uuid: '1-2-3',
|
|
||||||
subscriptionType: UserSubscriptionType.Regular,
|
|
||||||
user: Promise.resolve(regularUser),
|
|
||||||
} as jest.Mocked<UserSubscription>
|
|
||||||
|
|
||||||
sharedSubscription = {
|
|
||||||
uuid: '2-3-4',
|
|
||||||
subscriptionType: UserSubscriptionType.Shared,
|
|
||||||
user: Promise.resolve(sharedUser),
|
|
||||||
} as jest.Mocked<UserSubscription>
|
|
||||||
|
|
||||||
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
|
||||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
|
||||||
.fn()
|
|
||||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
|
||||||
|
|
||||||
subscriptionSettingService = {} as jest.Mocked<SubscriptionSettingServiceInterface>
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest.fn().mockReturnValue(null)
|
|
||||||
subscriptionSettingService.createOrReplace = jest.fn()
|
|
||||||
|
|
||||||
event = {} as jest.Mocked<FileRemovedEvent>
|
|
||||||
event.createdAt = new Date(1)
|
|
||||||
event.payload = {
|
|
||||||
userUuid: '1-2-3',
|
|
||||||
fileByteSize: 123,
|
|
||||||
filePath: '1-2-3/2-3-4',
|
|
||||||
fileName: '2-3-4',
|
|
||||||
regularSubscriptionUuid: '4-5-6',
|
|
||||||
}
|
|
||||||
|
|
||||||
logger = {} as jest.Mocked<Logger>
|
|
||||||
logger.warn = jest.fn()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should do nothing a bytes used setting does not exist', async () => {
|
|
||||||
await createHandler().handle(event)
|
|
||||||
|
|
||||||
expect(subscriptionSettingService.createOrReplace).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not do anything if a user subscription is not found', async () => {
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
|
||||||
value: 345,
|
|
||||||
})
|
|
||||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
|
||||||
.fn()
|
|
||||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
|
||||||
|
|
||||||
await createHandler().handle(event)
|
|
||||||
|
|
||||||
expect(subscriptionSettingService.createOrReplace).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update a bytes used setting', async () => {
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
|
||||||
value: 345,
|
|
||||||
})
|
|
||||||
await createHandler().handle(event)
|
|
||||||
|
|
||||||
expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
|
|
||||||
props: {
|
|
||||||
name: 'FILE_UPLOAD_BYTES_USED',
|
|
||||||
sensitive: false,
|
|
||||||
unencryptedValue: '222',
|
|
||||||
serverEncryptionVersion: 0,
|
|
||||||
},
|
|
||||||
user: regularUser,
|
|
||||||
userSubscription: {
|
|
||||||
uuid: '1-2-3',
|
|
||||||
subscriptionType: 'regular',
|
|
||||||
user: Promise.resolve(regularUser),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should update a bytes used setting on both shared and regular subscription', async () => {
|
|
||||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
|
||||||
.fn()
|
|
||||||
.mockReturnValue({ regularSubscription, sharedSubscription })
|
|
||||||
|
|
||||||
subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest.fn().mockReturnValue({
|
|
||||||
value: 345,
|
|
||||||
})
|
|
||||||
await createHandler().handle(event)
|
|
||||||
|
|
||||||
expect(subscriptionSettingService.createOrReplace).toHaveBeenNthCalledWith(1, {
|
|
||||||
props: {
|
|
||||||
name: 'FILE_UPLOAD_BYTES_USED',
|
|
||||||
sensitive: false,
|
|
||||||
unencryptedValue: '222',
|
|
||||||
serverEncryptionVersion: 0,
|
|
||||||
},
|
|
||||||
user: regularUser,
|
|
||||||
userSubscription: {
|
|
||||||
uuid: '1-2-3',
|
|
||||||
subscriptionType: 'regular',
|
|
||||||
user: Promise.resolve(regularUser),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(subscriptionSettingService.createOrReplace).toHaveBeenNthCalledWith(2, {
|
|
||||||
props: {
|
|
||||||
name: 'FILE_UPLOAD_BYTES_USED',
|
|
||||||
sensitive: false,
|
|
||||||
unencryptedValue: '222',
|
|
||||||
serverEncryptionVersion: 0,
|
|
||||||
},
|
|
||||||
user: sharedUser,
|
|
||||||
userSubscription: {
|
|
||||||
uuid: '2-3-4',
|
|
||||||
subscriptionType: 'shared',
|
|
||||||
user: Promise.resolve(sharedUser),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,63 +1,19 @@
|
|||||||
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
import { DomainEventHandlerInterface, FileRemovedEvent } from '@standardnotes/domain-events'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
|
||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
|
||||||
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||||
constructor(
|
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
|
||||||
@inject(TYPES.Auth_SubscriptionSettingService)
|
|
||||||
private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
|
||||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async handle(event: FileRemovedEvent): Promise<void> {
|
async handle(event: FileRemovedEvent): Promise<void> {
|
||||||
const { regularSubscription, sharedSubscription } =
|
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||||
await this.userSubscriptionService.findRegularSubscriptionForUserUuid(event.payload.userUuid)
|
userUuid: event.payload.userUuid,
|
||||||
if (regularSubscription === null) {
|
bytesUsed: -event.payload.fileByteSize,
|
||||||
this.logger.warn(`Could not find regular user subscription for user with uuid: ${event.payload.userUuid}`)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.updateUploadBytesUsedSetting(regularSubscription, event.payload.fileByteSize)
|
|
||||||
|
|
||||||
if (sharedSubscription !== null) {
|
|
||||||
await this.updateUploadBytesUsedSetting(sharedSubscription, event.payload.fileByteSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateUploadBytesUsedSetting(subscription: UserSubscription, byteSize: number): Promise<void> {
|
|
||||||
const user = await subscription.user
|
|
||||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
|
||||||
userUuid: user.uuid,
|
|
||||||
userSubscriptionUuid: subscription.uuid,
|
|
||||||
subscriptionSettingName: SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(),
|
|
||||||
})
|
})
|
||||||
if (bytesUsedSetting === null) {
|
|
||||||
this.logger.warn(`Could not find bytes used setting for user with uuid: ${user.uuid}`)
|
|
||||||
|
|
||||||
return
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to update storage quota used for user: ${result.getError()}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const bytesUsed = bytesUsedSetting.value as string
|
|
||||||
|
|
||||||
await this.subscriptionSettingService.createOrReplace({
|
|
||||||
userSubscription: subscription,
|
|
||||||
user,
|
|
||||||
props: {
|
|
||||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
|
||||||
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
|
||||||
sensitive: false,
|
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,19 @@
|
|||||||
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
import { DomainEventHandlerInterface, FileUploadedEvent } from '@standardnotes/domain-events'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
|
||||||
import { inject, injectable } from 'inversify'
|
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
|
||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
|
||||||
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
|
||||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
|
||||||
import { User } from '../User/User'
|
|
||||||
|
|
||||||
@injectable()
|
|
||||||
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||||
constructor(
|
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
|
||||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
|
||||||
@inject(TYPES.Auth_SubscriptionSettingService)
|
|
||||||
private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
|
||||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async handle(event: FileUploadedEvent): Promise<void> {
|
async handle(event: FileUploadedEvent): Promise<void> {
|
||||||
const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
|
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||||
if (user === null) {
|
userUuid: event.payload.userUuid,
|
||||||
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
|
bytesUsed: event.payload.fileByteSize,
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { regularSubscription, sharedSubscription } =
|
|
||||||
await this.userSubscriptionService.findRegularSubscriptionForUserUuid(event.payload.userUuid)
|
|
||||||
if (regularSubscription === null) {
|
|
||||||
this.logger.warn(`Could not find regular user subscription for user with uuid: ${event.payload.userUuid}`)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.updateUploadBytesUsedSetting(regularSubscription, user, event.payload.fileByteSize)
|
|
||||||
|
|
||||||
if (sharedSubscription !== null) {
|
|
||||||
await this.updateUploadBytesUsedSetting(sharedSubscription, user, event.payload.fileByteSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateUploadBytesUsedSetting(
|
|
||||||
subscription: UserSubscription,
|
|
||||||
user: User,
|
|
||||||
byteSize: number,
|
|
||||||
): Promise<void> {
|
|
||||||
let bytesUsed = '0'
|
|
||||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
|
||||||
userUuid: (await subscription.user).uuid,
|
|
||||||
userSubscriptionUuid: subscription.uuid,
|
|
||||||
subscriptionSettingName: SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(),
|
|
||||||
})
|
})
|
||||||
if (bytesUsedSetting !== null) {
|
|
||||||
bytesUsed = bytesUsedSetting.value as string
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.subscriptionSettingService.createOrReplace({
|
if (result.isFailed()) {
|
||||||
userSubscription: subscription,
|
this.logger.error(`Failed to update storage quota used for user: ${result.getError()}`)
|
||||||
user,
|
}
|
||||||
props: {
|
|
||||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
|
||||||
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
|
||||||
sensitive: false,
|
|
||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Logger } from 'winston'
|
||||||
|
import { Result } from '@standardnotes/domain-core'
|
||||||
|
import { PaymentsAccountDeletedEvent } from '@standardnotes/domain-events'
|
||||||
|
|
||||||
|
import { DeleteAccount } from '../UseCase/DeleteAccount/DeleteAccount'
|
||||||
|
import { PaymentsAccountDeletedEventHandler } from './PaymentsAccountDeletedEventHandler'
|
||||||
|
|
||||||
|
describe('PaymentsAccountDeletedEventHandler', () => {
|
||||||
|
let deleteAccountUseCase: DeleteAccount
|
||||||
|
let logger: Logger
|
||||||
|
let event: PaymentsAccountDeletedEvent
|
||||||
|
|
||||||
|
const createHandler = () => new PaymentsAccountDeletedEventHandler(deleteAccountUseCase, logger)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
deleteAccountUseCase = {} as jest.Mocked<DeleteAccount>
|
||||||
|
deleteAccountUseCase.execute = jest.fn().mockResolvedValue(Result.ok('success'))
|
||||||
|
|
||||||
|
logger = {} as jest.Mocked<Logger>
|
||||||
|
logger.error = jest.fn()
|
||||||
|
|
||||||
|
event = {
|
||||||
|
payload: {
|
||||||
|
username: 'username',
|
||||||
|
},
|
||||||
|
} as jest.Mocked<PaymentsAccountDeletedEvent>
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should delete account', async () => {
|
||||||
|
const handler = createHandler()
|
||||||
|
|
||||||
|
await handler.handle(event)
|
||||||
|
|
||||||
|
expect(deleteAccountUseCase.execute).toHaveBeenCalledWith({
|
||||||
|
username: 'username',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should log error if delete account fails', async () => {
|
||||||
|
const handler = createHandler()
|
||||||
|
|
||||||
|
deleteAccountUseCase.execute = jest.fn().mockResolvedValue(Result.fail('error'))
|
||||||
|
|
||||||
|
await handler.handle(event)
|
||||||
|
|
||||||
|
expect(logger.error).toHaveBeenCalledWith('Failed to delete account for user username: error')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { DomainEventHandlerInterface, PaymentsAccountDeletedEvent } from '@standardnotes/domain-events'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import { DeleteAccount } from '../UseCase/DeleteAccount/DeleteAccount'
|
||||||
|
|
||||||
|
export class PaymentsAccountDeletedEventHandler implements DomainEventHandlerInterface {
|
||||||
|
constructor(private deleteAccountUseCase: DeleteAccount, private logger: Logger) {}
|
||||||
|
|
||||||
|
async handle(event: PaymentsAccountDeletedEvent): Promise<void> {
|
||||||
|
const result = await this.deleteAccountUseCase.execute({
|
||||||
|
username: event.payload.username,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to delete account for user ${event.payload.username}: ${result.getError()}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { DomainEventHandlerInterface, SharedVaultFileRemovedEvent } from '@standardnotes/domain-events'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||||
|
|
||||||
|
export class SharedVaultFileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||||
|
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||||
|
|
||||||
|
async handle(event: SharedVaultFileRemovedEvent): Promise<void> {
|
||||||
|
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||||
|
userUuid: event.payload.vaultOwnerUuid,
|
||||||
|
bytesUsed: -event.payload.fileByteSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to update storage quota used for user: ${result.getError()}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { DomainEventHandlerInterface, SharedVaultFileUploadedEvent } from '@standardnotes/domain-events'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||||
|
|
||||||
|
export class SharedVaultFileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||||
|
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||||
|
|
||||||
|
async handle(event: SharedVaultFileUploadedEvent): Promise<void> {
|
||||||
|
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||||
|
userUuid: event.payload.vaultOwnerUuid,
|
||||||
|
bytesUsed: event.payload.fileByteSize,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to update storage quota used for user: ${result.getError()}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -214,13 +214,25 @@ describe('RoleService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should indicate if a user has given permission', async () => {
|
it('should indicate if a user has given permission', async () => {
|
||||||
const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.DailyEmailBackup)
|
const userHasPermission = await createService().userHasPermission(
|
||||||
|
'00000000-0000-0000-0000-000000000000',
|
||||||
|
PermissionName.DailyEmailBackup,
|
||||||
|
)
|
||||||
|
|
||||||
expect(userHasPermission).toBeTruthy()
|
expect(userHasPermission).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not indiciate if a user has permission if the user uuid is invalid', async () => {
|
||||||
|
const userHasPermission = await createService().userHasPermission('invalid', PermissionName.DailyEmailBackup)
|
||||||
|
|
||||||
|
expect(userHasPermission).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
it('should indicate if a user does not have a given permission', async () => {
|
it('should indicate if a user does not have a given permission', async () => {
|
||||||
const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.MarkdownProEditor)
|
const userHasPermission = await createService().userHasPermission(
|
||||||
|
'00000000-0000-0000-0000-000000000000',
|
||||||
|
PermissionName.MarkdownProEditor,
|
||||||
|
)
|
||||||
|
|
||||||
expect(userHasPermission).toBeFalsy()
|
expect(userHasPermission).toBeFalsy()
|
||||||
})
|
})
|
||||||
@@ -228,7 +240,10 @@ describe('RoleService', () => {
|
|||||||
it('should indicate user does not have a permission if user could not be found', async () => {
|
it('should indicate user does not have a permission if user could not be found', async () => {
|
||||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.MarkdownProEditor)
|
const userHasPermission = await createService().userHasPermission(
|
||||||
|
'00000000-0000-0000-0000-000000000000',
|
||||||
|
PermissionName.MarkdownProEditor,
|
||||||
|
)
|
||||||
|
|
||||||
expect(userHasPermission).toBeFalsy()
|
expect(userHasPermission).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { RoleToSubscriptionMapInterface } from './RoleToSubscriptionMapInterface
|
|||||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||||
import { Role } from './Role'
|
import { Role } from './Role'
|
||||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class RoleService implements RoleServiceInterface {
|
export class RoleService implements RoleServiceInterface {
|
||||||
@@ -26,10 +27,16 @@ export class RoleService implements RoleServiceInterface {
|
|||||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async userHasPermission(userUuid: string, permissionName: PermissionName): Promise<boolean> {
|
async userHasPermission(userUuidString: string, permissionName: PermissionName): Promise<boolean> {
|
||||||
|
const userUuidOrError = Uuid.create(userUuidString)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
this.logger.warn(`Could not find user with uuid ${userUuid} for permissions check`)
|
this.logger.warn(`Could not find user with uuid ${userUuid.value} for permissions check`)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,6 @@ export interface EphemeralSessionRepositoryInterface {
|
|||||||
findOneByUuid(uuid: string): Promise<EphemeralSession | null>
|
findOneByUuid(uuid: string): Promise<EphemeralSession | null>
|
||||||
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<EphemeralSession | null>
|
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<EphemeralSession | null>
|
||||||
findAllByUserUuid(userUuid: string): Promise<Array<EphemeralSession>>
|
findAllByUserUuid(userUuid: string): Promise<Array<EphemeralSession>>
|
||||||
updateTokensAndExpirationDates(
|
|
||||||
uuid: string,
|
|
||||||
hashedAccessToken: string,
|
|
||||||
hashedRefreshToken: string,
|
|
||||||
accessExpiration: Date,
|
|
||||||
refreshExpiration: Date,
|
|
||||||
): Promise<void>
|
|
||||||
deleteOne(uuid: string, userUuid: string): Promise<void>
|
deleteOne(uuid: string, userUuid: string): Promise<void>
|
||||||
save(ephemeralSession: EphemeralSession): Promise<void>
|
save(ephemeralSession: EphemeralSession): Promise<void>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { Session } from './Session'
|
import { Session } from './Session'
|
||||||
|
|
||||||
export interface SessionRepositoryInterface {
|
export interface SessionRepositoryInterface {
|
||||||
@@ -5,10 +7,8 @@ export interface SessionRepositoryInterface {
|
|||||||
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Session | null>
|
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Session | null>
|
||||||
findAllByRefreshExpirationAndUserUuid(userUuid: string): Promise<Array<Session>>
|
findAllByRefreshExpirationAndUserUuid(userUuid: string): Promise<Array<Session>>
|
||||||
findAllByUserUuid(userUuid: string): Promise<Array<Session>>
|
findAllByUserUuid(userUuid: string): Promise<Array<Session>>
|
||||||
deleteAllByUserUuid(userUuid: string, currentSessionUuid: string): Promise<void>
|
deleteAllByUserUuidExceptOne(dto: { userUuid: Uuid; currentSessionUuid: Uuid }): Promise<void>
|
||||||
deleteOneByUuid(uuid: string): Promise<void>
|
deleteOneByUuid(uuid: string): Promise<void>
|
||||||
updateHashedTokens(uuid: string, hashedAccessToken: string, hashedRefreshToken: string): Promise<void>
|
|
||||||
updatedTokenExpirationDates(uuid: string, accessExpiration: Date, refreshExpiration: Date): Promise<void>
|
|
||||||
save(session: Session): Promise<Session>
|
save(session: Session): Promise<Session>
|
||||||
remove(session: Session): Promise<Session>
|
remove(session: Session): Promise<Session>
|
||||||
clearUserAgentByUserUuid(userUuid: string): Promise<void>
|
clearUserAgentByUserUuid(userUuid: string): Promise<void>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ describe('SessionService', () => {
|
|||||||
let sessionRepository: SessionRepositoryInterface
|
let sessionRepository: SessionRepositoryInterface
|
||||||
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
|
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
|
||||||
let revokedSessionRepository: RevokedSessionRepositoryInterface
|
let revokedSessionRepository: RevokedSessionRepositoryInterface
|
||||||
let session: Session
|
let existingSession: Session
|
||||||
let ephemeralSession: EphemeralSession
|
let existingEphemeralSession: EphemeralSession
|
||||||
let revokedSession: RevokedSession
|
let revokedSession: RevokedSession
|
||||||
let settingService: SettingServiceInterface
|
let settingService: SettingServiceInterface
|
||||||
let deviceDetector: UAParser
|
let deviceDetector: UAParser
|
||||||
@@ -54,14 +54,14 @@ describe('SessionService', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
session = {} as jest.Mocked<Session>
|
existingSession = {} as jest.Mocked<Session>
|
||||||
session.uuid = '2e1e43'
|
existingSession.uuid = '2e1e43'
|
||||||
session.userUuid = '1-2-3'
|
existingSession.userUuid = '1-2-3'
|
||||||
session.userAgent = 'Chrome'
|
existingSession.userAgent = 'Chrome'
|
||||||
session.apiVersion = ApiVersion.v20200115
|
existingSession.apiVersion = ApiVersion.v20200115
|
||||||
session.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
existingSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||||
session.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
existingSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||||
session.readonlyAccess = false
|
existingSession.readonlyAccess = false
|
||||||
|
|
||||||
revokedSession = {} as jest.Mocked<RevokedSession>
|
revokedSession = {} as jest.Mocked<RevokedSession>
|
||||||
revokedSession.uuid = '2e1e43'
|
revokedSession.uuid = '2e1e43'
|
||||||
@@ -69,9 +69,7 @@ describe('SessionService', () => {
|
|||||||
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
|
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
sessionRepository.deleteOneByUuid = jest.fn()
|
sessionRepository.deleteOneByUuid = jest.fn()
|
||||||
sessionRepository.save = jest.fn().mockReturnValue(session)
|
sessionRepository.save = jest.fn().mockReturnValue(existingSession)
|
||||||
sessionRepository.updateHashedTokens = jest.fn()
|
|
||||||
sessionRepository.updatedTokenExpirationDates = jest.fn()
|
|
||||||
|
|
||||||
settingService = {} as jest.Mocked<SettingServiceInterface>
|
settingService = {} as jest.Mocked<SettingServiceInterface>
|
||||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(null)
|
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(null)
|
||||||
@@ -79,17 +77,18 @@ describe('SessionService', () => {
|
|||||||
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
||||||
ephemeralSessionRepository.save = jest.fn()
|
ephemeralSessionRepository.save = jest.fn()
|
||||||
ephemeralSessionRepository.findOneByUuid = jest.fn()
|
ephemeralSessionRepository.findOneByUuid = jest.fn()
|
||||||
ephemeralSessionRepository.updateTokensAndExpirationDates = jest.fn()
|
|
||||||
ephemeralSessionRepository.deleteOne = jest.fn()
|
ephemeralSessionRepository.deleteOne = jest.fn()
|
||||||
|
|
||||||
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
|
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
|
||||||
revokedSessionRepository.save = jest.fn()
|
revokedSessionRepository.save = jest.fn()
|
||||||
|
|
||||||
ephemeralSession = {} as jest.Mocked<EphemeralSession>
|
existingEphemeralSession = {} as jest.Mocked<EphemeralSession>
|
||||||
ephemeralSession.uuid = '2-3-4'
|
existingEphemeralSession.uuid = '2-3-4'
|
||||||
ephemeralSession.userAgent = 'Mozilla Firefox'
|
existingEphemeralSession.userUuid = '1-2-3'
|
||||||
ephemeralSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
existingEphemeralSession.userAgent = 'Mozilla Firefox'
|
||||||
ephemeralSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
existingEphemeralSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||||
|
existingEphemeralSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||||
|
existingEphemeralSession.readonlyAccess = false
|
||||||
|
|
||||||
timer = {} as jest.Mocked<TimerInterface>
|
timer = {} as jest.Mocked<TimerInterface>
|
||||||
timer.convertStringDateToMilliseconds = jest.fn().mockReturnValue(123)
|
timer.convertStringDateToMilliseconds = jest.fn().mockReturnValue(123)
|
||||||
@@ -138,7 +137,7 @@ describe('SessionService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should refresh access and refresh tokens for a session', async () => {
|
it('should refresh access and refresh tokens for a session', async () => {
|
||||||
expect(await createService().refreshTokens(session)).toEqual({
|
expect(await createService().refreshTokens({ session: existingSession, isEphemeral: false })).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_token: expect.any(String),
|
refresh_token: expect.any(String),
|
||||||
@@ -146,15 +145,28 @@ describe('SessionService', () => {
|
|||||||
readonly_access: false,
|
readonly_access: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sessionRepository.updateHashedTokens).toHaveBeenCalled()
|
expect(sessionRepository.save).toHaveBeenCalled()
|
||||||
expect(sessionRepository.updatedTokenExpirationDates).toHaveBeenCalled()
|
expect(ephemeralSessionRepository.save).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should refresh access and refresh tokens for an ephemeral session', async () => {
|
||||||
|
expect(await createService().refreshTokens({ session: existingEphemeralSession, isEphemeral: true })).toEqual({
|
||||||
|
access_expiration: 123,
|
||||||
|
access_token: expect.any(String),
|
||||||
|
refresh_token: expect.any(String),
|
||||||
|
refresh_expiration: 123,
|
||||||
|
readonly_access: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(sessionRepository.save).not.toHaveBeenCalled()
|
||||||
|
expect(ephemeralSessionRepository.save).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create new session for a user', async () => {
|
it('should create new session for a user', async () => {
|
||||||
const user = {} as jest.Mocked<User>
|
const user = {} as jest.Mocked<User>
|
||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -176,7 +188,7 @@ describe('SessionService', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -190,7 +202,7 @@ describe('SessionService', () => {
|
|||||||
user.email = 'demo@standardnotes.com'
|
user.email = 'demo@standardnotes.com'
|
||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -212,7 +224,7 @@ describe('SessionService', () => {
|
|||||||
readonlyAccess: true,
|
readonlyAccess: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -229,7 +241,7 @@ describe('SessionService', () => {
|
|||||||
value: LogSessionUserAgentOption.Disabled,
|
value: LogSessionUserAgentOption.Disabled,
|
||||||
} as jest.Mocked<Setting>)
|
} as jest.Mocked<Setting>)
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -250,7 +262,7 @@ describe('SessionService', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -305,7 +317,7 @@ describe('SessionService', () => {
|
|||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
user.email = 'test@test.te'
|
user.email = 'test@test.te'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -317,7 +329,7 @@ describe('SessionService', () => {
|
|||||||
username: 'test@test.te',
|
username: 'test@test.te',
|
||||||
subscriptionPlanName: null,
|
subscriptionPlanName: null,
|
||||||
})
|
})
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -333,7 +345,7 @@ describe('SessionService', () => {
|
|||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
user.email = 'test@test.te'
|
user.email = 'test@test.te'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -345,7 +357,7 @@ describe('SessionService', () => {
|
|||||||
username: 'test@test.te',
|
username: 'test@test.te',
|
||||||
subscriptionPlanName: null,
|
subscriptionPlanName: null,
|
||||||
})
|
})
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -361,7 +373,7 @@ describe('SessionService', () => {
|
|||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
user.email = 'test@test.te'
|
user.email = 'test@test.te'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewSessionForUser({
|
const result = await createService().createNewSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -373,7 +385,7 @@ describe('SessionService', () => {
|
|||||||
username: 'test@test.te',
|
username: 'test@test.te',
|
||||||
subscriptionPlanName: null,
|
subscriptionPlanName: null,
|
||||||
})
|
})
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -386,7 +398,7 @@ describe('SessionService', () => {
|
|||||||
const user = {} as jest.Mocked<User>
|
const user = {} as jest.Mocked<User>
|
||||||
user.uuid = '123'
|
user.uuid = '123'
|
||||||
|
|
||||||
const sessionPayload = await createService().createNewEphemeralSessionForUser({
|
const result = await createService().createNewEphemeralSessionForUser({
|
||||||
user,
|
user,
|
||||||
apiVersion: '003',
|
apiVersion: '003',
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
@@ -408,7 +420,7 @@ describe('SessionService', () => {
|
|||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sessionPayload).toEqual({
|
expect(result.sessionHttpRepresentation).toEqual({
|
||||||
access_expiration: 123,
|
access_expiration: 123,
|
||||||
access_token: expect.any(String),
|
access_token: expect.any(String),
|
||||||
refresh_expiration: 123,
|
refresh_expiration: 123,
|
||||||
@@ -420,7 +432,7 @@ describe('SessionService', () => {
|
|||||||
it('should delete a session by token', async () => {
|
it('should delete a session by token', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
if (uuid === '2') {
|
if (uuid === '2') {
|
||||||
return session
|
return existingSession
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -429,13 +441,28 @@ describe('SessionService', () => {
|
|||||||
await createService().deleteSessionByToken('1:2:3')
|
await createService().deleteSessionByToken('1:2:3')
|
||||||
|
|
||||||
expect(sessionRepository.deleteOneByUuid).toHaveBeenCalledWith('2e1e43')
|
expect(sessionRepository.deleteOneByUuid).toHaveBeenCalledWith('2e1e43')
|
||||||
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2e1e43', '1-2-3')
|
expect(ephemeralSessionRepository.deleteOne).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should delete an ephemeral session by token', async () => {
|
||||||
|
ephemeralSessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
|
if (uuid === '2') {
|
||||||
|
return existingEphemeralSession
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
await createService().deleteSessionByToken('1:2:3')
|
||||||
|
|
||||||
|
expect(sessionRepository.deleteOneByUuid).not.toHaveBeenCalled()
|
||||||
|
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '1-2-3')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not delete a session by token if session is not found', async () => {
|
it('should not delete a session by token if session is not found', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
if (uuid === '2') {
|
if (uuid === '2') {
|
||||||
return session
|
return existingSession
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -448,13 +475,13 @@ describe('SessionService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should determine if a refresh token is valid', async () => {
|
it('should determine if a refresh token is valid', async () => {
|
||||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:3')).toBeTruthy()
|
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2:3')).toBeTruthy()
|
||||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:4')).toBeFalsy()
|
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2:4')).toBeFalsy()
|
||||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2')).toBeFalsy()
|
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2')).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return device info based on user agent', () => {
|
it('should return device info based on user agent', () => {
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome 69.0 on Mac 10.13')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome 69.0 on Mac 10.13')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return device info based on undefined user agent', () => {
|
it('should return device info based on undefined user agent', () => {
|
||||||
@@ -463,7 +490,7 @@ describe('SessionService', () => {
|
|||||||
browser: { name: undefined, version: undefined },
|
browser: { name: undefined, version: undefined },
|
||||||
os: { name: undefined, version: undefined },
|
os: { name: undefined, version: undefined },
|
||||||
})
|
})
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Unknown Client on Unknown OS')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Unknown Client on Unknown OS')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on lack of client in user agent', () => {
|
it('should return a shorter info based on lack of client in user agent', () => {
|
||||||
@@ -473,7 +500,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: 'iOS', version: '10.3' },
|
os: { name: 'iOS', version: '10.3' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('iOS 10.3')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('iOS 10.3')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on lack of os in user agent', () => {
|
it('should return a shorter info based on lack of os in user agent', () => {
|
||||||
@@ -483,13 +510,13 @@ describe('SessionService', () => {
|
|||||||
os: { name: '', version: '' },
|
os: { name: '', version: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome 69.0')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome 69.0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return unknown client and os if user agent is cleaned out', () => {
|
it('should return unknown client and os if user agent is cleaned out', () => {
|
||||||
session.userAgent = null
|
existingSession.userAgent = null
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Unknown Client on Unknown OS')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Unknown Client on Unknown OS')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on partial os in user agent', () => {
|
it('should return a shorter info based on partial os in user agent', () => {
|
||||||
@@ -499,7 +526,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: 'Windows', version: '' },
|
os: { name: 'Windows', version: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome 69.0 on Windows')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome 69.0 on Windows')
|
||||||
|
|
||||||
deviceDetector.getResult = jest.fn().mockReturnValue({
|
deviceDetector.getResult = jest.fn().mockReturnValue({
|
||||||
ua: 'dummy-data',
|
ua: 'dummy-data',
|
||||||
@@ -507,7 +534,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: '', version: '7' },
|
os: { name: '', version: '7' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome 69.0 on 7')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome 69.0 on 7')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on partial client in user agent', () => {
|
it('should return a shorter info based on partial client in user agent', () => {
|
||||||
@@ -517,7 +544,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: 'Windows', version: '7' },
|
os: { name: 'Windows', version: '7' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('69.0 on Windows 7')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('69.0 on Windows 7')
|
||||||
|
|
||||||
deviceDetector.getResult = jest.fn().mockReturnValue({
|
deviceDetector.getResult = jest.fn().mockReturnValue({
|
||||||
ua: 'dummy-data',
|
ua: 'dummy-data',
|
||||||
@@ -525,7 +552,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: 'Windows', version: '7' },
|
os: { name: 'Windows', version: '7' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome on Windows 7')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome on Windows 7')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on iOS agent', () => {
|
it('should return a shorter info based on iOS agent', () => {
|
||||||
@@ -538,7 +565,7 @@ describe('SessionService', () => {
|
|||||||
cpu: { architecture: undefined },
|
cpu: { architecture: undefined },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('iOS')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('iOS')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a shorter info based on partial client and partial os in user agent', () => {
|
it('should return a shorter info based on partial client and partial os in user agent', () => {
|
||||||
@@ -548,7 +575,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: 'Windows', version: '' },
|
os: { name: 'Windows', version: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('69.0 on Windows')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('69.0 on Windows')
|
||||||
|
|
||||||
deviceDetector.getResult = jest.fn().mockReturnValue({
|
deviceDetector.getResult = jest.fn().mockReturnValue({
|
||||||
ua: 'dummy-data',
|
ua: 'dummy-data',
|
||||||
@@ -556,7 +583,7 @@ describe('SessionService', () => {
|
|||||||
os: { name: '', version: '7' },
|
os: { name: '', version: '7' },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Chrome on 7')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Chrome on 7')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return only Android os for okHttp client', () => {
|
it('should return only Android os for okHttp client', () => {
|
||||||
@@ -569,7 +596,7 @@ describe('SessionService', () => {
|
|||||||
cpu: { architecture: undefined },
|
cpu: { architecture: undefined },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Android')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Android')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should detect the StandardNotes app in user agent', () => {
|
it('should detect the StandardNotes app in user agent', () => {
|
||||||
@@ -582,7 +609,7 @@ describe('SessionService', () => {
|
|||||||
cpu: { architecture: undefined },
|
cpu: { architecture: undefined },
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Standard Notes Desktop 3.5.18 on Mac OS 10.16.0')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Standard Notes Desktop 3.5.18 on Mac OS 10.16.0')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return unknown device info as fallback', () => {
|
it('should return unknown device info as fallback', () => {
|
||||||
@@ -590,70 +617,72 @@ describe('SessionService', () => {
|
|||||||
throw new Error('something bad happened')
|
throw new Error('something bad happened')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(createService().getDeviceInfo(session)).toEqual('Unknown Client on Unknown OS')
|
expect(createService().getDeviceInfo(existingSession)).toEqual('Unknown Client on Unknown OS')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should retrieve a session from a session token', async () => {
|
it('should retrieve a session from a session token', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
if (uuid === '2') {
|
if (uuid === '2') {
|
||||||
return session
|
return existingSession
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await createService().getSessionFromToken('1:2:3')
|
const { session, isEphemeral } = await createService().getSessionFromToken('1:2:3')
|
||||||
|
|
||||||
expect(result).toEqual(session)
|
expect(session).toEqual(session)
|
||||||
|
expect(isEphemeral).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should retrieve an ephemeral session from a session token', async () => {
|
it('should retrieve an ephemeral session from a session token', async () => {
|
||||||
ephemeralSessionRepository.findOneByUuid = jest.fn().mockReturnValue(ephemeralSession)
|
ephemeralSessionRepository.findOneByUuid = jest.fn().mockReturnValue(existingEphemeralSession)
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
const result = await createService().getSessionFromToken('1:2:3')
|
const { session, isEphemeral } = await createService().getSessionFromToken('1:2:3')
|
||||||
|
|
||||||
expect(result).toEqual(ephemeralSession)
|
expect(session).toEqual(existingEphemeralSession)
|
||||||
|
expect(isEphemeral).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not retrieve a session from a session token that has access token missing', async () => {
|
it('should not retrieve a session from a session token that has access token missing', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
if (uuid === '2') {
|
if (uuid === '2') {
|
||||||
return session
|
return existingSession
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await createService().getSessionFromToken('1:2')
|
const { session } = await createService().getSessionFromToken('1:2')
|
||||||
|
|
||||||
expect(result).toBeUndefined()
|
expect(session).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not retrieve a session that is missing', async () => {
|
it('should not retrieve a session that is missing', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
const result = await createService().getSessionFromToken('1:2:3')
|
const { session } = await createService().getSessionFromToken('1:2:3')
|
||||||
|
|
||||||
expect(result).toBeUndefined()
|
expect(session).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not retrieve a session from a session token that has invalid access token', async () => {
|
it('should not retrieve a session from a session token that has invalid access token', async () => {
|
||||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||||
if (uuid === '2') {
|
if (uuid === '2') {
|
||||||
return session
|
return existingSession
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await createService().getSessionFromToken('1:2:4')
|
const { session } = await createService().getSessionFromToken('1:2:4')
|
||||||
|
|
||||||
expect(result).toBeUndefined()
|
expect(session).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should revoked a session', async () => {
|
it('should revoked a session', async () => {
|
||||||
await createService().createRevokedSession(session)
|
await createService().createRevokedSession(existingSession)
|
||||||
|
|
||||||
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
|
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
|
||||||
uuid: '2e1e43',
|
uuid: '2e1e43',
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
apiVersion: string
|
apiVersion: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<SessionBody> {
|
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||||
const session = await this.createSession({
|
const session = await this.createSession({
|
||||||
ephemeral: false,
|
ephemeral: false,
|
||||||
...dto,
|
...dto,
|
||||||
@@ -73,7 +73,10 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
this.logger.error(`Could not trace session while creating cross service token.: ${(error as Error).message}`)
|
this.logger.error(`Could not trace session while creating cross service token.: ${(error as Error).message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionPayload
|
return {
|
||||||
|
sessionHttpRepresentation: sessionPayload,
|
||||||
|
session,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNewEphemeralSessionForUser(dto: {
|
async createNewEphemeralSessionForUser(dto: {
|
||||||
@@ -81,7 +84,7 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
apiVersion: string
|
apiVersion: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<SessionBody> {
|
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||||
const ephemeralSession = await this.createSession({
|
const ephemeralSession = await this.createSession({
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
...dto,
|
...dto,
|
||||||
@@ -91,27 +94,20 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
|
|
||||||
await this.ephemeralSessionRepository.save(ephemeralSession)
|
await this.ephemeralSessionRepository.save(ephemeralSession)
|
||||||
|
|
||||||
return sessionPayload
|
return {
|
||||||
|
sessionHttpRepresentation: sessionPayload,
|
||||||
|
session: ephemeralSession,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async refreshTokens(session: Session): Promise<SessionBody> {
|
async refreshTokens(dto: { session: Session; isEphemeral: boolean }): Promise<SessionBody> {
|
||||||
const sessionPayload = await this.createTokens(session)
|
const sessionPayload = await this.createTokens(dto.session)
|
||||||
|
|
||||||
await this.sessionRepository.updateHashedTokens(session.uuid, session.hashedAccessToken, session.hashedRefreshToken)
|
if (dto.isEphemeral) {
|
||||||
|
await this.ephemeralSessionRepository.save(dto.session)
|
||||||
await this.sessionRepository.updatedTokenExpirationDates(
|
} else {
|
||||||
session.uuid,
|
await this.sessionRepository.save(dto.session)
|
||||||
session.accessExpiration,
|
}
|
||||||
session.refreshExpiration,
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.ephemeralSessionRepository.updateTokensAndExpirationDates(
|
|
||||||
session.uuid,
|
|
||||||
session.hashedAccessToken,
|
|
||||||
session.hashedRefreshToken,
|
|
||||||
session.accessExpiration,
|
|
||||||
session.refreshExpiration,
|
|
||||||
)
|
|
||||||
|
|
||||||
return sessionPayload
|
return sessionPayload
|
||||||
}
|
}
|
||||||
@@ -190,25 +186,25 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
return `${browserInfo} on ${osInfo}`
|
return `${browserInfo} on ${osInfo}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSessionFromToken(token: string): Promise<Session | undefined> {
|
async getSessionFromToken(token: string): Promise<{ session: Session | undefined; isEphemeral: boolean }> {
|
||||||
const tokenParts = token.split(':')
|
const tokenParts = token.split(':')
|
||||||
const sessionUuid = tokenParts[1]
|
const sessionUuid = tokenParts[1]
|
||||||
const accessToken = tokenParts[2]
|
const accessToken = tokenParts[2]
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
return undefined
|
return { session: undefined, isEphemeral: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await this.getSession(sessionUuid)
|
const { session, isEphemeral } = await this.getSession(sessionUuid)
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return undefined
|
return { session: undefined, isEphemeral: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedAccessToken = crypto.createHash('sha256').update(accessToken).digest('hex')
|
const hashedAccessToken = crypto.createHash('sha256').update(accessToken).digest('hex')
|
||||||
if (crypto.timingSafeEqual(Buffer.from(session.hashedAccessToken), Buffer.from(hashedAccessToken))) {
|
if (crypto.timingSafeEqual(Buffer.from(session.hashedAccessToken), Buffer.from(hashedAccessToken))) {
|
||||||
return session
|
return { session, isEphemeral }
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return { session: undefined, isEphemeral: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRevokedSessionFromToken(token: string): Promise<RevokedSession | null> {
|
async getRevokedSessionFromToken(token: string): Promise<RevokedSession | null> {
|
||||||
@@ -229,11 +225,14 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteSessionByToken(token: string): Promise<string | null> {
|
async deleteSessionByToken(token: string): Promise<string | null> {
|
||||||
const session = await this.getSessionFromToken(token)
|
const { session, isEphemeral } = await this.getSessionFromToken(token)
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
await this.sessionRepository.deleteOneByUuid(session.uuid)
|
if (isEphemeral) {
|
||||||
await this.ephemeralSessionRepository.deleteOne(session.uuid, session.userUuid)
|
await this.ephemeralSessionRepository.deleteOne(session.uuid, session.userUuid)
|
||||||
|
} else {
|
||||||
|
await this.sessionRepository.deleteOneByUuid(session.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
return session.userUuid
|
return session.userUuid
|
||||||
}
|
}
|
||||||
@@ -278,14 +277,19 @@ export class SessionService implements SessionServiceInterface {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSession(uuid: string): Promise<Session | null> {
|
private async getSession(uuid: string): Promise<{
|
||||||
|
session: Session | null
|
||||||
|
isEphemeral: boolean
|
||||||
|
}> {
|
||||||
let session = await this.ephemeralSessionRepository.findOneByUuid(uuid)
|
let session = await this.ephemeralSessionRepository.findOneByUuid(uuid)
|
||||||
|
let isEphemeral = true
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
session = await this.sessionRepository.findOneByUuid(uuid)
|
session = await this.sessionRepository.findOneByUuid(uuid)
|
||||||
|
isEphemeral = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
return { session, isEphemeral }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createTokens(session: Session): Promise<SessionBody> {
|
private async createTokens(session: Session): Promise<SessionBody> {
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ export interface SessionServiceInterface {
|
|||||||
apiVersion: string
|
apiVersion: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<SessionBody>
|
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }>
|
||||||
createNewEphemeralSessionForUser(dto: {
|
createNewEphemeralSessionForUser(dto: {
|
||||||
user: User
|
user: User
|
||||||
apiVersion: string
|
apiVersion: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
readonlyAccess: boolean
|
readonlyAccess: boolean
|
||||||
}): Promise<SessionBody>
|
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }>
|
||||||
refreshTokens(session: Session): Promise<SessionBody>
|
refreshTokens(dto: { session: Session; isEphemeral: boolean }): Promise<SessionBody>
|
||||||
getSessionFromToken(token: string): Promise<Session | undefined>
|
getSessionFromToken(token: string): Promise<{ session: Session | undefined; isEphemeral: boolean }>
|
||||||
getRevokedSessionFromToken(token: string): Promise<RevokedSession | null>
|
getRevokedSessionFromToken(token: string): Promise<RevokedSession | null>
|
||||||
markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession>
|
markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession>
|
||||||
deleteSessionByToken(token: string): Promise<string | null>
|
deleteSessionByToken(token: string): Promise<string | null>
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ describe('SettingDecrypter', () => {
|
|||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toEqual('decrypted')
|
expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toEqual(
|
||||||
|
'decrypted',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return null if the setting value is null', async () => {
|
it('should return null if the setting value is null', async () => {
|
||||||
@@ -41,7 +43,7 @@ describe('SettingDecrypter', () => {
|
|||||||
serverEncryptionVersion: EncryptionVersion.Default,
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toBeNull()
|
expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return unencrypted value if the setting value is unencrypted', async () => {
|
it('should return unencrypted value if the setting value is unencrypted', async () => {
|
||||||
@@ -50,7 +52,7 @@ describe('SettingDecrypter', () => {
|
|||||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
} as jest.Mocked<Setting>
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toEqual('test')
|
expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toEqual('test')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw if the user could not be found', async () => {
|
it('should throw if the user could not be found', async () => {
|
||||||
@@ -62,7 +64,23 @@ describe('SettingDecrypter', () => {
|
|||||||
|
|
||||||
let caughtError = null
|
let caughtError = null
|
||||||
try {
|
try {
|
||||||
await createDecrypter().decryptSettingValue(setting, '1-2-3')
|
await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')
|
||||||
|
} catch (error) {
|
||||||
|
caughtError = error
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(caughtError).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw if the user uuid is invalid', async () => {
|
||||||
|
const setting = {
|
||||||
|
value: 'encrypted',
|
||||||
|
serverEncryptionVersion: EncryptionVersion.Default,
|
||||||
|
} as jest.Mocked<Setting>
|
||||||
|
|
||||||
|
let caughtError = null
|
||||||
|
try {
|
||||||
|
await createDecrypter().decryptSettingValue(setting, 'invalid')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
caughtError = error
|
caughtError = error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
|||||||
import { Setting } from './Setting'
|
import { Setting } from './Setting'
|
||||||
import { SettingDecrypterInterface } from './SettingDecrypterInterface'
|
import { SettingDecrypterInterface } from './SettingDecrypterInterface'
|
||||||
import { SubscriptionSetting } from './SubscriptionSetting'
|
import { SubscriptionSetting } from './SubscriptionSetting'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SettingDecrypter implements SettingDecrypterInterface {
|
export class SettingDecrypter implements SettingDecrypterInterface {
|
||||||
@@ -14,12 +15,18 @@ export class SettingDecrypter implements SettingDecrypterInterface {
|
|||||||
@inject(TYPES.Auth_Crypter) private crypter: CrypterInterface,
|
@inject(TYPES.Auth_Crypter) private crypter: CrypterInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async decryptSettingValue(setting: Setting | SubscriptionSetting, userUuid: string): Promise<string | null> {
|
async decryptSettingValue(setting: Setting | SubscriptionSetting, userUuidString: string): Promise<string | null> {
|
||||||
if (setting.value !== null && setting.serverEncryptionVersion === EncryptionVersion.Default) {
|
if (setting.value !== null && setting.serverEncryptionVersion === EncryptionVersion.Default) {
|
||||||
|
const userUuidOrError = Uuid.create(userUuidString)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
throw new Error(userUuidOrError.getError())
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
throw new Error(`Could not find user with uuid: ${userUuid}`)
|
throw new Error(`Could not find user with uuid: ${userUuid.value}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.crypter.decryptForUser(setting.value, user)
|
return this.crypter.decryptForUser(setting.value, user)
|
||||||
|
|||||||
+1
-3
@@ -17,7 +17,7 @@ describe('AuthenticateSubscriptionToken', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
subscriptionTokenRepository = {} as jest.Mocked<SubscriptionTokenRepositoryInterface>
|
subscriptionTokenRepository = {} as jest.Mocked<SubscriptionTokenRepositoryInterface>
|
||||||
subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('1-2-3')
|
subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('00000000-0000-0000-0000-000000000000')
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
roles: Promise.resolve([{ name: RoleName.NAMES.CoreUser }]),
|
roles: Promise.resolve([{ name: RoleName.NAMES.CoreUser }]),
|
||||||
@@ -30,8 +30,6 @@ describe('AuthenticateSubscriptionToken', () => {
|
|||||||
it('should authenticate an subscription token', async () => {
|
it('should authenticate an subscription token', async () => {
|
||||||
const response = await createUseCase().execute({ token: 'test' })
|
const response = await createUseCase().execute({ token: 'test' })
|
||||||
|
|
||||||
expect(userRepository.findOneByUuid).toHaveBeenCalledWith('1-2-3')
|
|
||||||
|
|
||||||
expect(response.success).toBeTruthy()
|
expect(response.success).toBeTruthy()
|
||||||
|
|
||||||
expect(response.user).toEqual(user)
|
expect(response.user).toEqual(user)
|
||||||
|
|||||||
+5
-2
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
|||||||
import { UseCaseInterface } from '../UseCaseInterface'
|
import { UseCaseInterface } from '../UseCaseInterface'
|
||||||
import { AuthenticateSubscriptionTokenDTO } from './AuthenticateSubscriptionTokenDTO'
|
import { AuthenticateSubscriptionTokenDTO } from './AuthenticateSubscriptionTokenDTO'
|
||||||
import { AuthenticateSubscriptionTokenResponse } from './AuthenticateSubscriptionTokenResponse'
|
import { AuthenticateSubscriptionTokenResponse } from './AuthenticateSubscriptionTokenResponse'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthenticateSubscriptionToken implements UseCaseInterface {
|
export class AuthenticateSubscriptionToken implements UseCaseInterface {
|
||||||
@@ -16,12 +17,14 @@ export class AuthenticateSubscriptionToken implements UseCaseInterface {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: AuthenticateSubscriptionTokenDTO): Promise<AuthenticateSubscriptionTokenResponse> {
|
async execute(dto: AuthenticateSubscriptionTokenDTO): Promise<AuthenticateSubscriptionTokenResponse> {
|
||||||
const userUuid = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
|
const userUuidString = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
|
||||||
if (userUuid === undefined) {
|
const userUuidOrError = Uuid.create(userUuidString as string)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import { User } from '../../User/User'
|
|||||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||||
|
|
||||||
import { ChangeCredentials } from './ChangeCredentials'
|
import { ChangeCredentials } from './ChangeCredentials'
|
||||||
import { Username } from '@standardnotes/domain-core'
|
import { Result, Username } from '@standardnotes/domain-core'
|
||||||
|
import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
|
||||||
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
import { Session } from '../../Session/Session'
|
||||||
|
|
||||||
describe('ChangeCredentials', () => {
|
describe('ChangeCredentials', () => {
|
||||||
let userRepository: UserRepositoryInterface
|
let userRepository: UserRepositoryInterface
|
||||||
@@ -21,13 +24,23 @@ describe('ChangeCredentials', () => {
|
|||||||
let domainEventFactory: DomainEventFactoryInterface
|
let domainEventFactory: DomainEventFactoryInterface
|
||||||
let timer: TimerInterface
|
let timer: TimerInterface
|
||||||
let user: User
|
let user: User
|
||||||
|
let deleteOtherSessionsForUser: DeleteOtherSessionsForUser
|
||||||
|
|
||||||
const createUseCase = () =>
|
const createUseCase = () =>
|
||||||
new ChangeCredentials(userRepository, authResponseFactoryResolver, domainEventPublisher, domainEventFactory, timer)
|
new ChangeCredentials(
|
||||||
|
userRepository,
|
||||||
|
authResponseFactoryResolver,
|
||||||
|
domainEventPublisher,
|
||||||
|
domainEventFactory,
|
||||||
|
timer,
|
||||||
|
deleteOtherSessionsForUser,
|
||||||
|
)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
authResponseFactory = {} as jest.Mocked<AuthResponseFactoryInterface>
|
authResponseFactory = {} as jest.Mocked<AuthResponseFactoryInterface>
|
||||||
authResponseFactory.createResponse = jest.fn().mockReturnValue({ foo: 'bar' })
|
authResponseFactory.createResponse = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ response: { foo: 'bar' }, session: { uuid: '1-2-3' } as jest.Mocked<Session> })
|
||||||
|
|
||||||
authResponseFactoryResolver = {} as jest.Mocked<AuthResponseFactoryResolverInterface>
|
authResponseFactoryResolver = {} as jest.Mocked<AuthResponseFactoryResolverInterface>
|
||||||
authResponseFactoryResolver.resolveAuthResponseFactoryVersion = jest.fn().mockReturnValue(authResponseFactory)
|
authResponseFactoryResolver.resolveAuthResponseFactoryVersion = jest.fn().mockReturnValue(authResponseFactory)
|
||||||
@@ -49,27 +62,25 @@ describe('ChangeCredentials', () => {
|
|||||||
|
|
||||||
timer = {} as jest.Mocked<TimerInterface>
|
timer = {} as jest.Mocked<TimerInterface>
|
||||||
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
|
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
|
||||||
|
|
||||||
|
deleteOtherSessionsForUser = {} as jest.Mocked<DeleteOtherSessionsForUser>
|
||||||
|
deleteOtherSessionsForUser.execute = jest.fn().mockReturnValue(Result.ok())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should change password', async () => {
|
it('should change password', async () => {
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
kpCreated: '123',
|
||||||
kpCreated: '123',
|
kpOrigination: 'password-change',
|
||||||
kpOrigination: 'password-change',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: true,
|
|
||||||
authResponse: {
|
|
||||||
foo: 'bar',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
expect(userRepository.save).toHaveBeenCalledWith({
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
encryptedPassword: expect.any(String),
|
encryptedPassword: expect.any(String),
|
||||||
pwNonce: 'asdzxc',
|
pwNonce: 'asdzxc',
|
||||||
@@ -81,29 +92,24 @@ describe('ChangeCredentials', () => {
|
|||||||
})
|
})
|
||||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||||
|
expect(deleteOtherSessionsForUser.execute).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should change email', async () => {
|
it('should change email', async () => {
|
||||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValueOnce(user).mockReturnValueOnce(null)
|
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValueOnce(user).mockReturnValueOnce(null)
|
||||||
|
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
newEmail: 'new@test.te',
|
||||||
newEmail: 'new@test.te',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
kpCreated: '123',
|
||||||
kpCreated: '123',
|
kpOrigination: 'password-change',
|
||||||
kpOrigination: 'password-change',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: true,
|
|
||||||
authResponse: {
|
|
||||||
foo: 'bar',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
expect(userRepository.save).toHaveBeenCalledWith({
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
encryptedPassword: expect.any(String),
|
encryptedPassword: expect.any(String),
|
||||||
@@ -116,6 +122,7 @@ describe('ChangeCredentials', () => {
|
|||||||
})
|
})
|
||||||
expect(domainEventFactory.createUserEmailChangedEvent).toHaveBeenCalledWith('1-2-3', 'test@test.te', 'new@test.te')
|
expect(domainEventFactory.createUserEmailChangedEvent).toHaveBeenCalledWith('1-2-3', 'test@test.te', 'new@test.te')
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||||
|
expect(deleteOtherSessionsForUser.execute).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not change email if already taken', async () => {
|
it('should not change email if already taken', async () => {
|
||||||
@@ -124,22 +131,19 @@ describe('ChangeCredentials', () => {
|
|||||||
.mockReturnValueOnce(user)
|
.mockReturnValueOnce(user)
|
||||||
.mockReturnValueOnce({} as jest.Mocked<User>)
|
.mockReturnValueOnce({} as jest.Mocked<User>)
|
||||||
|
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
newEmail: 'new@test.te',
|
||||||
newEmail: 'new@test.te',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
kpCreated: '123',
|
||||||
kpCreated: '123',
|
kpOrigination: 'password-change',
|
||||||
kpOrigination: 'password-change',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: false,
|
|
||||||
errorMessage: 'The email you entered is already taken. Please try again.',
|
|
||||||
})
|
})
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(result.getError()).toEqual('The email you entered is already taken. Please try again.')
|
||||||
|
|
||||||
expect(userRepository.save).not.toHaveBeenCalled()
|
expect(userRepository.save).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||||
@@ -147,22 +151,19 @@ describe('ChangeCredentials', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should not change email if the new email is invalid', async () => {
|
it('should not change email if the new email is invalid', async () => {
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
newEmail: '',
|
||||||
newEmail: '',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
kpCreated: '123',
|
||||||
kpCreated: '123',
|
kpOrigination: 'password-change',
|
||||||
kpOrigination: 'password-change',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: false,
|
|
||||||
errorMessage: 'Username cannot be empty',
|
|
||||||
})
|
})
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(result.getError()).toEqual('Username cannot be empty')
|
||||||
|
|
||||||
expect(userRepository.save).not.toHaveBeenCalled()
|
expect(userRepository.save).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||||
@@ -172,63 +173,52 @@ describe('ChangeCredentials', () => {
|
|||||||
it('should not change email if the user is not found', async () => {
|
it('should not change email if the user is not found', async () => {
|
||||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
newEmail: '',
|
||||||
newEmail: '',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
kpCreated: '123',
|
||||||
kpCreated: '123',
|
kpOrigination: 'password-change',
|
||||||
kpOrigination: 'password-change',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: false,
|
|
||||||
errorMessage: 'User not found.',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(result.getError()).toEqual('User not found.')
|
||||||
|
|
||||||
expect(userRepository.save).not.toHaveBeenCalled()
|
expect(userRepository.save).not.toHaveBeenCalled()
|
||||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not change password if current password is incorrect', async () => {
|
it('should not change password if current password is incorrect', async () => {
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'test123',
|
||||||
currentPassword: 'test123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: false,
|
|
||||||
errorMessage: 'The current password you entered is incorrect. Please try again.',
|
|
||||||
})
|
})
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(result.getError()).toEqual('The current password you entered is incorrect. Please try again.')
|
||||||
|
|
||||||
expect(userRepository.save).not.toHaveBeenCalled()
|
expect(userRepository.save).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update protocol version while changing password', async () => {
|
it('should update protocol version while changing password', async () => {
|
||||||
expect(
|
const result = await createUseCase().execute({
|
||||||
await createUseCase().execute({
|
username: Username.create('test@test.te').getValue(),
|
||||||
username: Username.create('test@test.te').getValue(),
|
apiVersion: ApiVersion.v20200115,
|
||||||
apiVersion: '20190520',
|
currentPassword: 'qweqwe123123',
|
||||||
currentPassword: 'qweqwe123123',
|
newPassword: 'test234',
|
||||||
newPassword: 'test234',
|
pwNonce: 'asdzxc',
|
||||||
pwNonce: 'asdzxc',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
protocolVersion: '004',
|
||||||
protocolVersion: '004',
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
success: true,
|
|
||||||
authResponse: {
|
|
||||||
foo: 'bar',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
expect(userRepository.save).toHaveBeenCalledWith({
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
encryptedPassword: expect.any(String),
|
encryptedPassword: expect.any(String),
|
||||||
@@ -239,4 +229,63 @@ describe('ChangeCredentials', () => {
|
|||||||
updatedAt: new Date(1),
|
updatedAt: new Date(1),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not delete other sessions for user if neither passoword nor email are changed', async () => {
|
||||||
|
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValueOnce(user)
|
||||||
|
|
||||||
|
const result = await createUseCase().execute({
|
||||||
|
username: Username.create('test@test.te').getValue(),
|
||||||
|
apiVersion: ApiVersion.v20200115,
|
||||||
|
currentPassword: 'qweqwe123123',
|
||||||
|
newPassword: 'qweqwe123123',
|
||||||
|
newEmail: undefined,
|
||||||
|
pwNonce: 'asdzxc',
|
||||||
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
|
kpCreated: '123',
|
||||||
|
kpOrigination: 'password-change',
|
||||||
|
})
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
|
encryptedPassword: expect.any(String),
|
||||||
|
email: 'test@test.te',
|
||||||
|
uuid: '1-2-3',
|
||||||
|
pwNonce: 'asdzxc',
|
||||||
|
kpCreated: '123',
|
||||||
|
kpOrigination: 'password-change',
|
||||||
|
updatedAt: new Date(1),
|
||||||
|
})
|
||||||
|
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||||
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
|
expect(deleteOtherSessionsForUser.execute).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not delete other sessions for user if the caller does not support sessions', async () => {
|
||||||
|
authResponseFactory.createResponse = jest.fn().mockReturnValue({ response: { foo: 'bar' } })
|
||||||
|
|
||||||
|
const result = await createUseCase().execute({
|
||||||
|
username: Username.create('test@test.te').getValue(),
|
||||||
|
apiVersion: ApiVersion.v20200115,
|
||||||
|
currentPassword: 'qweqwe123123',
|
||||||
|
newPassword: 'test234',
|
||||||
|
pwNonce: 'asdzxc',
|
||||||
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
|
kpCreated: '123',
|
||||||
|
kpOrigination: 'password-change',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
|
encryptedPassword: expect.any(String),
|
||||||
|
pwNonce: 'asdzxc',
|
||||||
|
kpCreated: '123',
|
||||||
|
email: 'test@test.te',
|
||||||
|
uuid: '1-2-3',
|
||||||
|
kpOrigination: 'password-change',
|
||||||
|
updatedAt: new Date(1),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(deleteOtherSessionsForUser.execute).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
import * as bcrypt from 'bcryptjs'
|
import * as bcrypt from 'bcryptjs'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
|
import { DomainEventPublisherInterface, UserEmailChangedEvent } from '@standardnotes/domain-events'
|
||||||
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
|
import { Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
import { AuthResponseFactoryResolverInterface } from '../../Auth/AuthResponseFactoryResolverInterface'
|
import { AuthResponseFactoryResolverInterface } from '../../Auth/AuthResponseFactoryResolverInterface'
|
||||||
|
|
||||||
import { User } from '../../User/User'
|
import { User } from '../../User/User'
|
||||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||||
import { ChangeCredentialsDTO } from './ChangeCredentialsDTO'
|
import { ChangeCredentialsDTO } from './ChangeCredentialsDTO'
|
||||||
import { ChangeCredentialsResponse } from './ChangeCredentialsResponse'
|
|
||||||
import { UseCaseInterface } from '../UseCaseInterface'
|
|
||||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||||
import { DomainEventPublisherInterface, UserEmailChangedEvent } from '@standardnotes/domain-events'
|
import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { AuthResponse20161215 } from '../../Auth/AuthResponse20161215'
|
||||||
import { Username } from '@standardnotes/domain-core'
|
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
||||||
|
import { Session } from '../../Session/Session'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ChangeCredentials implements UseCaseInterface {
|
export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215 | AuthResponse20200115> {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
||||||
@inject(TYPES.Auth_AuthResponseFactoryResolver)
|
@inject(TYPES.Auth_AuthResponseFactoryResolver)
|
||||||
@@ -22,22 +24,18 @@ export class ChangeCredentials implements UseCaseInterface {
|
|||||||
@inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
@inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||||
@inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
@inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||||
|
@inject(TYPES.Auth_DeleteOtherSessionsForUser)
|
||||||
|
private deleteOtherSessionsForUserUseCase: DeleteOtherSessionsForUser,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: ChangeCredentialsDTO): Promise<ChangeCredentialsResponse> {
|
async execute(dto: ChangeCredentialsDTO): Promise<Result<AuthResponse20161215 | AuthResponse20200115>> {
|
||||||
const user = await this.userRepository.findOneByUsernameOrEmail(dto.username)
|
const user = await this.userRepository.findOneByUsernameOrEmail(dto.username)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return Result.fail('User not found.')
|
||||||
success: false,
|
|
||||||
errorMessage: 'User not found.',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await bcrypt.compare(dto.currentPassword, user.encryptedPassword))) {
|
if (!(await bcrypt.compare(dto.currentPassword, user.encryptedPassword))) {
|
||||||
return {
|
return Result.fail('The current password you entered is incorrect. Please try again.')
|
||||||
success: false,
|
|
||||||
errorMessage: 'The current password you entered is incorrect. Please try again.',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.encryptedPassword = await bcrypt.hash(dto.newPassword, User.PASSWORD_HASH_COST)
|
user.encryptedPassword = await bcrypt.hash(dto.newPassword, User.PASSWORD_HASH_COST)
|
||||||
@@ -46,19 +44,13 @@ export class ChangeCredentials implements UseCaseInterface {
|
|||||||
if (dto.newEmail !== undefined) {
|
if (dto.newEmail !== undefined) {
|
||||||
const newUsernameOrError = Username.create(dto.newEmail)
|
const newUsernameOrError = Username.create(dto.newEmail)
|
||||||
if (newUsernameOrError.isFailed()) {
|
if (newUsernameOrError.isFailed()) {
|
||||||
return {
|
return Result.fail(newUsernameOrError.getError())
|
||||||
success: false,
|
|
||||||
errorMessage: newUsernameOrError.getError(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const newUsername = newUsernameOrError.getValue()
|
const newUsername = newUsernameOrError.getValue()
|
||||||
|
|
||||||
const existingUser = await this.userRepository.findOneByUsernameOrEmail(newUsername)
|
const existingUser = await this.userRepository.findOneByUsernameOrEmail(newUsername)
|
||||||
if (existingUser !== null) {
|
if (existingUser !== null) {
|
||||||
return {
|
return Result.fail('The email you entered is already taken. Please try again.')
|
||||||
success: false,
|
|
||||||
errorMessage: 'The email you entered is already taken. Please try again.',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userEmailChangedEvent = this.domainEventFactory.createUserEmailChangedEvent(
|
userEmailChangedEvent = this.domainEventFactory.createUserEmailChangedEvent(
|
||||||
@@ -90,15 +82,35 @@ export class ChangeCredentials implements UseCaseInterface {
|
|||||||
|
|
||||||
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
|
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
|
||||||
|
|
||||||
return {
|
const authResponse = await authResponseFactory.createResponse({
|
||||||
success: true,
|
user: updatedUser,
|
||||||
authResponse: await authResponseFactory.createResponse({
|
apiVersion: dto.apiVersion,
|
||||||
user: updatedUser,
|
userAgent: dto.updatedWithUserAgent,
|
||||||
apiVersion: dto.apiVersion,
|
ephemeralSession: false,
|
||||||
userAgent: dto.updatedWithUserAgent,
|
readonlyAccess: false,
|
||||||
ephemeralSession: false,
|
})
|
||||||
readonlyAccess: false,
|
|
||||||
}),
|
if (authResponse.session) {
|
||||||
|
await this.deleteOtherSessionsForUserIfNeeded(user.uuid, authResponse.session, dto)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(authResponse.response)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async deleteOtherSessionsForUserIfNeeded(
|
||||||
|
userUuid: string,
|
||||||
|
session: Session,
|
||||||
|
dto: ChangeCredentialsDTO,
|
||||||
|
): Promise<void> {
|
||||||
|
const passwordHasChanged = dto.newPassword !== dto.currentPassword
|
||||||
|
const userEmailChanged = dto.newEmail !== undefined
|
||||||
|
|
||||||
|
if (passwordHasChanged || userEmailChanged) {
|
||||||
|
await this.deleteOtherSessionsForUserUseCase.execute({
|
||||||
|
userUuid,
|
||||||
|
currentSessionUuid: session.uuid,
|
||||||
|
markAsRevoked: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { AuthResponse20161215 } from '../../Auth/AuthResponse20161215'
|
|
||||||
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
|
||||||
|
|
||||||
export type ChangeCredentialsResponse = {
|
|
||||||
success: boolean
|
|
||||||
authResponse?: AuthResponse20161215 | AuthResponse20200115
|
|
||||||
errorMessage?: string
|
|
||||||
}
|
|
||||||
+22
-7
@@ -28,13 +28,15 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
session = {} as jest.Mocked<Session>
|
session = {} as jest.Mocked<Session>
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
uuid: '1-2-3',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
} as jest.Mocked<User>
|
} as jest.Mocked<User>
|
||||||
user.roles = Promise.resolve([role])
|
user.roles = Promise.resolve([role])
|
||||||
|
|
||||||
userProjector = {} as jest.Mocked<ProjectorInterface<User>>
|
userProjector = {} as jest.Mocked<ProjectorInterface<User>>
|
||||||
userProjector.projectSimple = jest.fn().mockReturnValue({ uuid: '1-2-3', email: 'test@test.te' })
|
userProjector.projectSimple = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({ uuid: '00000000-0000-0000-0000-000000000000', email: 'test@test.te' })
|
||||||
|
|
||||||
roleProjector = {} as jest.Mocked<ProjectorInterface<Role>>
|
roleProjector = {} as jest.Mocked<ProjectorInterface<Role>>
|
||||||
roleProjector.projectSimple = jest.fn().mockReturnValue({ name: 'role1', uuid: '1-3-4' })
|
roleProjector.projectSimple = jest.fn().mockReturnValue({ name: 'role1', uuid: '1-3-4' })
|
||||||
@@ -69,7 +71,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '1-2-3',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
@@ -91,7 +93,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
],
|
],
|
||||||
user: {
|
user: {
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '1-2-3',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
@@ -100,7 +102,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
|
|
||||||
it('should create a cross service token for user by user uuid', async () => {
|
it('should create a cross service token for user by user uuid', async () => {
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||||
@@ -113,7 +115,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
],
|
],
|
||||||
user: {
|
user: {
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '1-2-3',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
@@ -126,7 +128,20 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
let caughtError = null
|
let caughtError = null
|
||||||
try {
|
try {
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
userUuid: '1-2-3',
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
caughtError = error
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(caughtError).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if user uuid is invalid', async () => {
|
||||||
|
let caughtError = null
|
||||||
|
try {
|
||||||
|
await createUseCase().execute({
|
||||||
|
userUuid: 'invalid',
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
caughtError = error
|
caughtError = error
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { UseCaseInterface } from '../UseCaseInterface'
|
|||||||
|
|
||||||
import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
||||||
import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse'
|
import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse'
|
||||||
|
import { Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CreateCrossServiceToken implements UseCaseInterface {
|
export class CreateCrossServiceToken implements UseCaseInterface {
|
||||||
@@ -26,7 +27,13 @@ export class CreateCrossServiceToken implements UseCaseInterface {
|
|||||||
async execute(dto: CreateCrossServiceTokenDTO): Promise<CreateCrossServiceTokenResponse> {
|
async execute(dto: CreateCrossServiceTokenDTO): Promise<CreateCrossServiceTokenResponse> {
|
||||||
let user: User | undefined | null = dto.user
|
let user: User | undefined | null = dto.user
|
||||||
if (user === undefined && dto.userUuid !== undefined) {
|
if (user === undefined && dto.userUuid !== undefined) {
|
||||||
user = await this.userRepository.findOneByUuid(dto.userUuid)
|
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
throw new Error(userUuidOrError.getError())
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
|
user = await this.userRepository.findOneByUuid(userUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ describe('DeleteAccount', () => {
|
|||||||
} as jest.Mocked<UserSubscription>
|
} as jest.Mocked<UserSubscription>
|
||||||
|
|
||||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||||
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
|
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
|
||||||
|
|
||||||
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
||||||
@@ -53,65 +54,124 @@ describe('DeleteAccount', () => {
|
|||||||
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1)
|
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should trigger account deletion - no subscription', async () => {
|
describe('when user uuid is provided', () => {
|
||||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
it('should trigger account deletion - no subscription', async () => {
|
||||||
.fn()
|
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
.fn()
|
||||||
|
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||||
|
|
||||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||||
message: 'Successfully deleted user',
|
|
||||||
responseCode: 200,
|
expect(result.isFailed()).toBeFalsy()
|
||||||
success: true,
|
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||||
|
userUuid: '1-2-3',
|
||||||
|
userCreatedAtTimestamp: 1,
|
||||||
|
regularSubscriptionUuid: undefined,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
it('should trigger account deletion - subscription present', async () => {
|
||||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||||
userUuid: '1-2-3',
|
.fn()
|
||||||
userCreatedAtTimestamp: 1,
|
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||||
regularSubscriptionUuid: undefined,
|
|
||||||
|
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||||
|
userUuid: '1-2-3',
|
||||||
|
userCreatedAtTimestamp: 1,
|
||||||
|
regularSubscriptionUuid: '1-2-3',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not trigger account deletion if user is not found', async () => {
|
||||||
|
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
|
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not trigger account deletion if user uuid is invalid', async () => {
|
||||||
|
const result = await createUseCase().execute({ userUuid: 'invalid' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should trigger account deletion - subscription present', async () => {
|
describe('when username is provided', () => {
|
||||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
it('should trigger account deletion - no subscription', async () => {
|
||||||
.fn()
|
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
.fn()
|
||||||
|
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||||
|
|
||||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||||
message: 'Successfully deleted user',
|
|
||||||
responseCode: 200,
|
expect(result.isFailed()).toBeFalsy()
|
||||||
success: true,
|
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||||
|
userUuid: '1-2-3',
|
||||||
|
userCreatedAtTimestamp: 1,
|
||||||
|
regularSubscriptionUuid: undefined,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
it('should trigger account deletion - subscription present', async () => {
|
||||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||||
userUuid: '1-2-3',
|
.fn()
|
||||||
userCreatedAtTimestamp: 1,
|
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||||
regularSubscriptionUuid: '1-2-3',
|
|
||||||
|
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||||
|
userUuid: '1-2-3',
|
||||||
|
userCreatedAtTimestamp: 1,
|
||||||
|
regularSubscriptionUuid: '1-2-3',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not trigger account deletion if user is not found', async () => {
|
||||||
|
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
|
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not trigger account deletion if username is invalid', async () => {
|
||||||
|
const result = await createUseCase().execute({ username: '' })
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
|
||||||
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not trigger account deletion if user is not found', async () => {
|
describe('when neither user uuid nor username is provided', () => {
|
||||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
it('should not trigger account deletion', async () => {
|
||||||
|
const result = await createUseCase().execute({})
|
||||||
|
|
||||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
expect(result.isFailed()).toBeTruthy()
|
||||||
message: 'User not found',
|
|
||||||
responseCode: 404,
|
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||||
success: false,
|
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
|
||||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not trigger account deletion if username is invalid', async () => {
|
|
||||||
expect(await createUseCase().execute({ email: '' })).toEqual({
|
|
||||||
message: 'Username cannot be empty',
|
|
||||||
responseCode: 400,
|
|
||||||
success: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
|
||||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Username } from '@standardnotes/domain-core'
|
import { Result, UseCaseInterface, Username, Uuid } from '@standardnotes/domain-core'
|
||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
@@ -7,13 +7,12 @@ import TYPES from '../../../Bootstrap/Types'
|
|||||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||||
import { UseCaseInterface } from '../UseCaseInterface'
|
|
||||||
|
|
||||||
import { DeleteAccountDTO } from './DeleteAccountDTO'
|
import { DeleteAccountDTO } from './DeleteAccountDTO'
|
||||||
import { DeleteAccountResponse } from './DeleteAccountResponse'
|
import { User } from '../../User/User'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class DeleteAccount implements UseCaseInterface {
|
export class DeleteAccount implements UseCaseInterface<string> {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
||||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
||||||
@@ -22,25 +21,30 @@ export class DeleteAccount implements UseCaseInterface {
|
|||||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: DeleteAccountDTO): Promise<DeleteAccountResponse> {
|
async execute(dto: DeleteAccountDTO): Promise<Result<string>> {
|
||||||
const usernameOrError = Username.create(dto.email)
|
let user: User | null = null
|
||||||
if (usernameOrError.isFailed()) {
|
if (dto.userUuid !== undefined) {
|
||||||
return {
|
const uuidOrError = Uuid.create(dto.userUuid)
|
||||||
success: false,
|
if (uuidOrError.isFailed()) {
|
||||||
responseCode: 400,
|
return Result.fail(uuidOrError.getError())
|
||||||
message: usernameOrError.getError(),
|
|
||||||
}
|
}
|
||||||
}
|
const uuid = uuidOrError.getValue()
|
||||||
const username = usernameOrError.getValue()
|
|
||||||
|
|
||||||
const user = await this.userRepository.findOneByUsernameOrEmail(username)
|
user = await this.userRepository.findOneByUuid(uuid)
|
||||||
|
} else if (dto.username !== undefined) {
|
||||||
|
const usernameOrError = Username.create(dto.username)
|
||||||
|
if (usernameOrError.isFailed()) {
|
||||||
|
return Result.fail(usernameOrError.getError())
|
||||||
|
}
|
||||||
|
const username = usernameOrError.getValue()
|
||||||
|
|
||||||
|
user = await this.userRepository.findOneByUsernameOrEmail(username)
|
||||||
|
} else {
|
||||||
|
return Result.fail('Either userUuid or username must be provided.')
|
||||||
|
}
|
||||||
|
|
||||||
if (user === null) {
|
if (user === null) {
|
||||||
return {
|
return Result.ok('User already deleted.')
|
||||||
success: false,
|
|
||||||
responseCode: 404,
|
|
||||||
message: 'User not found',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let regularSubscriptionUuid = undefined
|
let regularSubscriptionUuid = undefined
|
||||||
@@ -57,10 +61,6 @@ export class DeleteAccount implements UseCaseInterface {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return Result.ok('Successfully deleted account.')
|
||||||
success: true,
|
|
||||||
message: 'Successfully deleted user',
|
|
||||||
responseCode: 200,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export type DeleteAccountDTO = {
|
export type DeleteAccountDTO = {
|
||||||
email: string
|
userUuid?: string
|
||||||
|
username?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export type DeleteAccountResponse = {
|
|
||||||
success: boolean
|
|
||||||
responseCode: number
|
|
||||||
message: string
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user