Compare commits

...

43 Commits

Author SHA1 Message Date
standardci
75e266cb9e chore(release): publish new version
- @standardnotes/home-server@1.13.21
 - @standardnotes/syncing-server@1.73.0
2023-08-01 05:16:33 +00:00
Karol Sójko
b9bb83c0ce feat(syncing-server): add shared vault snjs filter (#677)
Co-authored-by: Mo <mo@standardnotes.com>
2023-08-01 07:00:14 +02:00
standardci
da645c5ab3 chore(release): publish new version
- @standardnotes/auth-server@1.127.1
 - @standardnotes/home-server@1.13.20
2023-07-31 13:33:09 +00:00
Karol Sójko
318af5757d fix(auth): auth middleware on delete account 2023-07-31 15:09:52 +02:00
standardci
b1cc156a25 chore(release): publish new version
- @standardnotes/api-gateway@1.69.1
 - @standardnotes/home-server@1.13.19
2023-07-31 12:35:22 +00:00
Karol Sójko
79d71ca161 fix(api-gateway): remove duplicating req/res objects on return raw response from payments 2023-07-31 14:19:01 +02:00
standardci
cedd50b366 chore(release): publish new version
- @standardnotes/api-gateway@1.69.0
 - @standardnotes/auth-server@1.127.0
 - @standardnotes/home-server@1.13.18
2023-07-31 11:41:55 +00:00
Karol Sójko
0d5dcdd8ec feat: refactor deleting account (#676)
* fix(api-gateway): TYPES aliases

* feat: refactor account deleting
2023-07-31 13:23:39 +02:00
standardci
d2b0fb144b chore(release): publish new version
- @standardnotes/home-server@1.13.17
 - @standardnotes/syncing-server@1.72.2
2023-07-30 13:24:19 +00:00
Mo
053852b46c fix: missing var reference and brackets (#675) 2023-07-30 08:09:23 -05:00
Karol Sójko
6ad349d379 fix: db name encapsulation 2023-07-28 13:32:13 +02:00
Karol Sójko
f7d33c7164 fix: separate databases and redis instances 2023-07-28 12:00:17 +02:00
Karol Sójko
b53b67328f fix: displaying logs on docker failure 2023-07-28 11:23:16 +02:00
Karol Sójko
573ffbfcf3 fix: add showing logs on docker error 2023-07-28 10:19:32 +02:00
Karol Sójko
501ac0e99f fix: env vars for database runs 2023-07-27 14:32:55 +02:00
Karol Sójko
959a11293a fix: separate database for different configuration 2023-07-27 14:19:21 +02:00
standardci
fee1f1a3a7 chore(release): publish new version
- @standardnotes/analytics@2.25.6
 - @standardnotes/api-gateway@1.68.1
 - @standardnotes/auth-server@1.126.5
 - @standardnotes/domain-core@1.24.1
 - @standardnotes/event-store@1.11.13
 - @standardnotes/files-server@1.19.15
 - @standardnotes/home-server@1.13.16
 - @standardnotes/revisions-server@1.25.6
 - @standardnotes/scheduler-server@1.20.15
 - @standardnotes/settings@1.21.20
 - @standardnotes/syncing-server@1.72.1
 - @standardnotes/websockets-server@1.10.8
2023-07-27 11:10:28 +00:00
Karol Sójko
b0fbe0bb58 fix: extended access token refresh ttl during home server e2e 2023-07-27 12:54:50 +02:00
Karol Sójko
0087c70007 fix: missing env var on e2e 2023-07-27 12:25:31 +02:00
Karol Sójko
36e496dd7c fix: remove dns aliases on accessing mysql and redis in home server e2e 2023-07-27 12:19:01 +02:00
Karol Sójko
f2e2030e85 fix: disable fail-fast on common e2e suite 2023-07-27 12:11:38 +02:00
Karol Sójko
0c3737dc19 fix: redirect STDERR for e2e to common output 2023-07-27 12:07:32 +02:00
Karol Sójko
f7471119e1 fix: add logging error stack on home server failure 2023-07-27 12:00:40 +02:00
Karol Sójko
9bd97b95e9 fix: unset the custom dbsqlite path for e2e 2023-07-27 11:46:48 +02:00
Karol Sójko
b7400c198f fix: setting env vars on common-e2e test suite 2023-07-27 11:30:49 +02:00
Karol Sójko
f87036e3a8 fix: setting env vars on home server in e2e environment 2023-07-27 11:26:05 +02:00
Karol Sójko
a43e5ef724 fix: outputing logs on e2e - already existing logs directory 2023-07-27 10:32:43 +02:00
Karol Sójko
913ced70b0 fix: outputing logs on e2e 2023-07-27 08:33:49 +02:00
standardci
6ffce30a36 chore(release): publish new version
- @standardnotes/api-gateway@1.68.0
 - @standardnotes/home-server@1.13.15
 - @standardnotes/syncing-server@1.72.0
2023-07-27 06:28:46 +00:00
Karol Sójko
f5a57d886c fix: show logs on failing suite 2023-07-27 08:13:49 +02:00
Karol Sójko
e8ba49ecca feat(syncing-server): add deleting outbound messages
Co-authored-by: Mo <mo@standardnotes.com>
2023-07-27 08:06:56 +02:00
Karol Sójko
c79a5dc94b fix: add e2e yarn command for convenience 2023-07-26 15:23:08 +02:00
standardci
4db83ae678 chore(release): publish new version
- @standardnotes/analytics@2.25.5
 - @standardnotes/api-gateway@1.67.4
 - @standardnotes/auth-server@1.126.4
 - @standardnotes/domain-core@1.24.0
 - @standardnotes/event-store@1.11.12
 - @standardnotes/files-server@1.19.14
 - @standardnotes/home-server@1.13.14
 - @standardnotes/revisions-server@1.25.5
 - @standardnotes/scheduler-server@1.20.14
 - @standardnotes/settings@1.21.19
 - @standardnotes/syncing-server@1.71.0
 - @standardnotes/websockets-server@1.10.7
2023-07-26 12:07:57 +00:00
basiljelly
84ceb7ffd2 Update docker-compose.example.yml (#673)
added restart policy for container server_self_hosted so it restarts after rebooting system
2023-07-26 13:52:40 +02:00
Karol Sójko
e215ac4343 feat: extract shared vault user permission to domain-core 2023-07-26 13:45:53 +02:00
standardci
bc8048790f chore(release): publish new version
- @standardnotes/home-server@1.13.13
 - @standardnotes/syncing-server@1.70.5
2023-07-26 11:19:11 +00:00
Karol Sójko
886ccf84c1 fix(syncing-server): uuid comparison when removing user 2023-07-26 13:02:03 +02:00
standardci
c067cb9fe4 chore(release): publish new version
- @standardnotes/home-server@1.13.12
 - @standardnotes/syncing-server@1.70.4
2023-07-26 10:54:46 +00:00
Karol Sójko
6b2389cdc3 fix(syncing-serve): removing other users from shared vault 2023-07-26 12:39:24 +02:00
standardci
d93916b159 chore(release): publish new version
- @standardnotes/analytics@2.25.4
 - @standardnotes/api-gateway@1.67.3
 - @standardnotes/auth-server@1.126.3
 - @standardnotes/domain-core@1.23.4
 - @standardnotes/event-store@1.11.11
 - @standardnotes/files-server@1.19.13
 - @standardnotes/home-server@1.13.11
 - @standardnotes/revisions-server@1.25.4
 - @standardnotes/scheduler-server@1.20.13
 - @standardnotes/settings@1.21.18
 - @standardnotes/syncing-server@1.70.3
 - @standardnotes/websockets-server@1.10.6
2023-07-26 10:38:44 +00:00
Karol Sójko
c34f548e45 fix(syncing-server): persisting aggregate changes from root (#674) 2023-07-26 12:23:10 +02:00
standardci
6fcd56cc86 chore(release): publish new version
- @standardnotes/home-server@1.13.10
 - @standardnotes/syncing-server@1.70.2
2023-07-25 14:07:10 +00:00
Karol Sójko
8f88a87c93 fix(syncing-server): remove notifications after adding item to vault (#672) 2023-07-25 15:51:42 +02:00
164 changed files with 2136 additions and 545 deletions

View File

@@ -52,12 +52,28 @@ jobs:
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
- 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]
include:
- cache_type: redis
db_type: mysql
redis_port: 6380
- cache_type: redis
db_type: sqlite
redis_port: 6381
runs-on: ubuntu-latest
@@ -69,14 +85,14 @@ jobs:
cache:
image: redis
ports:
- 6379:6379
- ${{ matrix.redis_port }}:6379
db:
image: mysql
ports:
- 3306:3306
- 3307:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: standardnotes
MYSQL_DATABASE: standardnotes_${{ matrix.cache_type }}
MYSQL_USER: standardnotes
MYSQL_PASSWORD: standardnotes
@@ -106,20 +122,22 @@ 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 "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
echo "DB_HOST=db" >> packages/home-server/.env
echo "DB_PORT=3306" >> packages/home-server/.env
echo "DB_HOST=localhost" >> packages/home-server/.env
echo "DB_PORT=3307" >> packages/home-server/.env
echo "DB_DATABASE=standardnotes_${{ matrix.cache_type }}" >> packages/home-server/.env
echo "DB_SQLITE_DATABASE_PATH=sqlite_${{ matrix.cache_type }}.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:${{ matrix.redis_port }}" >> packages/home-server/.env
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> 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
@@ -128,3 +146,7 @@ jobs:
- name: Run E2E Test Suite
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
- name: Show logs on failure
if: ${{ failure() }}
run: tail -n 500 logs/output.log

View File

@@ -3,6 +3,7 @@ services:
image: standardnotes/server
env_file: .env
container_name: server_self_hosted
restart: unless-stopped
ports:
- 3000:3000
- 3125:3104

View File

@@ -17,7 +17,8 @@
"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"
},
"devDependencies": {
"@commitlint/cli": "^17.0.2",

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.25.3",
"version": "2.25.6",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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

View File

@@ -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) => {

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.67.2",
"version": "1.69.1",
"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/*'"

View File

@@ -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')

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -12,10 +12,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()
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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> {

View File

@@ -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,

View File

@@ -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(

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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.')

View File

@@ -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,

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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,

View File

@@ -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'],

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.126.2",
"version": "1.127.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -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>
@@ -50,17 +52,23 @@ 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)
@@ -71,6 +79,12 @@ describe('AuthenticationMethodResolver', () => {
})
})
it('should not resolve session authentication method with invalid user uuid on session', async () => {
sessionService.getSessionFromToken = jest.fn().mockReturnValue({ userUuid: 'invalid' })
expect(await createResolver().resolve('test')).toBeUndefined
})
it('should resolve archvied session authentication method', async () => {
sessionService.getRevokedSessionFromToken = jest.fn().mockReturnValue(revokedSession)

View File

@@ -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,9 +30,15 @@ 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,
}
}
@@ -40,9 +47,15 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
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,
}
}

View File

@@ -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')
})
})

