diff --git a/lib/permissions/minimally-encoded-thread-permissions.js b/lib/permissions/minimally-encoded-thread-permissions.js --- a/lib/permissions/minimally-encoded-thread-permissions.js +++ b/lib/permissions/minimally-encoded-thread-permissions.js @@ -1,6 +1,7 @@ // @flow import invariant from 'invariant'; +import t, { type TInterface } from 'tcomb'; import { parseThreadPermissionString } from './prefixes.js'; import type { @@ -8,7 +9,11 @@ ThreadPermissionsInfo, ThreadRolePermissionsBlob, } from '../types/thread-permission-types.js'; +import type { RoleInfo } from '../types/thread-types.js'; +import { roleInfoValidator } from '../types/thread-types.js'; import { entries, invertObjectToMap } from '../utils/objects.js'; +import { tRegex, tShape } from '../utils/validation-utils.js'; +import type { TRegex } from '../utils/validation-utils.js'; // `baseRolePermissionEncoding` maps permission names to indices. // These indices represent the 6-bit basePermission part of the 10-bit role @@ -179,6 +184,18 @@ ]), ); +export type MinimallyEncodedRoleInfo = { + ...RoleInfo, + +permissions: $ReadOnlyArray, +}; + +const tHexEncodedRolePermission: TRegex = tRegex(/\b[0-9a-fA-F]{3}\b/); +const minimallyEncodedRoleInfoValidator: TInterface = + tShape({ + ...roleInfoValidator.meta.props, + permissions: t.list(tHexEncodedRolePermission), + }); + export { permissionsToBitmaskHex, hasPermission, @@ -186,4 +203,5 @@ decodeRolePermissionBitmask, threadRolePermissionsBlobToBitmaskArray, decodeThreadRolePermissionsBitmaskArray, + minimallyEncodedRoleInfoValidator, }; diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -4,6 +4,7 @@ decodeRolePermissionBitmask, decodeThreadRolePermissionsBitmaskArray, hasPermission, + minimallyEncodedRoleInfoValidator, permissionsToBitmaskHex, rolePermissionToBitmaskHex, threadRolePermissionsBlobToBitmaskArray, @@ -221,3 +222,54 @@ ).toEqual(threadRolePermissionsBlob); }); }); + +describe('minimallyEncodedRoleInfoValidator', () => { + it('should validate correctly formed MinimallyEncodedRoleInfo', () => { + expect( + minimallyEncodedRoleInfoValidator.is({ + id: 'roleID', + name: 'roleName', + permissions: ['abc', 'def'], + isDefault: true, + }), + ).toBe(true); + }); + + it('should NOT validate malformed MinimallyEncodedRoleInfo', () => { + expect( + minimallyEncodedRoleInfoValidator.is({ + id: 1234, + name: 'roleName', + permissions: ['abc', 'def'], + isDefault: true, + }), + ).toBe(false); + + expect( + minimallyEncodedRoleInfoValidator.is({ + id: 'roleID', + name: 'roleName', + permissions: [123, 456], + isDefault: true, + }), + ).toBe(false); + + expect( + minimallyEncodedRoleInfoValidator.is({ + id: 'roleID', + name: 'roleName', + permissions: ['ZZZ', 'YYY'], + isDefault: true, + }), + ).toBe(false); + + expect( + minimallyEncodedRoleInfoValidator.is({ + id: 'roleID', + name: 'roleName', + permissions: ['AAAAA', 'YYY'], + isDefault: true, + }), + ).toBe(false); + }); +}); diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -65,7 +65,7 @@ +permissions: ThreadRolePermissionsBlob, +isDefault: boolean, }; -const roleInfoValidator = tShape({ +export const roleInfoValidator: TInterface = tShape({ id: tID, name: t.String, permissions: threadRolePermissionsBlobValidator, 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 @@ -40,7 +40,7 @@ return t.interface(spec, { strict: true }); } -type TRegex = TRefinement; +export type TRegex = TRefinement; function tRegex(regex: RegExp): TRegex { return t.refinement(t.String, val => regex.test(val)); }