diff --git a/keyserver/src/utils/validation-utils.js b/keyserver/src/utils/validation-utils.js --- a/keyserver/src/utils/validation-utils.js +++ b/keyserver/src/utils/validation-utils.js @@ -1,8 +1,6 @@ // @flow -import _mapKeys from 'lodash/fp/mapKeys.js'; -import _mapValues from 'lodash/fp/mapValues.js'; -import type { TType, TInterface } from 'tcomb'; +import type { TType } from 'tcomb'; import type { PolicyType } from 'lib/facts/policies.js'; import { @@ -17,16 +15,17 @@ tPlatform, tPlatformDetails, assertWithValidator, - tID, + convertToNewIDSchema, + keyserverPrefixID, + convertClientIDsToServerIDs, + convertObject, + convertServerIDsToClientIDs, } from 'lib/utils/validation-utils.js'; import { fetchNotAcknowledgedPolicies } from '../fetchers/policy-acknowledgment-fetchers.js'; import { verifyClientSupported } from '../session/version.js'; import type { Viewer } from '../session/viewer.js'; -const convertToNewIDSchema = false; -const keyserverPrefixID = '256'; - async function validateInput( viewer: Viewer, inputValidator: TType, @@ -42,11 +41,15 @@ !isWebPlatform(viewer.platformDetails?.platform) && convertToNewIDSchema ) { - return convertClientIDsToServerIDs( - keyserverPrefixID, - inputValidator, - convertedInput, - ); + try { + return convertClientIDsToServerIDs( + keyserverPrefixID, + inputValidator, + convertedInput, + ); + } catch (err) { + throw new ServerError(err.message); + } } return convertedInput; @@ -80,39 +83,6 @@ return data; } -function convertServerIDsToClientIDs( - serverPrefixID: string, - outputValidator: TType, - data: T, -): T { - const conversionFunction = id => { - if (id.indexOf('|') !== -1) { - console.warn(`Server id '${id}' already has a prefix`); - return id; - } - return `${serverPrefixID}|${id}`; - }; - - return convertObject(outputValidator, data, [tID], conversionFunction); -} - -function convertClientIDsToServerIDs( - serverPrefixID: string, - outputValidator: TType, - data: T, -): T { - const prefix = serverPrefixID + '|'; - const conversionFunction = id => { - if (id.startsWith(prefix)) { - return id.substr(prefix.length); - } - - throw new ServerError('invalid_client_id_prefix'); - }; - - return convertObject(outputValidator, data, [tID], conversionFunction); -} - function checkInputValidator(inputValidator: TType, input: mixed): T { if (inputValidator.is(input)) { return assertWithValidator(input, inputValidator); @@ -227,87 +197,6 @@ return null; } -function convertObject( - validator: TType, - input: I, - typesToConvert: $ReadOnlyArray>, - conversionFunction: T => T, -): I { - if (input === null || input === undefined) { - return input; - } - - // While they should be the same runtime object, - // `TValidator` is `TType` and `validator` is `TType`. - // Having them have different types allows us to use `assertWithValidator` - // to change `input` flow type - const TValidator = typesToConvert[typesToConvert.indexOf(validator)]; - if (TValidator && TValidator.is(input)) { - const TInput = assertWithValidator(input, TValidator); - const converted = conversionFunction(TInput); - return assertWithValidator(converted, validator); - } - - if (validator.meta.kind === 'maybe' || validator.meta.kind === 'subtype') { - return convertObject( - validator.meta.type, - input, - typesToConvert, - conversionFunction, - ); - } - if (validator.meta.kind === 'interface' && typeof input === 'object') { - const recastValidator: TInterface = (validator: any); - const result = {}; - for (const key in input) { - const innerValidator = recastValidator.meta.props[key]; - result[key] = convertObject( - innerValidator, - input[key], - typesToConvert, - conversionFunction, - ); - } - return assertWithValidator(result, recastValidator); - } - if (validator.meta.kind === 'union') { - for (const innerValidator of validator.meta.types) { - if (innerValidator.is(input)) { - return convertObject( - innerValidator, - input, - typesToConvert, - conversionFunction, - ); - } - } - return input; - } - if (validator.meta.kind === 'list' && Array.isArray(input)) { - const innerValidator = validator.meta.type; - return (input.map(value => - convertObject(innerValidator, value, typesToConvert, conversionFunction), - ): any); - } - if (validator.meta.kind === 'dict' && typeof input === 'object') { - const domainValidator = validator.meta.domain; - const codomainValidator = validator.meta.codomain; - if (typesToConvert.includes(domainValidator)) { - input = _mapKeys(key => conversionFunction(key))(input); - } - return _mapValues(value => - convertObject( - codomainValidator, - value, - typesToConvert, - conversionFunction, - ), - )(input); - } - - return input; -} - async function policiesValidator( viewer: Viewer, policies: $ReadOnlyArray, @@ -339,8 +228,5 @@ sanitizeInput, findFirstInputMatchingValidator, checkClientSupported, - convertServerIDsToClientIDs, - convertClientIDsToServerIDs, - convertObject, policiesValidator, }; diff --git a/keyserver/src/utils/validation-utils.test.js b/keyserver/src/utils/validation-utils.test.js --- a/keyserver/src/utils/validation-utils.test.js +++ b/keyserver/src/utils/validation-utils.test.js @@ -2,14 +2,15 @@ import t from 'tcomb'; -import { tPassword, tShape, tID } from 'lib/utils/validation-utils.js'; - import { + tPassword, + tShape, + tID, convertServerIDsToClientIDs, - sanitizeInput, - redactedString, convertClientIDsToServerIDs, -} from './validation-utils.js'; +} from 'lib/utils/validation-utils.js'; + +import { sanitizeInput, redactedString } from './validation-utils.js'; describe('sanitization', () => { it('should redact a string', () => { diff --git a/lib/utils/validation-utils.js b/lib/utils/validation-utils.js --- a/lib/utils/validation-utils.js +++ b/lib/utils/validation-utils.js @@ -1,6 +1,8 @@ // @flow import invariant from 'invariant'; +import _mapKeys from 'lodash/fp/mapKeys.js'; +import _mapValues from 'lodash/fp/mapValues.js'; import t from 'tcomb'; import type { TStructProps, @@ -101,6 +103,123 @@ return (data: any); } +const convertToNewIDSchema = false; +const keyserverPrefixID = '256'; + +function convertServerIDsToClientIDs( + serverPrefixID: string, + outputValidator: TType, + data: T, +): T { + const conversionFunction = id => { + if (id.indexOf('|') !== -1) { + console.warn(`Server id '${id}' already has a prefix`); + return id; + } + return `${serverPrefixID}|${id}`; + }; + + return convertObject(outputValidator, data, [tID], conversionFunction); +} + +function convertClientIDsToServerIDs( + serverPrefixID: string, + outputValidator: TType, + data: T, +): T { + const prefix = serverPrefixID + '|'; + const conversionFunction = id => { + if (id.startsWith(prefix)) { + return id.substr(prefix.length); + } + + throw new Error('invalid_client_id_prefix'); + }; + + return convertObject(outputValidator, data, [tID], conversionFunction); +} + +function convertObject( + validator: TType, + input: I, + typesToConvert: $ReadOnlyArray>, + conversionFunction: T => T, +): I { + if (input === null || input === undefined) { + return input; + } + + // While they should be the same runtime object, + // `TValidator` is `TType` and `validator` is `TType`. + // Having them have different types allows us to use `assertWithValidator` + // to change `input` flow type + const TValidator = typesToConvert[typesToConvert.indexOf(validator)]; + if (TValidator && TValidator.is(input)) { + const TInput = assertWithValidator(input, TValidator); + const converted = conversionFunction(TInput); + return assertWithValidator(converted, validator); + } + + if (validator.meta.kind === 'maybe' || validator.meta.kind === 'subtype') { + return convertObject( + validator.meta.type, + input, + typesToConvert, + conversionFunction, + ); + } + if (validator.meta.kind === 'interface' && typeof input === 'object') { + const recastValidator: TInterface = (validator: any); + const result = {}; + for (const key in input) { + const innerValidator = recastValidator.meta.props[key]; + result[key] = convertObject( + innerValidator, + input[key], + typesToConvert, + conversionFunction, + ); + } + return assertWithValidator(result, recastValidator); + } + if (validator.meta.kind === 'union') { + for (const innerValidator of validator.meta.types) { + if (innerValidator.is(input)) { + return convertObject( + innerValidator, + input, + typesToConvert, + conversionFunction, + ); + } + } + return input; + } + if (validator.meta.kind === 'list' && Array.isArray(input)) { + const innerValidator = validator.meta.type; + return (input.map(value => + convertObject(innerValidator, value, typesToConvert, conversionFunction), + ): any); + } + if (validator.meta.kind === 'dict' && typeof input === 'object') { + const domainValidator = validator.meta.domain; + const codomainValidator = validator.meta.codomain; + if (typesToConvert.includes(domainValidator)) { + input = _mapKeys(key => conversionFunction(key))(input); + } + return _mapValues(value => + convertObject( + codomainValidator, + value, + typesToConvert, + conversionFunction, + ), + )(input); + } + + return input; +} + export { tBool, tString, @@ -123,4 +242,9 @@ tMediaMessageVideo, tMediaMessageMedia, assertWithValidator, + convertToNewIDSchema, + keyserverPrefixID, + convertClientIDsToServerIDs, + convertServerIDsToClientIDs, + convertObject, };