View File

@@ -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> {

View File

@@ -33,7 +33,7 @@ describe('FileUploadedEventHandler', () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
regularSubscription = {
uuid: '1-2-3',
uuid: '00000000-0000-0000-0000-000000000000',
subscriptionType: UserSubscriptionType.Regular,
user: Promise.resolve(user),
} as jest.Mocked<UserSubscription>
@@ -56,9 +56,9 @@ describe('FileUploadedEventHandler', () => {
event = {} as jest.Mocked<FileUploadedEvent>
event.createdAt = new Date(1)
event.payload = {
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
fileByteSize: 123,
filePath: '1-2-3/2-3-4',
filePath: '00000000-0000-0000-0000-000000000000/2-3-4',
fileName: '2-3-4',
}
@@ -78,13 +78,21 @@ describe('FileUploadedEventHandler', () => {
},
user,
userSubscription: {
uuid: '1-2-3',
uuid: '00000000-0000-0000-0000-000000000000',
subscriptionType: 'regular',
user: Promise.resolve(user),
},
})
})
it('should not do anything if a user uuid is invalid', async () => {
event.payload.userUuid = 'invalid'
await createHandler().handle(event)
expect(subscriptionSettingService.createOrReplace).not.toHaveBeenCalled()
})
it('should not do anything if a user is not found', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
@@ -121,7 +129,7 @@ describe('FileUploadedEventHandler', () => {
},
user,
userSubscription: {
uuid: '1-2-3',
uuid: '00000000-0000-0000-0000-000000000000',
subscriptionType: 'regular',
user: Promise.resolve(user),
},
@@ -147,7 +155,7 @@ describe('FileUploadedEventHandler', () => {
},
user,
userSubscription: {
uuid: '1-2-3',
uuid: '00000000-0000-0000-0000-000000000000',
subscriptionType: 'regular',
user: Promise.resolve(user),
},

View File

@@ -10,6 +10,7 @@ import { UserSubscription } from '../Subscription/UserSubscription'
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { User } from '../User/User'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
@@ -22,17 +23,25 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
) {}
async handle(event: FileUploadedEvent): Promise<void> {
const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
const userUuidOrError = Uuid.create(event.payload.userUuid)
if (userUuidOrError.isFailed()) {
this.logger.warn(userUuidOrError.getError())
return
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
return
}
const { regularSubscription, sharedSubscription } =
await this.userSubscriptionService.findRegularSubscriptionForUserUuid(event.payload.userUuid)
await this.userSubscriptionService.findRegularSubscriptionForUserUuid(userUuid.value)
if (regularSubscription === null) {
this.logger.warn(`Could not find regular user subscription for user with uuid: ${event.payload.userUuid}`)
this.logger.warn(`Could not find regular user subscription for user with uuid: ${userUuid.value}`)
return
}

View File

@@ -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()
})

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -34,7 +34,7 @@ describe('DeleteAccount', () => {
} as jest.Mocked<UserSubscription>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
@@ -58,7 +58,7 @@ describe('DeleteAccount', () => {
.fn()
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
message: 'Successfully deleted user',
responseCode: 200,
success: true,
@@ -77,7 +77,7 @@ describe('DeleteAccount', () => {
.fn()
.mockReturnValue({ regularSubscription, sharedSubscription: null })
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
message: 'Successfully deleted user',
responseCode: 200,
success: true,
@@ -92,9 +92,9 @@ describe('DeleteAccount', () => {
})
it('should not trigger account deletion if user is not found', async () => {
userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
message: 'User not found',
responseCode: 404,
success: false,
@@ -104,9 +104,9 @@ describe('DeleteAccount', () => {
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',
it('should not trigger account deletion if user uuid is invalid', async () => {
expect(await createUseCase().execute({ userUuid: '' })).toEqual({
message: 'Given value is not a valid uuid: ',
responseCode: 400,
success: false,
})

View File

@@ -1,4 +1,4 @@
import { Username } from '@standardnotes/domain-core'
import { Uuid } from '@standardnotes/domain-core'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
@@ -23,17 +23,17 @@ export class DeleteAccount implements UseCaseInterface {
) {}
async execute(dto: DeleteAccountDTO): Promise<DeleteAccountResponse> {
const usernameOrError = Username.create(dto.email)
if (usernameOrError.isFailed()) {
const uuidOrError = Uuid.create(dto.userUuid)
if (uuidOrError.isFailed()) {
return {
success: false,
responseCode: 400,
message: usernameOrError.getError(),
message: uuidOrError.getError(),
}
}
const username = usernameOrError.getValue()
const uuid = uuidOrError.getValue()
const user = await this.userRepository.findOneByUsernameOrEmail(username)
const user = await this.userRepository.findOneByUuid(uuid)
if (user === null) {
return {

View File

@@ -1,3 +1,3 @@
export type DeleteAccountDTO = {
email: string
userUuid: string
}

View File

@@ -20,7 +20,7 @@ export class DeleteAuthenticator implements UseCaseInterface<string> {
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid.value)
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return Result.fail('Could not delete authenticator: user not found.')
}

View File

@@ -37,7 +37,7 @@ export class GenerateAuthenticatorRegistrationOptions
}
const username = usernameOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid.value)
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return Result.fail('Could not generate authenticator registration options: user not found.')
}

View File

@@ -19,7 +19,7 @@ export class GenerateRecoveryCodes implements UseCaseInterface<string> {
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid.value)
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return Result.fail('Could not generate recovery codes: user not found')
}

View File

@@ -45,6 +45,10 @@ describe('GetSettings', () => {
)
beforeEach(() => {
user = {
uuid: '00000000-0000-0000-0000-000000000000',
} as jest.Mocked<User>
setting = new Setting()
setting.name = 'test'
setting.updatedAt = 345
@@ -90,8 +94,6 @@ describe('GetSettings', () => {
subscriptionSettingProjector = {} as jest.Mocked<SubscriptionSettingProjector>
subscriptionSettingProjector.projectSimple = jest.fn().mockReturnValue({ foo: 'sub-bar' })
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
@@ -103,18 +105,27 @@ describe('GetSettings', () => {
it('should fail if a user is not found', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: false,
error: {
message: 'User 1-2-3 not found.',
message: 'User 00000000-0000-0000-0000-000000000000 not found.',
},
})
})
it('should fail if a user uuid is invalid', async () => {
expect(await createUseCase().execute({ userUuid: 'invalid' })).toEqual({
success: false,
error: {
message: 'Given value is not a valid uuid: invalid',
},
})
})
it('should return all user settings except mfa', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }],
})
@@ -131,9 +142,9 @@ describe('GetSettings', () => {
} as jest.Mocked<Setting>
settingRepository.findAllByUserUuid = jest.fn().mockReturnValue([setting])
expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }],
})
@@ -147,10 +158,14 @@ describe('GetSettings', () => {
it('should return all user settings of certain name', async () => {
expect(
await createUseCase().execute({ userUuid: '1-2-3', settingName: 'test', allowSensitiveRetrieval: true }),
await createUseCase().execute({
userUuid: '00000000-0000-0000-0000-000000000000',
settingName: 'test',
allowSensitiveRetrieval: true,
}),
).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }],
})
@@ -159,10 +174,14 @@ describe('GetSettings', () => {
it('should return all user settings updated after', async () => {
expect(
await createUseCase().execute({ userUuid: '1-2-3', allowSensitiveRetrieval: true, updatedAfter: 123 }),
await createUseCase().execute({
userUuid: '00000000-0000-0000-0000-000000000000',
allowSensitiveRetrieval: true,
updatedAfter: 123,
}),
).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }],
})
@@ -170,9 +189,14 @@ describe('GetSettings', () => {
})
it('should return all sensitive user settings if explicit', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3', allowSensitiveRetrieval: true })).toEqual({
expect(
await createUseCase().execute({
userUuid: '00000000-0000-0000-0000-000000000000',
allowSensitiveRetrieval: true,
}),
).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }, { foo: 'bar' }],
})
@@ -190,9 +214,9 @@ describe('GetSettings', () => {
})
it('should return all user settings except mfa', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }, { foo: 'sub-bar' }],
})
@@ -210,9 +234,9 @@ describe('GetSettings', () => {
})
it('should return all user settings except mfa', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: true,
userUuid: '1-2-3',
userUuid: '00000000-0000-0000-0000-000000000000',
settings: [{ foo: 'bar' }, { foo: 'sub-bar' }],
})

View File

@@ -15,6 +15,7 @@ import { SubscriptionSetting } from '../../Setting/SubscriptionSetting'
import { SimpleSetting } from '../../Setting/SimpleSetting'
import { SimpleSubscriptionSetting } from '../../Setting/SimpleSubscriptionSetting'
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class GetSettings implements UseCaseInterface {
@@ -30,7 +31,16 @@ export class GetSettings implements UseCaseInterface {
) {}
async execute(dto: GetSettingsDto): Promise<GetSettingsResponse> {
const { userUuid } = dto
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return {
success: false,
error: {
message: userUuidOrError.getError(),
},
}
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid)
@@ -38,13 +48,13 @@ export class GetSettings implements UseCaseInterface {
return {
success: false,
error: {
message: `User ${userUuid} not found.`,
message: `User ${userUuid.value} not found.`,
},
}
}
let settings: Array<Setting | SubscriptionSetting>
settings = await this.settingRepository.findAllByUserUuid(userUuid)
settings = await this.settingRepository.findAllByUserUuid(user.uuid)
const { regularSubscription, sharedSubscription } =
await this.userSubscriptionService.findRegularSubscriptionForUserUuid(user.uuid)
@@ -83,7 +93,7 @@ export class GetSettings implements UseCaseInterface {
return {
success: true,
userUuid,
userUuid: user.uuid,
settings: simpleSettings,
}
}

View File

@@ -31,23 +31,36 @@ describe('GetUserFeatures', () => {
it('should fail if a user is not found', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
expect(await createUseCase().execute({ userUuid: 'user-1-1-1', offline: false })).toEqual({
success: false,
error: {
message: 'User user-1-1-1 not found.',
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', offline: false })).toEqual(
{
success: false,
error: {
message: 'User 00000000-0000-0000-0000-000000000000 not found.',
},
},
})
)
})
it('should return user features', async () => {
expect(await createUseCase().execute({ userUuid: 'user-1-1-1', offline: false })).toEqual({
success: true,
userUuid: 'user-1-1-1',
features: [
{
name: 'foobar',
},
],
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', offline: false })).toEqual(
{
success: true,
userUuid: '00000000-0000-0000-0000-000000000000',
features: [
{
name: 'foobar',
},
],
},
)
})
it('should fail if a user uuid is invalid', async () => {
expect(await createUseCase().execute({ userUuid: 'invalid', offline: false })).toEqual({
success: false,
error: {
message: 'Given value is not a valid uuid: invalid',
},
})
})

View File

@@ -5,6 +5,7 @@ import { GetUserFeaturesDto } from './GetUserFeaturesDto'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { GetUserFeaturesResponse } from './GetUserFeaturesResponse'
import { FeatureServiceInterface } from '../../Feature/FeatureServiceInterface'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class GetUserFeatures implements UseCaseInterface {
@@ -24,13 +25,24 @@ export class GetUserFeatures implements UseCaseInterface {
}
}
const user = await this.userRepository.findOneByUuid(dto.userUuid)
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return {
success: false,
error: {
message: userUuidOrError.getError(),
},
}
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return {
success: false,
error: {
message: `User ${dto.userUuid} not found.`,
message: `User ${userUuid.value} not found.`,
},
}
}
@@ -39,7 +51,7 @@ export class GetUserFeatures implements UseCaseInterface {
return {
success: true,
userUuid: dto.userUuid,
userUuid: userUuid.value,
features: userFeatures,
}
}

View File

@@ -61,7 +61,9 @@ describe('GetUserKeyParams', () => {
})
it('should get key params for an authenticated user - searching by uuid', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: true })).toEqual({
expect(
await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: true }),
).toEqual({
keyParams: {
foo: 'bar',
},
@@ -71,7 +73,9 @@ describe('GetUserKeyParams', () => {
})
it('should get key params for an unauthenticated user - searching by uuid', async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: false })).toEqual({
expect(
await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: false }),
).toEqual({
keyParams: {
foo: 'bar',
},
@@ -81,7 +85,13 @@ describe('GetUserKeyParams', () => {
})
it("should get key params for an unauthenticated user and store it's code challenge", async () => {
expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: false, codeChallenge: 'test' })).toEqual({
expect(
await createUseCase().execute({
userUuid: '00000000-0000-0000-0000-000000000000',
authenticated: false,
codeChallenge: 'test',
}),
).toEqual({
keyParams: {
foo: 'bar',
},
@@ -107,7 +117,18 @@ describe('GetUserKeyParams', () => {
let error = null
try {
await createUseCase().execute({ userUuid: '1-2-3', authenticated: false })
await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: false })
} catch (e) {
error = e
}
expect(error).not.toBeNull()
})
it('should throw error for an invalid user uuid', async () => {
let error = null
try {
await createUseCase().execute({ userUuid: 'invalid', authenticated: false })
} catch (e) {
error = e
}

View File

@@ -10,7 +10,7 @@ import { User } from '../../User/User'
import { PKCERepositoryInterface } from '../../User/PKCERepositoryInterface'
import { GetUserKeyParamsDTOV2Challenged } from './GetUserKeyParamsDTOV2Challenged'
import { KeyParamsData } from '@standardnotes/responses'
import { Username } from '@standardnotes/domain-core'
import { Username, Uuid } from '@standardnotes/domain-core'
@injectable()
export class GetUserKeyParams implements UseCaseInterface {
@@ -39,7 +39,13 @@ export class GetUserKeyParams implements UseCaseInterface {
}
}
} else if (dto.userUuid) {
user = await this.userRepository.findOneByUuid(dto.userUuid)
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
throw Error(userUuidOrError.getError())
}
const userUuid = userUuidOrError.getValue()
user = await this.userRepository.findOneByUuid(userUuid)
}
if (!user) {

View File

@@ -15,7 +15,10 @@ describe('GetUserSubscription', () => {
const createUseCase = () => new GetUserSubscription(userRepository, userSubscriptionRepository)
beforeEach(() => {
user = { uuid: 'user-1-1-1', email: 'user-1-1-1@example.com' } as jest.Mocked<User>
user = {
uuid: '00000000-0000-0000-0000-000000000000',
email: '00000000-0000-0000-0000-000000000000@example.com',
} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
@@ -29,18 +32,27 @@ describe('GetUserSubscription', () => {
it('should fail if a user is not found', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
expect(await createUseCase().execute({ userUuid: 'user-1-1-1' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: false,
error: {
message: 'User user-1-1-1 not found.',
message: 'User 00000000-0000-0000-0000-000000000000 not found.',
},
})
})
it('should fail if a user uuid is invalid', async () => {
expect(await createUseCase().execute({ userUuid: 'invalid' })).toEqual({
success: false,
error: {
message: 'Given value is not a valid uuid: invalid',
},
})
})
it('should return user subscription', async () => {
expect(await createUseCase().execute({ userUuid: 'user-1-1-1' })).toEqual({
expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
success: true,
user: { uuid: 'user-1-1-1', email: 'user-1-1-1@example.com' },
user: { uuid: '00000000-0000-0000-0000-000000000000', email: '00000000-0000-0000-0000-000000000000@example.com' },
subscription: {
planName: SubscriptionName.ProPlan,
},

View File

@@ -5,6 +5,7 @@ import { GetUserSubscriptionDto } from './GetUserSubscriptionDto'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { GetUserSubscriptionResponse } from './GetUserSubscriptionResponse'
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class GetUserSubscription implements UseCaseInterface {
@@ -15,7 +16,16 @@ export class GetUserSubscription implements UseCaseInterface {
) {}
async execute(dto: GetUserSubscriptionDto): Promise<GetUserSubscriptionResponse> {
const { userUuid } = dto
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return {
success: false,
error: {
message: userUuidOrError.getError(),
},
}
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid)
@@ -23,12 +33,12 @@ export class GetUserSubscription implements UseCaseInterface {
return {
success: false,
error: {
message: `User ${userUuid} not found.`,
message: `User ${userUuid.value} not found.`,
},
}
}
const userSubscription = await this.userSubscriptionRepository.findOneByUserUuid(userUuid)
const userSubscription = await this.userSubscriptionRepository.findOneByUserUuid(userUuid.value)
return {
success: true,

View File

@@ -21,7 +21,7 @@ export class ListAuthenticators implements UseCaseInterface<Authenticator[]> {
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid.value)
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return Result.fail('Could not list authenticators: user not found.')
}

View File

@@ -62,7 +62,7 @@ describe('UpdateSetting', () => {
settingProjector.projectSimple = jest.fn().mockReturnValue(settingProjection)
regularSubscription = {
uuid: '1-2-3',
uuid: '00000000-0000-0000-0000-000000000000',
subscriptionType: UserSubscriptionType.Regular,
user: Promise.resolve(user),
} as jest.Mocked<UserSubscription>
@@ -109,7 +109,7 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).toHaveBeenCalledWith({
props: {
@@ -138,19 +138,40 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
expect(response).toEqual({
success: false,
error: {
message: 'User 1-2-3 not found.',
message: 'User 00000000-0000-0000-0000-000000000000 not found.',
},
statusCode: 404,
})
})
it('should not create a setting if user uuid is invalid', async () => {
const props = {
name: SettingName.NAMES.ExtensionKey,
unencryptedValue: 'test-setting-value',
serverEncryptionVersion: EncryptionVersion.Unencrypted,
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: 'invalid' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
expect(response).toEqual({
success: false,
error: {
message: 'Given value is not a valid uuid: invalid',
},
statusCode: 400,
})
})
it('should not create a subscription setting', async () => {
const props = {
name: SettingName.NAMES.MuteSignInEmails,
@@ -159,14 +180,14 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
expect(response).toEqual({
success: false,
error: {
message: 'User 1-2-3 has no subscription to change a subscription setting.',
message: 'User 00000000-0000-0000-0000-000000000000 has no subscription to change a subscription setting.',
},
statusCode: 400,
})
@@ -180,7 +201,7 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
@@ -207,14 +228,14 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
expect(response).toEqual({
success: false,
error: {
message: 'User 1-2-3 is not permitted to change the setting.',
message: 'User 00000000-0000-0000-0000-000000000000 is not permitted to change the setting.',
},
statusCode: 401,
})
@@ -230,14 +251,14 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(settingService.createOrReplace).not.toHaveBeenCalled()
expect(response).toEqual({
success: false,
error: {
message: 'User 1-2-3 is not permitted to change the setting.',
message: 'User 00000000-0000-0000-0000-000000000000 is not permitted to change the setting.',
},
statusCode: 401,
})
@@ -259,7 +280,7 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
props: {
@@ -295,7 +316,7 @@ describe('UpdateSetting', () => {
sensitive: false,
}
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
props: {

View File

@@ -16,6 +16,7 @@ import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionS
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
import { CreateOrReplaceSubscriptionSettingResponse } from '../../Setting/CreateOrReplaceSubscriptionSettingResponse'
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class UpdateSetting implements UseCaseInterface {
@@ -48,7 +49,17 @@ export class UpdateSetting implements UseCaseInterface {
this.logger.debug('[%s] Updating setting: %O', dto.userUuid, dto)
const { userUuid, props } = dto
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return {
success: false,
error: {
message: userUuidOrError.getError(),
},
statusCode: 400,
}
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid)
@@ -56,7 +67,7 @@ export class UpdateSetting implements UseCaseInterface {
return {
success: false,
error: {
message: `User ${userUuid} not found.`,
message: `User ${userUuid.value} not found.`,
},
statusCode: 404,
}
@@ -66,14 +77,14 @@ export class UpdateSetting implements UseCaseInterface {
return {
success: false,
error: {
message: `User ${userUuid} is not permitted to change the setting.`,
message: `User ${userUuid.value} is not permitted to change the setting.`,
},
statusCode: 401,
}
}
props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(settingName)
props.sensitive = this.settingsAssociationService.getSensitivityForSetting(settingName)
dto.props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(settingName)
dto.props.sensitive = this.settingsAssociationService.getSensitivityForSetting(settingName)
if (settingName.isASubscriptionSetting()) {
const { regularSubscription, sharedSubscription } =
@@ -83,7 +94,7 @@ export class UpdateSetting implements UseCaseInterface {
return {
success: false,
error: {
message: `User ${userUuid} has no subscription to change a subscription setting.`,
message: `User ${userUuid.value} has no subscription to change a subscription setting.`,
},
statusCode: 400,
}
@@ -92,7 +103,7 @@ export class UpdateSetting implements UseCaseInterface {
const response = await this.subscriptionSettingService.createOrReplace({
userSubscription: subscription,
user,
props,
props: dto.props,
})
return {
@@ -104,7 +115,7 @@ export class UpdateSetting implements UseCaseInterface {
const response = await this.settingService.createOrReplace({
user,
props,
props: dto.props,
})
return {

View File

@@ -27,7 +27,7 @@ export class VerifyAuthenticatorRegistrationResponse implements UseCaseInterface
}
const userUuid = userUuidOrError.getValue()
const user = await this.userRepository.findOneByUuid(userUuid.value)
const user = await this.userRepository.findOneByUuid(userUuid)
if (user === null) {
return Result.fail('Could not verify authenticator registration response: user not found.')
}

View File

@@ -1,4 +1,4 @@
import { Email, Username } from '@standardnotes/domain-core'
import { Email, Username, Uuid } from '@standardnotes/domain-core'
import { ReadStream } from 'fs'
import { User } from './User'
@@ -6,7 +6,7 @@ import { User } from './User'
export interface UserRepositoryInterface {
streamAll(): Promise<ReadStream>
streamTeam(memberEmail?: Email): Promise<ReadStream>
findOneByUuid(uuid: string): Promise<User | null>
findOneByUuid(uuid: Uuid): Promise<User | null>
findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
save(user: User): Promise<User>
remove(user: User): Promise<User>

View File

@@ -29,6 +29,7 @@ export class HomeServerUsersController extends BaseHttpController {
this.controllerContainer.register('auth.users.getKeyParams', this.keyParams.bind(this))
this.controllerContainer.register('auth.users.getSubscription', this.getSubscription.bind(this))
this.controllerContainer.register('auth.users.updateCredentials', this.changeCredentials.bind(this))
this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this))
}
}
@@ -102,9 +103,20 @@ export class HomeServerUsersController extends BaseHttpController {
return this.json(result.keyParams)
}
async deleteAccount(request: Request): Promise<results.JsonResult> {
async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
if (request.params.userUuid !== response.locals.user.uuid) {
return this.json(
{
error: {
message: 'Operation not allowed.',
},
},
401,
)
}
const result = await this.doDeleteAccount.execute({
email: request.params.email,
userUuid: request.params.userUuid,
})
return this.json({ message: result.message }, result.responseCode)

View File

@@ -170,17 +170,34 @@ describe('InversifyExpressUsersController', () => {
})
it('should delete user', async () => {
request.params.email = 'test@test.te'
request.params.userUuid = '1-2-3'
response.locals.user = {
uuid: '1-2-3',
}
const httpResponse = <results.JsonResult>await createController().deleteAccount(request)
const httpResponse = <results.JsonResult>await createController().deleteAccount(request, response)
const result = await httpResponse.executeAsync()
expect(deleteAccount.execute).toHaveBeenCalledWith({ email: 'test@test.te' })
expect(deleteAccount.execute).toHaveBeenCalledWith({ userUuid: '1-2-3' })
expect(result.statusCode).toEqual(200)
expect(await result.content.readAsStringAsync()).toEqual('{"message":"A OK"}')
})
it('should not delete user if user uuid is different than the one in the session', async () => {
request.params.userUuid = '1-2-3'
response.locals.user = {
uuid: '2-3-4',
}
const httpResponse = <results.JsonResult>await createController().deleteAccount(request, response)
const result = await httpResponse.executeAsync()
expect(deleteAccount.execute).not.toHaveBeenCalled()
expect(result.statusCode).toEqual(401)
})
it('should get user key params', async () => {
request.query = {
email: 'test@test.te',

View File

@@ -51,9 +51,9 @@ export class InversifyExpressUsersController extends HomeServerUsersController {
return super.keyParams(request)
}
@httpDelete('/:email')
override async deleteAccount(request: Request): Promise<results.JsonResult> {
return super.deleteAccount(request)
@httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
override async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
return super.deleteAccount(request, response)
}
@httpGet('/:userUuid/subscription', TYPES.Auth_RequiredCrossServiceTokenMiddleware)

View File

@@ -1,4 +1,4 @@
import { Email, Username } from '@standardnotes/domain-core'
import { Email, Username, Uuid } from '@standardnotes/domain-core'
import { ReadStream } from 'fs'
import { inject, injectable } from 'inversify'
import { Repository } from 'typeorm'
@@ -40,11 +40,11 @@ export class TypeORMUserRepository implements UserRepositoryInterface {
return queryBuilder.stream()
}
async findOneByUuid(uuid: string): Promise<User | null> {
async findOneByUuid(uuid: Uuid): Promise<User | null> {
return this.ormRepository
.createQueryBuilder('user')
.where('user.uuid = :uuid', { uuid })
.cache(`user_uuid_${uuid}`, 60000)
.where('user.uuid = :uuid', { uuid: uuid.value })
.cache(`user_uuid_${uuid.value}`, 60000)
.getOne()
}

View File

@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.24.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.0...@standardnotes/domain-core@1.24.1) (2023-07-27)
### Bug Fixes
* setting env vars on home server in e2e environment ([f87036e](https://github.com/standardnotes/server/commit/f87036e3a8dc6b7784e74e5f32ffd220033724f5))
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.4...@standardnotes/domain-core@1.24.0) (2023-07-26)
### Features
* extract shared vault user permission to domain-core ([e215ac4](https://github.com/standardnotes/server/commit/e215ac4343e9f8818f40004d31390d6ac23e369d))
## [1.23.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.3...@standardnotes/domain-core@1.23.4) (2023-07-26)
### Bug Fixes
* **syncing-server:** persisting aggregate changes from root ([#674](https://github.com/standardnotes/server/issues/674)) ([c34f548](https://github.com/standardnotes/server/commit/c34f548e45bbd8defb8d490936e90755fd284e78))
## [1.23.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.2...@standardnotes/domain-core@1.23.3) (2023-07-21)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-core",
"version": "1.23.3",
"version": "1.24.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -1,5 +1,20 @@
/* istanbul ignore file */
import { Change } from './Change'
import { Entity } from './Entity'
export abstract class Aggregate<T> extends Entity<T> {}
export abstract class Aggregate<T> extends Entity<T> {
private changesOnAggregateRoot: Change[] = []
addChange(change: Change): void {
this.changesOnAggregateRoot.push(change)
}
flushChanges(): void {
this.changesOnAggregateRoot = []
}
getChanges(): Change[] {
return this.changesOnAggregateRoot
}
}

View File

@@ -0,0 +1,26 @@
/* istanbul ignore file */
import { ChangeProps } from './ChangeProps'
import { Result } from './Result'
export class Change {
static readonly TYPES = {
Add: 'add',
Remove: 'remove',
Modify: 'modify',
}
public readonly props: ChangeProps
constructor(props: ChangeProps) {
this.props = Object.freeze(props)
}
static create(props: ChangeProps): Result<Change> {
if (!Object.values(Change.TYPES).includes(props.changeType)) {
return Result.fail('Invalid change type')
}
return Result.ok(new Change(props))
}
}

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
import { Entity } from './Entity'
export interface ChangeProps {
aggregateRootUuid: string
changeType: string
changeData: Entity<unknown>
}

View File

@@ -23,4 +23,12 @@ export abstract class AbstractEnv {
return <string>process.env[key]
}
getAll(): { [key: string]: string } {
if (!this.env) {
this.load()
}
return this.env as { [key: string]: string }
}
}

View File

@@ -1,4 +1,5 @@
import { Result, ValueObject } from '@standardnotes/domain-core'
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { SharedVaultUserPermissionProps } from './SharedVaultUserPermissionProps'

View File

@@ -27,6 +27,8 @@ export * from './Common/Uuid'
export * from './Common/UuidProps'
export * from './Core/Aggregate'
export * from './Core/Change'
export * from './Core/ChangeProps'
export * from './Core/Entity'
export * from './Core/Id'
export * from './Core/Result'
@@ -57,6 +59,9 @@ export * from './Service/ServiceIdentifier'
export * from './Service/ServiceIdentifierProps'
export * from './Service/ServiceInterface'
export * from './SharedVault/SharedVaultUserPermission'
export * from './SharedVault/SharedVaultUserPermissionProps'
export * from './Subscription/SubscriptionPlanName'
export * from './Subscription/SubscriptionPlanNameProps'

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.13](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.12...@standardnotes/event-store@1.11.13) (2023-07-27)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.12](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.11...@standardnotes/event-store@1.11.12) (2023-07-26)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.11](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.10...@standardnotes/event-store@1.11.11) (2023-07-26)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.10](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.9...@standardnotes/event-store@1.11.10) (2023-07-21)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.11.10",
"version": "1.11.13",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.19.15](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.14...@standardnotes/files-server@1.19.15) (2023-07-27)
**Note:** Version bump only for package @standardnotes/files-server
## [1.19.14](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.13...@standardnotes/files-server@1.19.14) (2023-07-26)
**Note:** Version bump only for package @standardnotes/files-server
## [1.19.13](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.12...@standardnotes/files-server@1.19.13) (2023-07-26)
**Note:** Version bump only for package @standardnotes/files-server
## [1.19.12](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.11...@standardnotes/files-server@1.19.12) (2023-07-21)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.19.12",
"version": "1.19.15",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,57 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.13.21](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.20...@standardnotes/home-server@1.13.21) (2023-08-01)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.20](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.19...@standardnotes/home-server@1.13.20) (2023-07-31)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.19](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.18...@standardnotes/home-server@1.13.19) (2023-07-31)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.18](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.17...@standardnotes/home-server@1.13.18) (2023-07-31)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.17](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.16...@standardnotes/home-server@1.13.17) (2023-07-30)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.16](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.15...@standardnotes/home-server@1.13.16) (2023-07-27)
### Bug Fixes
* add logging error stack on home server failure ([f747111](https://github.com/standardnotes/server/commit/f7471119e1c4107dd36d37dcda4660870931fc83))
* setting env vars on home server in e2e environment ([f87036e](https://github.com/standardnotes/server/commit/f87036e3a8dc6b7784e74e5f32ffd220033724f5))
## [1.13.15](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.14...@standardnotes/home-server@1.13.15) (2023-07-27)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.14](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.13...@standardnotes/home-server@1.13.14) (2023-07-26)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.13](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.12...@standardnotes/home-server@1.13.13) (2023-07-26)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.12](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.11...@standardnotes/home-server@1.13.12) (2023-07-26)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.11](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.10...@standardnotes/home-server@1.13.11) (2023-07-26)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.10](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.9...@standardnotes/home-server@1.13.10) (2023-07-25)
**Note:** Version bump only for package @standardnotes/home-server
## [1.13.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.13.8...@standardnotes/home-server@1.13.9) (2023-07-25)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,16 +1,26 @@
import { Env } from '../src/Bootstrap/Env'
import { HomeServer } from '../src/Server/HomeServer'
const homeServer = new HomeServer()
Promise.resolve(
homeServer.start({
dataDirectoryPath: `${__dirname}/../data`,
logStreamCallback: (chunk: Buffer) => {
// eslint-disable-next-line no-console
console.log(chunk.toString())
},
}),
).catch((error) => {
const env: Env = new Env()
env.load()
try {
Promise.resolve(
homeServer.start({
dataDirectoryPath: `${__dirname}/../data`,
logStreamCallback: (chunk: Buffer) => {
// eslint-disable-next-line no-console
console.log(chunk.toString())
},
environment: env.getAll(),
}),
).catch((error) => {
// eslint-disable-next-line no-console
console.error(`Could not start server: ${error.message}`)
})
} catch (error) {
// eslint-disable-next-line no-console
console.log(`Could not start server: ${error.message}`)
})
console.error((error as Error).stack)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.13.9",
"version": "1.13.21",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.25.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.25.5...@standardnotes/revisions-server@1.25.6) (2023-07-27)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.25.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.25.4...@standardnotes/revisions-server@1.25.5) (2023-07-26)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.25.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.25.3...@standardnotes/revisions-server@1.25.4) (2023-07-26)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.25.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.25.2...@standardnotes/revisions-server@1.25.3) (2023-07-21)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.25.3",
"version": "1.25.6",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.15](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.14...@standardnotes/scheduler-server@1.20.15) (2023-07-27)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.14](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.13...@standardnotes/scheduler-server@1.20.14) (2023-07-26)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.13](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.12...@standardnotes/scheduler-server@1.20.13) (2023-07-26)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.12](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.11...@standardnotes/scheduler-server@1.20.12) (2023-07-21)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.12",
"version": "1.20.15",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.20](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.19...@standardnotes/settings@1.21.20) (2023-07-27)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.19](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.18...@standardnotes/settings@1.21.19) (2023-07-26)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.18](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.17...@standardnotes/settings@1.21.18) (2023-07-26)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.17](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.16...@standardnotes/settings@1.21.17) (2023-07-21)
**Note:** Version bump only for package @standardnotes/settings

Some files were not shown because too many files have changed in this diff Show More