class SCHEMA_CLS(BaseSchema): organization_addr = BackendOrganizationAddrField(required=True) device_id = DeviceIDField(required=True) device_label = fields.String(allow_none=True, missing=None) human_handle = HumanHandleField(allow_none=True, missing=None) signing_key = fields.SigningKey(required=True) private_key = fields.PrivateKey(required=True) # `profile` replaces `is_admin` field (which is still required for backward # compatibility), hence `None` is not allowed is_admin = fields.Boolean(required=True) profile = UserProfileField(allow_none=False) user_manifest_id = EntryIDField(required=True) user_manifest_key = fields.SecretKey(required=True) local_symkey = fields.SecretKey(required=True) @post_load def make_obj(self, data): # Handle legacy `is_admin` field default_profile = UserProfile.ADMIN if data.pop( "is_admin") else UserProfile.STANDARD try: profile = data["profile"] except KeyError: data["profile"] = default_profile else: if default_profile == UserProfile.ADMIN and profile != UserProfile.ADMIN: raise ValidationError( "Fields `profile` and `is_admin` have incompatible values" ) return LocalDevice(**data)
class DeviceFileSchema(BaseSchema): type = fields.CheckedConstant("password", required=True) salt = fields.Bytes(required=True) ciphertext = fields.Bytes(required=True) # Since human_handle/device_label has been introduced, device_id is # redacted (i.e. user_id and device_name are 2 random uuids), hence # those fields have been added to the device file so the login page in # the GUI can use them to provide useful information. human_handle = HumanHandleField(allow_none=True, missing=None) device_label = fields.String(allow_none=True, missing=None) # Store device and organization ID in the device file # For legacy versions, this information is available in the file name device_id = DeviceIDField(allow_none=True, missing=None) organization_id = OrganizationIDField(allow_none=True, missing=None) root_verify_key_hash = fields.String(allow_none=True, missing=None)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(MessageContentType.PING, required=True) ping = fields.String(required=True) @post_load def make_obj(self, data): data.pop("type") return PingMessageContent(**data)
class SCHEMA_CLS(BaseSchema): type = fields.CheckedConstant("invite_device_data", required=True) # Claimer ask for device_label, but greeter has final word on this requested_device_label = fields.String(allow_none=True, missing=None) verify_key = fields.VerifyKey(required=True) @post_load def make_obj(self, data): data.pop("type") return InviteDeviceData(**data)
class SCHEMA_CLS(BaseSchema): type = fields.CheckedConstant("device_claim", required=True) token = fields.String(required=True) device_id = DeviceIDField(required=True) verify_key = fields.VerifyKey(required=True) answer_public_key = fields.PublicKey(required=True) @post_load def make_obj(self, data): data.pop("type") return APIV1_DeviceClaimContent(**data)
class SCHEMA_CLS(BaseSchema): type = fields.CheckedConstant("user_claim", required=True) token = fields.String(required=True) # Note claiming user also imply creating a first device device_id = DeviceIDField(required=True) public_key = fields.PublicKey(required=True) verify_key = fields.VerifyKey(required=True) @post_load def make_obj(self, data): data.pop("type") return APIV1_UserClaimContent(**data)
class SCHEMA_CLS(BaseSchema): type = fields.CheckedConstant("invite_user_confirmation", required=True) device_id = DeviceIDField(required=True) device_label = fields.String(allow_none=True, missing=None) human_handle = HumanHandleField(allow_none=True, missing=None) profile = UserProfileField(required=True) root_verify_key = fields.VerifyKey(required=True) @post_load def make_obj(self, data): data.pop("type") return InviteUserConfirmation(**data)
class BaseReqSchema(BaseSchema): cmd = fields.String(required=True) @post_load def _drop_cmd_field(self, item): if self.drop_cmd_field: item.pop("cmd") return item def __init__(self, drop_cmd_field=True, **kwargs): super().__init__(**kwargs) self.drop_cmd_field = drop_cmd_field
class SCHEMA_CLS(BaseSchema): type = fields.CheckedConstant("invite_user_data", required=True) # Claimer ask for device_label/human_handle, but greeter has final word on this requested_device_label = fields.String(allow_none=True, missing=None) requested_human_handle = HumanHandleField(allow_none=True, missing=None) # Note claiming user also imply creating a first device public_key = fields.PublicKey(required=True) verify_key = fields.VerifyKey(required=True) @post_load def make_obj(self, data): data.pop("type") return InviteUserData(**data)
class APIV1_OrganizationBootstrapReqSchema(BaseReqSchema): bootstrap_token = fields.String(required=True) root_verify_key = fields.VerifyKey(required=True) user_certificate = fields.Bytes(required=True) device_certificate = fields.Bytes(required=True) # Same certificates than above, but expurged of human_handle/device_label # Backward compatibility prevent those field to be required, however # they should be considered so by recent version of Parsec (hence the # `allow_none=False`). # Hence only old version of Parsec will provide a payload with missing # redacted fields. In such case we consider the non-redacted can also # be used as redacted given the to-be-redacted fields have been introduce # in later version of Parsec. redacted_user_certificate = fields.Bytes(allow_none=False) redacted_device_certificate = fields.Bytes(allow_none=False) root_verify_key = fields.VerifyKey(required=True)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(MessageContentType.SHARING_GRANTED, required=True) name = fields.String(required=True) author = DeviceIDField(required=True, allow_none=False) id = EntryIDField(required=True) encryption_revision = fields.Integer(required=True) encrypted_on = fields.DateTime(required=True) key = fields.SecretKey(required=True) # Don't include role given the only reliable way to get this information # is to fetch the realm role certificate from the backend. # Besides, we will also need the message sender's realm role certificate # to make sure he is an owner. @post_load def make_obj(self, data): data.pop("type") return SharingGrantedMessageContent(**data)
class APIV1_UserFindReqSchema(BaseReqSchema): query = fields.String(missing=None) omit_revoked = fields.Boolean(missing=False) page = fields.Int(missing=1, validate=lambda n: n > 0) per_page = fields.Integer(missing=100, validate=lambda n: 0 < n <= 100)
class PingReqSchema(BaseReqSchema): ping = fields.String(required=True)
class InviteListItemUserSchema(BaseSchema): type = fields.EnumCheckedConstant(InvitationType.USER, required=True) token = fields.UUID(required=True) created_on = fields.DateTime(required=True) claimer_email = fields.String(required=True) status = InvitationStatusField(required=True)
class ErrorRepSchema(BaseRepSchema): status = fields.String(required=True) reason = fields.String(allow_none=True) # TODO: should errors be better checked ? errors = fields.Dict(allow_none=True)
class APIV1_OrganizationCreateRepSchema(BaseRepSchema): bootstrap_token = fields.String(required=True) expiration_date = fields.DateTime(allow_none=True, required=False)
class InviteInfoUserRepSchema(BaseRepSchema): type = fields.EnumCheckedConstant(InvitationType.USER, required=True) claimer_email = fields.String(required=True) greeter_user_id = UserIDField(required=True) greeter_human_handle = HumanHandleField(allow_none=True, missing=True)
class NewInstanceReqSchema(BaseSchema): cmd = fields.EnumCheckedConstant(IPCCommand.NEW_INSTANCE, required=True) start_arg = fields.String(allow_none=True)
class APIV1_HandshakeAdministrationAnswerSchema(BaseSchema): handshake = fields.CheckedConstant("answer", required=True) type = fields.EnumCheckedConstant(APIV1_HandshakeType.ADMINISTRATION, required=True) client_api_version = ApiVersionField(required=True) token = fields.String(required=True)
def __init__(self, **kwargs): email = fields.String(required=True) label = fields.String(required=True) super().__init__(email, label, **kwargs)
class OrganizationBootstrapWebhookSchema(BaseSchema): organization_id = OrganizationIDField(required=True) device_id = DeviceIDField(required=True) device_label = fields.String(allow_none=True, required=True) human_email = fields.String(allow_none=True, required=True) human_label = fields.String(allow_none=True, required=True)
class HandshakeResultSchema(BaseSchema): handshake = fields.CheckedConstant("result", required=True) result = fields.String(required=True) help = fields.String(missing=None)
class InviteNewUserReqSchema(BaseReqSchema): type = fields.EnumCheckedConstant(InvitationType.USER, required=True) claimer_email = fields.String(required=True) send_email = fields.Boolean(required=True)
class CommandRepSchema(BaseSchema): status = fields.String(required=True) reason = fields.String(allow_none=True)
class EventsPingedRepSchema(BaseRepSchema): event = fields.EnumCheckedConstant(APIEvent.PINGED, required=True) ping = fields.String(validate=validate.Length(max=64), required=True)