chore: add types for response locals (#1015)
This commit is contained in:
parent
6ee18bffe6
commit
d44866b3c0
44 changed files with 455 additions and 261 deletions
|
@ -36,6 +36,7 @@ import { InversifyExpressServer } from 'inversify-express-utils'
|
||||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||||
import { TYPES } from '../src/Bootstrap/Types'
|
import { TYPES } from '../src/Bootstrap/Types'
|
||||||
import { Env } from '../src/Bootstrap/Env'
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
|
import { ResponseLocals } from '../src/Controller/ResponseLocals'
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader()
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
|
@ -91,12 +92,14 @@ void container.load().then((container) => {
|
||||||
|
|
||||||
server.setErrorConfig((app) => {
|
server.setErrorConfig((app) => {
|
||||||
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
logger.error(`${error.stack}`, {
|
logger.error(`${error.stack}`, {
|
||||||
method: request.method,
|
method: request.method,
|
||||||
url: request.url,
|
url: request.url,
|
||||||
snjs: request.headers['x-snjs-version'],
|
snjs: request.headers['x-snjs-version'],
|
||||||
application: request.headers['x-application-version'],
|
application: request.headers['x-application-version'],
|
||||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
userId: locals.user ? locals.user.uuid : undefined,
|
||||||
})
|
})
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`[URL: |${request.method}| ${request.url}][SNJS: ${request.headers['x-snjs-version']}][Application: ${
|
`[URL: |${request.method}| ${request.url}][SNJS: ${request.headers['x-snjs-version']}][Application: ${
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { Logger } from 'winston'
|
||||||
|
|
||||||
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
|
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
|
||||||
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
|
||||||
|
import { ResponseLocals } from './ResponseLocals'
|
||||||
|
import { RoleName } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
export abstract class AuthMiddleware extends BaseMiddleware {
|
export abstract class AuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -55,33 +57,27 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
||||||
crossServiceTokenFetchedFromCache = false
|
crossServiceTokenFetchedFromCache = false
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.authToken = crossServiceToken
|
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>(
|
|
||||||
verify(response.locals.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
|
||||||
)
|
|
||||||
|
|
||||||
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
||||||
await this.crossServiceTokenCache.set({
|
await this.crossServiceTokenCache.set({
|
||||||
key: cacheKey,
|
key: cacheKey,
|
||||||
encodedCrossServiceToken: response.locals.authToken,
|
encodedCrossServiceToken: crossServiceToken,
|
||||||
expiresAtInSeconds: this.getCrossServiceTokenCacheExpireTimestamp(decodedToken),
|
expiresAtInSeconds: this.getCrossServiceTokenCacheExpireTimestamp(decodedToken),
|
||||||
userUuid: decodedToken.user.uuid,
|
userUuid: decodedToken.user.uuid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.user = decodedToken.user
|
Object.assign(response.locals, {
|
||||||
response.locals.session = decodedToken.session
|
authToken: crossServiceToken,
|
||||||
response.locals.roles = decodedToken.roles
|
user: decodedToken.user,
|
||||||
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
session: decodedToken.session,
|
||||||
response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false
|
roles: decodedToken.roles,
|
||||||
if (response.locals.readOnlyAccess) {
|
sharedVaultOwnerContext: decodedToken.shared_vault_owner_context,
|
||||||
this.logger.debug('User operates on read-only access', {
|
readOnlyAccess: decodedToken.session?.readonly_access ?? false,
|
||||||
codeTag: 'AuthMiddleware',
|
isFreeUser: decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser,
|
||||||
userId: response.locals.user.uuid,
|
belongsToSharedVaults: decodedToken.belongs_to_shared_vaults ?? [],
|
||||||
})
|
} as ResponseLocals)
|
||||||
}
|
|
||||||
response.locals.belongsToSharedVaults = decodedToken.belongs_to_shared_vaults ?? []
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let detailedErrorMessage = (error as Error).message
|
let detailedErrorMessage = (error as Error).message
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { verify } from 'jsonwebtoken'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { ConnectionValidationResponse, IAuthClient, WebsocketConnectionAuthorizationHeader } from '@standardnotes/grpc'
|
import { ConnectionValidationResponse, IAuthClient, WebsocketConnectionAuthorizationHeader } from '@standardnotes/grpc'
|
||||||
import { RoleName } from '@standardnotes/domain-core'
|
import { RoleName } from '@standardnotes/domain-core'
|
||||||
|
import { ResponseLocals } from './ResponseLocals'
|
||||||
|
|
||||||
export class GRPCWebSocketAuthMiddleware extends BaseMiddleware {
|
export class GRPCWebSocketAuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -90,15 +91,16 @@ export class GRPCWebSocketAuthMiddleware extends BaseMiddleware {
|
||||||
|
|
||||||
const crossServiceToken = authResponse.data.authToken as string
|
const crossServiceToken = authResponse.data.authToken as string
|
||||||
|
|
||||||
response.locals.authToken = crossServiceToken
|
|
||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
response.locals.user = decodedToken.user
|
Object.assign(response.locals, {
|
||||||
response.locals.session = decodedToken.session
|
authToken: crossServiceToken,
|
||||||
response.locals.roles = decodedToken.roles
|
user: decodedToken.user,
|
||||||
response.locals.isFreeUser =
|
session: decodedToken.session,
|
||||||
decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser
|
roles: decodedToken.roles,
|
||||||
|
isFreeUser: decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser,
|
||||||
|
readOnlyAccess: decodedToken.session?.readonly_access ?? false,
|
||||||
|
} as ResponseLocals)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Could not pass the request to websocket connection validation on underlying service: ${
|
`Could not pass the request to websocket connection validation on underlying service: ${
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface OfflineResponseLocals {
|
||||||
|
offlineAuthToken: string
|
||||||
|
userEmail: string
|
||||||
|
featuresToken: string
|
||||||
|
}
|
29
packages/api-gateway/src/Controller/ResponseLocals.ts
Normal file
29
packages/api-gateway/src/Controller/ResponseLocals.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Role } from '@standardnotes/security'
|
||||||
|
|
||||||
|
export interface ResponseLocals {
|
||||||
|
authToken: string
|
||||||
|
user: {
|
||||||
|
uuid: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
roles: Array<Role>
|
||||||
|
session?: {
|
||||||
|
uuid: string
|
||||||
|
api_version: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
device_info: string
|
||||||
|
readonly_access: boolean
|
||||||
|
access_expiration: string
|
||||||
|
refresh_expiration: string
|
||||||
|
}
|
||||||
|
readOnlyAccess: boolean
|
||||||
|
isFreeUser: boolean
|
||||||
|
belongsToSharedVaults?: Array<{
|
||||||
|
shared_vault_uuid: string
|
||||||
|
permission: string
|
||||||
|
}>
|
||||||
|
sharedVaultOwnerContext?: {
|
||||||
|
upload_bytes_limit: number
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
||||||
|
|
||||||
|
export interface SubscriptionResponseLocals {
|
||||||
|
tokenAuthenticationMethod: TokenAuthenticationMethod
|
||||||
|
}
|
|
@ -7,6 +7,9 @@ import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { TYPES } from '../Bootstrap/Types'
|
import { TYPES } from '../Bootstrap/Types'
|
||||||
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
||||||
|
import { ResponseLocals } from './ResponseLocals'
|
||||||
|
import { OfflineResponseLocals } from './OfflineResponseLocals'
|
||||||
|
import { SubscriptionResponseLocals } from './SubscriptionResponseLocals'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||||
|
@ -34,13 +37,16 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.tokenAuthenticationMethod = email
|
const locals = {
|
||||||
? TokenAuthenticationMethod.OfflineSubscriptionToken
|
tokenAuthenticationMethod: email
|
||||||
: TokenAuthenticationMethod.SubscriptionToken
|
? TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||||
|
: TokenAuthenticationMethod.SubscriptionToken,
|
||||||
|
} as SubscriptionResponseLocals
|
||||||
|
Object.assign(response.locals, locals)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url =
|
const url =
|
||||||
response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken
|
locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||||
? `${this.authServerUrl}/offline/subscription-tokens/${subscriptionToken}/validate`
|
? `${this.authServerUrl}/offline/subscription-tokens/${subscriptionToken}/validate`
|
||||||
: `${this.authServerUrl}/subscription-tokens/${subscriptionToken}/validate`
|
: `${this.authServerUrl}/subscription-tokens/${subscriptionToken}/validate`
|
||||||
|
|
||||||
|
@ -65,7 +71,7 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
if (locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||||
this.handleOfflineAuthTokenValidationResponse(response, authResponse)
|
this.handleOfflineAuthTokenValidationResponse(response, authResponse)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
|
@ -101,24 +107,26 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleOfflineAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
private handleOfflineAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
||||||
response.locals.offlineAuthToken = authResponse.data.authToken
|
|
||||||
|
|
||||||
const decodedToken = <OfflineUserTokenData>(
|
const decodedToken = <OfflineUserTokenData>(
|
||||||
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
)
|
)
|
||||||
|
|
||||||
response.locals.offlineUserEmail = decodedToken.userEmail
|
Object.assign(response.locals, {
|
||||||
response.locals.offlineFeaturesToken = decodedToken.featuresToken
|
offlineAuthToken: authResponse.data.authToken,
|
||||||
|
userEmail: decodedToken.userEmail,
|
||||||
|
featuresToken: decodedToken.featuresToken,
|
||||||
|
} as OfflineResponseLocals)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
private handleAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
||||||
response.locals.authToken = authResponse.data.authToken
|
|
||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>(
|
const decodedToken = <CrossServiceTokenData>(
|
||||||
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
)
|
)
|
||||||
|
|
||||||
response.locals.user = decodedToken.user
|
Object.assign(response.locals, {
|
||||||
response.locals.roles = decodedToken.roles
|
authToken: authResponse.data.authToken,
|
||||||
|
user: decodedToken.user,
|
||||||
|
roles: decodedToken.roles,
|
||||||
|
} as ResponseLocals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { AxiosError, AxiosInstance } from 'axios'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
import { TYPES } from '../Bootstrap/Types'
|
import { TYPES } from '../Bootstrap/Types'
|
||||||
|
import { ResponseLocals } from './ResponseLocals'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||||
|
@ -55,13 +56,14 @@ export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||||
|
|
||||||
const crossServiceToken = authResponse.data.authToken
|
const crossServiceToken = authResponse.data.authToken
|
||||||
|
|
||||||
response.locals.authToken = crossServiceToken
|
|
||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
response.locals.user = decodedToken.user
|
Object.assign(response.locals, {
|
||||||
response.locals.session = decodedToken.session
|
authToken: crossServiceToken,
|
||||||
response.locals.roles = decodedToken.roles
|
user: decodedToken.user,
|
||||||
|
session: decodedToken.session,
|
||||||
|
roles: decodedToken.roles,
|
||||||
|
} as ResponseLocals)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = (error as AxiosError).isAxiosError
|
const errorMessage = (error as AxiosError).isAxiosError
|
||||||
? JSON.stringify((error as AxiosError).response?.data)
|
? JSON.stringify((error as AxiosError).response?.data)
|
||||||
|
|
|
@ -16,6 +16,8 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||||
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
|
||||||
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
|
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
|
||||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
import { SubscriptionResponseLocals } from '../SubscriptionResponseLocals'
|
||||||
|
|
||||||
@controller('/v1/users')
|
@controller('/v1/users')
|
||||||
export class UsersController extends BaseHttpController {
|
export class UsersController extends BaseHttpController {
|
||||||
|
@ -214,7 +216,9 @@ export class UsersController extends BaseHttpController {
|
||||||
|
|
||||||
@httpGet('/subscription', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
@httpGet('/subscription', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||||
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
|
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
|
||||||
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
const locals = response.locals as SubscriptionResponseLocals & ResponseLocals
|
||||||
|
|
||||||
|
if (locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
|
@ -227,11 +231,7 @@ export class UsersController extends BaseHttpController {
|
||||||
await this.httpService.callAuthServer(
|
await this.httpService.callAuthServer(
|
||||||
request,
|
request,
|
||||||
response,
|
response,
|
||||||
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'users/:userUuid/subscription', locals.user.uuid),
|
||||||
'GET',
|
|
||||||
'users/:userUuid/subscription',
|
|
||||||
response.locals.user.uuid,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Request, Response } from 'express'
|
||||||
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||||
|
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||||
|
|
||||||
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -134,11 +135,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||||
response: Response,
|
response: Response,
|
||||||
serviceResponse: { statusCode: number; json: Record<string, unknown> },
|
serviceResponse: { statusCode: number; json: Record<string, unknown> },
|
||||||
): void {
|
): void {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
void response.status(serviceResponse.statusCode).send({
|
void response.status(serviceResponse.statusCode).send({
|
||||||
meta: {
|
meta: {
|
||||||
auth: {
|
auth: {
|
||||||
userUuid: response.locals.user?.uuid,
|
userUuid: locals.user?.uuid,
|
||||||
roles: response.locals.roles,
|
roles: locals.roles,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
filesServerUrl: this.filesServerUrl,
|
filesServerUrl: this.filesServerUrl,
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||||
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
||||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
|
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||||
|
import { OfflineResponseLocals } from '../../Controller/OfflineResponseLocals'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class HttpServiceProxy implements ServiceProxyInterface {
|
export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
|
@ -176,6 +178,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<AxiosResponse | undefined> {
|
): Promise<AxiosResponse | undefined> {
|
||||||
|
const locals = response.locals as ResponseLocals | OfflineResponseLocals
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
for (const headerName of Object.keys(request.headers)) {
|
for (const headerName of Object.keys(request.headers)) {
|
||||||
|
@ -185,12 +189,12 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
delete headers.host
|
delete headers.host
|
||||||
delete headers['content-length']
|
delete headers['content-length']
|
||||||
|
|
||||||
if (response.locals.authToken) {
|
if ('authToken' in locals && locals.authToken) {
|
||||||
headers['X-Auth-Token'] = response.locals.authToken
|
headers['X-Auth-Token'] = locals.authToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.locals.offlineAuthToken) {
|
if ('offlineAuthToken' in locals && locals.offlineAuthToken) {
|
||||||
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
|
headers['X-Auth-Offline-Token'] = locals.offlineAuthToken
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceResponse = await this.httpClient.request({
|
const serviceResponse = await this.httpClient.request({
|
||||||
|
@ -222,7 +226,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
`Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
||||||
{
|
{
|
||||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
userId: (locals as ResponseLocals).user ? (locals as ResponseLocals).user.uuid : undefined,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -257,6 +261,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
||||||
|
|
||||||
if (!serviceResponse) {
|
if (!serviceResponse) {
|
||||||
|
@ -274,8 +279,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||||
response.status(serviceResponse.status).send({
|
response.status(serviceResponse.status).send({
|
||||||
meta: {
|
meta: {
|
||||||
auth: {
|
auth: {
|
||||||
userUuid: response.locals.user?.uuid,
|
userUuid: locals.user?.uuid,
|
||||||
roles: response.locals.roles,
|
roles: locals.roles,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
filesServerUrl: this.filesServerUrl,
|
filesServerUrl: this.filesServerUrl,
|
||||||
|
|
|
@ -40,7 +40,6 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||||
// Tokens Controller
|
// Tokens Controller
|
||||||
['[POST]:subscription-tokens', 'auth.subscription-tokens.create'],
|
['[POST]:subscription-tokens', 'auth.subscription-tokens.create'],
|
||||||
// Users Controller
|
// Users Controller
|
||||||
['[PATCH]:users/:userId', 'auth.users.update'],
|
|
||||||
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
||||||
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
||||||
['[POST]:listed', 'auth.users.createListedAccount'],
|
['[POST]:listed', 'auth.users.createListedAccount'],
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCache
|
||||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||||
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
|
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
|
||||||
import { Status } from '@grpc/grpc-js/build/src/constants'
|
import { Status } from '@grpc/grpc-js/build/src/constants'
|
||||||
|
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||||
|
import { OfflineResponseLocals } from '../../Controller/OfflineResponseLocals'
|
||||||
|
|
||||||
export class GRPCServiceProxy implements ServiceProxyInterface {
|
export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -135,13 +137,15 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
response: Response,
|
response: Response,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
||||||
|
|
||||||
response.status(result.status).send({
|
response.status(result.status).send({
|
||||||
meta: {
|
meta: {
|
||||||
auth: {
|
auth: {
|
||||||
userUuid: response.locals.user?.uuid,
|
userUuid: locals.user?.uuid,
|
||||||
roles: response.locals.roles,
|
roles: locals.roles,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
filesServerUrl: this.filesServerUrl,
|
filesServerUrl: this.filesServerUrl,
|
||||||
|
@ -250,6 +254,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
retryAttempt?: number,
|
retryAttempt?: number,
|
||||||
): Promise<AxiosResponse | undefined> {
|
): Promise<AxiosResponse | undefined> {
|
||||||
|
const locals = response.locals as ResponseLocals | OfflineResponseLocals
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
for (const headerName of Object.keys(request.headers)) {
|
for (const headerName of Object.keys(request.headers)) {
|
||||||
|
@ -259,12 +265,12 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
delete headers.host
|
delete headers.host
|
||||||
delete headers['content-length']
|
delete headers['content-length']
|
||||||
|
|
||||||
if (response.locals.authToken) {
|
if ('authToken' in locals && locals.authToken) {
|
||||||
headers['X-Auth-Token'] = response.locals.authToken
|
headers['X-Auth-Token'] = locals.authToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.locals.offlineAuthToken) {
|
if ('offlineAuthToken' in locals && locals.offlineAuthToken) {
|
||||||
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
|
headers['X-Auth-Offline-Token'] = locals.offlineAuthToken
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceResponse = await this.httpClient.request({
|
const serviceResponse = await this.httpClient.request({
|
||||||
|
@ -314,7 +320,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
|
? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
|
||||||
: `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
: `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
||||||
{
|
{
|
||||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
userId: (locals as ResponseLocals).user ? (locals as ResponseLocals).user.uuid : undefined,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -349,6 +355,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
||||||
|
|
||||||
if (!serviceResponse) {
|
if (!serviceResponse) {
|
||||||
|
@ -366,8 +374,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||||
response.status(serviceResponse.status).send({
|
response.status(serviceResponse.status).send({
|
||||||
meta: {
|
meta: {
|
||||||
auth: {
|
auth: {
|
||||||
userUuid: response.locals.user?.uuid,
|
userUuid: locals.user?.uuid,
|
||||||
roles: response.locals.roles,
|
roles: locals.roles,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
filesServerUrl: this.filesServerUrl,
|
filesServerUrl: this.filesServerUrl,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Metadata } from '@grpc/grpc-js'
|
||||||
import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
|
import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
|
||||||
import { Status } from '@grpc/grpc-js/build/src/constants'
|
import { Status } from '@grpc/grpc-js/build/src/constants'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||||
|
|
||||||
export class GRPCSyncingServerServiceProxy {
|
export class GRPCSyncingServerServiceProxy {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -20,24 +21,26 @@ export class GRPCSyncingServerServiceProxy {
|
||||||
response: Response,
|
response: Response,
|
||||||
payload?: Record<string, unknown> | string,
|
payload?: Record<string, unknown> | string,
|
||||||
): Promise<{ status: number; data: unknown }> {
|
): Promise<{ status: number; data: unknown }> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const syncRequest = this.syncRequestGRPCMapper.toProjection(payload as Record<string, unknown>)
|
const syncRequest = this.syncRequestGRPCMapper.toProjection(payload as Record<string, unknown>)
|
||||||
|
|
||||||
const metadata = new Metadata()
|
const metadata = new Metadata()
|
||||||
metadata.set('x-user-uuid', response.locals.user.uuid)
|
metadata.set('x-user-uuid', locals.user.uuid)
|
||||||
metadata.set('x-snjs-version', request.headers['x-snjs-version'] as string)
|
metadata.set('x-snjs-version', request.headers['x-snjs-version'] as string)
|
||||||
metadata.set('x-read-only-access', response.locals.readOnlyAccess ? 'true' : 'false')
|
metadata.set('x-read-only-access', locals.readOnlyAccess ? 'true' : 'false')
|
||||||
if (response.locals.readOnlyAccess) {
|
if (locals.readOnlyAccess) {
|
||||||
this.logger.debug('Syncing with read-only access', {
|
this.logger.debug('Syncing with read-only access', {
|
||||||
codeTag: 'GRPCSyncingServerServiceProxy',
|
codeTag: 'GRPCSyncingServerServiceProxy',
|
||||||
userId: response.locals.user.uuid,
|
userId: locals.user.uuid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (response.locals.session) {
|
if (locals.session) {
|
||||||
metadata.set('x-session-uuid', response.locals.session.uuid)
|
metadata.set('x-session-uuid', locals.session.uuid)
|
||||||
}
|
}
|
||||||
metadata.set('x-is-free-user', response.locals.isFreeUser ? 'true' : 'false')
|
metadata.set('x-is-free-user', locals.isFreeUser ? 'true' : 'false')
|
||||||
|
|
||||||
this.syncingClient.syncItems(syncRequest, metadata, (error, syncResponse) => {
|
this.syncingClient.syncItems(syncRequest, metadata, (error, syncResponse) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -52,7 +55,7 @@ export class GRPCSyncingServerServiceProxy {
|
||||||
if (error.code === Status.INTERNAL) {
|
if (error.code === Status.INTERNAL) {
|
||||||
this.logger.error(`Internal gRPC error: ${error.message}. Payload: ${JSON.stringify(payload)}`, {
|
this.logger.error(`Internal gRPC error: ${error.message}. Payload: ${JSON.stringify(payload)}`, {
|
||||||
codeTag: 'GRPCSyncingServerServiceProxy',
|
codeTag: 'GRPCSyncingServerServiceProxy',
|
||||||
userId: response.locals.user.uuid,
|
userId: locals.user.uuid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +71,7 @@ export class GRPCSyncingServerServiceProxy {
|
||||||
) {
|
) {
|
||||||
this.logger.error(`Internal gRPC error: ${JSON.stringify(error)}. Payload: ${JSON.stringify(payload)}`, {
|
this.logger.error(`Internal gRPC error: ${JSON.stringify(error)}. Payload: ${JSON.stringify(payload)}`, {
|
||||||
codeTag: 'GRPCSyncingServerServiceProxy.catch',
|
codeTag: 'GRPCSyncingServerServiceProxy.catch',
|
||||||
userId: response.locals.user.uuid,
|
userId: locals.user.uuid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { AuthService } from '@standardnotes/grpc'
|
||||||
import { AuthenticateRequest } from '../src/Domain/UseCase/AuthenticateRequest'
|
import { AuthenticateRequest } from '../src/Domain/UseCase/AuthenticateRequest'
|
||||||
import { CreateCrossServiceToken } from '../src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
|
import { CreateCrossServiceToken } from '../src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
|
||||||
import { TokenDecoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security'
|
import { TokenDecoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security'
|
||||||
|
import { ResponseLocals } from '../src/Infra/InversifyExpressUtils/ResponseLocals'
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader()
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
|
@ -59,12 +60,13 @@ void container.load().then((container) => {
|
||||||
|
|
||||||
server.setErrorConfig((app) => {
|
server.setErrorConfig((app) => {
|
||||||
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
logger.error(`${error.stack}`, {
|
logger.error(`${error.stack}`, {
|
||||||
method: request.method,
|
method: request.method,
|
||||||
url: request.url,
|
url: request.url,
|
||||||
snjs: request.headers['x-snjs-version'],
|
snjs: request.headers['x-snjs-version'],
|
||||||
application: request.headers['x-application-version'],
|
application: request.headers['x-application-version'],
|
||||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
userId: locals.user ? locals.user.uuid : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
response.status(500).send({
|
response.status(500).send({
|
||||||
|
|
|
@ -1708,7 +1708,6 @@ export class ContainerConfigLoader {
|
||||||
.bind<BaseUsersController>(TYPES.Auth_BaseUsersController)
|
.bind<BaseUsersController>(TYPES.Auth_BaseUsersController)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
new BaseUsersController(
|
new BaseUsersController(
|
||||||
container.get<UpdateUser>(TYPES.Auth_UpdateUser),
|
|
||||||
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
||||||
container.get<GetUserSubscription>(TYPES.Auth_GetUserSubscription),
|
container.get<GetUserSubscription>(TYPES.Auth_GetUserSubscription),
|
||||||
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
||||||
|
|
|
@ -4,14 +4,12 @@ import {
|
||||||
controller,
|
controller,
|
||||||
httpDelete,
|
httpDelete,
|
||||||
httpGet,
|
httpGet,
|
||||||
httpPatch,
|
|
||||||
httpPut,
|
httpPut,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
results,
|
results,
|
||||||
} from 'inversify-express-utils'
|
} from 'inversify-express-utils'
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import TYPES from '../../Bootstrap/Types'
|
||||||
import { DeleteAccount } from '../../Domain/UseCase/DeleteAccount/DeleteAccount'
|
import { DeleteAccount } from '../../Domain/UseCase/DeleteAccount/DeleteAccount'
|
||||||
import { UpdateUser } from '../../Domain/UseCase/UpdateUser'
|
|
||||||
import { GetUserSubscription } from '../../Domain/UseCase/GetUserSubscription/GetUserSubscription'
|
import { GetUserSubscription } from '../../Domain/UseCase/GetUserSubscription/GetUserSubscription'
|
||||||
import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts'
|
import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts'
|
||||||
import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempts'
|
import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempts'
|
||||||
|
@ -21,26 +19,13 @@ import { BaseUsersController } from './Base/BaseUsersController'
|
||||||
@controller('/users')
|
@controller('/users')
|
||||||
export class AnnotatedUsersController extends BaseUsersController {
|
export class AnnotatedUsersController extends BaseUsersController {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(TYPES.Auth_UpdateUser) override updateUser: UpdateUser,
|
|
||||||
@inject(TYPES.Auth_DeleteAccount) override doDeleteAccount: DeleteAccount,
|
@inject(TYPES.Auth_DeleteAccount) override doDeleteAccount: DeleteAccount,
|
||||||
@inject(TYPES.Auth_GetUserSubscription) override doGetUserSubscription: GetUserSubscription,
|
@inject(TYPES.Auth_GetUserSubscription) override doGetUserSubscription: GetUserSubscription,
|
||||||
@inject(TYPES.Auth_ClearLoginAttempts) override clearLoginAttempts: ClearLoginAttempts,
|
@inject(TYPES.Auth_ClearLoginAttempts) override clearLoginAttempts: ClearLoginAttempts,
|
||||||
@inject(TYPES.Auth_IncreaseLoginAttempts) override increaseLoginAttempts: IncreaseLoginAttempts,
|
@inject(TYPES.Auth_IncreaseLoginAttempts) override increaseLoginAttempts: IncreaseLoginAttempts,
|
||||||
@inject(TYPES.Auth_ChangeCredentials) override changeCredentialsUseCase: ChangeCredentials,
|
@inject(TYPES.Auth_ChangeCredentials) override changeCredentialsUseCase: ChangeCredentials,
|
||||||
) {
|
) {
|
||||||
super(
|
super(doDeleteAccount, doGetUserSubscription, clearLoginAttempts, increaseLoginAttempts, changeCredentialsUseCase)
|
||||||
updateUser,
|
|
||||||
doDeleteAccount,
|
|
||||||
doGetUserSubscription,
|
|
||||||
clearLoginAttempts,
|
|
||||||
increaseLoginAttempts,
|
|
||||||
changeCredentialsUseCase,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@httpPatch('/:userId', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
|
||||||
override async update(request: Request, response: Response): Promise<results.JsonResult> {
|
|
||||||
return super.update(request, response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
@httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { IncreaseLoginAttempts } from '../../../Domain/UseCase/IncreaseLoginAtte
|
||||||
import { SignIn } from '../../../Domain/UseCase/SignIn'
|
import { SignIn } from '../../../Domain/UseCase/SignIn'
|
||||||
import { VerifyMFA } from '../../../Domain/UseCase/VerifyMFA'
|
import { VerifyMFA } from '../../../Domain/UseCase/VerifyMFA'
|
||||||
import { AuthController } from '../../../Controller/AuthController'
|
import { AuthController } from '../../../Controller/AuthController'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
|
|
||||||
export class BaseAuthController extends BaseHttpController {
|
export class BaseAuthController extends BaseHttpController {
|
||||||
|
@ -37,9 +38,11 @@ export class BaseAuthController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async params(request: Request, response: Response): Promise<results.JsonResult> {
|
async params(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.session) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.session) {
|
||||||
const result = await this.getUserKeyParams.execute({
|
const result = await this.getUserKeyParams.execute({
|
||||||
email: response.locals.user.email,
|
email: locals.user.email,
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -145,6 +148,8 @@ export class BaseAuthController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async pkceParams(request: Request, response: Response): Promise<results.JsonResult> {
|
async pkceParams(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
if (!request.body.code_challenge) {
|
if (!request.body.code_challenge) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
|
@ -156,9 +161,9 @@ export class BaseAuthController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.locals.session) {
|
if (locals.session) {
|
||||||
const result = await this.getUserKeyParams.execute({
|
const result = await this.getUserKeyParams.execute({
|
||||||
email: response.locals.user.email,
|
email: locals.user.email,
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
codeChallenge: request.body.code_challenge as string,
|
codeChallenge: request.body.code_challenge as string,
|
||||||
})
|
})
|
||||||
|
@ -248,8 +253,10 @@ export class BaseAuthController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateRecoveryCodes(_request: Request, response: Response): Promise<results.JsonResult> {
|
async generateRecoveryCodes(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authController.generateRecoveryCodes({
|
const result = await this.authController.generateRecoveryCodes({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
|
@ -280,8 +287,10 @@ export class BaseAuthController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async signOut(request: Request, response: Response): Promise<results.JsonResult | void> {
|
async signOut(request: Request, response: Response): Promise<results.JsonResult | void> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authController.signOut({
|
const result = await this.authController.signOut({
|
||||||
readOnlyAccess: response.locals.readOnlyAccess,
|
readOnlyAccess: locals.readOnlyAccess,
|
||||||
authorizationHeader: <string>request.headers.authorization,
|
authorizationHeader: <string>request.headers.authorization,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Request, Response } from 'express'
|
||||||
|
|
||||||
import { AuthenticatorsController } from '../../../Controller/AuthenticatorsController'
|
import { AuthenticatorsController } from '../../../Controller/AuthenticatorsController'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseAuthenticatorsController extends BaseHttpController {
|
export class BaseAuthenticatorsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -30,16 +31,20 @@ export class BaseAuthenticatorsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async list(_request: Request, response: Response): Promise<results.JsonResult> {
|
async list(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authenticatorsController.list({
|
const result = await this.authenticatorsController.list({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(request: Request, response: Response): Promise<results.JsonResult> {
|
async delete(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authenticatorsController.delete({
|
const result = await this.authenticatorsController.delete({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
authenticatorId: request.params.authenticatorId,
|
authenticatorId: request.params.authenticatorId,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -47,17 +52,21 @@ export class BaseAuthenticatorsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateRegistrationOptions(_request: Request, response: Response): Promise<results.JsonResult> {
|
async generateRegistrationOptions(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authenticatorsController.generateRegistrationOptions({
|
const result = await this.authenticatorsController.generateRegistrationOptions({
|
||||||
username: response.locals.user.email,
|
username: locals.user.email,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyRegistration(request: Request, response: Response): Promise<results.JsonResult> {
|
async verifyRegistration(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.authenticatorsController.verifyRegistrationResponse({
|
const result = await this.authenticatorsController.verifyRegistrationResponse({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
attestationResponse: request.body.attestationResponse,
|
attestationResponse: request.body.attestationResponse,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Request, Response } from 'express'
|
||||||
|
|
||||||
import { GetUserFeatures } from '../../../Domain/UseCase/GetUserFeatures/GetUserFeatures'
|
import { GetUserFeatures } from '../../../Domain/UseCase/GetUserFeatures/GetUserFeatures'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseFeaturesController extends BaseHttpController {
|
export class BaseFeaturesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -17,7 +18,9 @@ export class BaseFeaturesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFeatures(request: Request, response: Response): Promise<results.JsonResult> {
|
async getFeatures(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Request, Response } from 'express'
|
||||||
|
|
||||||
import { CreateListedAccount } from '../../../Domain/UseCase/CreateListedAccount/CreateListedAccount'
|
import { CreateListedAccount } from '../../../Domain/UseCase/CreateListedAccount/CreateListedAccount'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseListedController extends BaseHttpController {
|
export class BaseListedController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -18,7 +19,9 @@ export class BaseListedController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createListedAccount(_request: Request, response: Response): Promise<results.JsonResult> {
|
async createListedAccount(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -31,8 +34,8 @@ export class BaseListedController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.doCreateListedAccount.execute({
|
await this.doCreateListedAccount.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
userEmail: response.locals.user.email,
|
userEmail: locals.user.email,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json({
|
return this.json({
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { AuthenticateOfflineSubscriptionToken } from '../../../Domain/UseCase/Au
|
||||||
import { CreateOfflineSubscriptionToken } from '../../../Domain/UseCase/CreateOfflineSubscriptionToken/CreateOfflineSubscriptionToken'
|
import { CreateOfflineSubscriptionToken } from '../../../Domain/UseCase/CreateOfflineSubscriptionToken/CreateOfflineSubscriptionToken'
|
||||||
import { GetUserFeatures } from '../../../Domain/UseCase/GetUserFeatures/GetUserFeatures'
|
import { GetUserFeatures } from '../../../Domain/UseCase/GetUserFeatures/GetUserFeatures'
|
||||||
import { GetUserOfflineSubscription } from '../../../Domain/UseCase/GetUserOfflineSubscription/GetUserOfflineSubscription'
|
import { GetUserOfflineSubscription } from '../../../Domain/UseCase/GetUserOfflineSubscription/GetUserOfflineSubscription'
|
||||||
|
import { OfflineResponseLocals } from '../OfflineResponseLocals'
|
||||||
|
|
||||||
export class BaseOfflineController extends BaseHttpController {
|
export class BaseOfflineController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -30,8 +31,10 @@ export class BaseOfflineController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOfflineFeatures(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getOfflineFeatures(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as OfflineResponseLocals
|
||||||
|
|
||||||
const result = await this.doGetUserFeatures.execute({
|
const result = await this.doGetUserFeatures.execute({
|
||||||
email: response.locals.offlineUserEmail,
|
email: locals.userEmail,
|
||||||
offline: true,
|
offline: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -115,8 +118,10 @@ export class BaseOfflineController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscription(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getSubscription(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as OfflineResponseLocals
|
||||||
|
|
||||||
const result = await this.getUserOfflineSubscription.execute({
|
const result = await this.getUserOfflineSubscription.execute({
|
||||||
userEmail: response.locals.userEmail,
|
userEmail: locals.userEmail,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ErrorTag } from '@standardnotes/responses'
|
||||||
import { DeleteOtherSessionsForUser } from '../../../Domain/UseCase/DeleteOtherSessionsForUser'
|
import { DeleteOtherSessionsForUser } from '../../../Domain/UseCase/DeleteOtherSessionsForUser'
|
||||||
import { DeleteSessionForUser } from '../../../Domain/UseCase/DeleteSessionForUser'
|
import { DeleteSessionForUser } from '../../../Domain/UseCase/DeleteSessionForUser'
|
||||||
import { RefreshSessionToken } from '../../../Domain/UseCase/RefreshSessionToken'
|
import { RefreshSessionToken } from '../../../Domain/UseCase/RefreshSessionToken'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSessionController extends BaseHttpController {
|
export class BaseSessionController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -24,7 +25,9 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSession(request: Request, response: Response): Promise<results.JsonResult | results.StatusCodeResult> {
|
async deleteSession(request: Request, response: Response): Promise<results.JsonResult | results.StatusCodeResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -36,7 +39,7 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.body.uuid) {
|
if (!request.body.uuid || !locals.session) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -47,7 +50,7 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.body.uuid === response.locals.session.uuid) {
|
if (request.body.uuid === locals.session.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -59,7 +62,7 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCaseResponse = await this.deleteSessionForUser.execute({
|
const useCaseResponse = await this.deleteSessionForUser.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
sessionUuid: request.body.uuid,
|
sessionUuid: request.body.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.statusCode(204)
|
return this.statusCode(204)
|
||||||
}
|
}
|
||||||
|
@ -83,7 +86,9 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
_request: Request,
|
_request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
): Promise<results.JsonResult | results.StatusCodeResult> {
|
): Promise<results.JsonResult | results.StatusCodeResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -95,7 +100,7 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.locals.user) {
|
if (!locals.user || !locals.session) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -107,12 +112,12 @@ export class BaseSessionController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.deleteOtherSessionsForUser.execute({
|
await this.deleteOtherSessionsForUser.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
currentSessionUuid: response.locals.session.uuid,
|
currentSessionUuid: locals.session.uuid,
|
||||||
markAsRevoked: true,
|
markAsRevoked: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.statusCode(204)
|
return this.statusCode(204)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { Session } from '../../../Domain/Session/Session'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
import { User } from '../../../Domain/User/User'
|
import { User } from '../../../Domain/User/User'
|
||||||
import { SessionProjector } from '../../../Projection/SessionProjector'
|
import { SessionProjector } from '../../../Projection/SessionProjector'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSessionsController extends BaseHttpController {
|
export class BaseSessionsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -67,12 +68,14 @@ export class BaseSessionsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSessions(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getSessions(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json([])
|
return this.json([])
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCaseResponse = await this.getActiveSessionsForUser.execute({
|
const useCaseResponse = await this.getActiveSessionsForUser.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(
|
return this.json(
|
||||||
|
@ -80,7 +83,7 @@ export class BaseSessionsController extends BaseHttpController {
|
||||||
this.sessionProjector.projectCustom(
|
this.sessionProjector.projectCustom(
|
||||||
SessionProjector.CURRENT_SESSION_PROJECTION.toString(),
|
SessionProjector.CURRENT_SESSION_PROJECTION.toString(),
|
||||||
session,
|
session,
|
||||||
response.locals.session,
|
locals.session,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { SubscriptionSetting } from '../../../Domain/Setting/SubscriptionSetting
|
||||||
import { SubscriptionSettingHttpRepresentation } from '../../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
import { SubscriptionSettingHttpRepresentation } from '../../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
||||||
import { SettingHttpRepresentation } from '../../../Mapping/Http/SettingHttpRepresentation'
|
import { SettingHttpRepresentation } from '../../../Mapping/Http/SettingHttpRepresentation'
|
||||||
import { TriggerPostSettingUpdateActions } from '../../../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
import { TriggerPostSettingUpdateActions } from '../../../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSettingsController extends BaseHttpController {
|
export class BaseSettingsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -40,7 +41,9 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSettings(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSettings(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -86,7 +89,9 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -135,7 +140,9 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSetting(request: Request, response: Response): Promise<results.JsonResult | results.StatusCodeResult> {
|
async updateSetting(request: Request, response: Response): Promise<results.JsonResult | results.StatusCodeResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -147,7 +154,7 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -163,7 +170,7 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
const result = await this.setSettingValue.execute({
|
const result = await this.setSettingValue.execute({
|
||||||
settingName: name,
|
settingName: name,
|
||||||
value,
|
value,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
checkUserPermissions: true,
|
checkUserPermissions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -181,8 +188,8 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
|
|
||||||
const triggerResult = await this.triggerPostSettingUpdateActions.execute({
|
const triggerResult = await this.triggerPostSettingUpdateActions.execute({
|
||||||
updatedSettingName: setting.props.name,
|
updatedSettingName: setting.props.name,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
userEmail: response.locals.user.email,
|
userEmail: locals.user.email,
|
||||||
unencryptedValue: value,
|
unencryptedValue: value,
|
||||||
})
|
})
|
||||||
if (triggerResult.isFailed()) {
|
if (triggerResult.isFailed()) {
|
||||||
|
@ -196,7 +203,9 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -208,7 +217,7 @@ export class BaseSettingsController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
import { ApiVersion } from '@standardnotes/api'
|
import { ApiVersion } from '@standardnotes/api'
|
||||||
|
|
||||||
import { SubscriptionInvitesController } from '../../../Controller/SubscriptionInvitesController'
|
import { SubscriptionInvitesController } from '../../../Controller/SubscriptionInvitesController'
|
||||||
import { Role } from '../../../Domain/Role/Role'
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
import { Role } from '@standardnotes/security'
|
||||||
|
|
||||||
export class BaseSubscriptionInvitesController extends BaseHttpController {
|
export class BaseSubscriptionInvitesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -23,12 +24,14 @@ export class BaseSubscriptionInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async acceptInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async acceptInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.subscriptionInvitesController.acceptInvite({
|
const result = await this.subscriptionInvitesController.acceptInvite({
|
||||||
api: request.query.api as ApiVersion,
|
api: request.query.api as ApiVersion,
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
}
|
}
|
||||||
|
@ -43,30 +46,36 @@ export class BaseSubscriptionInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<results.JsonResult> {
|
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.subscriptionInvitesController.invite({
|
const result = await this.subscriptionInvitesController.invite({
|
||||||
...request.body,
|
...request.body,
|
||||||
inviterEmail: response.locals.user.email,
|
inviterEmail: locals.user.email,
|
||||||
inviterUuid: response.locals.user.uuid,
|
inviterUuid: locals.user.uuid,
|
||||||
inviterRoles: response.locals.roles.map((role: Role) => role.name),
|
inviterRoles: locals.roles.map((role: Role) => role.name),
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelSubscriptionSharing(request: Request, response: Response): Promise<results.JsonResult> {
|
async cancelSubscriptionSharing(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.subscriptionInvitesController.cancelInvite({
|
const result = await this.subscriptionInvitesController.cancelInvite({
|
||||||
...request.body,
|
...request.body,
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
inviterEmail: response.locals.user.email,
|
inviterEmail: locals.user.email,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
async listInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.subscriptionInvitesController.listInvites({
|
const result = await this.subscriptionInvitesController.listInvites({
|
||||||
...request.body,
|
...request.body,
|
||||||
inviterEmail: response.locals.user.email,
|
inviterEmail: locals.user.email,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { GetSubscriptionSetting } from '../../../Domain/UseCase/GetSubscriptionS
|
||||||
import { GetSharedOrRegularSubscriptionForUser } from '../../../Domain/UseCase/GetSharedOrRegularSubscriptionForUser/GetSharedOrRegularSubscriptionForUser'
|
import { GetSharedOrRegularSubscriptionForUser } from '../../../Domain/UseCase/GetSharedOrRegularSubscriptionForUser/GetSharedOrRegularSubscriptionForUser'
|
||||||
import { SubscriptionSetting } from '../../../Domain/Setting/SubscriptionSetting'
|
import { SubscriptionSetting } from '../../../Domain/Setting/SubscriptionSetting'
|
||||||
import { SubscriptionSettingHttpRepresentation } from '../../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
import { SubscriptionSettingHttpRepresentation } from '../../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSubscriptionSettingsController extends BaseHttpController {
|
export class BaseSubscriptionSettingsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -22,8 +23,10 @@ export class BaseSubscriptionSettingsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSubscriptionSetting(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const subscriptionOrError = await this.getSharedOrRegularSubscription.execute({
|
const subscriptionOrError = await this.getSharedOrRegularSubscription.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
if (subscriptionOrError.isFailed()) {
|
if (subscriptionOrError.isFailed()) {
|
||||||
return this.json(
|
return this.json(
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { CreateSubscriptionToken } from '../../../Domain/UseCase/CreateSubscript
|
||||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||||
import { User } from '../../../Domain/User/User'
|
import { User } from '../../../Domain/User/User'
|
||||||
import { GetSetting } from '../../../Domain/UseCase/GetSetting/GetSetting'
|
import { GetSetting } from '../../../Domain/UseCase/GetSetting/GetSetting'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSubscriptionTokensController extends BaseHttpController {
|
export class BaseSubscriptionTokensController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -29,7 +30,9 @@ export class BaseSubscriptionTokensController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createToken(_request: Request, response: Response): Promise<results.JsonResult> {
|
async createToken(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -42,7 +45,7 @@ export class BaseSubscriptionTokensController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.createSubscriptionToken.execute({
|
const result = await this.createSubscriptionToken.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json({
|
return this.json({
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { BaseHttpController, results } from 'inversify-express-utils'
|
||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
|
|
||||||
import { UserRequestsController } from '../../../Controller/UserRequestsController'
|
import { UserRequestsController } from '../../../Controller/UserRequestsController'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseUserRequestsController extends BaseHttpController {
|
export class BaseUserRequestsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -17,10 +18,12 @@ export class BaseUserRequestsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitRequest(request: Request, response: Response): Promise<results.JsonResult> {
|
async submitRequest(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.userRequestsController.submitUserRequest({
|
const result = await this.userRequestsController.submitUserRequest({
|
||||||
requestType: request.body.requestType,
|
requestType: request.body.requestType,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
userEmail: response.locals.user.email,
|
userEmail: locals.user.email,
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.json(result.data, result.status)
|
return this.json(result.data, result.status)
|
||||||
|
|
|
@ -7,12 +7,11 @@ import { ClearLoginAttempts } from '../../../Domain/UseCase/ClearLoginAttempts'
|
||||||
import { DeleteAccount } from '../../../Domain/UseCase/DeleteAccount/DeleteAccount'
|
import { DeleteAccount } from '../../../Domain/UseCase/DeleteAccount/DeleteAccount'
|
||||||
import { GetUserSubscription } from '../../../Domain/UseCase/GetUserSubscription/GetUserSubscription'
|
import { GetUserSubscription } from '../../../Domain/UseCase/GetUserSubscription/GetUserSubscription'
|
||||||
import { IncreaseLoginAttempts } from '../../../Domain/UseCase/IncreaseLoginAttempts'
|
import { IncreaseLoginAttempts } from '../../../Domain/UseCase/IncreaseLoginAttempts'
|
||||||
import { UpdateUser } from '../../../Domain/UseCase/UpdateUser'
|
|
||||||
import { ErrorTag } from '@standardnotes/responses'
|
import { ErrorTag } from '@standardnotes/responses'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseUsersController extends BaseHttpController {
|
export class BaseUsersController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
protected updateUser: UpdateUser,
|
|
||||||
protected doDeleteAccount: DeleteAccount,
|
protected doDeleteAccount: DeleteAccount,
|
||||||
protected doGetUserSubscription: GetUserSubscription,
|
protected doGetUserSubscription: GetUserSubscription,
|
||||||
protected clearLoginAttempts: ClearLoginAttempts,
|
protected clearLoginAttempts: ClearLoginAttempts,
|
||||||
|
@ -23,61 +22,16 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
if (this.controllerContainer !== undefined) {
|
if (this.controllerContainer !== undefined) {
|
||||||
this.controllerContainer.register('auth.users.update', this.update.bind(this))
|
|
||||||
this.controllerContainer.register('auth.users.getSubscription', this.getSubscription.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.updateCredentials', this.changeCredentials.bind(this))
|
||||||
this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this))
|
this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(request: Request, response: Response): Promise<results.JsonResult> {
|
|
||||||
if (response.locals.readOnlyAccess) {
|
|
||||||
return this.json(
|
|
||||||
{
|
|
||||||
error: {
|
|
||||||
tag: ErrorTag.ReadOnlyAccess,
|
|
||||||
message: 'Session has read-only access.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
401,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.params.userId !== response.locals.user.uuid) {
|
|
||||||
return this.json(
|
|
||||||
{
|
|
||||||
error: {
|
|
||||||
message: 'Operation not allowed.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
401,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateResult = await this.updateUser.execute({
|
|
||||||
user: response.locals.user,
|
|
||||||
updatedWithUserAgent: <string>request.headers['user-agent'],
|
|
||||||
apiVersion: request.body.api,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (updateResult.success) {
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
|
||||||
|
|
||||||
return this.json(updateResult.authResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.json(
|
|
||||||
{
|
|
||||||
error: {
|
|
||||||
message: 'Could not update user.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
400,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -107,7 +61,9 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscription(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSubscription(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (request.params.userUuid !== locals.user.uuid) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -130,7 +86,9 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async changeCredentials(request: Request, response: Response): Promise<results.JsonResult> {
|
async changeCredentials(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
if (response.locals.readOnlyAccess) {
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
|
if (locals.readOnlyAccess) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -175,7 +133,7 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
400,
|
400,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const usernameOrError = Username.create(response.locals.user.email)
|
const usernameOrError = Username.create(locals.user.email)
|
||||||
if (usernameOrError.isFailed()) {
|
if (usernameOrError.isFailed()) {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
|
@ -202,7 +160,7 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (changeCredentialsResult.isFailed()) {
|
if (changeCredentialsResult.isFailed()) {
|
||||||
await this.increaseLoginAttempts.execute({ email: response.locals.user.email })
|
await this.increaseLoginAttempts.execute({ email: locals.user.email })
|
||||||
|
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
|
@ -214,9 +172,9 @@ export class BaseUsersController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.clearLoginAttempts.execute({ email: response.locals.user.email })
|
await this.clearLoginAttempts.execute({ email: locals.user.email })
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.json(changeCredentialsResult.getValue())
|
return this.json(changeCredentialsResult.getValue())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ValetTokenOperation } from '@standardnotes/security'
|
||||||
|
|
||||||
import { CreateValetToken } from '../../../Domain/UseCase/CreateValetToken/CreateValetToken'
|
import { CreateValetToken } from '../../../Domain/UseCase/CreateValetToken/CreateValetToken'
|
||||||
import { CreateValetTokenPayload } from '../../../Domain/ValetToken/CreateValetTokenPayload'
|
import { CreateValetTokenPayload } from '../../../Domain/ValetToken/CreateValetTokenPayload'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseValetTokenController extends BaseHttpController {
|
export class BaseValetTokenController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -20,9 +21,11 @@ export class BaseValetTokenController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async create(request: Request, response: Response): Promise<results.JsonResult> {
|
public async create(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const payload: CreateValetTokenPayload = request.body
|
const payload: CreateValetTokenPayload = request.body
|
||||||
|
|
||||||
if (response.locals.readOnlyAccess && payload.operation !== 'read') {
|
if (locals.readOnlyAccess && payload.operation !== 'read') {
|
||||||
return this.json(
|
return this.json(
|
||||||
{
|
{
|
||||||
error: {
|
error: {
|
||||||
|
@ -50,7 +53,7 @@ export class BaseValetTokenController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const createValetKeyResponse = await this.createValetKey.execute({
|
const createValetKeyResponse = await this.createValetKey.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
operation: payload.operation as ValetTokenOperation,
|
operation: payload.operation as ValetTokenOperation,
|
||||||
resources: payload.resources,
|
resources: payload.resources,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/sec
|
||||||
import { NextFunction, Request, Response } from 'express'
|
import { NextFunction, Request, Response } from 'express'
|
||||||
import { BaseMiddleware } from 'inversify-express-utils'
|
import { BaseMiddleware } from 'inversify-express-utils'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
|
export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -34,10 +35,12 @@ export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.user = token.user
|
Object.assign(response.locals, {
|
||||||
response.locals.roles = token.roles
|
user: token.user,
|
||||||
response.locals.session = token.session
|
roles: token.roles,
|
||||||
response.locals.readOnlyAccess = token.session?.readonly_access ?? false
|
session: token.session,
|
||||||
|
readOnlyAccess: token.session?.readonly_access ?? false,
|
||||||
|
} as ResponseLocals)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { inject, injectable } from 'inversify'
|
||||||
import { BaseMiddleware } from 'inversify-express-utils'
|
import { BaseMiddleware } from 'inversify-express-utils'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
|
import { OfflineResponseLocals } from '../OfflineResponseLocals'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class ApiGatewayOfflineAuthMiddleware extends BaseMiddleware {
|
export class ApiGatewayOfflineAuthMiddleware extends BaseMiddleware {
|
||||||
|
@ -48,8 +49,10 @@ export class ApiGatewayOfflineAuthMiddleware extends BaseMiddleware {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.featuresToken = token.featuresToken
|
Object.assign(response.locals, {
|
||||||
response.locals.userEmail = token.userEmail
|
featuresToken: token.featuresToken,
|
||||||
|
userEmail: token.userEmail,
|
||||||
|
} as OfflineResponseLocals)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -44,8 +44,8 @@ describe('OfflineUserAuthMiddleware', () => {
|
||||||
|
|
||||||
await createMiddleware().handler(request, response, next)
|
await createMiddleware().handler(request, response, next)
|
||||||
|
|
||||||
expect(response.locals.offlineUserEmail).toEqual('test@test.com')
|
expect(response.locals.userEmail).toEqual('test@test.com')
|
||||||
expect(response.locals.offlineFeaturesToken).toEqual('offline-features-token')
|
expect(response.locals.featuresToken).toEqual('offline-features-token')
|
||||||
|
|
||||||
expect(next).toHaveBeenCalled()
|
expect(next).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Logger } from 'winston'
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
import { OfflineSettingName } from '../../../Domain/Setting/OfflineSettingName'
|
import { OfflineSettingName } from '../../../Domain/Setting/OfflineSettingName'
|
||||||
import { OfflineSettingRepositoryInterface } from '../../../Domain/Setting/OfflineSettingRepositoryInterface'
|
import { OfflineSettingRepositoryInterface } from '../../../Domain/Setting/OfflineSettingRepositoryInterface'
|
||||||
|
import { OfflineResponseLocals } from '../OfflineResponseLocals'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class OfflineUserAuthMiddleware extends BaseMiddleware {
|
export class OfflineUserAuthMiddleware extends BaseMiddleware {
|
||||||
|
@ -47,8 +48,10 @@ export class OfflineUserAuthMiddleware extends BaseMiddleware {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.locals.offlineUserEmail = offlineFeaturesTokenSetting.email
|
Object.assign(response.locals, {
|
||||||
response.locals.offlineFeaturesToken = offlineFeaturesTokenSetting.value
|
featuresToken: offlineFeaturesTokenSetting.value,
|
||||||
|
userEmail: offlineFeaturesTokenSetting.email,
|
||||||
|
} as OfflineResponseLocals)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface OfflineResponseLocals {
|
||||||
|
userEmail: string
|
||||||
|
featuresToken: string
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Role } from '@standardnotes/security'
|
||||||
|
|
||||||
|
export interface ResponseLocals {
|
||||||
|
user: {
|
||||||
|
uuid: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
roles: Array<Role>
|
||||||
|
session?: {
|
||||||
|
uuid: string
|
||||||
|
api_version: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
device_info: string
|
||||||
|
readonly_access: boolean
|
||||||
|
access_expiration: string
|
||||||
|
refresh_expiration: string
|
||||||
|
}
|
||||||
|
readOnlyAccess: boolean
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import { ItemHash } from '../../../Domain/Item/ItemHash'
|
||||||
import { CheckForTrafficAbuse } from '../../../Domain/UseCase/Syncing/CheckForTrafficAbuse/CheckForTrafficAbuse'
|
import { CheckForTrafficAbuse } from '../../../Domain/UseCase/Syncing/CheckForTrafficAbuse/CheckForTrafficAbuse'
|
||||||
import { Metric } from '../../../Domain/Metrics/Metric'
|
import { Metric } from '../../../Domain/Metrics/Metric'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseItemsController extends BaseHttpController {
|
export class BaseItemsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -41,15 +42,16 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sync(request: Request, response: Response): Promise<results.JsonResult> {
|
async sync(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
const checkForItemOperationsAbuseResult = await this.checkForTrafficAbuse.execute({
|
const checkForItemOperationsAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||||
metricToCheck: Metric.NAMES.ItemOperation,
|
metricToCheck: Metric.NAMES.ItemOperation,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
threshold: this.itemOperationsAbuseThreshold,
|
threshold: this.itemOperationsAbuseThreshold,
|
||||||
timeframeLengthInMinutes: this.itemOperationsAbuseTimeframeLengthInMinutes,
|
timeframeLengthInMinutes: this.itemOperationsAbuseTimeframeLengthInMinutes,
|
||||||
})
|
})
|
||||||
if (checkForItemOperationsAbuseResult.isFailed()) {
|
if (checkForItemOperationsAbuseResult.isFailed()) {
|
||||||
this.logger.warn(checkForItemOperationsAbuseResult.getError(), {
|
this.logger.warn(checkForItemOperationsAbuseResult.getError(), {
|
||||||
userId: response.locals.user.uuid,
|
userId: locals.user.uuid,
|
||||||
})
|
})
|
||||||
if (this.strictAbuseProtection) {
|
if (this.strictAbuseProtection) {
|
||||||
return this.json({ error: { message: checkForItemOperationsAbuseResult.getError() } }, 429)
|
return this.json({ error: { message: checkForItemOperationsAbuseResult.getError() } }, 429)
|
||||||
|
@ -58,13 +60,13 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
|
|
||||||
const checkForPayloadSizeAbuseResult = await this.checkForTrafficAbuse.execute({
|
const checkForPayloadSizeAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||||
metricToCheck: Metric.NAMES.ContentSizeUtilized,
|
metricToCheck: Metric.NAMES.ContentSizeUtilized,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
threshold: this.payloadSizeAbuseThreshold,
|
threshold: this.payloadSizeAbuseThreshold,
|
||||||
timeframeLengthInMinutes: this.payloadSizeAbuseTimeframeLengthInMinutes,
|
timeframeLengthInMinutes: this.payloadSizeAbuseTimeframeLengthInMinutes,
|
||||||
})
|
})
|
||||||
if (checkForPayloadSizeAbuseResult.isFailed()) {
|
if (checkForPayloadSizeAbuseResult.isFailed()) {
|
||||||
this.logger.warn(checkForPayloadSizeAbuseResult.getError(), {
|
this.logger.warn(checkForPayloadSizeAbuseResult.getError(), {
|
||||||
userId: response.locals.user.uuid,
|
userId: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.strictAbuseProtection) {
|
if (this.strictAbuseProtection) {
|
||||||
|
@ -77,7 +79,7 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
for (const itemHashInput of request.body.items) {
|
for (const itemHashInput of request.body.items) {
|
||||||
const itemHashOrError = ItemHash.create({
|
const itemHashOrError = ItemHash.create({
|
||||||
...itemHashInput,
|
...itemHashInput,
|
||||||
user_uuid: response.locals.user.uuid,
|
user_uuid: locals.user.uuid,
|
||||||
key_system_identifier: itemHashInput.key_system_identifier ?? null,
|
key_system_identifier: itemHashInput.key_system_identifier ?? null,
|
||||||
shared_vault_uuid: itemHashInput.shared_vault_uuid ?? null,
|
shared_vault_uuid: itemHashInput.shared_vault_uuid ?? null,
|
||||||
})
|
})
|
||||||
|
@ -99,7 +101,7 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncResult = await this.syncItems.execute({
|
const syncResult = await this.syncItems.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
itemHashes,
|
itemHashes,
|
||||||
computeIntegrityHash: request.body.compute_integrity === true,
|
computeIntegrityHash: request.body.compute_integrity === true,
|
||||||
syncToken: request.body.sync_token,
|
syncToken: request.body.sync_token,
|
||||||
|
@ -108,10 +110,10 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
contentType: request.body.content_type,
|
contentType: request.body.content_type,
|
||||||
apiVersion: request.body.api ?? ApiVersion.v20161215,
|
apiVersion: request.body.api ?? ApiVersion.v20161215,
|
||||||
snjsVersion: <string>request.headers['x-snjs-version'],
|
snjsVersion: <string>request.headers['x-snjs-version'],
|
||||||
readOnlyAccess: response.locals.readOnlyAccess,
|
readOnlyAccess: locals.readOnlyAccess,
|
||||||
sessionUuid: response.locals.session ? response.locals.session.uuid : null,
|
sessionUuid: locals.session ? locals.session.uuid : null,
|
||||||
sharedVaultUuids,
|
sharedVaultUuids,
|
||||||
isFreeUser: response.locals.isFreeUser,
|
isFreeUser: locals.isFreeUser,
|
||||||
})
|
})
|
||||||
if (syncResult.isFailed()) {
|
if (syncResult.isFailed()) {
|
||||||
return this.json({ error: { message: syncResult.getError() } }, HttpStatusCode.BadRequest)
|
return this.json({ error: { message: syncResult.getError() } }, HttpStatusCode.BadRequest)
|
||||||
|
@ -125,13 +127,15 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkItemsIntegrity(request: Request, response: Response): Promise<results.JsonResult> {
|
async checkItemsIntegrity(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
let integrityPayloads = []
|
let integrityPayloads = []
|
||||||
if ('integrityPayloads' in request.body) {
|
if ('integrityPayloads' in request.body) {
|
||||||
integrityPayloads = request.body.integrityPayloads
|
integrityPayloads = request.body.integrityPayloads
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.checkIntegrity.execute({
|
const result = await this.checkIntegrity.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
integrityPayloads,
|
integrityPayloads,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -145,8 +149,10 @@ export class BaseItemsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSingleItem(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getItem.execute({
|
const result = await this.getItem.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
itemUuid: request.params.uuid,
|
itemUuid: request.params.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { SendMessageToUser } from '../../../Domain/UseCase/Messaging/SendMessage
|
||||||
import { DeleteAllMessagesSentToUser } from '../../../Domain/UseCase/Messaging/DeleteAllMessagesSentToUser/DeleteAllMessagesSentToUser'
|
import { DeleteAllMessagesSentToUser } from '../../../Domain/UseCase/Messaging/DeleteAllMessagesSentToUser/DeleteAllMessagesSentToUser'
|
||||||
import { DeleteMessage } from '../../../Domain/UseCase/Messaging/DeleteMessage/DeleteMessage'
|
import { DeleteMessage } from '../../../Domain/UseCase/Messaging/DeleteMessage/DeleteMessage'
|
||||||
import { GetMessagesSentByUser } from '../../../Domain/UseCase/Messaging/GetMessagesSentByUser/GetMessagesSentByUser'
|
import { GetMessagesSentByUser } from '../../../Domain/UseCase/Messaging/GetMessagesSentByUser/GetMessagesSentByUser'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseMessagesController extends BaseHttpController {
|
export class BaseMessagesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -32,8 +33,10 @@ export class BaseMessagesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMessages(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getMessages(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getMessageSentToUserUseCase.execute({
|
const result = await this.getMessageSentToUserUseCase.execute({
|
||||||
recipientUuid: response.locals.user.uuid,
|
recipientUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -53,8 +56,10 @@ export class BaseMessagesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMessagesSent(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getMessagesSent(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getMessagesSentByUserUseCase.execute({
|
const result = await this.getMessagesSentByUserUseCase.execute({
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -74,8 +79,10 @@ export class BaseMessagesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMessage(request: Request, response: Response): Promise<results.JsonResult> {
|
async sendMessage(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.sendMessageToUserUseCase.execute({
|
const result = await this.sendMessageToUserUseCase.execute({
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
recipientUuid: request.body.recipient_uuid,
|
recipientUuid: request.body.recipient_uuid,
|
||||||
encryptedMessage: request.body.encrypted_message,
|
encryptedMessage: request.body.encrypted_message,
|
||||||
replaceabilityIdentifier: request.body.replaceability_identifier,
|
replaceabilityIdentifier: request.body.replaceability_identifier,
|
||||||
|
@ -98,8 +105,10 @@ export class BaseMessagesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteMessagesSentToUser(_request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteMessagesSentToUser(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteMessagesSentToUserUseCase.execute({
|
const result = await this.deleteMessagesSentToUserUseCase.execute({
|
||||||
recipientUuid: response.locals.user.uuid,
|
recipientUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -117,9 +126,11 @@ export class BaseMessagesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteMessage(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteMessage(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteMessageUseCase.execute({
|
const result = await this.deleteMessageUseCase.execute({
|
||||||
messageUuid: request.params.messageUuid,
|
messageUuid: request.params.messageUuid,
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { DeleteSharedVaultInvitesToUser } from '../../../Domain/UseCase/SharedVa
|
||||||
import { GetSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultInvitesSentByUser/GetSharedVaultInvitesSentByUser'
|
import { GetSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultInvitesSentByUser/GetSharedVaultInvitesSentByUser'
|
||||||
import { DeleteSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/SharedVaults/DeleteSharedVaultInvitesSentByUser/DeleteSharedVaultInvitesSentByUser'
|
import { DeleteSharedVaultInvitesSentByUser } from '../../../Domain/UseCase/SharedVaults/DeleteSharedVaultInvitesSentByUser/DeleteSharedVaultInvitesSentByUser'
|
||||||
import { GetSharedVaultInvitesSentToUser } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultInvitesSentToUser/GetSharedVaultInvitesSentToUser'
|
import { GetSharedVaultInvitesSentToUser } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultInvitesSentToUser/GetSharedVaultInvitesSentToUser'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSharedVaultInvitesController extends BaseHttpController {
|
export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -63,9 +64,11 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async createSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.inviteUserToSharedVaultUseCase.execute({
|
const result = await this.inviteUserToSharedVaultUseCase.execute({
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
recipientUuid: request.body.recipient_uuid,
|
recipientUuid: request.body.recipient_uuid,
|
||||||
encryptedMessage: request.body.encrypted_message,
|
encryptedMessage: request.body.encrypted_message,
|
||||||
permission: request.body.permission,
|
permission: request.body.permission,
|
||||||
|
@ -88,10 +91,12 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async updateSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.updateSharedVaultInviteUseCase.execute({
|
const result = await this.updateSharedVaultInviteUseCase.execute({
|
||||||
encryptedMessage: request.body.encrypted_message,
|
encryptedMessage: request.body.encrypted_message,
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
permission: request.body.permission,
|
permission: request.body.permission,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -112,9 +117,11 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async acceptSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async acceptSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.acceptSharedVaultInviteUseCase.execute({
|
const result = await this.acceptSharedVaultInviteUseCase.execute({
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -128,7 +135,7 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.json({
|
return this.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -136,9 +143,11 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async declineSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async declineSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.declineSharedVaultInviteUseCase.execute({
|
const result = await this.declineSharedVaultInviteUseCase.execute({
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -158,8 +167,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteInboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteInboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteSharedVaultInvitesToUserUseCase.execute({
|
const result = await this.deleteSharedVaultInvitesToUserUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -179,8 +190,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteOutboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteOutboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteSharedVaultInvitesSentByUserUseCase.execute({
|
const result = await this.deleteSharedVaultInvitesSentByUserUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -200,8 +213,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOutboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getOutboundUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -221,8 +236,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
const result = await this.getSharedVaultInvitesSentByUserUseCase.execute({
|
||||||
senderUuid: response.locals.user.uuid,
|
senderUuid: locals.user.uuid,
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -243,8 +260,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getUserInvites(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getSharedVaultInvitesSentToUserUseCase.execute({
|
const result = await this.getSharedVaultInvitesSentToUserUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -264,9 +283,11 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteSharedVaultInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.declineSharedVaultInviteUseCase.execute({
|
const result = await this.declineSharedVaultInviteUseCase.execute({
|
||||||
inviteUuid: request.params.inviteUuid,
|
inviteUuid: request.params.inviteUuid,
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -286,8 +307,10 @@ export class BaseSharedVaultInvitesController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAllSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteAllSharedVaultInvites(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteSharedVaultInvitesSentByUserUseCase.execute({
|
const result = await this.deleteSharedVaultInvitesSentByUserUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { SharedVaultUserHttpRepresentation } from '../../../Mapping/Http/SharedV
|
||||||
import { GetSharedVaultUsers } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers'
|
import { GetSharedVaultUsers } from '../../../Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers'
|
||||||
import { RemoveUserFromSharedVault } from '../../../Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
import { RemoveUserFromSharedVault } from '../../../Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault'
|
||||||
import { DesignateSurvivor } from '../../../Domain/UseCase/SharedVaults/DesignateSurvivor/DesignateSurvivor'
|
import { DesignateSurvivor } from '../../../Domain/UseCase/SharedVaults/DesignateSurvivor/DesignateSurvivor'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSharedVaultUsersController extends BaseHttpController {
|
export class BaseSharedVaultUsersController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -29,8 +30,10 @@ export class BaseSharedVaultUsersController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSharedVaultUsers(request: Request, response: Response): Promise<results.JsonResult> {
|
async getSharedVaultUsers(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.getSharedVaultUsersUseCase.execute({
|
const result = await this.getSharedVaultUsersUseCase.execute({
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -51,10 +54,12 @@ export class BaseSharedVaultUsersController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeUserFromSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
async removeUserFromSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.removeUserFromSharedVaultUseCase.execute({
|
const result = await this.removeUserFromSharedVaultUseCase.execute({
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
userUuid: request.params.userUuid,
|
userUuid: request.params.userUuid,
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -76,10 +81,12 @@ export class BaseSharedVaultUsersController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async designateSurvivor(request: Request, response: Response): Promise<results.JsonResult> {
|
async designateSurvivor(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.designateSurvivorUseCase.execute({
|
const result = await this.designateSurvivorUseCase.execute({
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
userUuid: request.params.userUuid,
|
userUuid: request.params.userUuid,
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { CreateSharedVault } from '../../../Domain/UseCase/SharedVaults/CreateSh
|
||||||
import { SharedVaultUserHttpRepresentation } from '../../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
import { SharedVaultUserHttpRepresentation } from '../../../Mapping/Http/SharedVaultUserHttpRepresentation'
|
||||||
import { DeleteSharedVault } from '../../../Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault'
|
import { DeleteSharedVault } from '../../../Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault'
|
||||||
import { CreateSharedVaultFileValetToken } from '../../../Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken'
|
import { CreateSharedVaultFileValetToken } from '../../../Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class BaseSharedVaultsController extends BaseHttpController {
|
export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -36,8 +37,10 @@ export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSharedVaults(_request: Request, response: Response): Promise<results.JsonResult> {
|
async getSharedVaults(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const resultOrError = await this.getSharedVaultsUseCase.execute({
|
const resultOrError = await this.getSharedVaultsUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
includeDesignatedSurvivors: true,
|
includeDesignatedSurvivors: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -64,9 +67,11 @@ export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSharedVault(_request: Request, response: Response): Promise<results.JsonResult> {
|
async createSharedVault(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.createSharedVaultUseCase.execute({
|
const result = await this.createSharedVaultUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
userRoleNames: response.locals.roles.map((role: Role) => role.name),
|
userRoleNames: locals.roles.map((role: Role) => role.name),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
@ -80,7 +85,7 @@ export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.json({
|
return this.json({
|
||||||
sharedVault: this.sharedVaultHttpMapper.toProjection(result.getValue().sharedVault),
|
sharedVault: this.sharedVaultHttpMapper.toProjection(result.getValue().sharedVault),
|
||||||
|
@ -89,9 +94,11 @@ export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
async deleteSharedVault(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.deleteSharedVaultUseCase.execute({
|
const result = await this.deleteSharedVaultUseCase.execute({
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
originatorUuid: response.locals.user.uuid,
|
originatorUuid: locals.user.uuid,
|
||||||
allowSurviving: false,
|
allowSurviving: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -106,16 +113,18 @@ export class BaseSharedVaultsController extends BaseHttpController {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setHeader('x-invalidate-cache', response.locals.user.uuid)
|
response.setHeader('x-invalidate-cache', locals.user.uuid)
|
||||||
|
|
||||||
return this.json({ success: true })
|
return this.json({ success: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValetTokenForSharedVaultFile(request: Request, response: Response): Promise<results.JsonResult> {
|
async createValetTokenForSharedVaultFile(request: Request, response: Response): Promise<results.JsonResult> {
|
||||||
|
const locals = response.locals as ResponseLocals
|
||||||
|
|
||||||
const result = await this.createSharedVaultFileValetTokenUseCase.execute({
|
const result = await this.createSharedVaultFileValetTokenUseCase.execute({
|
||||||
userUuid: response.locals.user.uuid,
|
userUuid: locals.user.uuid,
|
||||||
sharedVaultUuid: request.params.sharedVaultUuid,
|
sharedVaultUuid: request.params.sharedVaultUuid,
|
||||||
sharedVaultOwnerUploadBytesLimit: response.locals.sharedVaultOwnerContext?.upload_bytes_limit,
|
sharedVaultOwnerUploadBytesLimit: locals.sharedVaultOwnerContext?.upload_bytes_limit,
|
||||||
fileUuid: request.body.file_uuid,
|
fileUuid: request.body.file_uuid,
|
||||||
remoteIdentifier: request.body.remote_identifier,
|
remoteIdentifier: request.body.remote_identifier,
|
||||||
operation: request.body.operation,
|
operation: request.body.operation,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { verify } from 'jsonwebtoken'
|
||||||
import { CrossServiceTokenData } from '@standardnotes/security'
|
import { CrossServiceTokenData } from '@standardnotes/security'
|
||||||
import * as winston from 'winston'
|
import * as winston from 'winston'
|
||||||
import { RoleName } from '@standardnotes/domain-core'
|
import { RoleName } from '@standardnotes/domain-core'
|
||||||
|
import { ResponseLocals } from '../ResponseLocals'
|
||||||
|
|
||||||
export class InversifyExpressAuthMiddleware extends BaseMiddleware {
|
export class InversifyExpressAuthMiddleware extends BaseMiddleware {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -25,13 +26,14 @@ export class InversifyExpressAuthMiddleware extends BaseMiddleware {
|
||||||
|
|
||||||
const decodedToken = <CrossServiceTokenData>verify(authToken, this.authJWTSecret, { algorithms: ['HS256'] })
|
const decodedToken = <CrossServiceTokenData>verify(authToken, this.authJWTSecret, { algorithms: ['HS256'] })
|
||||||
|
|
||||||
response.locals.user = decodedToken.user
|
Object.assign(response.locals, {
|
||||||
response.locals.roles = decodedToken.roles
|
user: decodedToken.user,
|
||||||
response.locals.isFreeUser =
|
roles: decodedToken.roles,
|
||||||
decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser
|
isFreeUser: decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser,
|
||||||
response.locals.session = decodedToken.session
|
session: decodedToken.session,
|
||||||
response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false
|
readOnlyAccess: decodedToken.session?.readonly_access ?? false,
|
||||||
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
sharedVaultOwnerContext: decodedToken.shared_vault_owner_context,
|
||||||
|
} as ResponseLocals)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Role } from '@standardnotes/security'
|
||||||
|
|
||||||
|
export interface ResponseLocals {
|
||||||
|
user: {
|
||||||
|
uuid: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
roles: Array<Role>
|
||||||
|
isFreeUser: boolean
|
||||||
|
session?: {
|
||||||
|
uuid: string
|
||||||
|
api_version: string
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
device_info: string
|
||||||
|
readonly_access: boolean
|
||||||
|
access_expiration: string
|
||||||
|
refresh_expiration: string
|
||||||
|
}
|
||||||
|
readOnlyAccess: boolean
|
||||||
|
sharedVaultOwnerContext?: {
|
||||||
|
upload_bytes_limit: number
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue