mirror of
https://github.com/standardnotes/server
synced 2026-01-16 20:04:32 -05:00
fix: error handling on gRPC (#937)
This commit is contained in:
@@ -88,7 +88,10 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
if (endpoint === 'items/sync') {
|
||||
const requestIsUsingLatestApiVersions =
|
||||
payload !== undefined && typeof payload !== 'string' && 'api' in payload && payload.api === '20200115'
|
||||
|
||||
if (requestIsUsingLatestApiVersions && endpoint === 'items/sync') {
|
||||
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
||||
|
||||
response.status(result.status).send({
|
||||
|
||||
@@ -19,62 +19,75 @@ export class SessionsServer implements ISessionsServer {
|
||||
call: grpc.ServerUnaryCall<AuthorizationHeader, SessionValidationResponse>,
|
||||
callback: grpc.sendUnaryData<SessionValidationResponse>,
|
||||
): Promise<void> {
|
||||
this.logger.debug('[SessionsServer] Validating session via gRPC')
|
||||
try {
|
||||
this.logger.debug('[SessionsServer] Validating session via gRPC')
|
||||
|
||||
const authenticateRequestResponse = await this.authenticateRequest.execute({
|
||||
authorizationHeader: call.request.getBearerToken(),
|
||||
})
|
||||
const authenticateRequestResponse = await this.authenticateRequest.execute({
|
||||
authorizationHeader: call.request.getBearerToken(),
|
||||
})
|
||||
|
||||
if (!authenticateRequestResponse.success) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-auth-error-message', authenticateRequestResponse.errorMessage as string)
|
||||
metadata.set('x-auth-error-tag', authenticateRequestResponse.errorTag as string)
|
||||
metadata.set('x-auth-error-response-code', authenticateRequestResponse.responseCode.toString())
|
||||
return callback(
|
||||
if (!authenticateRequestResponse.success) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-auth-error-message', authenticateRequestResponse.errorMessage as string)
|
||||
metadata.set('x-auth-error-tag', authenticateRequestResponse.errorTag as string)
|
||||
metadata.set('x-auth-error-response-code', authenticateRequestResponse.responseCode.toString())
|
||||
return callback(
|
||||
{
|
||||
code: Status.PERMISSION_DENIED,
|
||||
message: authenticateRequestResponse.errorMessage,
|
||||
name: authenticateRequestResponse.errorTag,
|
||||
metadata,
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
const user = authenticateRequestResponse.user as User
|
||||
|
||||
const sharedVaultOwnerMetadata = call.metadata.get('x-shared-vault-owner-context')
|
||||
let sharedVaultOwnerContext = undefined
|
||||
if (sharedVaultOwnerMetadata.length > 0 && sharedVaultOwnerMetadata[0].length > 0) {
|
||||
sharedVaultOwnerContext = sharedVaultOwnerMetadata[0].toString()
|
||||
}
|
||||
|
||||
const resultOrError = await this.createCrossServiceToken.execute({
|
||||
user,
|
||||
session: authenticateRequestResponse.session,
|
||||
sharedVaultOwnerContext,
|
||||
})
|
||||
if (resultOrError.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-auth-error-message', resultOrError.getError())
|
||||
metadata.set('x-auth-error-response-code', '400')
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.INVALID_ARGUMENT,
|
||||
message: resultOrError.getError(),
|
||||
name: 'INVALID_ARGUMENT',
|
||||
metadata,
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
const response = new SessionValidationResponse()
|
||||
response.setCrossServiceToken(resultOrError.getValue())
|
||||
|
||||
this.logger.debug('[SessionsServer] Session validated via gRPC')
|
||||
|
||||
callback(null, response)
|
||||
} catch (error) {
|
||||
this.logger.error(`[SessionsServer] Error validating session via gRPC: ${(error as Error).message}`)
|
||||
|
||||
callback(
|
||||
{
|
||||
code: Status.PERMISSION_DENIED,
|
||||
message: authenticateRequestResponse.errorMessage,
|
||||
name: authenticateRequestResponse.errorTag,
|
||||
metadata,
|
||||
code: Status.UNKNOWN,
|
||||
message: 'An error occurred while validating session',
|
||||
name: 'UNKNOWN',
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
const user = authenticateRequestResponse.user as User
|
||||
|
||||
const sharedVaultOwnerMetadata = call.metadata.get('x-shared-vault-owner-context')
|
||||
let sharedVaultOwnerContext = undefined
|
||||
if (sharedVaultOwnerMetadata.length > 0 && sharedVaultOwnerMetadata[0].length > 0) {
|
||||
sharedVaultOwnerContext = sharedVaultOwnerMetadata[0].toString()
|
||||
}
|
||||
|
||||
const resultOrError = await this.createCrossServiceToken.execute({
|
||||
user,
|
||||
session: authenticateRequestResponse.session,
|
||||
sharedVaultOwnerContext,
|
||||
})
|
||||
if (resultOrError.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-auth-error-message', resultOrError.getError())
|
||||
metadata.set('x-auth-error-response-code', '400')
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.INVALID_ARGUMENT,
|
||||
message: resultOrError.getError(),
|
||||
name: 'INVALID_ARGUMENT',
|
||||
metadata,
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
const response = new SessionValidationResponse()
|
||||
response.setCrossServiceToken(resultOrError.getValue())
|
||||
|
||||
this.logger.debug('[SessionsServer] Session validated via gRPC')
|
||||
|
||||
callback(null, response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,38 +22,82 @@ export class SyncingServer implements ISyncingServer {
|
||||
call: grpc.ServerUnaryCall<SyncRequest, SyncResponse>,
|
||||
callback: grpc.sendUnaryData<SyncResponse>,
|
||||
): Promise<void> {
|
||||
this.logger.debug('[SyncingServer] Syncing items via gRPC')
|
||||
try {
|
||||
this.logger.debug('[SyncingServer] Syncing items via gRPC')
|
||||
|
||||
const itemHashesRPC = call.request.getItemsList()
|
||||
const itemHashes: ItemHash[] = []
|
||||
for (const itemHash of itemHashesRPC) {
|
||||
const itemHashOrError = ItemHash.create({
|
||||
uuid: itemHash.getUuid(),
|
||||
content: itemHash.hasContent() ? itemHash.getContent() : undefined,
|
||||
content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
|
||||
deleted: itemHash.hasDeleted() ? itemHash.getDeleted() : undefined,
|
||||
duplicate_of: itemHash.hasDuplicateOf() ? itemHash.getDuplicateOf() : undefined,
|
||||
auth_hash: itemHash.hasAuthHash() ? itemHash.getAuthHash() : undefined,
|
||||
enc_item_key: itemHash.hasEncItemKey() ? itemHash.getEncItemKey() : undefined,
|
||||
items_key_id: itemHash.hasItemsKeyId() ? itemHash.getItemsKeyId() : undefined,
|
||||
created_at: itemHash.hasCreatedAt() ? itemHash.getCreatedAt() : undefined,
|
||||
created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? itemHash.getCreatedAtTimestamp() : undefined,
|
||||
updated_at: itemHash.hasUpdatedAt() ? itemHash.getUpdatedAt() : undefined,
|
||||
updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? itemHash.getUpdatedAtTimestamp() : undefined,
|
||||
user_uuid: call.metadata.get('userUuid').pop() as string,
|
||||
key_system_identifier: itemHash.hasKeySystemIdentifier() ? (itemHash.getKeySystemIdentifier() as string) : null,
|
||||
shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
|
||||
const itemHashesRPC = call.request.getItemsList()
|
||||
const itemHashes: ItemHash[] = []
|
||||
for (const itemHash of itemHashesRPC) {
|
||||
const itemHashOrError = ItemHash.create({
|
||||
uuid: itemHash.getUuid(),
|
||||
content: itemHash.hasContent() ? itemHash.getContent() : undefined,
|
||||
content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
|
||||
deleted: itemHash.hasDeleted() ? itemHash.getDeleted() : undefined,
|
||||
duplicate_of: itemHash.hasDuplicateOf() ? itemHash.getDuplicateOf() : undefined,
|
||||
auth_hash: itemHash.hasAuthHash() ? itemHash.getAuthHash() : undefined,
|
||||
enc_item_key: itemHash.hasEncItemKey() ? itemHash.getEncItemKey() : undefined,
|
||||
items_key_id: itemHash.hasItemsKeyId() ? itemHash.getItemsKeyId() : undefined,
|
||||
created_at: itemHash.hasCreatedAt() ? itemHash.getCreatedAt() : undefined,
|
||||
created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? itemHash.getCreatedAtTimestamp() : undefined,
|
||||
updated_at: itemHash.hasUpdatedAt() ? itemHash.getUpdatedAt() : undefined,
|
||||
updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? itemHash.getUpdatedAtTimestamp() : undefined,
|
||||
user_uuid: call.metadata.get('userUuid').pop() as string,
|
||||
key_system_identifier: itemHash.hasKeySystemIdentifier()
|
||||
? (itemHash.getKeySystemIdentifier() as string)
|
||||
: null,
|
||||
shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
|
||||
})
|
||||
|
||||
if (itemHashOrError.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-sync-error-message', itemHashOrError.getError())
|
||||
metadata.set('x-sync-error-response-code', '400')
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.INVALID_ARGUMENT,
|
||||
message: itemHashOrError.getError(),
|
||||
name: 'INVALID_ARGUMENT',
|
||||
metadata,
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
itemHashes.push(itemHashOrError.getValue())
|
||||
}
|
||||
|
||||
let sharedVaultUuids: string[] | undefined = undefined
|
||||
const sharedVaultUuidsList = call.request.getSharedVaultUuidsList()
|
||||
if (sharedVaultUuidsList.length > 0) {
|
||||
sharedVaultUuids = sharedVaultUuidsList
|
||||
}
|
||||
|
||||
const apiVersion = call.request.hasApiVersion() ? (call.request.getApiVersion() as string) : ApiVersion.v20161215
|
||||
|
||||
const syncResult = await this.syncItemsUseCase.execute({
|
||||
userUuid: call.metadata.get('x-user-uuid').pop() as string,
|
||||
itemHashes,
|
||||
computeIntegrityHash: call.request.hasComputeIntegrity() ? call.request.getComputeIntegrity() === true : false,
|
||||
syncToken: call.request.hasSyncToken() ? call.request.getSyncToken() : undefined,
|
||||
cursorToken: call.request.getCursorToken() ? call.request.getCursorToken() : undefined,
|
||||
limit: call.request.hasLimit() ? call.request.getLimit() : undefined,
|
||||
contentType: call.request.hasContentType() ? call.request.getContentType() : undefined,
|
||||
apiVersion,
|
||||
snjsVersion: call.metadata.get('x-snjs-version').pop() as string,
|
||||
readOnlyAccess: call.metadata.get('x-read-only-access').pop() === 'true',
|
||||
sessionUuid: call.metadata.get('x-session-uuid').pop() as string,
|
||||
sharedVaultUuids,
|
||||
})
|
||||
|
||||
if (itemHashOrError.isFailed()) {
|
||||
if (syncResult.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-sync-error-message', itemHashOrError.getError())
|
||||
metadata.set('x-sync-error-message', syncResult.getError())
|
||||
metadata.set('x-sync-error-response-code', '400')
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.INVALID_ARGUMENT,
|
||||
message: itemHashOrError.getError(),
|
||||
message: syncResult.getError(),
|
||||
name: 'INVALID_ARGUMENT',
|
||||
metadata,
|
||||
},
|
||||
@@ -61,53 +105,24 @@ export class SyncingServer implements ISyncingServer {
|
||||
)
|
||||
}
|
||||
|
||||
itemHashes.push(itemHashOrError.getValue())
|
||||
}
|
||||
const syncResponse = await this.syncResponseFactoryResolver
|
||||
.resolveSyncResponseFactoryVersion(apiVersion)
|
||||
.createResponse(syncResult.getValue())
|
||||
|
||||
let sharedVaultUuids: string[] | undefined = undefined
|
||||
const sharedVaultUuidsList = call.request.getSharedVaultUuidsList()
|
||||
if (sharedVaultUuidsList.length > 0) {
|
||||
sharedVaultUuids = sharedVaultUuidsList
|
||||
}
|
||||
const projection = this.mapper.toProjection(syncResponse as SyncResponse20200115)
|
||||
|
||||
const apiVersion = call.request.hasApiVersion() ? (call.request.getApiVersion() as string) : ApiVersion.v20161215
|
||||
|
||||
const syncResult = await this.syncItemsUseCase.execute({
|
||||
userUuid: call.metadata.get('x-user-uuid').pop() as string,
|
||||
itemHashes,
|
||||
computeIntegrityHash: call.request.hasComputeIntegrity() ? call.request.getComputeIntegrity() === true : false,
|
||||
syncToken: call.request.hasSyncToken() ? call.request.getSyncToken() : undefined,
|
||||
cursorToken: call.request.getCursorToken() ? call.request.getCursorToken() : undefined,
|
||||
limit: call.request.hasLimit() ? call.request.getLimit() : undefined,
|
||||
contentType: call.request.hasContentType() ? call.request.getContentType() : undefined,
|
||||
apiVersion,
|
||||
snjsVersion: call.metadata.get('x-snjs-version').pop() as string,
|
||||
readOnlyAccess: call.metadata.get('x-read-only-access').pop() === 'true',
|
||||
sessionUuid: call.metadata.get('x-session-uuid').pop() as string,
|
||||
sharedVaultUuids,
|
||||
})
|
||||
if (syncResult.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-sync-error-message', syncResult.getError())
|
||||
metadata.set('x-sync-error-response-code', '400')
|
||||
callback(null, projection)
|
||||
} catch (error) {
|
||||
this.logger.error(`[SyncingServer] Error syncing items via gRPC: ${(error as Error).message}`)
|
||||
|
||||
return callback(
|
||||
{
|
||||
code: Status.INVALID_ARGUMENT,
|
||||
message: syncResult.getError(),
|
||||
name: 'INVALID_ARGUMENT',
|
||||
metadata,
|
||||
code: Status.UNKNOWN,
|
||||
message: 'An error occurred while syncing items',
|
||||
name: 'UNKNOWN',
|
||||
},
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
const syncResponse = await this.syncResponseFactoryResolver
|
||||
.resolveSyncResponseFactoryVersion(apiVersion)
|
||||
.createResponse(syncResult.getValue())
|
||||
|
||||
const projection = this.mapper.toProjection(syncResponse as SyncResponse20200115)
|
||||
|
||||
callback(null, projection)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user