class EventsBeaconUpdatedRepSchema(BaseRepSchema): status = fields.CheckedConstant("ok", required=True) event = fields.CheckedConstant("beacon.updated", required=True) beacon_id = fields.UUID(required=True) index = fields.Integer(required=True) src_id = fields.UUID(required=True) src_version = fields.Integer(required=True)
class EventsRealmVlobsUpdatedRepSchema(BaseRepSchema): status = fields.CheckedConstant("ok", required=True) event = fields.CheckedConstant("realm.vlobs_updated", required=True) realm_id = fields.UUID(required=True) checkpoint = fields.Integer(required=True) src_id = fields.UUID(required=True) src_version = fields.Integer(required=True)
class EventsRealmVlobsUpdatedRepSchema(BaseRepSchema): event = fields.EnumCheckedConstant(APIEvent.REALM_VLOBS_UPDATED, required=True) realm_id = fields.UUID(required=True) checkpoint = fields.Integer(required=True) src_id = fields.UUID(required=True) src_version = fields.Integer(required=True)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(ManifestType.FILE_MANIFEST, required=True) id = EntryIDField(required=True) parent = EntryIDField(required=True) # Version 0 means the data is not synchronized version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) size = fields.Integer(required=True, validate=validate.Range(min=0)) blocksize = fields.Integer(required=True, validate=validate.Range(min=8)) blocks = fields.FrozenList(fields.Nested(BlockAccess.SCHEMA_CLS), required=True) @pre_load def fix_legacy(self, data: Dict[str, T]) -> Dict[str, T]: # Compatibility with versions <= 1.14 if data["author"] is None: data["author"] = LOCAL_AUTHOR_LEGACY_PLACEHOLDER return data @post_load def make_obj(self, data: Dict[str, Any]) -> "FileManifest": data.pop("type") return FileManifest(**data)
class OrganizationStatsRepSchema(BaseSchema): data_size = fields.Integer(required=True) metadata_size = fields.Integer(required=True) realms = fields.Integer(required=True) users = fields.Integer(required=True) active_users = fields.Integer(required=True) users_per_profile_detail = fields.List( fields.Nested(UsersPerProfileDetailItemSchema), required=True)
class VlobReadReqSchema(BaseReqSchema): encryption_revision = fields.Integer(required=True) vlob_id = VlobIDField(required=True) version = fields.Integer( required=True, allow_none=True, validate=lambda n: n is None or _validate_version(n)) timestamp = fields.DateTime(required=True, allow_none=True)
class RealmVlobsUpdatedSchema(BaseSchema): __id__ = fields.String(required=True) __signal__ = fields.EnumCheckedConstant(BackendEvent.REALM_VLOBS_UPDATED, required=True) organization_id = OrganizationIDField(required=True) author = DeviceIDField(required=True) realm_id = RealmIDField(required=True) checkpoint = fields.Integer(required=True) src_id = VlobIDField(required=True) src_version = fields.Integer(required=True)
class SCHEMA_CLS(BaseSchema): id = BlockIDField(required=True) key = fields.SecretKey(required=True) offset = fields.Integer(required=True, validate=validate.Range(min=0)) size = fields.Integer(required=True, validate=validate.Range(min=0)) digest = fields.HashDigest(required=True) @post_load def make_obj(self, data: Dict[str, Any]) -> "BlockAccess": return BlockAccess(**data)
class SCHEMA_CLS(BaseSchema): id = ChunkIDField(required=True) start = fields.Integer(required=True, validate=validate.Range(min=0)) stop = fields.Integer(required=True, validate=validate.Range(min=1)) raw_offset = fields.Integer(required=True, validate=validate.Range(min=0)) raw_size = fields.Integer(required=True, validate=validate.Range(min=1)) access = fields.Nested(BlockAccess.SCHEMA_CLS, required=True, allow_none=True) @post_load def make_obj(self, data): return Chunk(**data)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(ManifestType.WORKSPACE_MANIFEST, required=True) id = EntryIDField(required=True) # Version 0 means the data is not synchronized version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) children = fields.FrozenMap( EntryNameField(validate=validate.Length(min=1, max=256)), EntryIDField(required=True), required=True, ) @pre_load def fix_legacy(self, data: Dict[str, T]) -> Dict[str, T]: # Compatibility with versions <= 1.14 if data["author"] is None: data["author"] = LOCAL_AUTHOR_LEGACY_PLACEHOLDER return data @post_load def make_obj(self, data: Dict[str, Any]) -> "WorkspaceManifest": data.pop("type") return WorkspaceManifest(**data)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.CheckedConstant("user_manifest", required=True) id = EntryIDField(required=True) # Version 0 means the data is not synchronized (hence author sould be None) version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) last_processed_message = fields.Integer(required=True, validate=validate.Range(min=0)) workspaces = fields.List(fields.Nested(WorkspaceEntry.SCHEMA_CLS), required=True) @post_load def make_obj(self, data): data.pop("type") return UserManifest(**data)
class VlobListVersionsRepSchema(BaseRepSchema): versions = fields.Map( fields.Integer(required=True), fields.Tuple(fields.DateTime(required=True), DeviceIDField(required=True)), required=True, )
class HumanFindReqSchema(BaseReqSchema): query = fields.String(required=True, allow_none=True) omit_revoked = fields.Boolean(required=True) omit_non_human = fields.Boolean(required=True) # First page is 1 page = fields.Int(required=True, validate=lambda n: n > 0) per_page = fields.Integer(required=True, validate=lambda n: 0 < n <= 100)
class RealmStartReencryptionMaintenanceReqSchema(BaseReqSchema): realm_id = fields.UUID(required=True) encryption_revision = fields.Integer(required=True) timestamp = fields.DateTime(required=True) per_participant_message = fields.Map(UserIDField(), fields.Bytes(required=True), required=True)
class RealmMaintenanceStartedSchema(BaseSchema): __id__ = fields.String(required=True) __signal__ = fields.EnumCheckedConstant( BackendEvent.REALM_MAINTENANCE_STARTED, required=True) organization_id = OrganizationIDField(required=True) author = DeviceIDField(required=True) realm_id = RealmIDField(required=True) encryption_revision = fields.Integer(required=True)
class MessageReceivedSchema(BaseSchema): __id__ = fields.String(required=True) __signal__ = fields.EnumCheckedConstant(BackendEvent.MESSAGE_RECEIVED, required=True) organization_id = OrganizationIDField(required=True) author = DeviceIDField(required=True) recipient = UserIDField(required=True) index = fields.Integer(required=True)
class SCHEMA_CLS(BaseSchema): type = fields.EnumCheckedConstant( LocalManifestType.LOCAL_FILE_MANIFEST, required=True) base = fields.Nested(RemoteFileManifest.SCHEMA_CLS, required=True) need_sync = fields.Boolean(required=True) updated = fields.DateTime(required=True) size = fields.Integer(required=True, validate=validate.Range(min=0)) blocksize = fields.Integer(required=True, validate=validate.Range(min=8)) blocks = fields.FrozenList(fields.FrozenList( fields.Nested(Chunk.SCHEMA_CLS)), required=True) @post_load def make_obj(self, data): data.pop("type") return LocalFileManifest(**data)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.CheckedConstant("file_manifest", required=True) id = EntryIDField(required=True) parent = EntryIDField(required=True) # Version 0 means the data is not synchronized (hence author sould be None) version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) size = fields.Integer(required=True, validate=validate.Range(min=0)) blocksize = fields.Integer(required=True, validate=validate.Range(min=8)) blocks = fields.FrozenList(fields.Nested(BlockAccess.SCHEMA_CLS), required=True) @post_load def make_obj(self, data): data.pop("type") return FileManifest(**data)
class OrganizationUpdateReqSchema(BaseSchema): # /!\ Missing field and field set to `None` does not mean the same thing: # - missing field: don't modify this field # - field set to `None`: `None` is a valid value to use for this field is_expired = fields.Boolean(required=False) user_profile_outsider_allowed = fields.Boolean(required=False) # `None` stands for "no limit" here active_users_limit = fields.Integer(required=False, allow_none=True, validate=lambda x: x >= 0)
class OrganizationCreateReqSchema(BaseSchema): organization_id = OrganizationIDField(required=True) # /!\ Missing field and field set to `None` does not mean the same thing: # - missing field: ask the backend to use it default value for this field # - field set to `None`: `None` is a valid value to use for this field user_profile_outsider_allowed = fields.Boolean(required=False, allow_none=False) # `None` stands for "no limit" here active_users_limit = fields.Integer(required=False, allow_none=True, validate=lambda x: x >= 0)
class VlobReadRepSchema(BaseRepSchema): version = fields.Integer(required=True, validate=_validate_version) blob = fields.Bytes(required=True) author = DeviceIDField(required=True) timestamp = fields.DateTime(required=True) # This field is used by the client to figure out if its role certificate cache is up-to-date enough # to be able to perform the proper integrity checks on the manifest timestamp. # The `missing=None` argument is used to provide compatibilty of new clients with old backends. # New in API version 2.3 (Parsec 2.6.0) author_last_role_granted_on = fields.DateTime(required=False, allow_none=True, missing=None)
class VlobCreateReqSchema(BaseReqSchema): realm_id = fields.UUID(required=True) encryption_revision = fields.Integer(required=True) vlob_id = fields.UUID(required=True) # If blob contains a signed message, it timestamp cannot be directly enforced # by the backend (given the message is probably also encrypted). # Hence the timestamp is passed in clear so backend can reject the message # if it considers the timestamp invalid. On top of that each client asking # for the message will receive the declared timestamp to check against # the actual timestamp within the message. timestamp = fields.DateTime(required=True) blob = fields.Bytes(required=True)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(ManifestType.USER_MANIFEST, required=True) id = EntryIDField(required=True) # Version 0 means the data is not synchronized version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) last_processed_message = fields.Integer(required=True, validate=validate.Range(min=0)) workspaces = fields.List(fields.Nested(WorkspaceEntry.SCHEMA_CLS), required=True) @pre_load def fix_legacy(self, data: Dict[str, T]) -> Dict[str, T]: # Compatibility with versions <= 1.14 if data["author"] is None: data["author"] = LOCAL_AUTHOR_LEGACY_PLACEHOLDER return data @post_load def make_obj(self, data: Dict[str, Any]) -> "UserManifest": data.pop("type") return UserManifest(**data)
class SCHEMA_CLS(OneOfSchema, BaseSignedDataSchema): type_field = "type" version = fields.Integer(required=True, validate=validate.Range(min=0)) @property def type_schemas(self): return { ManifestType.FILE_MANIFEST: FileManifest.SCHEMA_CLS, ManifestType.FOLDER_MANIFEST: FolderManifest.SCHEMA_CLS, ManifestType.WORKSPACE_MANIFEST: WorkspaceManifest.SCHEMA_CLS, ManifestType.USER_MANIFEST: UserManifest.SCHEMA_CLS, } def get_obj_type(self, obj): return obj["type"]
class SCHEMA_CLS(BaseSchema): type = fields.EnumCheckedConstant( LocalManifestType.LOCAL_USER_MANIFEST, required=True) base = fields.Nested(RemoteUserManifest.SCHEMA_CLS, required=True) need_sync = fields.Boolean(required=True) updated = fields.DateTime(required=True) last_processed_message = fields.Integer(required=True, validate=validate.Range(min=0)) workspaces = fields.FrozenList(fields.Nested( WorkspaceEntry.SCHEMA_CLS), required=True) @post_load def make_obj(self, data): data.pop("type") return LocalUserManifest(**data)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.CheckedConstant("sharing.granted", required=True) name = fields.String(required=True) 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 SCHEMA_CLS(BaseSignedDataSchema): type = fields.CheckedConstant("workspace_manifest", required=True) id = EntryIDField(required=True) # Version 0 means the data is not synchronized (hence author sould be None) version = fields.Integer(required=True, validate=validate.Range(min=0)) created = fields.DateTime(required=True) updated = fields.DateTime(required=True) children = fields.FrozenMap( EntryNameField(validate=validate.Length(min=1, max=256)), EntryIDField(required=True), required=True, ) @post_load def make_obj(self, data): data.pop("type") return WorkspaceManifest(**data)
class SCHEMA_CLS(BaseSignedDataSchema): type = fields.EnumCheckedConstant(MessageContentType.SHARING_GRANTED, required=True) name = EntryNameField(required=True) 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( # type: ignore[misc] self, data: Dict[str, Any]) -> "SharingGrantedMessageContent": data.pop("type") return SharingGrantedMessageContent(**data)
class SCHEMA_CLS(OneOfSchema, BaseSignedDataSchema): type_field = "type" version = fields.Integer(required=True, validate=validate.Range(min=0)) id = EntryIDField(required=True) @property def type_schemas( self ) -> Dict[ManifestType, Type[OneOfSchema]]: # type: ignore[override] return { ManifestType.FILE_MANIFEST: FileManifest.SCHEMA_CLS, ManifestType.FOLDER_MANIFEST: FolderManifest.SCHEMA_CLS, ManifestType.WORKSPACE_MANIFEST: WorkspaceManifest.SCHEMA_CLS, ManifestType.USER_MANIFEST: UserManifest.SCHEMA_CLS, } def get_obj_type(self, obj: Dict[str, T]) -> T: return obj["type"]
class SCHEMA_CLS(BaseSchema): type = fields.EnumCheckedConstant( LocalManifestType.LOCAL_USER_MANIFEST, required=True) base = fields.Nested(_PyUserManifest.SCHEMA_CLS, required=True) need_sync = fields.Boolean(required=True) updated = fields.DateTime(required=True) last_processed_message = fields.Integer(required=True, validate=validate.Range(min=0)) workspaces = fields.FrozenList(fields.Nested( _PyWorkspaceEntry.SCHEMA_CLS), required=True) # Added in Parsec v1.15 # Speculative placeholders are created when we want to access the # user manifest but didn't retrieve it from backend yet. This implies: # - non-placeholders cannot be speculative # - the only non-speculative placeholder is the placeholder initialized # during the initial user claim (by opposition of subsequent device # claims on the same user) # This speculative information is useful during merge to understand if # a data is not present in the placeholder compared with a remote because: # a) the data is not locally known (speculative is True) # b) the data is known, but has been locally removed (speculative is False) # Prevented to be `required=True` by backward compatibility speculative = fields.Boolean(allow_none=False, required=False, missing=False) @post_load def make_obj(self, data): data.pop("type") # TODO: Ensure non-placeholder cannot be marked speculative assert data["speculative"] is False or data["base"].version == 0 # TODO: Should this assert be in remote workspace manifest definition instead ? # TODO: but in theory remote workspace manifest should assert version > 0 ! assert data["base"].version != 0 or not data["base"].workspaces return LocalUserManifest(**data)