Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32171309
D7572.1765060625.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D7572.1765060625.diff
View Options
diff --git a/keyserver/src/responders/responder-validators.test.js b/keyserver/src/responders/responder-validators.test.js
new file mode 100644
--- /dev/null
+++ b/keyserver/src/responders/responder-validators.test.js
@@ -0,0 +1,333 @@
+// @flow
+
+import {
+ logInResponseValidator,
+ registerResponseValidator,
+ logOutResponseValidator,
+} from './user-responders.js';
+
+describe('user responder validators', () => {
+ it('should validate logout response', () => {
+ const response = { currentUserInfo: { id: '93078', anonymous: true } };
+ expect(logOutResponseValidator.is(response)).toBe(true);
+ response.currentUserInfo.anonymous = false;
+ expect(logOutResponseValidator.is(response)).toBe(false);
+ });
+
+ it('should validate register response', () => {
+ const response = {
+ id: '93079',
+ rawMessageInfos: [
+ {
+ type: 1,
+ threadID: '93095',
+ creatorID: '93079',
+ time: 1682086407469,
+ initialThreadState: {
+ type: 6,
+ name: null,
+ parentThreadID: '1',
+ color: '648caa',
+ memberIDs: ['256', '93079'],
+ },
+ id: '93110',
+ },
+ {
+ type: 0,
+ threadID: '93095',
+ creatorID: '256',
+ time: 1682086407575,
+ text: 'welcome to Comm!',
+ id: '93113',
+ },
+ ],
+ currentUserInfo: { id: '93079', username: 'user' },
+ cookieChange: {
+ threadInfos: {
+ '1': {
+ id: '1',
+ type: 12,
+ name: 'GENESIS',
+ description: 'desc',
+ color: 'c85000',
+ creationTime: 1672934346213,
+ parentThreadID: null,
+ members: [
+ {
+ id: '256',
+ role: '83796',
+ permissions: {
+ know_of: { value: true, source: '1' },
+ membership: { value: false, source: null },
+ visible: { value: true, source: '1' },
+ voiced: { value: true, source: '1' },
+ edit_entries: { value: true, source: '1' },
+ edit_thread: { value: true, source: '1' },
+ edit_thread_description: { value: true, source: '1' },
+ edit_thread_color: { value: true, source: '1' },
+ delete_thread: { value: true, source: '1' },
+ create_subthreads: { value: true, source: '1' },
+ create_sidebars: { value: true, source: '1' },
+ join_thread: { value: false, source: null },
+ edit_permissions: { value: false, source: null },
+ add_members: { value: true, source: '1' },
+ remove_members: { value: true, source: '1' },
+ change_role: { value: true, source: '1' },
+ leave_thread: { value: false, source: null },
+ react_to_message: { value: true, source: '1' },
+ edit_message: { value: true, source: '1' },
+ },
+ isSender: false,
+ },
+ ],
+ roles: {
+ '83795': {
+ id: '83795',
+ name: 'Members',
+ permissions: {
+ know_of: true,
+ visible: true,
+ descendant_open_know_of: true,
+ descendant_open_visible: true,
+ descendant_opentoplevel_join_thread: true,
+ },
+ isDefault: true,
+ },
+ },
+ currentUser: {
+ role: '83795',
+ permissions: {
+ know_of: { value: true, source: '1' },
+ membership: { value: false, source: null },
+ visible: { value: true, source: '1' },
+ voiced: { value: false, source: null },
+ edit_entries: { value: false, source: null },
+ edit_thread: { value: false, source: null },
+ edit_thread_description: { value: false, source: null },
+ edit_thread_color: { value: false, source: null },
+ delete_thread: { value: false, source: null },
+ create_subthreads: { value: false, source: null },
+ create_sidebars: { value: false, source: null },
+ join_thread: { value: false, source: null },
+ edit_permissions: { value: false, source: null },
+ add_members: { value: false, source: null },
+ remove_members: { value: false, source: null },
+ change_role: { value: false, source: null },
+ leave_thread: { value: false, source: null },
+ react_to_message: { value: false, source: null },
+ edit_message: { value: false, source: null },
+ },
+ subscription: { home: true, pushNotifs: true },
+ unread: true,
+ },
+ repliesCount: 0,
+ containingThreadID: null,
+ community: null,
+ },
+ },
+ userInfos: [
+ { id: '5', username: 'commbot' },
+ { id: '256', username: 'ashoat' },
+ { id: '93079', username: 'temp_user7' },
+ ],
+ },
+ };
+
+ expect(registerResponseValidator.is(response)).toBe(true);
+ response.cookieChange.userInfos = undefined;
+ expect(registerResponseValidator.is(response)).toBe(false);
+ });
+
+ it('should validate login response', () => {
+ const response = {
+ currentUserInfo: { id: '93079', username: 'temp_user7' },
+ rawMessageInfos: [
+ {
+ type: 0,
+ id: '93115',
+ threadID: '93094',
+ time: 1682086407577,
+ creatorID: '5',
+ text: 'This is your private chat, where you can set',
+ },
+ {
+ type: 1,
+ id: '93111',
+ threadID: '93094',
+ time: 1682086407467,
+ creatorID: '93079',
+ initialThreadState: {
+ type: 7,
+ name: 'temp_user7',
+ parentThreadID: '1',
+ color: '575757',
+ memberIDs: ['93079'],
+ },
+ },
+ ],
+ truncationStatuses: { '93094': 'exhaustive', '93095': 'exhaustive' },
+ serverTime: 1682086579416,
+ userInfos: [
+ { id: '5', username: 'commbot' },
+ { id: '256', username: 'ashoat' },
+ { id: '93079', username: 'temp_user7' },
+ ],
+ cookieChange: {
+ threadInfos: {
+ '1': {
+ id: '1',
+ type: 12,
+ name: 'GENESIS',
+ description:
+ 'This is the first community on Comm. In the future it will',
+ color: 'c85000',
+ creationTime: 1672934346213,
+ parentThreadID: null,
+ members: [
+ {
+ id: '256',
+ role: '83796',
+ permissions: {
+ know_of: { value: true, source: '1' },
+ membership: { value: false, source: null },
+ visible: { value: true, source: '1' },
+ voiced: { value: true, source: '1' },
+ edit_entries: { value: true, source: '1' },
+ edit_thread: { value: true, source: '1' },
+ edit_thread_description: { value: true, source: '1' },
+ edit_thread_color: { value: true, source: '1' },
+ delete_thread: { value: true, source: '1' },
+ create_subthreads: { value: true, source: '1' },
+ create_sidebars: { value: true, source: '1' },
+ join_thread: { value: false, source: null },
+ edit_permissions: { value: false, source: null },
+ add_members: { value: true, source: '1' },
+ remove_members: { value: true, source: '1' },
+ change_role: { value: true, source: '1' },
+ leave_thread: { value: false, source: null },
+ react_to_message: { value: true, source: '1' },
+ edit_message: { value: true, source: '1' },
+ },
+ isSender: false,
+ },
+ {
+ id: '93079',
+ role: '83795',
+ permissions: {
+ know_of: { value: true, source: '1' },
+ membership: { value: false, source: null },
+ visible: { value: true, source: '1' },
+ voiced: { value: false, source: null },
+ edit_entries: { value: false, source: null },
+ edit_thread: { value: false, source: null },
+ edit_thread_description: { value: false, source: null },
+ edit_thread_color: { value: false, source: null },
+ delete_thread: { value: false, source: null },
+ create_subthreads: { value: false, source: null },
+ create_sidebars: { value: false, source: null },
+ join_thread: { value: false, source: null },
+ edit_permissions: { value: false, source: null },
+ add_members: { value: false, source: null },
+ remove_members: { value: false, source: null },
+ change_role: { value: false, source: null },
+ leave_thread: { value: false, source: null },
+ react_to_message: { value: false, source: null },
+ edit_message: { value: false, source: null },
+ },
+ isSender: false,
+ },
+ ],
+ roles: {
+ '83795': {
+ id: '83795',
+ name: 'Members',
+ permissions: {
+ know_of: true,
+ visible: true,
+ descendant_open_know_of: true,
+ descendant_open_visible: true,
+ descendant_opentoplevel_join_thread: true,
+ },
+ isDefault: true,
+ },
+ '83796': {
+ id: '83796',
+ name: 'Admins',
+ permissions: {
+ know_of: true,
+ visible: true,
+ voiced: true,
+ react_to_message: true,
+ edit_message: true,
+ edit_entries: true,
+ edit_thread: true,
+ edit_thread_color: true,
+ edit_thread_description: true,
+ create_subthreads: true,
+ create_sidebars: true,
+ add_members: true,
+ delete_thread: true,
+ remove_members: true,
+ change_role: true,
+ descendant_know_of: true,
+ descendant_visible: true,
+ descendant_toplevel_join_thread: true,
+ child_join_thread: true,
+ descendant_voiced: true,
+ descendant_edit_entries: true,
+ descendant_edit_thread: true,
+ descendant_edit_thread_color: true,
+ descendant_edit_thread_description: true,
+ descendant_toplevel_create_subthreads: true,
+ descendant_toplevel_create_sidebars: true,
+ descendant_add_members: true,
+ descendant_delete_thread: true,
+ descendant_edit_permissions: true,
+ descendant_remove_members: true,
+ descendant_change_role: true,
+ },
+ isDefault: false,
+ },
+ },
+ currentUser: {
+ role: '83795',
+ permissions: {
+ know_of: { value: true, source: '1' },
+ membership: { value: false, source: null },
+ visible: { value: true, source: '1' },
+ voiced: { value: false, source: null },
+ edit_entries: { value: false, source: null },
+ edit_thread: { value: false, source: null },
+ edit_thread_description: { value: false, source: null },
+ edit_thread_color: { value: false, source: null },
+ delete_thread: { value: false, source: null },
+ create_subthreads: { value: false, source: null },
+ create_sidebars: { value: false, source: null },
+ join_thread: { value: false, source: null },
+ edit_permissions: { value: false, source: null },
+ add_members: { value: false, source: null },
+ remove_members: { value: false, source: null },
+ change_role: { value: false, source: null },
+ leave_thread: { value: false, source: null },
+ react_to_message: { value: false, source: null },
+ edit_message: { value: false, source: null },
+ },
+ subscription: { home: true, pushNotifs: true },
+ unread: true,
+ },
+ repliesCount: 0,
+ containingThreadID: null,
+ community: null,
+ },
+ },
+ userInfos: [],
+ },
+ rawEntryInfos: [],
+ };
+
+ expect(logInResponseValidator.is(response)).toBe(true);
+ expect(
+ logInResponseValidator.is({ ...response, currentUserInfo: undefined }),
+ ).toBe(false);
+ });
+});
diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js
--- a/keyserver/src/responders/user-responders.js
+++ b/keyserver/src/responders/user-responders.js
@@ -3,10 +3,14 @@
import type { Utility as OlmUtility } from '@commapp/olm';
import invariant from 'invariant';
import { ErrorTypes, SiweMessage } from 'siwe';
-import t from 'tcomb';
+import t, { type TInterface } from 'tcomb';
import bcrypt from 'twin-bcrypt';
-import { baseLegalPolicies, policies } from 'lib/facts/policies.js';
+import {
+ baseLegalPolicies,
+ policies,
+ policyTypeValidator,
+} from 'lib/facts/policies.js';
import { hasMinCodeVersion } from 'lib/shared/version-utils.js';
import type {
ResetPasswordRequest,
@@ -33,18 +37,33 @@
IdentityKeysBlob,
SignedIdentityKeysBlob,
} from 'lib/types/crypto-types.js';
-import type { CalendarQuery } from 'lib/types/entry-types.js';
-import { defaultNumberPerThread } from 'lib/types/message-types.js';
+import {
+ type CalendarQuery,
+ rawEntryInfoValidator,
+} from 'lib/types/entry-types.js';
+import {
+ defaultNumberPerThread,
+ rawMessageInfoValidator,
+ messageTruncationStatusesValidator,
+} from 'lib/types/message-types.js';
import type {
SIWEAuthRequest,
SIWEMessage,
SIWESocialProof,
} from 'lib/types/siwe-types.js';
-import type {
- SubscriptionUpdateRequest,
- SubscriptionUpdateResponse,
+import {
+ type SubscriptionUpdateRequest,
+ type SubscriptionUpdateResponse,
+ threadSubscriptionValidator,
} from 'lib/types/subscription-types.js';
-import type { PasswordUpdate } from 'lib/types/user-types.js';
+import { rawThreadInfoValidator } from 'lib/types/thread-types.js';
+import {
+ type PasswordUpdate,
+ loggedOutUserInfoValidator,
+ loggedInUserInfoValidator,
+ oldLoggedInUserInfoValidator,
+ userInfoValidator,
+} from 'lib/types/user-types.js';
import { updateUserAvatarRequestValidator } from 'lib/utils/avatar-utils.js';
import {
identityKeysBlobValidator,
@@ -66,6 +85,7 @@
tEmail,
tOldValidUsername,
tRegex,
+ tID,
} from 'lib/utils/validation-utils.js';
import {
@@ -118,6 +138,11 @@
}),
});
+export const subscriptionUpdateResponseValidator: TInterface<SubscriptionUpdateResponse> =
+ tShape<SubscriptionUpdateResponse>({
+ threadSubscription: threadSubscriptionValidator,
+ });
+
async function userSubscriptionUpdateResponder(
viewer: Viewer,
input: any,
@@ -163,6 +188,11 @@
await checkAndSendPasswordResetEmail(request);
}
+export const logOutResponseValidator: TInterface<LogOutResponse> =
+ tShape<LogOutResponse>({
+ currentUserInfo: loggedOutUserInfoValidator,
+ });
+
async function logOutResponder(viewer: Viewer): Promise<LogOutResponse> {
await validateInput(viewer, null, null);
if (viewer.loggedIn) {
@@ -216,6 +246,20 @@
signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator),
});
+export const registerResponseValidator: TInterface<RegisterResponse> =
+ tShape<RegisterResponse>({
+ id: t.String,
+ rawMessageInfos: t.list(rawMessageInfoValidator),
+ currentUserInfo: t.union([
+ oldLoggedInUserInfoValidator,
+ loggedInUserInfoValidator,
+ ]),
+ cookieChange: tShape({
+ threadInfos: t.dict(t.String, rawThreadInfoValidator),
+ userInfos: t.list(userInfoValidator),
+ }),
+ });
+
async function accountCreationResponder(
viewer: Viewer,
input: any,
@@ -363,6 +407,24 @@
signedIdentityKeysBlob: t.maybe(signedIdentityKeysBlobValidator),
});
+export const logInResponseValidator: TInterface<LogInResponse> =
+ tShape<LogInResponse>({
+ currentUserInfo: t.union([
+ loggedInUserInfoValidator,
+ oldLoggedInUserInfoValidator,
+ ]),
+ rawMessageInfos: t.list(rawMessageInfoValidator),
+ truncationStatuses: messageTruncationStatusesValidator,
+ userInfos: t.list(userInfoValidator),
+ rawEntryInfos: t.maybe(t.list(rawEntryInfoValidator)),
+ serverTime: t.Number,
+ cookieChange: tShape({
+ threadInfos: t.dict(tID, rawThreadInfoValidator),
+ userInfos: t.list(userInfoValidator),
+ }),
+ notAcknowledgedPolicies: t.maybe(t.list(policyTypeValidator)),
+ });
+
async function logInResponder(
viewer: Viewer,
input: any,
diff --git a/lib/facts/policies.js b/lib/facts/policies.js
--- a/lib/facts/policies.js
+++ b/lib/facts/policies.js
@@ -1,5 +1,7 @@
// @flow
+import t, { type TEnums } from 'tcomb';
+
import { values } from '../utils/objects.js';
export const policyTypes = Object.freeze({
@@ -9,5 +11,6 @@
export const policies: $ReadOnlyArray<string> = values(policyTypes);
export type PolicyType = $Values<typeof policyTypes>;
+export const policyTypeValidator: TEnums = t.enums.of(policies);
export const baseLegalPolicies = [policyTypes.tosAndPrivacyPolicy];
diff --git a/lib/types/account-types.js b/lib/types/account-types.js
--- a/lib/types/account-types.js
+++ b/lib/types/account-types.js
@@ -1,5 +1,7 @@
// @flow
+import t, { type TInterface } from 'tcomb';
+
import type { SignedIdentityKeysBlob } from './crypto-types.js';
import type { PlatformDetails } from './device-types.js';
import type {
@@ -7,21 +9,22 @@
CalendarResult,
RawEntryInfo,
} from './entry-types.js';
-import type {
- RawMessageInfo,
- MessageTruncationStatuses,
- GenericMessagesResult,
+import {
+ type RawMessageInfo,
+ type MessageTruncationStatuses,
+ type GenericMessagesResult,
} from './message-types.js';
import type { PreRequestUserState } from './session-types.js';
-import type { RawThreadInfo } from './thread-types.js';
-import type {
- UserInfo,
- LoggedOutUserInfo,
- LoggedInUserInfo,
- OldLoggedInUserInfo,
+import { type RawThreadInfo } from './thread-types.js';
+import {
+ type UserInfo,
+ type LoggedOutUserInfo,
+ type LoggedInUserInfo,
+ type OldLoggedInUserInfo,
} from './user-types.js';
import type { PolicyType } from '../facts/policies.js';
import { values } from '../utils/objects.js';
+import { tShape } from '../utils/validation-utils.js';
export type ResetPasswordRequest = {
+usernameOrEmail: string,
@@ -175,10 +178,6 @@
DEFAULT_NOTIFICATIONS: 'default_user_notifications',
});
-export type DefaultNotificationPayload = {
- +default_user_notifications: ?NotificationTypes,
-};
-
export const notificationTypes = Object.freeze({
FOCUSED: 'focused',
BADGE_ONLY: 'badge_only',
@@ -189,3 +188,12 @@
export const notificationTypeValues: $ReadOnlyArray<NotificationTypes> =
values(notificationTypes);
+
+export type DefaultNotificationPayload = {
+ +default_user_notifications: ?NotificationTypes,
+};
+
+export const defaultNotificationPayloadValidator: TInterface<DefaultNotificationPayload> =
+ tShape<DefaultNotificationPayload>({
+ default_user_notifications: t.maybe(t.enums.of(notificationTypeValues)),
+ });
diff --git a/lib/types/message-types.js b/lib/types/message-types.js
--- a/lib/types/message-types.js
+++ b/lib/types/message-types.js
@@ -1,7 +1,12 @@
// @flow
import invariant from 'invariant';
-import t, { type TUnion, type TInterface } from 'tcomb';
+import t, {
+ type TUnion,
+ type TDict,
+ type TEnums,
+ type TInterface,
+} from 'tcomb';
import { type ClientDBMediaInfo } from './media-types.js';
import { messageTypes, type MessageType } from './message-types-enum.js';
@@ -133,6 +138,7 @@
} from './messages/update-relationship.js';
import { type RelativeUserInfo, type UserInfos } from './user-types.js';
import type { CallServerEndpointResultInfoInterface } from '../utils/call-server-endpoint.js';
+import { values } from '../utils/objects.js';
import { tNumber, tShape, tID } from '../utils/validation-utils.js';
const composableMessageTypes = new Set([
@@ -532,9 +538,14 @@
);
return ourMessageTruncationStatus;
}
+export const messageTruncationStatusValidator: TEnums = t.enums.of(
+ values(messageTruncationStatus),
+);
export type MessageTruncationStatuses = {
[threadID: string]: MessageTruncationStatus,
};
+export const messageTruncationStatusesValidator: TDict<MessageTruncationStatuses> =
+ t.dict(tID, messageTruncationStatusValidator);
export type ThreadCursors = { +[threadID: string]: ?string };
diff --git a/lib/types/relationship-types.js b/lib/types/relationship-types.js
--- a/lib/types/relationship-types.js
+++ b/lib/types/relationship-types.js
@@ -1,7 +1,10 @@
// @flow
+import type { TRefinement } from 'tcomb';
+
import type { AccountUserInfo } from './user-types.js';
import { values } from '../utils/objects.js';
+import { tNumEnum } from '../utils/validation-utils.js';
export const undirectedStatus = Object.freeze({
KNOW_OF: 0,
@@ -24,6 +27,9 @@
BOTH_BLOCKED: 6,
});
export type UserRelationshipStatus = $Values<typeof userRelationshipStatus>;
+export const userRelationshipStatusValidator: TRefinement<number> = tNumEnum(
+ values(userRelationshipStatus),
+);
export const relationshipActions = Object.freeze({
FRIEND: 'friend',
diff --git a/lib/types/user-types.js b/lib/types/user-types.js
--- a/lib/types/user-types.js
+++ b/lib/types/user-types.js
@@ -1,9 +1,18 @@
// @flow
-import type { DefaultNotificationPayload } from './account-types.js';
-import type { ClientAvatar } from './avatar-types.js';
-import type { UserRelationshipStatus } from './relationship-types.js';
+import t, { type TInterface } from 'tcomb';
+
+import {
+ type DefaultNotificationPayload,
+ defaultNotificationPayloadValidator,
+} from './account-types.js';
+import { type ClientAvatar, clientAvatarValidator } from './avatar-types.js';
+import {
+ type UserRelationshipStatus,
+ userRelationshipStatusValidator,
+} from './relationship-types.js';
import type { UserInconsistencyReportCreationRequest } from './report-types.js';
+import { tBool, tShape } from '../utils/validation-utils.js';
export type GlobalUserInfo = {
+id: string,
@@ -23,6 +32,12 @@
+relationshipStatus?: UserRelationshipStatus,
+avatar?: ?ClientAvatar,
};
+export const userInfoValidator: TInterface<UserInfo> = tShape<UserInfo>({
+ id: t.String,
+ username: t.maybe(t.String),
+ relationshipStatus: t.maybe(userRelationshipStatusValidator),
+ avatar: t.maybe(clientAvatarValidator),
+});
export type UserInfos = { +[id: string]: UserInfo };
export type AccountUserInfo = {
@@ -50,6 +65,13 @@
+email: string,
+emailVerified: boolean,
};
+export const oldLoggedInUserInfoValidator: TInterface<OldLoggedInUserInfo> =
+ tShape<OldLoggedInUserInfo>({
+ id: t.String,
+ username: t.String,
+ email: t.String,
+ emailVerified: t.Boolean,
+ });
export type LoggedInUserInfo = {
+id: string,
@@ -57,11 +79,20 @@
+settings?: DefaultNotificationPayload,
+avatar?: ?ClientAvatar,
};
+export const loggedInUserInfoValidator: TInterface<LoggedInUserInfo> =
+ tShape<LoggedInUserInfo>({
+ id: t.String,
+ username: t.String,
+ settings: t.maybe(defaultNotificationPayloadValidator),
+ avatar: t.maybe(clientAvatarValidator),
+ });
export type LoggedOutUserInfo = {
+id: string,
+anonymous: true,
};
+export const loggedOutUserInfoValidator: TInterface<LoggedOutUserInfo> =
+ tShape<LoggedOutUserInfo>({ id: t.String, anonymous: tBool(true) });
export type OldCurrentUserInfo = OldLoggedInUserInfo | LoggedOutUserInfo;
export type CurrentUserInfo = LoggedInUserInfo | LoggedOutUserInfo;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 10:37 PM (23 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5841135
Default Alt Text
D7572.1765060625.diff (24 KB)
Attached To
Mode
D7572: [lib] Introduce user responder validators
Attached
Detach File
Event Timeline
Log In to Comment