mirror of
https://github.com/standardnotes/server
synced 2026-04-22 09:01:42 -04:00
Compare commits
127 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 |
+7
-1
@@ -10,7 +10,7 @@ REDIS_HOST=cache
|
||||
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
||||
AUTH_SERVER_REFRESH_TOKEN_AGE=10
|
||||
AUTH_SERVER_EPHEMERAL_SESSION_AGE=300
|
||||
SYNCING_SERVER_REVISIONS_FREQUENCY=5
|
||||
SYNCING_SERVER_REVISIONS_FREQUENCY=2
|
||||
AUTH_SERVER_LOG_LEVEL=debug
|
||||
SYNCING_SERVER_LOG_LEVEL=debug
|
||||
FILES_SERVER_LOG_LEVEL=debug
|
||||
@@ -22,6 +22,12 @@ MYSQL_USER=std_notes_user
|
||||
MYSQL_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_SERVER_ENCRYPTION_SERVER_KEY=1087415dfde3093797f9a7ca93a49e7d7aa1861735eb0d32aae9c303b8c3d060
|
||||
VALET_TOKEN_SECRET=4b886819ebe1e908077c6cae96311b48a8416bd60cc91c03060e15bdf6b30d1f
|
||||
|
||||
@@ -20,6 +20,11 @@ on:
|
||||
jobs:
|
||||
e2e:
|
||||
name: (Docker) E2E Test Suite
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
secondary_db_enabled: [true, false]
|
||||
transition_mode_enabled: [true, false]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
@@ -45,19 +50,41 @@ jobs:
|
||||
env:
|
||||
DB_TYPE: mysql
|
||||
CACHE_TYPE: redis
|
||||
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
|
||||
TRANSITION_MODE_ENABLED: ${{ matrix.transition_mode_enabled }}
|
||||
|
||||
- name: Wait for server to start
|
||||
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
|
||||
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:
|
||||
name: (Home Server) E2E Test Suite
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
db_type: [mysql, sqlite]
|
||||
cache_type: [redis, memory]
|
||||
secondary_db_enabled: [true, false]
|
||||
transition_mode_enabled: [true, false]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -79,6 +106,14 @@ jobs:
|
||||
MYSQL_DATABASE: standardnotes
|
||||
MYSQL_USER: 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:
|
||||
- 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/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 "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
|
||||
echo "DB_HOST=db" >> packages/home-server/.env
|
||||
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
|
||||
echo "DB_HOST=localhost" >> 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_PASSWORD=standardnotes" >> 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 "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 "E2E_TESTING=true" >> packages/home-server/.env
|
||||
|
||||
- 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:
|
||||
PORT: 3123
|
||||
|
||||
- name: Wait for server to start
|
||||
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
|
||||
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"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["jsonwebtoken", "npm:9.0.0"],\
|
||||
["mongodb", "virtual:67ad3a1ca34e24ce4821cc48979e98af0c3e5dd7aabc7ad0b5d22d1d977d6f943f81c9f141a420105ebdc61ef777e508a96c7946081decd98f8c30543d468b33#npm:5.7.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["newrelic", "npm:10.1.2"],\
|
||||
["nodemon", "npm:2.0.22"],\
|
||||
@@ -5201,7 +5202,7 @@ const RAW_RUNTIME_STATE =
|
||||
["semver", "npm:7.5.1"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["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"],\
|
||||
["ua-parser-js", "npm:1.0.35"],\
|
||||
["uuid", "npm:9.0.0"],\
|
||||
@@ -5259,7 +5260,6 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.17.5"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
@@ -5870,6 +5870,26 @@ const RAW_RUNTIME_STATE =
|
||||
"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", [\
|
||||
["npm:17.0.24", {\
|
||||
"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"\
|
||||
}]\
|
||||
]],\
|
||||
["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", [\
|
||||
["npm:5.7.1", {\
|
||||
"packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-8e611bed4d.zip/node_modules/buffer/",\
|
||||
@@ -11933,6 +11962,15 @@ const RAW_RUNTIME_STATE =
|
||||
"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", [\
|
||||
["npm:8.1.2", {\
|
||||
"packageLocation": "./.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-e36c879078.zip/node_modules/meow/",\
|
||||
@@ -12291,6 +12329,59 @@ const RAW_RUNTIME_STATE =
|
||||
"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", [\
|
||||
["npm:2.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-de027828fc.zip/node_modules/ms/",\
|
||||
@@ -14250,6 +14341,16 @@ const RAW_RUNTIME_STATE =
|
||||
"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", [\
|
||||
["npm:3.1.2", {\
|
||||
"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"\
|
||||
}]\
|
||||
]],\
|
||||
["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", [\
|
||||
["npm:2.0.1", {\
|
||||
"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"]\
|
||||
],\
|
||||
"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", [\
|
||||
@@ -15758,6 +15877,98 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"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", {\
|
||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-fc9b7b780b/0/cache/typeorm-npm-0.3.16-5ac12a7afc-19803f935e.zip/node_modules/typeorm/",\
|
||||
"packageDependencies": [\
|
||||
@@ -16192,6 +16403,13 @@ const RAW_RUNTIME_STATE =
|
||||
["webidl-conversions", "npm:3.0.1"]\
|
||||
],\
|
||||
"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", [\
|
||||
@@ -16250,6 +16468,15 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["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", {\
|
||||
"packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-bd0cc6b75b.zip/node_modules/whatwg-url/",\
|
||||
"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:
|
||||
DB_TYPE: "${DB_TYPE}"
|
||||
CACHE_TYPE: "${CACHE_TYPE}"
|
||||
SECONDARY_DB_ENABLED: "${SECONDARY_DB_ENABLED}"
|
||||
TRANSITION_MODE_ENABLED: "${TRANSITION_MODE_ENABLED}"
|
||||
container_name: server-ci
|
||||
ports:
|
||||
- 3123:3000
|
||||
@@ -61,6 +63,21 @@ services:
|
||||
networks:
|
||||
- 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:
|
||||
image: redis:6.0-alpine
|
||||
container_name: cache-ci
|
||||
|
||||
@@ -3,6 +3,7 @@ services:
|
||||
image: standardnotes/server
|
||||
env_file: .env
|
||||
container_name: server_self_hosted
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3000:3000
|
||||
- 3125:3104
|
||||
|
||||
@@ -63,6 +63,12 @@ fi
|
||||
if [ -z "$CACHE_TYPE" ]; then
|
||||
export CACHE_TYPE="redis"
|
||||
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"
|
||||
|
||||
#########
|
||||
|
||||
+4
-1
@@ -12,12 +12,15 @@
|
||||
},
|
||||
"scripts": {
|
||||
"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",
|
||||
"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\"",
|
||||
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
|
||||
"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": {
|
||||
"@commitlint/cli": "^17.0.2",
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.25.3",
|
||||
"version": "2.25.14",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,70 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
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
|
||||
|
||||
@@ -46,7 +46,7 @@ void container.load().then((container) => {
|
||||
|
||||
server.setConfig((app) => {
|
||||
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()
|
||||
})
|
||||
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) => {
|
||||
app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.67.2",
|
||||
"version": "1.70.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -21,6 +21,7 @@
|
||||
"clean": "rm -fr dist",
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --fix --ext .ts",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
|
||||
@@ -57,7 +57,7 @@ export class ContainerConfigLoader {
|
||||
defaultMeta: { service: 'api-gateway' },
|
||||
})
|
||||
}
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||
container.bind<winston.Logger>(TYPES.ApiGateway_Logger).toConstantValue(logger)
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
@@ -68,36 +68,39 @@ export class ContainerConfigLoader {
|
||||
} else {
|
||||
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
|
||||
container.bind(TYPES.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.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.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.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_SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
|
||||
container.bind(TYPES.ApiGateway_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
|
||||
container.bind(TYPES.ApiGateway_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
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)
|
||||
container.bind(TYPES.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_VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
|
||||
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
|
||||
container
|
||||
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
.to(RequiredCrossServiceTokenMiddleware)
|
||||
container
|
||||
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.OptionalCrossServiceTokenMiddleware)
|
||||
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
|
||||
.to(OptionalCrossServiceTokenMiddleware)
|
||||
container.bind<WebSocketAuthMiddleware>(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
|
||||
container.bind<WebSocketAuthMiddleware>(TYPES.ApiGateway_WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
|
||||
container
|
||||
.bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
|
||||
.bind<SubscriptionTokenAuthMiddleware>(TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||
.to(SubscriptionTokenAuthMiddleware)
|
||||
|
||||
// Services
|
||||
@@ -106,24 +109,26 @@ export class ContainerConfigLoader {
|
||||
throw new Error('Service container is required when configured for home server')
|
||||
}
|
||||
container
|
||||
.bind<ServiceProxyInterface>(TYPES.ServiceProxy)
|
||||
.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
|
||||
.toConstantValue(
|
||||
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.FILES_SERVER_URL)),
|
||||
new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.ApiGateway_FILES_SERVER_URL)),
|
||||
)
|
||||
} 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) {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache)
|
||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.Timer)))
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
|
||||
} else {
|
||||
container.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache)
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.to(RedisCrossServiceTokenCache)
|
||||
}
|
||||
container
|
||||
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
|
||||
.bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
|
||||
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
||||
|
||||
logger.debug('Configuration complete')
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
export const TYPES = {
|
||||
Logger: Symbol.for('Logger'),
|
||||
Redis: Symbol.for('Redis'),
|
||||
HTTPClient: Symbol.for('HTTPClient'),
|
||||
ApiGateway_Logger: Symbol.for('ApiGateway_Logger'),
|
||||
ApiGateway_Redis: Symbol.for('ApiGateway_Redis'),
|
||||
ApiGateway_HTTPClient: Symbol.for('ApiGateway_HTTPClient'),
|
||||
// env vars
|
||||
SYNCING_SERVER_JS_URL: Symbol.for('SYNCING_SERVER_JS_URL'),
|
||||
AUTH_SERVER_URL: Symbol.for('AUTH_SERVER_URL'),
|
||||
PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
|
||||
FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
|
||||
REVISIONS_SERVER_URL: Symbol.for('REVISIONS_SERVER_URL'),
|
||||
EMAIL_SERVER_URL: Symbol.for('EMAIL_SERVER_URL'),
|
||||
WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
|
||||
AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
|
||||
HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
|
||||
VERSION: Symbol.for('VERSION'),
|
||||
CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'),
|
||||
ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
|
||||
ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
|
||||
ApiGateway_PAYMENTS_SERVER_URL: Symbol.for('ApiGateway_PAYMENTS_SERVER_URL'),
|
||||
ApiGateway_FILES_SERVER_URL: Symbol.for('ApiGateway_FILES_SERVER_URL'),
|
||||
ApiGateway_REVISIONS_SERVER_URL: Symbol.for('ApiGateway_REVISIONS_SERVER_URL'),
|
||||
ApiGateway_EMAIL_SERVER_URL: Symbol.for('ApiGateway_EMAIL_SERVER_URL'),
|
||||
ApiGateway_WEB_SOCKET_SERVER_URL: Symbol.for('ApiGateway_WEB_SOCKET_SERVER_URL'),
|
||||
ApiGateway_AUTH_JWT_SECRET: Symbol.for('ApiGateway_AUTH_JWT_SECRET'),
|
||||
ApiGateway_HTTP_CALL_TIMEOUT: Symbol.for('ApiGateway_HTTP_CALL_TIMEOUT'),
|
||||
ApiGateway_VERSION: Symbol.for('ApiGateway_VERSION'),
|
||||
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
|
||||
RequiredCrossServiceTokenMiddleware: Symbol.for('RequiredCrossServiceTokenMiddleware'),
|
||||
OptionalCrossServiceTokenMiddleware: Symbol.for('OptionalCrossServiceTokenMiddleware'),
|
||||
WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'),
|
||||
SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'),
|
||||
ApiGateway_RequiredCrossServiceTokenMiddleware: Symbol.for('ApiGateway_RequiredCrossServiceTokenMiddleware'),
|
||||
ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
|
||||
ApiGateway_WebSocketAuthMiddleware: Symbol.for('ApiGateway_WebSocketAuthMiddleware'),
|
||||
ApiGateway_SubscriptionTokenAuthMiddleware: Symbol.for('ApiGateway_SubscriptionTokenAuthMiddleware'),
|
||||
// Services
|
||||
ServiceProxy: Symbol.for('ServiceProxy'),
|
||||
CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'),
|
||||
Timer: Symbol.for('Timer'),
|
||||
EndpointResolver: Symbol.for('EndpointResolver'),
|
||||
ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
|
||||
ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
|
||||
ApiGateway_Timer: Symbol.for('ApiGateway_Timer'),
|
||||
ApiGateway_EndpointResolver: Symbol.for('ApiGateway_EndpointResolver'),
|
||||
}
|
||||
|
||||
// export default TYPES
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CrossServiceTokenData } from '@standardnotes/security'
|
||||
import { RoleName } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
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'] })
|
||||
|
||||
response.locals.freeUser =
|
||||
decodedToken.roles.length === 1 &&
|
||||
decodedToken.roles.find((role) => role.name === RoleName.NAMES.CoreUser) !== undefined
|
||||
|
||||
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
||||
await this.crossServiceTokenCache.set({
|
||||
authorizationHeaderValue: authHeaderValue,
|
||||
|
||||
@@ -9,7 +9,7 @@ export class LegacyController extends BaseHttpController {
|
||||
private 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()
|
||||
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
|
||||
@injectable()
|
||||
export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
|
||||
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.Timer) timer: TimerInterface,
|
||||
@inject(TYPES.Logger) logger: Logger,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
|
||||
@inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||
@inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) logger: Logger,
|
||||
) {
|
||||
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
|
||||
@injectable()
|
||||
export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
|
||||
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||
@inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.Timer) timer: TimerInterface,
|
||||
@inject(TYPES.Logger) logger: Logger,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
|
||||
@inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
|
||||
@inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) logger: Logger,
|
||||
) {
|
||||
super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
||||
@injectable()
|
||||
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
constructor(
|
||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CrossServiceTokenData } from '@standardnotes/security'
|
||||
import { RoleName } from '@standardnotes/domain-core'
|
||||
import { NextFunction, Request, Response } from 'express'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { BaseMiddleware } from 'inversify-express-utils'
|
||||
@@ -12,10 +11,10 @@ import { TYPES } from '../Bootstrap/Types'
|
||||
@injectable()
|
||||
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||
constructor(
|
||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -60,9 +59,6 @@ export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||
|
||||
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.roles = decodedToken.roles
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1')
|
||||
export class ActionsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
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> {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
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> {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
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> {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/authenticators')
|
||||
export class AuthenticatorsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpDelete('/:authenticatorId', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpDelete('/:authenticatorId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async delete(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/files')
|
||||
export class FilesController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/valet-tokens', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpPost('/valet-tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async createToken(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -6,11 +6,11 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
||||
@controller('/v1')
|
||||
export class InvoicesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/invoices/send-latest', TYPES.SubscriptionTokenAuthMiddleware)
|
||||
@httpPost('/invoices/send-latest', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||
async sendLatestInvoice(request: Request, response: Response): Promise<void> {
|
||||
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 { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
|
||||
@controller('/v1/items', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@controller('/v1/items', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
export class ItemsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
|
||||
@controller('/v1/messages', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@controller('/v1/messages', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
export class MessagesController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/offline')
|
||||
export class OfflineController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
||||
@controller('/v1')
|
||||
export class PaymentsController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -40,12 +40,12 @@ export class PaymentsController extends BaseHttpController {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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 { 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 {
|
||||
@httpGet('/')
|
||||
async getRevisions(): Promise<results.JsonResult> {
|
||||
|
||||
@@ -8,13 +8,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/sessions')
|
||||
export class SessionsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async getSessions(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
|
||||
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
export class SharedVaultInvitesController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
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')
|
||||
async getOutboundUserInvites(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callSyncingServer(
|
||||
|
||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
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 {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
|
||||
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
export class SharedVaultsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/subscription-invites')
|
||||
export class SubscriptionInvitesController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/subscription-tokens')
|
||||
export class TokensController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async createToken(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -20,9 +20,10 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/users')
|
||||
export class UsersController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER) private isConfiguredForHomeServer: boolean,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -32,12 +33,12 @@ export class UsersController extends BaseHttpController {
|
||||
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> {
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
this.logger.debug(
|
||||
'[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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
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> {
|
||||
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||
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> {
|
||||
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> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -10,14 +10,14 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
|
||||
@controller('/v1/sockets')
|
||||
export class WebSocketsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/tokens', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
@httpPost('/tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async createWebSocketConnectionToken(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWebSocketServer(
|
||||
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> {
|
||||
if (!request.headers.connectionid) {
|
||||
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')
|
||||
export class ActionsControllerV2 extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
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> {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
||||
@controller('/v2')
|
||||
export class PaymentsControllerV2 extends BaseHttpController {
|
||||
constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -15,22 +15,22 @@ export class PaymentsControllerV2 extends BaseHttpController {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
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> {
|
||||
await this.httpService.callPaymentsServer(
|
||||
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> {
|
||||
await this.httpService.callPaymentsServer(
|
||||
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> {
|
||||
await this.httpService.callPaymentsServer(
|
||||
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> {
|
||||
await this.httpService.callPaymentsServer(
|
||||
request,
|
||||
|
||||
@@ -6,11 +6,11 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
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 {
|
||||
constructor(
|
||||
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterf
|
||||
private readonly PREFIX = '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: {
|
||||
authorizationHeaderValue: string
|
||||
|
||||
@@ -11,17 +11,17 @@ import { ServiceProxyInterface } from './ServiceProxyInterface'
|
||||
@injectable()
|
||||
export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
constructor(
|
||||
@inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
|
||||
@inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
|
||||
@inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string,
|
||||
@inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
||||
@inject(TYPES.REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
||||
@inject(TYPES.EMAIL_SERVER_URL) private emailServerUrl: string,
|
||||
@inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
||||
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
|
||||
@inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
|
||||
@inject(TYPES.ApiGateway_PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_FILES_SERVER_URL) private filesServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_REVISIONS_SERVER_URL) private revisionsServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_EMAIL_SERVER_URL) private emailServerUrl: string,
|
||||
@inject(TYPES.ApiGateway_HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
|
||||
@inject(TYPES.ApiGateway_CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async validateSession(
|
||||
@@ -130,19 +130,26 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||
if (!this.paymentsServerUrl) {
|
||||
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
|
||||
|
||||
return
|
||||
}
|
||||
await this.callServerWithLegacyFormat(
|
||||
|
||||
const rawResponse = await this.callServerWithLegacyFormat(
|
||||
this.paymentsServerUrl,
|
||||
request,
|
||||
response,
|
||||
endpointOrMethodIdentifier,
|
||||
payload,
|
||||
returnRawResponse,
|
||||
)
|
||||
|
||||
if (returnRawResponse) {
|
||||
return rawResponse
|
||||
}
|
||||
}
|
||||
|
||||
async callAuthServerWithLegacyFormat(
|
||||
@@ -279,7 +286,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||
const serviceResponse = await this.getServerResponse(
|
||||
serverUrl,
|
||||
request,
|
||||
@@ -295,9 +303,21 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
this.applyResponseHeaders(serviceResponse, response)
|
||||
|
||||
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 {
|
||||
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,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>>
|
||||
callWebSocketServer(
|
||||
request: Request,
|
||||
response: Response,
|
||||
|
||||
@@ -43,6 +43,7 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
['[PATCH]:users/:userId', 'auth.users.update'],
|
||||
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
||||
['[PUT]:auth/params', 'auth.users.getKeyParams'],
|
||||
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
||||
['[POST]:listed', 'auth.users.createListedAccount'],
|
||||
['[POST]:auth', 'auth.users.register'],
|
||||
['[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/decline', 'sync.shared-vault-invites.decline'],
|
||||
['[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', 'sync.shared-vault-invites.get-user-invites'],
|
||||
['[GET]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.get-vault-invites'],
|
||||
|
||||
@@ -3,6 +3,98 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
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
|
||||
|
||||
+18
-18
@@ -1,23 +1,23 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAuthController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSessionsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressUsersController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressAdminController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSettingsController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressSessionController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressOfflineController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressListedController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressInternalController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressHealthCheckController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthenticatorsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSessionsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionInvitesController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedUserRequestsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedWebSocketsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedUsersController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedValetTokenController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedAdminController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionTokensController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSettingsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSessionController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedOfflineController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedListedController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedInternalController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedFeaturesController'
|
||||
|
||||
import * as cors from 'cors'
|
||||
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",
|
||||
"version": "1.126.2",
|
||||
"version": "1.132.0",
|
||||
"engines": {
|
||||
"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 { RedisEphemeralSessionRepository } from '../Infra/Redis/RedisEphemeralSessionRepository'
|
||||
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 { Register } from '../Domain/UseCase/Register'
|
||||
import { LockRepository } from '../Infra/Redis/LockRepository'
|
||||
@@ -234,24 +234,28 @@ import { OfflineUserAuthMiddleware } from '../Infra/InversifyExpressUtils/Middle
|
||||
import { LockMiddleware } from '../Infra/InversifyExpressUtils/Middleware/LockMiddleware'
|
||||
import { RequiredCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware'
|
||||
import { OptionalCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware'
|
||||
import { HomeServerSettingsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSettingsController'
|
||||
import { HomeServerAdminController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAdminController'
|
||||
import { HomeServerAuthController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAuthController'
|
||||
import { HomeServerAuthenticatorsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerAuthenticatorsController'
|
||||
import { HomeServerFeaturesController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerFeaturesController'
|
||||
import { HomeServerListedController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerListedController'
|
||||
import { HomeServerOfflineController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerOfflineController'
|
||||
import { HomeServerSessionController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionController'
|
||||
import { HomeServerSubscriptionInvitesController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionInvitesController'
|
||||
import { HomeServerSubscriptionSettingsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionSettingsController'
|
||||
import { HomeServerSubscriptionTokensController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSubscriptionTokensController'
|
||||
import { HomeServerUserRequestsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerUserRequestsController'
|
||||
import { HomeServerUsersController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerUsersController'
|
||||
import { HomeServerValetTokenController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerValetTokenController'
|
||||
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
|
||||
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
|
||||
import { BaseSettingsController } from '../Infra/InversifyExpressUtils/Base/BaseSettingsController'
|
||||
import { BaseAdminController } from '../Infra/InversifyExpressUtils/Base/BaseAdminController'
|
||||
import { BaseAuthController } from '../Infra/InversifyExpressUtils/Base/BaseAuthController'
|
||||
import { BaseAuthenticatorsController } from '../Infra/InversifyExpressUtils/Base/BaseAuthenticatorsController'
|
||||
import { BaseFeaturesController } from '../Infra/InversifyExpressUtils/Base/BaseFeaturesController'
|
||||
import { BaseListedController } from '../Infra/InversifyExpressUtils/Base/BaseListedController'
|
||||
import { BaseOfflineController } from '../Infra/InversifyExpressUtils/Base/BaseOfflineController'
|
||||
import { BaseSessionController } from '../Infra/InversifyExpressUtils/Base/BaseSessionController'
|
||||
import { BaseSubscriptionInvitesController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionInvitesController'
|
||||
import { BaseSubscriptionSettingsController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController'
|
||||
import { BaseSubscriptionTokensController } from '../Infra/InversifyExpressUtils/Base/BaseSubscriptionTokensController'
|
||||
import { BaseUserRequestsController } from '../Infra/InversifyExpressUtils/Base/BaseUserRequestsController'
|
||||
import { BaseUsersController } from '../Infra/InversifyExpressUtils/Base/BaseUsersController'
|
||||
import { BaseValetTokenController } from '../Infra/InversifyExpressUtils/Base/BaseValetTokenController'
|
||||
import { BaseWebSocketsController } from '../Infra/InversifyExpressUtils/Base/BaseWebSocketsController'
|
||||
import { BaseSessionsController } from '../Infra/InversifyExpressUtils/Base/BaseSessionsController'
|
||||
import { Transform } from 'stream'
|
||||
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 {
|
||||
async load(configuration?: {
|
||||
@@ -556,6 +560,9 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.Auth_READONLY_USERS)
|
||||
.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) {
|
||||
container
|
||||
@@ -826,9 +833,7 @@ export class ContainerConfigLoader {
|
||||
container.bind<UpdateUser>(TYPES.Auth_UpdateUser).to(UpdateUser)
|
||||
container.bind<Register>(TYPES.Auth_Register).to(Register)
|
||||
container.bind<GetActiveSessionsForUser>(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser)
|
||||
container
|
||||
.bind<DeletePreviousSessionsForUser>(TYPES.Auth_DeletePreviousSessionsForUser)
|
||||
.to(DeletePreviousSessionsForUser)
|
||||
container.bind<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser).to(DeleteOtherSessionsForUser)
|
||||
container.bind<DeleteSessionForUser>(TYPES.Auth_DeleteSessionForUser).to(DeleteSessionForUser)
|
||||
container.bind<ChangeCredentials>(TYPES.Auth_ChangeCredentials).to(ChangeCredentials)
|
||||
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<CreateCrossServiceToken>(TYPES.Auth_CreateCrossServiceToken).to(CreateCrossServiceToken)
|
||||
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
|
||||
container
|
||||
@@ -952,8 +966,38 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<UserEmailChangedEventHandler>(TYPES.Auth_UserEmailChangedEventHandler)
|
||||
.to(UserEmailChangedEventHandler)
|
||||
container.bind<FileUploadedEventHandler>(TYPES.Auth_FileUploadedEventHandler).to(FileUploadedEventHandler)
|
||||
container.bind<FileRemovedEventHandler>(TYPES.Auth_FileRemovedEventHandler).to(FileRemovedEventHandler)
|
||||
container
|
||||
.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
|
||||
.bind<ListedAccountCreatedEventHandler>(TYPES.Auth_ListedAccountCreatedEventHandler)
|
||||
.to(ListedAccountCreatedEventHandler)
|
||||
@@ -978,6 +1022,14 @@ export class ContainerConfigLoader {
|
||||
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([
|
||||
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
||||
@@ -992,7 +1044,9 @@ export class ContainerConfigLoader {
|
||||
['SUBSCRIPTION_REASSIGNED', container.get(TYPES.Auth_SubscriptionReassignedEventHandler)],
|
||||
['USER_EMAIL_CHANGED', container.get(TYPES.Auth_UserEmailChangedEventHandler)],
|
||||
['FILE_UPLOADED', container.get(TYPES.Auth_FileUploadedEventHandler)],
|
||||
['SHARED_VAULT_FILE_UPLOADED', container.get(TYPES.Auth_SharedVaultFileUploadedEventHandler)],
|
||||
['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_DELETED', container.get(TYPES.Auth_ListedAccountDeletedEventHandler)],
|
||||
[
|
||||
@@ -1005,6 +1059,7 @@ export class ContainerConfigLoader {
|
||||
],
|
||||
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.Auth_PredicateVerificationRequestedEventHandler)],
|
||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
||||
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
@@ -1037,9 +1092,9 @@ export class ContainerConfigLoader {
|
||||
}
|
||||
|
||||
container
|
||||
.bind<HomeServerAuthController>(TYPES.Auth_HomeServerAuthController)
|
||||
.bind<BaseAuthController>(TYPES.Auth_BaseAuthController)
|
||||
.toConstantValue(
|
||||
new HomeServerAuthController(
|
||||
new BaseAuthController(
|
||||
container.get(TYPES.Auth_VerifyMFA),
|
||||
container.get(TYPES.Auth_SignIn),
|
||||
container.get(TYPES.Auth_GetUserKeyParams),
|
||||
@@ -1054,42 +1109,42 @@ export class ContainerConfigLoader {
|
||||
// Inversify Controllers
|
||||
if (isConfiguredForHomeServer) {
|
||||
container
|
||||
.bind<HomeServerAuthenticatorsController>(TYPES.Auth_HomeServerAuthenticatorsController)
|
||||
.bind<BaseAuthenticatorsController>(TYPES.Auth_BaseAuthenticatorsController)
|
||||
.toConstantValue(
|
||||
new HomeServerAuthenticatorsController(
|
||||
new BaseAuthenticatorsController(
|
||||
container.get(TYPES.Auth_AuthenticatorsController),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSubscriptionInvitesController>(TYPES.Auth_HomeServerSubscriptionInvitesController)
|
||||
.bind<BaseSubscriptionInvitesController>(TYPES.Auth_BaseSubscriptionInvitesController)
|
||||
.toConstantValue(
|
||||
new HomeServerSubscriptionInvitesController(
|
||||
new BaseSubscriptionInvitesController(
|
||||
container.get(TYPES.Auth_SubscriptionInvitesController),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerUserRequestsController>(TYPES.Auth_HomeServerUserRequestsController)
|
||||
.bind<BaseUserRequestsController>(TYPES.Auth_BaseUserRequestsController)
|
||||
.toConstantValue(
|
||||
new HomeServerUserRequestsController(
|
||||
new BaseUserRequestsController(
|
||||
container.get(TYPES.Auth_UserRequestsController),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerWebSocketsController>(TYPES.Auth_HomeServerWebSocketsController)
|
||||
.bind<BaseWebSocketsController>(TYPES.Auth_BaseWebSocketsController)
|
||||
.toConstantValue(
|
||||
new HomeServerWebSocketsController(
|
||||
new BaseWebSocketsController(
|
||||
container.get(TYPES.Auth_CreateCrossServiceToken),
|
||||
container.get(TYPES.Auth_WebSocketConnectionTokenDecoder),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSessionsController>(TYPES.Auth_HomeServerSessionsController)
|
||||
.bind<BaseSessionsController>(TYPES.Auth_BaseSessionsController)
|
||||
.toConstantValue(
|
||||
new HomeServerSessionsController(
|
||||
new BaseSessionsController(
|
||||
container.get(TYPES.Auth_GetActiveSessionsForUser),
|
||||
container.get(TYPES.Auth_AuthenticateRequest),
|
||||
container.get(TYPES.Auth_SessionProjector),
|
||||
@@ -1098,17 +1153,17 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerValetTokenController>(TYPES.Auth_HomeServerValetTokenController)
|
||||
.bind<BaseValetTokenController>(TYPES.Auth_BaseValetTokenController)
|
||||
.toConstantValue(
|
||||
new HomeServerValetTokenController(
|
||||
new BaseValetTokenController(
|
||||
container.get(TYPES.Auth_CreateValetToken),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerUsersController>(TYPES.Auth_HomeServerUsersController)
|
||||
.bind<BaseUsersController>(TYPES.Auth_BaseUsersController)
|
||||
.toConstantValue(
|
||||
new HomeServerUsersController(
|
||||
new BaseUsersController(
|
||||
container.get(TYPES.Auth_UpdateUser),
|
||||
container.get(TYPES.Auth_GetUserKeyParams),
|
||||
container.get(TYPES.Auth_DeleteAccount),
|
||||
@@ -1120,9 +1175,9 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerAdminController>(TYPES.Auth_HomeServerAdminController)
|
||||
.bind<BaseAdminController>(TYPES.Auth_BaseAdminController)
|
||||
.toConstantValue(
|
||||
new HomeServerAdminController(
|
||||
new BaseAdminController(
|
||||
container.get(TYPES.Auth_DeleteSetting),
|
||||
container.get(TYPES.Auth_UserRepository),
|
||||
container.get(TYPES.Auth_CreateSubscriptionToken),
|
||||
@@ -1131,9 +1186,9 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSubscriptionTokensController>(TYPES.Auth_HomeServerSubscriptionTokensController)
|
||||
.bind<BaseSubscriptionTokensController>(TYPES.Auth_BaseSubscriptionTokensController)
|
||||
.toConstantValue(
|
||||
new HomeServerSubscriptionTokensController(
|
||||
new BaseSubscriptionTokensController(
|
||||
container.get(TYPES.Auth_CreateSubscriptionToken),
|
||||
container.get(TYPES.Auth_AuthenticateSubscriptionToken),
|
||||
container.get(TYPES.Auth_SettingService),
|
||||
@@ -1145,17 +1200,17 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSubscriptionSettingsController>(TYPES.Auth_HomeServerSubscriptionSettingsController)
|
||||
.bind<BaseSubscriptionSettingsController>(TYPES.Auth_BaseSubscriptionSettingsController)
|
||||
.toConstantValue(
|
||||
new HomeServerSubscriptionSettingsController(
|
||||
new BaseSubscriptionSettingsController(
|
||||
container.get(TYPES.Auth_GetSetting),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSettingsController>(TYPES.Auth_HomeServerSettingsController)
|
||||
.bind<BaseSettingsController>(TYPES.Auth_BaseSettingsController)
|
||||
.toConstantValue(
|
||||
new HomeServerSettingsController(
|
||||
new BaseSettingsController(
|
||||
container.get(TYPES.Auth_GetSettings),
|
||||
container.get(TYPES.Auth_GetSetting),
|
||||
container.get(TYPES.Auth_UpdateSetting),
|
||||
@@ -1164,19 +1219,19 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerSessionController>(TYPES.Auth_HomeServerSessionController)
|
||||
.bind<BaseSessionController>(TYPES.Auth_BaseSessionController)
|
||||
.toConstantValue(
|
||||
new HomeServerSessionController(
|
||||
new BaseSessionController(
|
||||
container.get(TYPES.Auth_DeleteSessionForUser),
|
||||
container.get(TYPES.Auth_DeletePreviousSessionsForUser),
|
||||
container.get(TYPES.Auth_DeleteOtherSessionsForUser),
|
||||
container.get(TYPES.Auth_RefreshSessionToken),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerOfflineController>(TYPES.Auth_HomeServerOfflineController)
|
||||
.bind<BaseOfflineController>(TYPES.Auth_BaseOfflineController)
|
||||
.toConstantValue(
|
||||
new HomeServerOfflineController(
|
||||
new BaseOfflineController(
|
||||
container.get(TYPES.Auth_GetUserFeatures),
|
||||
container.get(TYPES.Auth_GetUserOfflineSubscription),
|
||||
container.get(TYPES.Auth_CreateOfflineSubscriptionToken),
|
||||
@@ -1188,17 +1243,17 @@ export class ContainerConfigLoader {
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerListedController>(TYPES.Auth_HomeServerListedController)
|
||||
.bind<BaseListedController>(TYPES.Auth_BaseListedController)
|
||||
.toConstantValue(
|
||||
new HomeServerListedController(
|
||||
new BaseListedController(
|
||||
container.get(TYPES.Auth_CreateListedAccount),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<HomeServerFeaturesController>(TYPES.Auth_HomeServerFeaturesController)
|
||||
.bind<BaseFeaturesController>(TYPES.Auth_BaseFeaturesController)
|
||||
.toConstantValue(
|
||||
new HomeServerFeaturesController(
|
||||
new BaseFeaturesController(
|
||||
container.get(TYPES.Auth_GetUserFeatures),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
|
||||
@@ -114,6 +114,8 @@ export class AppDataSource {
|
||||
...commonDataSourceOptions,
|
||||
type: 'sqlite',
|
||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||
enableWAL: true,
|
||||
busyErrorRetry: 2000,
|
||||
}
|
||||
|
||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
|
||||
@@ -101,6 +101,7 @@ const TYPES = {
|
||||
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
|
||||
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
|
||||
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
|
||||
Auth_TRANSITION_MODE_ENABLED: Symbol.for('Auth_TRANSITION_MODE_ENABLED'),
|
||||
// use cases
|
||||
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
|
||||
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
|
||||
@@ -113,7 +114,7 @@ const TYPES = {
|
||||
Auth_UpdateUser: Symbol.for('Auth_UpdateUser'),
|
||||
Auth_Register: Symbol.for('Auth_Register'),
|
||||
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_ChangeCredentials: Symbol.for('Auth_ChangePassword'),
|
||||
Auth_GetSettings: Symbol.for('Auth_GetSettings'),
|
||||
@@ -152,6 +153,7 @@ const TYPES = {
|
||||
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
||||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||
// Handlers
|
||||
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
@@ -165,7 +167,9 @@ const TYPES = {
|
||||
Auth_ExtensionKeyGrantedEventHandler: Symbol.for('Auth_ExtensionKeyGrantedEventHandler'),
|
||||
Auth_UserEmailChangedEventHandler: Symbol.for('Auth_UserEmailChangedEventHandler'),
|
||||
Auth_FileUploadedEventHandler: Symbol.for('Auth_FileUploadedEventHandler'),
|
||||
Auth_SharedVaultFileUploadedEventHandler: Symbol.for('Auth_SharedVaultFileUploadedEventHandler'),
|
||||
Auth_FileRemovedEventHandler: Symbol.for('Auth_FileRemovedEventHandler'),
|
||||
Auth_SharedVaultFileRemovedEventHandler: Symbol.for('Auth_SharedVaultFileRemovedEventHandler'),
|
||||
Auth_ListedAccountCreatedEventHandler: Symbol.for('Auth_ListedAccountCreatedEventHandler'),
|
||||
Auth_ListedAccountDeletedEventHandler: Symbol.for('Auth_ListedAccountDeletedEventHandler'),
|
||||
Auth_UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for(
|
||||
@@ -176,6 +180,7 @@ const TYPES = {
|
||||
),
|
||||
Auth_PredicateVerificationRequestedEventHandler: Symbol.for('Auth_PredicateVerificationRequestedEventHandler'),
|
||||
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
||||
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
||||
// Services
|
||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||
@@ -217,22 +222,22 @@ const TYPES = {
|
||||
Auth_ProtocolVersionSelector: Symbol.for('Auth_ProtocolVersionSelector'),
|
||||
Auth_BooleanSelector: Symbol.for('Auth_BooleanSelector'),
|
||||
Auth_UserSubscriptionService: Symbol.for('Auth_UserSubscriptionService'),
|
||||
Auth_HomeServerAuthController: Symbol.for('Auth_HomeServerAuthController'),
|
||||
Auth_HomeServerAuthenticatorsController: Symbol.for('Auth_HomeServerAuthenticatorsController'),
|
||||
Auth_HomeServerSubscriptionInvitesController: Symbol.for('Auth_HomeServerSubscriptionInvitesController'),
|
||||
Auth_HomeServerUserRequestsController: Symbol.for('Auth_HomeServerUserRequestsController'),
|
||||
Auth_HomeServerWebSocketsController: Symbol.for('Auth_HomeServerWebSocketsController'),
|
||||
Auth_HomeServerSessionsController: Symbol.for('Auth_HomeServerSessionsController'),
|
||||
Auth_HomeServerValetTokenController: Symbol.for('Auth_HomeServerValetTokenController'),
|
||||
Auth_HomeServerUsersController: Symbol.for('Auth_HomeServerUsersController'),
|
||||
Auth_HomeServerAdminController: Symbol.for('Auth_HomeServerAdminController'),
|
||||
Auth_HomeServerSubscriptionTokensController: Symbol.for('Auth_HomeServerSubscriptionTokensController'),
|
||||
Auth_HomeServerSubscriptionSettingsController: Symbol.for('Auth_HomeServerSubscriptionSettingsController'),
|
||||
Auth_HomeServerSettingsController: Symbol.for('Auth_HomeServerSettingsController'),
|
||||
Auth_HomeServerSessionController: Symbol.for('Auth_HomeServerSessionController'),
|
||||
Auth_HomeServerOfflineController: Symbol.for('Auth_HomeServerOfflineController'),
|
||||
Auth_HomeServerListedController: Symbol.for('Auth_HomeServerListedController'),
|
||||
Auth_HomeServerFeaturesController: Symbol.for('Auth_HomeServerFeaturesController'),
|
||||
Auth_BaseAuthController: Symbol.for('Auth_BaseAuthController'),
|
||||
Auth_BaseAuthenticatorsController: Symbol.for('Auth_BaseAuthenticatorsController'),
|
||||
Auth_BaseSubscriptionInvitesController: Symbol.for('Auth_BaseSubscriptionInvitesController'),
|
||||
Auth_BaseUserRequestsController: Symbol.for('Auth_BaseUserRequestsController'),
|
||||
Auth_BaseWebSocketsController: Symbol.for('Auth_BaseWebSocketsController'),
|
||||
Auth_BaseSessionsController: Symbol.for('Auth_BaseSessionsController'),
|
||||
Auth_BaseValetTokenController: Symbol.for('Auth_BaseValetTokenController'),
|
||||
Auth_BaseUsersController: Symbol.for('Auth_BaseUsersController'),
|
||||
Auth_BaseAdminController: Symbol.for('Auth_BaseAdminController'),
|
||||
Auth_BaseSubscriptionTokensController: Symbol.for('Auth_BaseSubscriptionTokensController'),
|
||||
Auth_BaseSubscriptionSettingsController: Symbol.for('Auth_BaseSubscriptionSettingsController'),
|
||||
Auth_BaseSettingsController: Symbol.for('Auth_BaseSettingsController'),
|
||||
Auth_BaseSessionController: Symbol.for('Auth_BaseSessionController'),
|
||||
Auth_BaseOfflineController: Symbol.for('Auth_BaseOfflineController'),
|
||||
Auth_BaseListedController: Symbol.for('Auth_BaseListedController'),
|
||||
Auth_BaseFeaturesController: Symbol.for('Auth_BaseFeaturesController'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('AuthResponseFactory20161215', () => {
|
||||
})
|
||||
|
||||
it('should create a 20161215 auth response', async () => {
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20161215',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -38,7 +38,7 @@ describe('AuthResponseFactory20161215', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
user: { foo: 'bar' },
|
||||
token: 'foobar',
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import { User } from '../User/User'
|
||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||
import { AuthResponseFactoryInterface } from './AuthResponseFactoryInterface'
|
||||
import { Session } from '../Session/Session'
|
||||
|
||||
@injectable()
|
||||
export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface {
|
||||
@@ -26,7 +27,7 @@ export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface
|
||||
userAgent: string
|
||||
ephemeralSession: boolean
|
||||
readonlyAccess: boolean
|
||||
}): Promise<AuthResponse20161215 | AuthResponse20200115> {
|
||||
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }> {
|
||||
this.logger.debug(`Creating JWT auth response for user ${dto.user.uuid}`)
|
||||
|
||||
const data: SessionTokenData = {
|
||||
@@ -39,12 +40,14 @@ export class AuthResponseFactory20161215 implements AuthResponseFactoryInterface
|
||||
this.logger.debug(`Created JWT token for user ${dto.user.uuid}: ${token}`)
|
||||
|
||||
return {
|
||||
user: this.userProjector.projectSimple(dto.user) as {
|
||||
uuid: string
|
||||
email: string
|
||||
protocolVersion: ProtocolVersion
|
||||
response: {
|
||||
user: this.userProjector.projectSimple(dto.user) as {
|
||||
uuid: string
|
||||
email: string
|
||||
protocolVersion: ProtocolVersion
|
||||
},
|
||||
token,
|
||||
},
|
||||
token,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('AuthResponseFactory20190520', () => {
|
||||
})
|
||||
|
||||
it('should create a 20161215 auth response', async () => {
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20161215',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -37,7 +37,7 @@ describe('AuthResponseFactory20190520', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
user: { foo: 'bar' },
|
||||
token: 'foobar',
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import { User } from '../User/User'
|
||||
import { AuthResponseFactory20200115 } from './AuthResponseFactory20200115'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { Session } from '../Session/Session'
|
||||
|
||||
describe('AuthResponseFactory20200115', () => {
|
||||
let sessionService: SessionServiceInterface
|
||||
@@ -48,8 +49,12 @@ describe('AuthResponseFactory20200115', () => {
|
||||
}
|
||||
|
||||
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
||||
sessionService.createNewSessionForUser = jest.fn().mockReturnValue(sessionPayload)
|
||||
sessionService.createNewEphemeralSessionForUser = jest.fn().mockReturnValue(sessionPayload)
|
||||
sessionService.createNewSessionForUser = jest
|
||||
.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.create = jest.fn().mockReturnValue({
|
||||
@@ -76,7 +81,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
it('should create a 20161215 auth response if user does not support sessions', async () => {
|
||||
user.supportsSessions = jest.fn().mockReturnValue(false)
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20161215',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -84,7 +89,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
user: { foo: 'bar' },
|
||||
token: expect.any(String),
|
||||
})
|
||||
@@ -93,7 +98,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
it('should create a 20200115 auth response', async () => {
|
||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20200115',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -101,7 +106,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
@@ -124,7 +129,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
domainEventPublisher.publish = jest.fn().mockRejectedValue(new Error('test'))
|
||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20200115',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -132,7 +137,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
@@ -153,7 +158,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
it('should create a 20200115 auth response with an ephemeral session', async () => {
|
||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20200115',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -161,7 +166,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
@@ -183,11 +188,14 @@ describe('AuthResponseFactory20200115', () => {
|
||||
user.supportsSessions = jest.fn().mockReturnValue(true)
|
||||
|
||||
sessionService.createNewSessionForUser = jest.fn().mockReturnValue({
|
||||
...sessionPayload,
|
||||
readonly_access: true,
|
||||
sessionHttpRepresentation: {
|
||||
...sessionPayload,
|
||||
readonly_access: true,
|
||||
},
|
||||
session: {} as jest.Mocked<Session>,
|
||||
})
|
||||
|
||||
const response = await createFactory().createResponse({
|
||||
const result = await createFactory().createResponse({
|
||||
user,
|
||||
apiVersion: '20200115',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -195,7 +203,7 @@ describe('AuthResponseFactory20200115', () => {
|
||||
readonlyAccess: true,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
expect(result.response).toEqual({
|
||||
key_params: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
|
||||
@@ -19,6 +19,7 @@ import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterfac
|
||||
|
||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||
import { Session } from '../Session/Session'
|
||||
|
||||
@injectable()
|
||||
export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
@@ -40,21 +41,28 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
userAgent: string
|
||||
ephemeralSession: boolean
|
||||
readonlyAccess: boolean
|
||||
}): Promise<AuthResponse20161215 | AuthResponse20200115> {
|
||||
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }> {
|
||||
if (!dto.user.supportsSessions()) {
|
||||
this.logger.debug(`User ${dto.user.uuid} does not support sessions. Falling back to JWT auth response`)
|
||||
|
||||
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 {
|
||||
session: sessionPayload,
|
||||
key_params: this.keyParamsFactory.create(dto.user, true),
|
||||
user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection,
|
||||
response: {
|
||||
session: sessionCreationResult.sessionHttpRepresentation,
|
||||
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
|
||||
ephemeralSession: boolean
|
||||
readonlyAccess: boolean
|
||||
}): Promise<SessionBody> {
|
||||
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||
if (dto.ephemeralSession) {
|
||||
return this.sessionService.createNewEphemeralSessionForUser(dto)
|
||||
}
|
||||
|
||||
const session = this.sessionService.createNewSessionForUser(dto)
|
||||
const sessionCreationResult = await this.sessionService.createNewSessionForUser(dto)
|
||||
|
||||
try {
|
||||
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}`)
|
||||
}
|
||||
|
||||
return session
|
||||
return sessionCreationResult
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Session } from '../Session/Session'
|
||||
import { User } from '../User/User'
|
||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||
@@ -9,5 +10,5 @@ export interface AuthResponseFactoryInterface {
|
||||
userAgent: string
|
||||
ephemeralSession: boolean
|
||||
readonlyAccess: boolean
|
||||
}): Promise<AuthResponse20161215 | AuthResponse20200115>
|
||||
}): Promise<{ response: AuthResponse20161215 | AuthResponse20200115; session?: Session }>
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@ describe('AuthenticationMethodResolver', () => {
|
||||
|
||||
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>
|
||||
|
||||
@@ -38,7 +40,7 @@ describe('AuthenticationMethodResolver', () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||
|
||||
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
||||
sessionService.getSessionFromToken = jest.fn()
|
||||
sessionService.getSessionFromToken = jest.fn().mockReturnValue({ session: undefined, isEphemeral: false })
|
||||
sessionService.getRevokedSessionFromToken = jest.fn()
|
||||
sessionService.markRevokedSessionAsReceived = jest.fn().mockReturnValue(revokedSession)
|
||||
|
||||
@@ -50,19 +52,25 @@ describe('AuthenticationMethodResolver', () => {
|
||||
})
|
||||
|
||||
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({
|
||||
claims: {
|
||||
user_uuid: '123',
|
||||
user_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
type: 'jwt',
|
||||
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 () => {
|
||||
sessionService.getSessionFromToken = jest.fn().mockReturnValue(session)
|
||||
sessionService.getSessionFromToken = jest.fn().mockReturnValue({ session, isEphemeral: false })
|
||||
|
||||
expect(await createResolver().resolve('test')).toEqual({
|
||||
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 () => {
|
||||
sessionService.getRevokedSessionFromToken = jest.fn().mockReturnValue(revokedSession)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { AuthenticationMethod } from './AuthenticationMethod'
|
||||
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
||||
import { Logger } from 'winston'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
|
||||
@@ -29,20 +30,32 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
||||
if (decodedToken) {
|
||||
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 {
|
||||
type: 'jwt',
|
||||
user: await this.userRepository.findOneByUuid(<string>decodedToken.user_uuid),
|
||||
user: await this.userRepository.findOneByUuid(userUuid),
|
||||
claims: decodedToken,
|
||||
}
|
||||
}
|
||||
|
||||
const session = await this.sessionService.getSessionFromToken(token)
|
||||
const { session } = await this.sessionService.getSessionFromToken(token)
|
||||
if (session) {
|
||||
this.logger.debug('Token decoded successfully. Session found.')
|
||||
|
||||
const userUuidOrError = Uuid.create(session.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return undefined
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
return {
|
||||
type: 'session_token',
|
||||
user: await this.userRepository.findOneByUuid(session.userUuid),
|
||||
user: await this.userRepository.findOneByUuid(userUuid),
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
||||
|
||||
ephemeralSession = {
|
||||
uuid: '2-3-4',
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
} as jest.Mocked<EphemeralSession>
|
||||
|
||||
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
||||
@@ -68,7 +68,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: '2-3-4',
|
||||
}
|
||||
@@ -84,6 +84,14 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
||||
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 () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
@@ -100,6 +108,6 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
||||
|
||||
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
|
||||
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 { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
@@ -19,19 +20,27 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
||||
const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
|
||||
|
||||
if (user === null) {
|
||||
const userUuidOrError = Uuid.create(event.payload.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
|
||||
|
||||
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)
|
||||
|
||||
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> {
|
||||
|
||||
@@ -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 { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { EncryptionVersion } from '../Encryption/EncryptionVersion'
|
||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
||||
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||
|
||||
@injectable()
|
||||
export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
||||
@inject(TYPES.Auth_SubscriptionSettingService)
|
||||
private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
) {}
|
||||
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||
|
||||
async handle(event: FileRemovedEvent): Promise<void> {
|
||||
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, 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(),
|
||||
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
bytesUsed: -event.payload.fileByteSize,
|
||||
})
|
||||
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 { SettingName } from '@standardnotes/settings'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
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'
|
||||
import { UpdateStorageQuotaUsedForUser } from '../UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser'
|
||||
|
||||
@injectable()
|
||||
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@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,
|
||||
) {}
|
||||
constructor(private updateStorageQuotaUsedForUserUseCase: UpdateStorageQuotaUsedForUser, private logger: Logger) {}
|
||||
|
||||
async handle(event: FileUploadedEvent): Promise<void> {
|
||||
const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
|
||||
if (user === null) {
|
||||
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
|
||||
|
||||
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(),
|
||||
const result = await this.updateStorageQuotaUsedForUserUseCase.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
bytesUsed: event.payload.fileByteSize,
|
||||
})
|
||||
if (bytesUsedSetting !== null) {
|
||||
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,
|
||||
},
|
||||
})
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Failed to update storage quota used for user: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 () => {
|
||||
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()
|
||||
})
|
||||
|
||||
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 () => {
|
||||
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()
|
||||
})
|
||||
@@ -228,7 +240,10 @@ describe('RoleService', () => {
|
||||
it('should indicate user does not have a permission if user could not be found', async () => {
|
||||
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()
|
||||
})
|
||||
|
||||
@@ -13,6 +13,7 @@ import { RoleToSubscriptionMapInterface } from './RoleToSubscriptionMapInterface
|
||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||
import { Role } from './Role'
|
||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class RoleService implements RoleServiceInterface {
|
||||
@@ -26,10 +27,16 @@ export class RoleService implements RoleServiceInterface {
|
||||
@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)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,13 +4,6 @@ export interface EphemeralSessionRepositoryInterface {
|
||||
findOneByUuid(uuid: string): Promise<EphemeralSession | null>
|
||||
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<EphemeralSession | null>
|
||||
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>
|
||||
save(ephemeralSession: EphemeralSession): Promise<void>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Session } from './Session'
|
||||
|
||||
export interface SessionRepositoryInterface {
|
||||
@@ -5,10 +7,8 @@ export interface SessionRepositoryInterface {
|
||||
findOneByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Session | null>
|
||||
findAllByRefreshExpirationAndUserUuid(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>
|
||||
updateHashedTokens(uuid: string, hashedAccessToken: string, hashedRefreshToken: string): Promise<void>
|
||||
updatedTokenExpirationDates(uuid: string, accessExpiration: Date, refreshExpiration: Date): Promise<void>
|
||||
save(session: Session): Promise<Session>
|
||||
remove(session: Session): Promise<Session>
|
||||
clearUserAgentByUserUuid(userUuid: string): Promise<void>
|
||||
|
||||
@@ -24,8 +24,8 @@ describe('SessionService', () => {
|
||||
let sessionRepository: SessionRepositoryInterface
|
||||
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
|
||||
let revokedSessionRepository: RevokedSessionRepositoryInterface
|
||||
let session: Session
|
||||
let ephemeralSession: EphemeralSession
|
||||
let existingSession: Session
|
||||
let existingEphemeralSession: EphemeralSession
|
||||
let revokedSession: RevokedSession
|
||||
let settingService: SettingServiceInterface
|
||||
let deviceDetector: UAParser
|
||||
@@ -54,14 +54,14 @@ describe('SessionService', () => {
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
session = {} as jest.Mocked<Session>
|
||||
session.uuid = '2e1e43'
|
||||
session.userUuid = '1-2-3'
|
||||
session.userAgent = 'Chrome'
|
||||
session.apiVersion = ApiVersion.v20200115
|
||||
session.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
session.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
session.readonlyAccess = false
|
||||
existingSession = {} as jest.Mocked<Session>
|
||||
existingSession.uuid = '2e1e43'
|
||||
existingSession.userUuid = '1-2-3'
|
||||
existingSession.userAgent = 'Chrome'
|
||||
existingSession.apiVersion = ApiVersion.v20200115
|
||||
existingSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
existingSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
existingSession.readonlyAccess = false
|
||||
|
||||
revokedSession = {} as jest.Mocked<RevokedSession>
|
||||
revokedSession.uuid = '2e1e43'
|
||||
@@ -69,9 +69,7 @@ describe('SessionService', () => {
|
||||
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
|
||||
sessionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
sessionRepository.deleteOneByUuid = jest.fn()
|
||||
sessionRepository.save = jest.fn().mockReturnValue(session)
|
||||
sessionRepository.updateHashedTokens = jest.fn()
|
||||
sessionRepository.updatedTokenExpirationDates = jest.fn()
|
||||
sessionRepository.save = jest.fn().mockReturnValue(existingSession)
|
||||
|
||||
settingService = {} as jest.Mocked<SettingServiceInterface>
|
||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(null)
|
||||
@@ -79,17 +77,18 @@ describe('SessionService', () => {
|
||||
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
||||
ephemeralSessionRepository.save = jest.fn()
|
||||
ephemeralSessionRepository.findOneByUuid = jest.fn()
|
||||
ephemeralSessionRepository.updateTokensAndExpirationDates = jest.fn()
|
||||
ephemeralSessionRepository.deleteOne = jest.fn()
|
||||
|
||||
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
|
||||
revokedSessionRepository.save = jest.fn()
|
||||
|
||||
ephemeralSession = {} as jest.Mocked<EphemeralSession>
|
||||
ephemeralSession.uuid = '2-3-4'
|
||||
ephemeralSession.userAgent = 'Mozilla Firefox'
|
||||
ephemeralSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
ephemeralSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
existingEphemeralSession = {} as jest.Mocked<EphemeralSession>
|
||||
existingEphemeralSession.uuid = '2-3-4'
|
||||
existingEphemeralSession.userUuid = '1-2-3'
|
||||
existingEphemeralSession.userAgent = 'Mozilla Firefox'
|
||||
existingEphemeralSession.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
existingEphemeralSession.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
existingEphemeralSession.readonlyAccess = false
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.convertStringDateToMilliseconds = jest.fn().mockReturnValue(123)
|
||||
@@ -138,7 +137,7 @@ describe('SessionService', () => {
|
||||
})
|
||||
|
||||
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_token: expect.any(String),
|
||||
refresh_token: expect.any(String),
|
||||
@@ -146,15 +145,28 @@ describe('SessionService', () => {
|
||||
readonly_access: false,
|
||||
})
|
||||
|
||||
expect(sessionRepository.updateHashedTokens).toHaveBeenCalled()
|
||||
expect(sessionRepository.updatedTokenExpirationDates).toHaveBeenCalled()
|
||||
expect(sessionRepository.save).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 () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
user.uuid = '123'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -176,7 +188,7 @@ describe('SessionService', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -190,7 +202,7 @@ describe('SessionService', () => {
|
||||
user.email = 'demo@standardnotes.com'
|
||||
user.uuid = '123'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -212,7 +224,7 @@ describe('SessionService', () => {
|
||||
readonlyAccess: true,
|
||||
})
|
||||
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -229,7 +241,7 @@ describe('SessionService', () => {
|
||||
value: LogSessionUserAgentOption.Disabled,
|
||||
} as jest.Mocked<Setting>)
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -250,7 +262,7 @@ describe('SessionService', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -305,7 +317,7 @@ describe('SessionService', () => {
|
||||
user.uuid = '123'
|
||||
user.email = 'test@test.te'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -317,7 +329,7 @@ describe('SessionService', () => {
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -333,7 +345,7 @@ describe('SessionService', () => {
|
||||
user.uuid = '123'
|
||||
user.email = 'test@test.te'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -345,7 +357,7 @@ describe('SessionService', () => {
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -361,7 +373,7 @@ describe('SessionService', () => {
|
||||
user.uuid = '123'
|
||||
user.email = 'test@test.te'
|
||||
|
||||
const sessionPayload = await createService().createNewSessionForUser({
|
||||
const result = await createService().createNewSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -373,7 +385,7 @@ describe('SessionService', () => {
|
||||
username: 'test@test.te',
|
||||
subscriptionPlanName: null,
|
||||
})
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -386,7 +398,7 @@ describe('SessionService', () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
user.uuid = '123'
|
||||
|
||||
const sessionPayload = await createService().createNewEphemeralSessionForUser({
|
||||
const result = await createService().createNewEphemeralSessionForUser({
|
||||
user,
|
||||
apiVersion: '003',
|
||||
userAgent: 'Google Chrome',
|
||||
@@ -408,7 +420,7 @@ describe('SessionService', () => {
|
||||
readonlyAccess: false,
|
||||
})
|
||||
|
||||
expect(sessionPayload).toEqual({
|
||||
expect(result.sessionHttpRepresentation).toEqual({
|
||||
access_expiration: 123,
|
||||
access_token: expect.any(String),
|
||||
refresh_expiration: 123,
|
||||
@@ -420,7 +432,7 @@ describe('SessionService', () => {
|
||||
it('should delete a session by token', async () => {
|
||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||
if (uuid === '2') {
|
||||
return session
|
||||
return existingSession
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -429,13 +441,28 @@ describe('SessionService', () => {
|
||||
await createService().deleteSessionByToken('1:2:3')
|
||||
|
||||
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 () => {
|
||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||
if (uuid === '2') {
|
||||
return session
|
||||
return existingSession
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -448,13 +475,13 @@ describe('SessionService', () => {
|
||||
})
|
||||
|
||||
it('should determine if a refresh token is valid', async () => {
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:3')).toBeTruthy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2:4')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(session, '1:2')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2:3')).toBeTruthy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2:4')).toBeFalsy()
|
||||
expect(createService().isRefreshTokenMatchingHashedSessionToken(existingSession, '1:2')).toBeFalsy()
|
||||
})
|
||||
|
||||
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', () => {
|
||||
@@ -463,7 +490,7 @@ describe('SessionService', () => {
|
||||
browser: { 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', () => {
|
||||
@@ -473,7 +500,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -483,13 +510,13 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
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', () => {
|
||||
@@ -499,7 +526,7 @@ describe('SessionService', () => {
|
||||
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({
|
||||
ua: 'dummy-data',
|
||||
@@ -507,7 +534,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -517,7 +544,7 @@ describe('SessionService', () => {
|
||||
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({
|
||||
ua: 'dummy-data',
|
||||
@@ -525,7 +552,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -538,7 +565,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -548,7 +575,7 @@ describe('SessionService', () => {
|
||||
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({
|
||||
ua: 'dummy-data',
|
||||
@@ -556,7 +583,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -569,7 +596,7 @@ describe('SessionService', () => {
|
||||
cpu: { architecture: undefined },
|
||||
})
|
||||
|
||||
expect(createService().getDeviceInfo(session)).toEqual('Android')
|
||||
expect(createService().getDeviceInfo(existingSession)).toEqual('Android')
|
||||
})
|
||||
|
||||
it('should detect the StandardNotes app in user agent', () => {
|
||||
@@ -582,7 +609,7 @@ describe('SessionService', () => {
|
||||
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', () => {
|
||||
@@ -590,70 +617,72 @@ describe('SessionService', () => {
|
||||
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 () => {
|
||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||
if (uuid === '2') {
|
||||
return session
|
||||
return existingSession
|
||||
}
|
||||
|
||||
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 () => {
|
||||
ephemeralSessionRepository.findOneByUuid = jest.fn().mockReturnValue(ephemeralSession)
|
||||
ephemeralSessionRepository.findOneByUuid = jest.fn().mockReturnValue(existingEphemeralSession)
|
||||
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 () => {
|
||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||
if (uuid === '2') {
|
||||
return session
|
||||
return existingSession
|
||||
}
|
||||
|
||||
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 () => {
|
||||
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 () => {
|
||||
sessionRepository.findOneByUuid = jest.fn().mockImplementation((uuid) => {
|
||||
if (uuid === '2') {
|
||||
return session
|
||||
return existingSession
|
||||
}
|
||||
|
||||
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 () => {
|
||||
await createService().createRevokedSession(session)
|
||||
await createService().createRevokedSession(existingSession)
|
||||
|
||||
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
|
||||
uuid: '2e1e43',
|
||||
|
||||
@@ -49,7 +49,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
apiVersion: string
|
||||
userAgent: string
|
||||
readonlyAccess: boolean
|
||||
}): Promise<SessionBody> {
|
||||
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||
const session = await this.createSession({
|
||||
ephemeral: false,
|
||||
...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}`)
|
||||
}
|
||||
|
||||
return sessionPayload
|
||||
return {
|
||||
sessionHttpRepresentation: sessionPayload,
|
||||
session,
|
||||
}
|
||||
}
|
||||
|
||||
async createNewEphemeralSessionForUser(dto: {
|
||||
@@ -81,7 +84,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
apiVersion: string
|
||||
userAgent: string
|
||||
readonlyAccess: boolean
|
||||
}): Promise<SessionBody> {
|
||||
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }> {
|
||||
const ephemeralSession = await this.createSession({
|
||||
ephemeral: true,
|
||||
...dto,
|
||||
@@ -91,27 +94,20 @@ export class SessionService implements SessionServiceInterface {
|
||||
|
||||
await this.ephemeralSessionRepository.save(ephemeralSession)
|
||||
|
||||
return sessionPayload
|
||||
return {
|
||||
sessionHttpRepresentation: sessionPayload,
|
||||
session: ephemeralSession,
|
||||
}
|
||||
}
|
||||
|
||||
async refreshTokens(session: Session): Promise<SessionBody> {
|
||||
const sessionPayload = await this.createTokens(session)
|
||||
async refreshTokens(dto: { session: Session; isEphemeral: boolean }): Promise<SessionBody> {
|
||||
const sessionPayload = await this.createTokens(dto.session)
|
||||
|
||||
await this.sessionRepository.updateHashedTokens(session.uuid, session.hashedAccessToken, session.hashedRefreshToken)
|
||||
|
||||
await this.sessionRepository.updatedTokenExpirationDates(
|
||||
session.uuid,
|
||||
session.accessExpiration,
|
||||
session.refreshExpiration,
|
||||
)
|
||||
|
||||
await this.ephemeralSessionRepository.updateTokensAndExpirationDates(
|
||||
session.uuid,
|
||||
session.hashedAccessToken,
|
||||
session.hashedRefreshToken,
|
||||
session.accessExpiration,
|
||||
session.refreshExpiration,
|
||||
)
|
||||
if (dto.isEphemeral) {
|
||||
await this.ephemeralSessionRepository.save(dto.session)
|
||||
} else {
|
||||
await this.sessionRepository.save(dto.session)
|
||||
}
|
||||
|
||||
return sessionPayload
|
||||
}
|
||||
@@ -190,25 +186,25 @@ export class SessionService implements SessionServiceInterface {
|
||||
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 sessionUuid = tokenParts[1]
|
||||
const accessToken = tokenParts[2]
|
||||
if (!accessToken) {
|
||||
return undefined
|
||||
return { session: undefined, isEphemeral: false }
|
||||
}
|
||||
|
||||
const session = await this.getSession(sessionUuid)
|
||||
const { session, isEphemeral } = await this.getSession(sessionUuid)
|
||||
if (!session) {
|
||||
return undefined
|
||||
return { session: undefined, isEphemeral: false }
|
||||
}
|
||||
|
||||
const hashedAccessToken = crypto.createHash('sha256').update(accessToken).digest('hex')
|
||||
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> {
|
||||
@@ -229,11 +225,14 @@ export class SessionService implements SessionServiceInterface {
|
||||
}
|
||||
|
||||
async deleteSessionByToken(token: string): Promise<string | null> {
|
||||
const session = await this.getSessionFromToken(token)
|
||||
const { session, isEphemeral } = await this.getSessionFromToken(token)
|
||||
|
||||
if (session) {
|
||||
await this.sessionRepository.deleteOneByUuid(session.uuid)
|
||||
await this.ephemeralSessionRepository.deleteOne(session.uuid, session.userUuid)
|
||||
if (isEphemeral) {
|
||||
await this.ephemeralSessionRepository.deleteOne(session.uuid, session.userUuid)
|
||||
} else {
|
||||
await this.sessionRepository.deleteOneByUuid(session.uuid)
|
||||
}
|
||||
|
||||
return session.userUuid
|
||||
}
|
||||
@@ -278,14 +277,19 @@ export class SessionService implements SessionServiceInterface {
|
||||
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 isEphemeral = true
|
||||
|
||||
if (!session) {
|
||||
session = await this.sessionRepository.findOneByUuid(uuid)
|
||||
isEphemeral = false
|
||||
}
|
||||
|
||||
return session
|
||||
return { session, isEphemeral }
|
||||
}
|
||||
|
||||
private async createTokens(session: Session): Promise<SessionBody> {
|
||||
|
||||
@@ -9,15 +9,15 @@ export interface SessionServiceInterface {
|
||||
apiVersion: string
|
||||
userAgent: string
|
||||
readonlyAccess: boolean
|
||||
}): Promise<SessionBody>
|
||||
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }>
|
||||
createNewEphemeralSessionForUser(dto: {
|
||||
user: User
|
||||
apiVersion: string
|
||||
userAgent: string
|
||||
readonlyAccess: boolean
|
||||
}): Promise<SessionBody>
|
||||
refreshTokens(session: Session): Promise<SessionBody>
|
||||
getSessionFromToken(token: string): Promise<Session | undefined>
|
||||
}): Promise<{ sessionHttpRepresentation: SessionBody; session: Session }>
|
||||
refreshTokens(dto: { session: Session; isEphemeral: boolean }): Promise<SessionBody>
|
||||
getSessionFromToken(token: string): Promise<{ session: Session | undefined; isEphemeral: boolean }>
|
||||
getRevokedSessionFromToken(token: string): Promise<RevokedSession | null>
|
||||
markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession>
|
||||
deleteSessionByToken(token: string): Promise<string | null>
|
||||
|
||||
@@ -32,7 +32,9 @@ describe('SettingDecrypter', () => {
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
} 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 () => {
|
||||
@@ -41,7 +43,7 @@ describe('SettingDecrypter', () => {
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
} 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 () => {
|
||||
@@ -50,7 +52,7 @@ describe('SettingDecrypter', () => {
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
} 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 () => {
|
||||
@@ -62,7 +64,23 @@ describe('SettingDecrypter', () => {
|
||||
|
||||
let caughtError = null
|
||||
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) {
|
||||
caughtError = error
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { Setting } from './Setting'
|
||||
import { SettingDecrypterInterface } from './SettingDecrypterInterface'
|
||||
import { SubscriptionSetting } from './SubscriptionSetting'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class SettingDecrypter implements SettingDecrypterInterface {
|
||||
@@ -14,12 +15,18 @@ export class SettingDecrypter implements SettingDecrypterInterface {
|
||||
@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) {
|
||||
const userUuidOrError = Uuid.create(userUuidString)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||
|
||||
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)
|
||||
|
||||
+1
-3
@@ -17,7 +17,7 @@ describe('AuthenticateSubscriptionToken', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
subscriptionTokenRepository = {} as jest.Mocked<SubscriptionTokenRepositoryInterface>
|
||||
subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('1-2-3')
|
||||
subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('00000000-0000-0000-0000-000000000000')
|
||||
|
||||
user = {
|
||||
roles: Promise.resolve([{ name: RoleName.NAMES.CoreUser }]),
|
||||
@@ -30,8 +30,6 @@ describe('AuthenticateSubscriptionToken', () => {
|
||||
it('should authenticate an subscription token', async () => {
|
||||
const response = await createUseCase().execute({ token: 'test' })
|
||||
|
||||
expect(userRepository.findOneByUuid).toHaveBeenCalledWith('1-2-3')
|
||||
|
||||
expect(response.success).toBeTruthy()
|
||||
|
||||
expect(response.user).toEqual(user)
|
||||
|
||||
+5
-2
@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { AuthenticateSubscriptionTokenDTO } from './AuthenticateSubscriptionTokenDTO'
|
||||
import { AuthenticateSubscriptionTokenResponse } from './AuthenticateSubscriptionTokenResponse'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class AuthenticateSubscriptionToken implements UseCaseInterface {
|
||||
@@ -16,12 +17,14 @@ export class AuthenticateSubscriptionToken implements UseCaseInterface {
|
||||
) {}
|
||||
|
||||
async execute(dto: AuthenticateSubscriptionTokenDTO): Promise<AuthenticateSubscriptionTokenResponse> {
|
||||
const userUuid = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
|
||||
if (userUuid === undefined) {
|
||||
const userUuidString = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
|
||||
const userUuidOrError = Uuid.create(userUuidString as string)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||
if (user === null) {
|
||||
|
||||
@@ -11,7 +11,10 @@ import { User } from '../../User/User'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
|
||||
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', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
@@ -21,13 +24,23 @@ describe('ChangeCredentials', () => {
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let timer: TimerInterface
|
||||
let user: User
|
||||
let deleteOtherSessionsForUser: DeleteOtherSessionsForUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new ChangeCredentials(userRepository, authResponseFactoryResolver, domainEventPublisher, domainEventFactory, timer)
|
||||
new ChangeCredentials(
|
||||
userRepository,
|
||||
authResponseFactoryResolver,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
timer,
|
||||
deleteOtherSessionsForUser,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
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.resolveAuthResponseFactoryVersion = jest.fn().mockReturnValue(authResponseFactory)
|
||||
@@ -49,27 +62,25 @@ describe('ChangeCredentials', () => {
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
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 () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
}),
|
||||
).toEqual({
|
||||
success: true,
|
||||
authResponse: {
|
||||
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',
|
||||
@@ -81,29 +92,24 @@ describe('ChangeCredentials', () => {
|
||||
})
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||
expect(deleteOtherSessionsForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should change email', async () => {
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValueOnce(user).mockReturnValueOnce(null)
|
||||
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: 'new@test.te',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
}),
|
||||
).toEqual({
|
||||
success: true,
|
||||
authResponse: {
|
||||
foo: 'bar',
|
||||
},
|
||||
const result = await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: 'new@test.te',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
})
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
||||
expect(userRepository.save).toHaveBeenCalledWith({
|
||||
encryptedPassword: expect.any(String),
|
||||
@@ -116,6 +122,7 @@ describe('ChangeCredentials', () => {
|
||||
})
|
||||
expect(domainEventFactory.createUserEmailChangedEvent).toHaveBeenCalledWith('1-2-3', 'test@test.te', 'new@test.te')
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(deleteOtherSessionsForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not change email if already taken', async () => {
|
||||
@@ -124,22 +131,19 @@ describe('ChangeCredentials', () => {
|
||||
.mockReturnValueOnce(user)
|
||||
.mockReturnValueOnce({} as jest.Mocked<User>)
|
||||
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: 'new@test.te',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
errorMessage: 'The email you entered is already taken. Please try again.',
|
||||
const result = await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: 'new@test.te',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
})
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.getError()).toEqual('The email you entered is already taken. Please try again.')
|
||||
|
||||
expect(userRepository.save).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 () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: '',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
errorMessage: 'Username cannot be empty',
|
||||
const result = await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: '',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
})
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.getError()).toEqual('Username cannot be empty')
|
||||
|
||||
expect(userRepository.save).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 () => {
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
||||
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: '',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
errorMessage: 'User not found.',
|
||||
const result = await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
newEmail: '',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
kpCreated: '123',
|
||||
kpOrigination: 'password-change',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.getError()).toEqual('User not found.')
|
||||
|
||||
expect(userRepository.save).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserEmailChangedEvent).not.toHaveBeenCalled()
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not change password if current password is incorrect', async () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'test123',
|
||||
newPassword: 'test234',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
}),
|
||||
).toEqual({
|
||||
success: false,
|
||||
errorMessage: 'The current password you entered is incorrect. Please try again.',
|
||||
const result = await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
currentPassword: 'test123',
|
||||
newPassword: 'test234',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
})
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.getError()).toEqual('The current password you entered is incorrect. Please try again.')
|
||||
|
||||
expect(userRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should update protocol version while changing password', async () => {
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
username: Username.create('test@test.te').getValue(),
|
||||
apiVersion: '20190520',
|
||||
currentPassword: 'qweqwe123123',
|
||||
newPassword: 'test234',
|
||||
pwNonce: 'asdzxc',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
protocolVersion: '004',
|
||||
}),
|
||||
).toEqual({
|
||||
success: true,
|
||||
authResponse: {
|
||||
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',
|
||||
protocolVersion: '004',
|
||||
})
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
||||
expect(userRepository.save).toHaveBeenCalledWith({
|
||||
encryptedPassword: expect.any(String),
|
||||
@@ -239,4 +229,63 @@ describe('ChangeCredentials', () => {
|
||||
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 { 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 { AuthResponseFactoryResolverInterface } from '../../Auth/AuthResponseFactoryResolverInterface'
|
||||
|
||||
import { User } from '../../User/User'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { ChangeCredentialsDTO } from './ChangeCredentialsDTO'
|
||||
import { ChangeCredentialsResponse } from './ChangeCredentialsResponse'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { DomainEventPublisherInterface, UserEmailChangedEvent } from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Username } from '@standardnotes/domain-core'
|
||||
import { DeleteOtherSessionsForUser } from '../DeleteOtherSessionsForUser'
|
||||
import { AuthResponse20161215 } from '../../Auth/AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
||||
import { Session } from '../../Session/Session'
|
||||
|
||||
@injectable()
|
||||
export class ChangeCredentials implements UseCaseInterface {
|
||||
export class ChangeCredentials implements UseCaseInterface<AuthResponse20161215 | AuthResponse20200115> {
|
||||
constructor(
|
||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.Auth_AuthResponseFactoryResolver)
|
||||
@@ -22,22 +24,18 @@ export class ChangeCredentials implements UseCaseInterface {
|
||||
@inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
@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)
|
||||
if (!user) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: 'User not found.',
|
||||
}
|
||||
return Result.fail('User not found.')
|
||||
}
|
||||
|
||||
if (!(await bcrypt.compare(dto.currentPassword, user.encryptedPassword))) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: 'The current password you entered is incorrect. Please try again.',
|
||||
}
|
||||
return Result.fail('The current password you entered is incorrect. Please try again.')
|
||||
}
|
||||
|
||||
user.encryptedPassword = await bcrypt.hash(dto.newPassword, User.PASSWORD_HASH_COST)
|
||||
@@ -46,19 +44,13 @@ export class ChangeCredentials implements UseCaseInterface {
|
||||
if (dto.newEmail !== undefined) {
|
||||
const newUsernameOrError = Username.create(dto.newEmail)
|
||||
if (newUsernameOrError.isFailed()) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: newUsernameOrError.getError(),
|
||||
}
|
||||
return Result.fail(newUsernameOrError.getError())
|
||||
}
|
||||
const newUsername = newUsernameOrError.getValue()
|
||||
|
||||
const existingUser = await this.userRepository.findOneByUsernameOrEmail(newUsername)
|
||||
if (existingUser !== null) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: 'The email you entered is already taken. Please try again.',
|
||||
}
|
||||
return Result.fail('The email you entered is already taken. Please try again.')
|
||||
}
|
||||
|
||||
userEmailChangedEvent = this.domainEventFactory.createUserEmailChangedEvent(
|
||||
@@ -90,15 +82,35 @@ export class ChangeCredentials implements UseCaseInterface {
|
||||
|
||||
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authResponse: await authResponseFactory.createResponse({
|
||||
user: updatedUser,
|
||||
apiVersion: dto.apiVersion,
|
||||
userAgent: dto.updatedWithUserAgent,
|
||||
ephemeralSession: false,
|
||||
readonlyAccess: false,
|
||||
}),
|
||||
const authResponse = await authResponseFactory.createResponse({
|
||||
user: updatedUser,
|
||||
apiVersion: dto.apiVersion,
|
||||
userAgent: dto.updatedWithUserAgent,
|
||||
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>
|
||||
|
||||
user = {
|
||||
uuid: '1-2-3',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
email: 'test@test.te',
|
||||
} as jest.Mocked<User>
|
||||
user.roles = Promise.resolve([role])
|
||||
|
||||
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.projectSimple = jest.fn().mockReturnValue({ name: 'role1', uuid: '1-3-4' })
|
||||
@@ -69,7 +71,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
},
|
||||
60,
|
||||
@@ -91,7 +93,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
],
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
},
|
||||
60,
|
||||
@@ -100,7 +102,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
|
||||
it('should create a cross service token for user by user uuid', async () => {
|
||||
await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
@@ -113,7 +115,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
],
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '1-2-3',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
},
|
||||
60,
|
||||
@@ -126,7 +128,20 @@ describe('CreateCrossServiceToken', () => {
|
||||
let caughtError = null
|
||||
try {
|
||||
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) {
|
||||
caughtError = error
|
||||
|
||||
@@ -11,6 +11,7 @@ import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
||||
import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class CreateCrossServiceToken implements UseCaseInterface {
|
||||
@@ -26,7 +27,13 @@ export class CreateCrossServiceToken implements UseCaseInterface {
|
||||
async execute(dto: CreateCrossServiceTokenDTO): Promise<CreateCrossServiceTokenResponse> {
|
||||
let user: User | undefined | null = dto.user
|
||||
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) {
|
||||
|
||||
@@ -34,6 +34,7 @@ describe('DeleteAccount', () => {
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
|
||||
|
||||
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
||||
@@ -53,65 +54,124 @@ describe('DeleteAccount', () => {
|
||||
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1)
|
||||
})
|
||||
|
||||
it('should trigger account deletion - no subscription', async () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||
describe('when user uuid is provided', () => {
|
||||
it('should trigger account deletion - no subscription', async () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||
|
||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
||||
message: 'Successfully deleted user',
|
||||
responseCode: 200,
|
||||
success: true,
|
||||
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: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: undefined,
|
||||
it('should trigger account deletion - subscription present', async () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||
|
||||
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 () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||
describe('when username is provided', () => {
|
||||
it('should trigger account deletion - no subscription', async () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||
|
||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
||||
message: 'Successfully deleted user',
|
||||
responseCode: 200,
|
||||
success: true,
|
||||
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: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
it('should trigger account deletion - subscription present', async () => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||
|
||||
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 () => {
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
|
||||
describe('when neither user uuid nor username is provided', () => {
|
||||
it('should not trigger account deletion', async () => {
|
||||
const result = await createUseCase().execute({})
|
||||
|
||||
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
|
||||
message: 'User not found',
|
||||
responseCode: 404,
|
||||
success: false,
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
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 { TimerInterface } from '@standardnotes/time'
|
||||
import { inject, injectable } from 'inversify'
|
||||
@@ -7,13 +7,12 @@ import TYPES from '../../../Bootstrap/Types'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { DeleteAccountDTO } from './DeleteAccountDTO'
|
||||
import { DeleteAccountResponse } from './DeleteAccountResponse'
|
||||
import { User } from '../../User/User'
|
||||
|
||||
@injectable()
|
||||
export class DeleteAccount implements UseCaseInterface {
|
||||
export class DeleteAccount implements UseCaseInterface<string> {
|
||||
constructor(
|
||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
||||
@@ -22,25 +21,30 @@ export class DeleteAccount implements UseCaseInterface {
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: DeleteAccountDTO): Promise<DeleteAccountResponse> {
|
||||
const usernameOrError = Username.create(dto.email)
|
||||
if (usernameOrError.isFailed()) {
|
||||
return {
|
||||
success: false,
|
||||
responseCode: 400,
|
||||
message: usernameOrError.getError(),
|
||||
async execute(dto: DeleteAccountDTO): Promise<Result<string>> {
|
||||
let user: User | null = null
|
||||
if (dto.userUuid !== undefined) {
|
||||
const uuidOrError = Uuid.create(dto.userUuid)
|
||||
if (uuidOrError.isFailed()) {
|
||||
return Result.fail(uuidOrError.getError())
|
||||
}
|
||||
}
|
||||
const username = usernameOrError.getValue()
|
||||
const uuid = uuidOrError.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) {
|
||||
return {
|
||||
success: false,
|
||||
responseCode: 404,
|
||||
message: 'User not found',
|
||||
}
|
||||
return Result.ok('User already deleted.')
|
||||
}
|
||||
|
||||
let regularSubscriptionUuid = undefined
|
||||
@@ -57,10 +61,6 @@ export class DeleteAccount implements UseCaseInterface {
|
||||
}),
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Successfully deleted user',
|
||||
responseCode: 200,
|
||||
}
|
||||
return Result.ok('Successfully deleted account.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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