diff --git a/server/parsec/_parsec_pyi/protocol/authenticated_cmds/v4/vlob_update.pyi b/server/parsec/_parsec_pyi/protocol/authenticated_cmds/v4/vlob_update.pyi index 7d50525480d..c54f04181d9 100644 --- a/server/parsec/_parsec_pyi/protocol/authenticated_cmds/v4/vlob_update.pyi +++ b/server/parsec/_parsec_pyi/protocol/authenticated_cmds/v4/vlob_update.pyi @@ -62,7 +62,7 @@ class RepVlobNotFound(Rep): self, ) -> None: ... -class RepVlobVersionAlreadyExists(Rep): +class RepBadVlobVersion(Rep): def __init__( self, ) -> None: ... diff --git a/server/parsec/components/memory/vlob.py b/server/parsec/components/memory/vlob.py index 145a9def0b5..a8dab1575c6 100644 --- a/server/parsec/components/memory/vlob.py +++ b/server/parsec/components/memory/vlob.py @@ -28,6 +28,7 @@ from parsec.components.vlob import ( BaseVlobComponent, RejectedBySequesterService, + SequesterInconsistency, SequesterServiceNotAvailable, VlobCreateBadOutcome, VlobPollChangesAsUserBadOutcome, @@ -74,6 +75,7 @@ async def create( | RequireGreaterTimestamp | RejectedBySequesterService | SequesterServiceNotAvailable + | SequesterInconsistency ): author_user_id = author.user_id try: @@ -108,9 +110,7 @@ async def create( # We only accept the last key if len(realm.key_rotations) != key_index: return BadKeyIndex( - last_key_rotation_certificate_timestamp=realm.key_rotations[-1].cooked.timestamp - if realm.key_rotations - else None + last_realm_certificate_timestamp=realm.last_realm_certificate_timestamp ) if vlob_id in org.vlobs: @@ -126,7 +126,9 @@ async def create( if org.is_sequestered: assert org.sequester_services is not None if sequester_blob is None or sequester_blob.keys() != org.sequester_services.keys(): - return VlobCreateBadOutcome.SEQUESTER_INCONSISTENCY + return SequesterInconsistency( + last_common_certificate_timestamp=org.last_common_certificate_timestamp + ) blob_for_storage_sequester_services = {} for service_id, service in org.sequester_services.items(): @@ -208,6 +210,7 @@ async def update( | RequireGreaterTimestamp | RejectedBySequesterService | SequesterServiceNotAvailable + | SequesterInconsistency ): author_user_id = author.user_id try: @@ -243,9 +246,7 @@ async def update( # We only accept the last key if len(realm.key_rotations) != key_index: return BadKeyIndex( - last_key_rotation_certificate_timestamp=realm.key_rotations[-1].cooked.timestamp - if realm.key_rotations - else None + last_realm_certificate_timestamp=realm.last_realm_certificate_timestamp ) maybe_error = timestamps_in_the_ballpark(timestamp, now) @@ -259,7 +260,9 @@ async def update( if org.is_sequestered: assert org.sequester_services is not None if sequester_blob is None or sequester_blob.keys() != org.sequester_services.keys(): - return VlobUpdateBadOutcome.SEQUESTER_INCONSISTENCY + return SequesterInconsistency( + last_common_certificate_timestamp=org.last_common_certificate_timestamp + ) blob_for_storage_sequester_services = {} for service_id, service in org.sequester_services.items(): @@ -287,7 +290,7 @@ async def update( blob_for_storage_sequester_services = None if version != len(vlobs) + 1: - return VlobUpdateBadOutcome.VLOB_VERSION_ALREADY_EXISTS + return VlobUpdateBadOutcome.BAD_VLOB_VERSION # All checks are good, now we do the actual insertion diff --git a/server/parsec/components/vlob.py b/server/parsec/components/vlob.py index a9b9f089bd1..1e0e3370fbd 100644 --- a/server/parsec/components/vlob.py +++ b/server/parsec/components/vlob.py @@ -26,6 +26,11 @@ logger = get_logger() +@dataclass(slots=True) +class SequesterInconsistency: + last_common_certificate_timestamp: DateTime + + @dataclass(slots=True) class SequesterServiceNotAvailable: service_id: SequesterServiceID @@ -56,7 +61,6 @@ class VlobReadResult: "REALM_NOT_FOUND", "VLOB_ALREADY_EXISTS", "ORGANIZATION_NOT_SEQUESTERED", - "SEQUESTER_INCONSISTENCY", ), ) VlobUpdateBadOutcome = Enum( @@ -68,10 +72,8 @@ class VlobReadResult: "AUTHOR_REVOKED", "AUTHOR_NOT_ALLOWED", "VLOB_NOT_FOUND", - "VLOB_VERSION_ALREADY_EXISTS", + "BAD_VLOB_VERSION", "ORGANIZATION_NOT_SEQUESTERED", - "SEQUESTER_INCONSISTENCY", - "SEQUESTER_SERVICE_UNAVAILABLE", ), ) VlobReadAsUserBadOutcome = Enum( @@ -127,6 +129,7 @@ async def create( | RequireGreaterTimestamp | RejectedBySequesterService | SequesterServiceNotAvailable + | SequesterInconsistency ): raise NotImplementedError @@ -150,6 +153,7 @@ async def update( | RequireGreaterTimestamp | RejectedBySequesterService | SequesterServiceNotAvailable + | SequesterInconsistency ): raise NotImplementedError @@ -285,7 +289,7 @@ async def api_vlob_create( return authenticated_cmds.latest.vlob_create.RepAuthorNotAllowed() case BadKeyIndex() as error: return authenticated_cmds.latest.vlob_create.RepBadKeyIndex( - last_key_rotation_certificate_timestamp=error.last_key_rotation_certificate_timestamp, + last_realm_certificate_timestamp=error.last_realm_certificate_timestamp, ) case VlobCreateBadOutcome.REALM_NOT_FOUND: return authenticated_cmds.latest.vlob_create.RepRealmNotFound() @@ -293,8 +297,6 @@ async def api_vlob_create( return authenticated_cmds.latest.vlob_create.RepVlobAlreadyExists() case VlobCreateBadOutcome.ORGANIZATION_NOT_SEQUESTERED: return authenticated_cmds.latest.vlob_create.RepOrganizationNotSequestered() - case VlobCreateBadOutcome.SEQUESTER_INCONSISTENCY: - return authenticated_cmds.latest.vlob_create.RepSequesterInconsistency() case TimestampOutOfBallpark() as error: return authenticated_cmds.latest.vlob_create.RepTimestampOutOfBallpark( server_timestamp=error.server_timestamp, @@ -306,6 +308,10 @@ async def api_vlob_create( return authenticated_cmds.latest.vlob_create.RepRequireGreaterTimestamp( strictly_greater_than=error.strictly_greater_than ) + case SequesterInconsistency() as error: + return authenticated_cmds.latest.vlob_create.RepSequesterInconsistency( + last_common_certificate_timestamp=error.last_common_certificate_timestamp + ) case RejectedBySequesterService() as error: return authenticated_cmds.latest.vlob_create.RepRejectedBySequesterService( service_id=error.service_id, @@ -354,18 +360,14 @@ async def api_vlob_update( return authenticated_cmds.latest.vlob_update.RepAuthorNotAllowed() case BadKeyIndex() as error: return authenticated_cmds.latest.vlob_update.RepBadKeyIndex( - last_key_rotation_certificate_timestamp=error.last_key_rotation_certificate_timestamp, + last_realm_certificate_timestamp=error.last_realm_certificate_timestamp, ) - case VlobUpdateBadOutcome.VLOB_VERSION_ALREADY_EXISTS: - return authenticated_cmds.latest.vlob_update.RepVlobVersionAlreadyExists() + case VlobUpdateBadOutcome.BAD_VLOB_VERSION: + return authenticated_cmds.latest.vlob_update.RepBadVlobVersion() case VlobUpdateBadOutcome.VLOB_NOT_FOUND: return authenticated_cmds.latest.vlob_update.RepVlobNotFound() case VlobUpdateBadOutcome.ORGANIZATION_NOT_SEQUESTERED: return authenticated_cmds.latest.vlob_update.RepOrganizationNotSequestered() - case VlobUpdateBadOutcome.SEQUESTER_INCONSISTENCY: - return authenticated_cmds.latest.vlob_update.RepSequesterInconsistency() - case VlobUpdateBadOutcome.SEQUESTER_SERVICE_UNAVAILABLE: - return authenticated_cmds.latest.vlob_update.RepSequesterServiceUnavailable() case TimestampOutOfBallpark() as error: return authenticated_cmds.latest.vlob_update.RepTimestampOutOfBallpark( server_timestamp=error.server_timestamp, @@ -377,6 +379,10 @@ async def api_vlob_update( return authenticated_cmds.latest.vlob_update.RepRequireGreaterTimestamp( strictly_greater_than=error.strictly_greater_than ) + case SequesterInconsistency() as error: + return authenticated_cmds.latest.vlob_update.RepSequesterInconsistency( + last_common_certificate_timestamp=error.last_common_certificate_timestamp + ) case RejectedBySequesterService() as error: return authenticated_cmds.latest.vlob_update.RepRejectedBySequesterService( service_id=error.service_id,