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 @@ -6,6 +6,7 @@ import type { PolicyType } from 'lib/facts/policies.js'; import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; +import { isWebPlatform } from 'lib/types/device-types.js'; import { ServerError } from 'lib/utils/errors.js'; import { tCookie, @@ -13,6 +14,7 @@ tPlatform, tPlatformDetails, assertWithValidator, + tID, } from 'lib/utils/validation-utils.js'; import { fetchNotAcknowledgedPolicies } from '../fetchers/policy-acknowledgment-fetchers.js'; @@ -30,6 +32,70 @@ checkInputValidator(inputValidator, input); } +const convertToNewIDSchema = false; +const keyserverPrefixID = '256'; + +function validateOutput( + viewer: Viewer, + outputValidator: TType, + data: T, +): T { + if (!outputValidator.is(data)) { + console.trace( + 'Output validation failed, validator is:', + outputValidator.displayName, + ); + return data; + } + + if ( + hasMinCodeVersion(viewer.platformDetails, 1000) && + !isWebPlatform(viewer.platformDetails?.platform) && + convertToNewIDSchema + ) { + return convertServerIDsToClientIDs( + keyserverPrefixID, + outputValidator, + data, + ); + } + + 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: T) { if (!inputValidator || inputValidator.is(input)) { return; @@ -250,11 +316,14 @@ export { validateInput, + validateOutput, checkInputValidator, redactedString, 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,9 +2,14 @@ import t from 'tcomb'; -import { tPassword, tShape } from 'lib/utils/validation-utils.js'; +import { tPassword, tShape, tID } from 'lib/utils/validation-utils.js'; -import { sanitizeInput, redactedString } from './validation-utils.js'; +import { + convertServerIDsToClientIDs, + sanitizeInput, + redactedString, + convertClientIDsToServerIDs, +} from './validation-utils.js'; describe('sanitization', () => { it('should redact a string', () => { @@ -72,3 +77,33 @@ expect(sanitizeInput(validator, object)).toStrictEqual(redacted); }); }); + +describe('id conversion', () => { + it('should convert string id', () => { + const validator = tShape({ id: tID }); + const serverData = { id: '1' }; + const clientData = { id: '0|1' }; + + expect( + convertServerIDsToClientIDs('0', validator, serverData), + ).toStrictEqual(clientData); + expect( + convertClientIDsToServerIDs('0', validator, clientData), + ).toStrictEqual(serverData); + }); + + it('should convert a complex type', () => { + const validator = tShape({ ids: t.dict(tID, t.list(tID)) }); + const serverData = { ids: { '1': ['11', '12'], '2': [], '3': ['13'] } }; + const clientData = { + ids: { '0|1': ['0|11', '0|12'], '0|2': [], '0|3': ['0|13'] }, + }; + + expect( + convertServerIDsToClientIDs('0', validator, serverData), + ).toStrictEqual(clientData); + expect( + convertClientIDsToServerIDs('0', validator, clientData), + ).toStrictEqual(serverData); + }); +});