diff --git a/lib/types/tunnelbroker/connection-initialization-response-types.js b/lib/types/tunnelbroker/connection-initialization-response-types.js new file mode 100644 index 000000000..078df8259 --- /dev/null +++ b/lib/types/tunnelbroker/connection-initialization-response-types.js @@ -0,0 +1,26 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type ConnectionInitializationStatus = + | { +type: 'Success' } + | { +type: 'Error', +data: string }; + +const connectionInitializationStatusValidator = t.union([ + tShape({ type: tString('Success') }), + tShape({ type: tString('Error'), data: t.String }), +]); + +export type ConnectionInitializationResponse = { + +type: 'ConnectionInitializationResponse', + +status: ConnectionInitializationStatus, +}; + +export const connectionInitializationResponseValidator: TInterface = + tShape({ + type: tString('ConnectionInitializationResponse'), + status: connectionInitializationStatusValidator, + }); diff --git a/lib/types/tunnelbroker/keys-types.js b/lib/types/tunnelbroker/keys-types.js new file mode 100644 index 000000000..f84c2df62 --- /dev/null +++ b/lib/types/tunnelbroker/keys-types.js @@ -0,0 +1,19 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type RefreshKeyRequest = { + +type: 'RefreshKeyRequest', + +deviceID: string, + +numberOfKeys: number, +}; + +export const refreshKeysRequestValidator: TInterface = + tShape({ + type: tString('RefreshKeyRequest'), + deviceID: t.String, + numberOfKeys: t.Number, + }); diff --git a/lib/types/tunnelbroker/message-receive-confirmation-types.js b/lib/types/tunnelbroker/message-receive-confirmation-types.js new file mode 100644 index 000000000..db7c32ab9 --- /dev/null +++ b/lib/types/tunnelbroker/message-receive-confirmation-types.js @@ -0,0 +1,17 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type MessageReceiveConfirmation = { + +type: 'MessageReceiveConfirmation', + +messageIDs: $ReadOnlyArray, +}; + +export const messageReceiveConfirmationValidator: TInterface = + tShape({ + type: tString('MessageReceiveConfirmation'), + messageIDs: t.list(t.String), + }); diff --git a/lib/types/tunnelbroker/message-to-device-request-status-types.js b/lib/types/tunnelbroker/message-to-device-request-status-types.js new file mode 100644 index 000000000..86204c482 --- /dev/null +++ b/lib/types/tunnelbroker/message-to-device-request-status-types.js @@ -0,0 +1,40 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type Failure = { + +id: string, + +error: string, +}; + +const failureValidator: TInterface = tShape({ + id: t.String, + error: t.String, +}); + +export type MessageSentStatus = + | { +type: 'Success', +data: string } + | { +type: 'Error', +data: Failure } + | { +type: 'InvalidRequest' } + | { +type: 'SerializationError', +data: string }; + +const messageSentStatusValidator = t.union([ + tShape({ type: tString('Success'), data: t.String }), + tShape({ type: tString('Error'), data: failureValidator }), + tShape({ type: tString('InvalidRequest') }), + tShape({ type: tString('SerializationError'), data: t.String }), +]); + +export type MessageToDeviceRequestStatus = { + +type: 'MessageToDeviceRequestStatus', + +clientMessageIDs: $ReadOnlyArray, +}; + +export const messageToDeviceRequestStatusValidator: TInterface = + tShape({ + type: tString('MessageToDeviceRequestStatus'), + clientMessageIDs: t.list(messageSentStatusValidator), + }); diff --git a/lib/types/tunnelbroker/message-to-device-request-types.js b/lib/types/tunnelbroker/message-to-device-request-types.js new file mode 100644 index 000000000..9302e1aad --- /dev/null +++ b/lib/types/tunnelbroker/message-to-device-request-types.js @@ -0,0 +1,21 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type MessageToDeviceRequest = { + +type: 'MessageToDeviceRequest', + +clientMessageID: string, + +deviceID: string, + +payload: string, +}; + +export const messageToDeviceRequestValidator: TInterface = + tShape({ + type: tString('MessageToDeviceRequest'), + clientMessageID: t.String, + deviceID: t.String, + payload: t.String, + }); diff --git a/lib/types/tunnelbroker/message-to-device-types.js b/lib/types/tunnelbroker/message-to-device-types.js new file mode 100644 index 000000000..cf59d0fa2 --- /dev/null +++ b/lib/types/tunnelbroker/message-to-device-types.js @@ -0,0 +1,21 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type MessageToDevice = { + +type: 'MessageToDevice', + +messageID: string, + +deviceID: string, + +payload: string, +}; + +export const messageToDeviceValidator: TInterface = + tShape({ + type: tString('MessageToDevice'), + messageID: t.String, + deviceID: t.String, + payload: t.String, + }); diff --git a/lib/types/tunnelbroker/messages.js b/lib/types/tunnelbroker/messages.js new file mode 100644 index 000000000..904c8e8e1 --- /dev/null +++ b/lib/types/tunnelbroker/messages.js @@ -0,0 +1,74 @@ +// @flow + +import type { TUnion } from 'tcomb'; +import t from 'tcomb'; + +import { + type ConnectionInitializationResponse, + connectionInitializationResponseValidator, +} from './connection-initialization-response-types.js'; +import { + type RefreshKeyRequest, + refreshKeysRequestValidator, +} from './keys-types.js'; +import { + type MessageReceiveConfirmation, + messageReceiveConfirmationValidator, +} from './message-receive-confirmation-types.js'; +import { + type MessageToDeviceRequestStatus, + messageToDeviceRequestStatusValidator, +} from './message-to-device-request-status-types.js'; +import { + type MessageToDeviceRequest, + messageToDeviceRequestValidator, +} from './message-to-device-request-types.js'; +import { + type MessageToDevice, + messageToDeviceValidator, +} from './message-to-device-types.js'; +import { + type ConnectionInitializationMessage, + connectionInitializationMessageValidator, +} from './session-types.js'; + +/* + * This file defines types and validation for messages exchanged + * with the Tunnelbroker. The definitions in this file should remain in sync + * with the structures defined in the corresponding + * Rust file at `shared/tunnelbroker_messages/src/messages/mod.rs`. + * + * If you edit the definitions in one file, + * please make sure to update the corresponding definitions in the other. + * + */ + +export const tunnelbrokerMessageTypes = Object.freeze({ + REFRESH_KEYS_REQUEST: 'RefreshKeyRequest', + CONNECTION_INITIALIZATION_MESSAGE: 'ConnectionInitializationMessage', + CONNECTION_INITIALIZATION_RESPONSE: 'ConnectionInitializationResponse', + MESSAGE_TO_DEVICE_REQUEST_STATUS: 'MessageToDeviceRequestStatus', + MESSAGE_TO_DEVICE_REQUEST: 'MessageToDeviceRequest', + MESSAGE_TO_DEVICE: 'MessageToDevice', + MESSAGE_RECEIVE_CONFIRMATION: 'MessageReceiveConfirmation', +}); + +export const tunnelbrokerMessageValidator: TUnion = + t.union([ + refreshKeysRequestValidator, + connectionInitializationMessageValidator, + connectionInitializationResponseValidator, + messageToDeviceRequestStatusValidator, + messageToDeviceRequestValidator, + messageToDeviceValidator, + messageReceiveConfirmationValidator, + ]); + +export type TunnelbrokerMessage = + | RefreshKeyRequest + | ConnectionInitializationMessage + | ConnectionInitializationResponse + | MessageToDeviceRequestStatus + | MessageToDeviceRequest + | MessageToDevice + | MessageReceiveConfirmation; diff --git a/lib/types/tunnelbroker/session-types.js b/lib/types/tunnelbroker/session-types.js new file mode 100644 index 000000000..ab15da5ed --- /dev/null +++ b/lib/types/tunnelbroker/session-types.js @@ -0,0 +1,31 @@ +// @flow + +import type { TInterface } from 'tcomb'; +import t from 'tcomb'; + +import { tShape, tString } from '../../utils/validation-utils.js'; + +export type DeviceTypes = 'mobile' | 'web' | 'keyserver'; + +export type ConnectionInitializationMessage = { + +type: 'ConnectionInitializationMessage', + +deviceID: string, + +accessToken: string, + +userID: string, + +notifyToken?: ?string, + +deviceType: DeviceTypes, + +deviceAppVersion?: ?string, + +deviceOS?: ?string, +}; + +export const connectionInitializationMessageValidator: TInterface = + tShape({ + type: tString('ConnectionInitializationMessage'), + deviceID: t.String, + accessToken: t.String, + userID: t.String, + notifyToken: t.maybe(t.String), + deviceType: t.enums.of(['Mobile', 'Web', 'Keyserver']), + deviceAppVersion: t.maybe(t.String), + deviceOS: t.maybe(t.String), + }); diff --git a/shared/tunnelbroker_messages/src/messages/mod.rs b/shared/tunnelbroker_messages/src/messages/mod.rs index 3364d4638..3282c9b33 100644 --- a/shared/tunnelbroker_messages/src/messages/mod.rs +++ b/shared/tunnelbroker_messages/src/messages/mod.rs @@ -1,34 +1,42 @@ //! Messages sent between Tunnelbroker and a device. pub mod connection_initialization_response; pub mod keys; pub mod message_receive_confirmation; pub mod message_to_device; pub mod message_to_device_request; pub mod message_to_device_request_status; pub mod session; pub use connection_initialization_response::*; pub use keys::*; pub use message_receive_confirmation::*; pub use message_to_device::*; pub use message_to_device_request::*; pub use message_to_device_request_status::*; pub use session::*; use serde::{Deserialize, Serialize}; +// This file defines types and validation for messages exchanged +// with the Tunnelbroker. The definitions in this file should remain in sync +// with the structures defined in the corresponding +// JavaScript file at `lib/types/tunnelbroker/messages.js`. + +// If you edit the definitions in one file, +// please make sure to update the corresponding definitions in the other. + #[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum Messages { RefreshKeysRequest(RefreshKeyRequest), ConnectionInitializationMessage(ConnectionInitializationMessage), ConnectionInitializationResponse(ConnectionInitializationResponse), // MessageToDeviceRequestStatus must be placed before MessageToDeviceRequest. // This is due to serde's pattern matching behavior where it prioritizes // the first matching pattern it encounters. MessageToDeviceRequestStatus(MessageToDeviceRequestStatus), MessageToDeviceRequest(MessageToDeviceRequest), MessageToDevice(MessageToDevice), MessageReceiveConfirmation(MessageReceiveConfirmation), }