async def read( self, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: VlobID, version: Optional[int] = None, timestamp: Optional[DateTime] = None, ) -> Tuple[int, bytes, DeviceID, DateTime, DateTime]: vlob = self._get_vlob(organization_id, vlob_id) realm = self._check_realm_read_access(organization_id, vlob.realm_id, author.user_id, encryption_revision, timestamp) if version is None: if timestamp is None: version = vlob.current_version else: for i in range(vlob.current_version, 0, -1): if vlob.data[i - 1][2] <= timestamp: version = i break else: raise VlobVersionError() try: vlob_data, vlob_device_id, vlob_timestamp = vlob.data[version - 1] last_role = realm.get_last_role(vlob_device_id.user_id) # Given the vlob exists, the author must have had a role assert last_role is not None return (version, vlob_data, vlob_device_id, vlob_timestamp, last_role.granted_on) except IndexError: raise VlobVersionError()
async def read( self, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: UUID, version: Optional[int] = None, timestamp: Optional[pendulum.DateTime] = None, ) -> Tuple[int, bytes, DeviceID, pendulum.DateTime]: vlob = self._get_vlob(organization_id, vlob_id) self._check_realm_read_access(organization_id, vlob.realm_id, author.user_id, encryption_revision) if version is None: if timestamp is None: version = vlob.current_version else: for i in range(vlob.current_version, 0, -1): if vlob.data[i - 1][2] <= timestamp: version = i break else: raise VlobVersionError() try: vlob_data, vlob_device_id, vlob_timestamp = vlob.data[version - 1] return (version, vlob_data, vlob_device_id, vlob_timestamp) except IndexError: raise VlobVersionError()
async def query_read( conn, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: VlobID, version: Optional[int] = None, timestamp: Optional[DateTime] = None, ) -> Tuple[int, bytes, DeviceID, DateTime, DateTime]: realm_id = await _get_realm_id_from_vlob_id(conn, organization_id, vlob_id) await _check_realm_and_read_access(conn, organization_id, author, realm_id, encryption_revision) if version is None: if timestamp is None: data = await conn.fetchrow(*_q_read_data_without_timestamp( organization_id=organization_id.str, realm_id=realm_id.uuid, encryption_revision=encryption_revision, vlob_id=vlob_id, )) assert data # _get_realm_id_from_vlob_id checks vlob presence else: data = await conn.fetchrow(*_q_read_data_with_timestamp( organization_id=organization_id.str, realm_id=realm_id.uuid, encryption_revision=encryption_revision, vlob_id=vlob_id.uuid, timestamp=timestamp, )) if not data: raise VlobVersionError() else: data = await conn.fetchrow(*_q_read_data_with_version( organization_id=organization_id.str, realm_id=realm_id.uuid, encryption_revision=encryption_revision, vlob_id=vlob_id.uuid, version=version, )) if not data: raise VlobVersionError() version, blob, vlob_author, created_on = data assert isinstance(version, int) assert isinstance(blob, bytes) vlob_author = DeviceID(vlob_author) created_on = pendulum_instance(created_on) author_last_role_granted_on = await _get_last_role_granted_on( conn, organization_id, realm_id, vlob_author) assert isinstance(author_last_role_granted_on, DateTime) return version, blob, vlob_author, created_on, author_last_role_granted_on
async def query_read( conn, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: UUID, version: Optional[int] = None, timestamp: Optional[pendulum.DateTime] = None, ) -> Tuple[int, bytes, DeviceID, pendulum.DateTime]: realm_id = await _get_realm_id_from_vlob_id(conn, organization_id, vlob_id) await _check_realm_and_read_access(conn, organization_id, author, realm_id, encryption_revision) if version is None: if timestamp is None: data = await conn.fetchrow( *_q_read_data_without_timestamp( organization_id=organization_id, realm_id=realm_id, encryption_revision=encryption_revision, vlob_id=vlob_id, ) ) assert data # _get_realm_id_from_vlob_id checks vlob presence else: data = await conn.fetchrow( *_q_read_data_with_timestamp( organization_id=organization_id, realm_id=realm_id, encryption_revision=encryption_revision, vlob_id=vlob_id, timestamp=timestamp, ) ) if not data: raise VlobVersionError() else: data = await conn.fetchrow( *_q_read_data_with_version( organization_id=organization_id, realm_id=realm_id, encryption_revision=encryption_revision, vlob_id=vlob_id, version=version, ) ) if not data: raise VlobVersionError() version, blob, author, created_on = data return version, blob, author, created_on
async def update( self, organization_id: OrganizationID, id: UUID, wts: str, version: int, blob: bytes, author: DeviceID, notify_beacon: UUID = None, ) -> None: vlobs = self._organizations[organization_id] try: vlob = vlobs[id] if vlob.wts != wts: raise VlobTrustSeedError() except KeyError: raise VlobNotFoundError() if version - 1 == len(vlob.blob_versions): vlob.blob_versions.append((blob, author)) else: raise VlobVersionError() if notify_beacon: await self.beacon_component.update(organization_id, notify_beacon, id, version, author)
async def update( self, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: VlobID, version: int, timestamp: DateTime, blob: bytes, ) -> None: vlob = self._get_vlob(organization_id, vlob_id) self._check_realm_write_access(organization_id, vlob.realm_id, author.user_id, encryption_revision, timestamp) if version - 1 != vlob.current_version: raise VlobVersionError() if timestamp < vlob.data[vlob.current_version - 1][2]: raise VlobRequireGreaterTimestampError( vlob.data[vlob.current_version - 1][2]) vlob.data.append((blob, author, timestamp)) await self._update_changes(organization_id, author, vlob.realm_id, vlob_id, timestamp, version)
async def query_update( conn, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: VlobID, version: int, timestamp: DateTime, blob: bytes, ) -> None: realm_id = await _get_realm_id_from_vlob_id(conn, organization_id, vlob_id) await _check_realm_and_write_access(conn, organization_id, author, realm_id, encryption_revision, timestamp) previous = await conn.fetchrow(*_q_get_vlob_version( organization_id=organization_id.str, vlob_id=vlob_id.uuid)) if not previous: raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist") elif previous["version"] != version - 1: raise VlobVersionError() elif previous["created_on"] > timestamp: raise VlobRequireGreaterTimestampError(previous["created_on"]) try: vlob_atom_internal_id = await conn.fetchval(*_q_insert_vlob_atom( organization_id=organization_id.str, author=author.str, realm_id=realm_id.uuid, encryption_revision=encryption_revision, vlob_id=vlob_id.uuid, blob=blob, blob_len=len(blob), timestamp=timestamp, version=version, )) except UniqueViolationError: # Should not occur in theory given we are in a transaction raise VlobVersionError() await _set_vlob_updated(conn, vlob_atom_internal_id, organization_id, author, realm_id, vlob_id, timestamp, version)
async def read( self, organization_id: OrganizationID, id: UUID, rts: str, version: int = None ) -> Tuple[int, bytes]: vlobs = self._organizations[organization_id] try: vlob = vlobs[id] if vlob.rts != rts: raise VlobTrustSeedError() except KeyError: raise VlobNotFoundError() if version is None: version = len(vlob.blob_versions) try: return (version, vlob.blob_versions[version - 1][0]) except IndexError: raise VlobVersionError()
async def update( self, organization_id: OrganizationID, id: UUID, wts: str, version: int, blob: bytes, author: DeviceID, notify_beacon: UUID = None, ) -> None: async with self.dbh.pool.acquire() as conn: async with conn.transaction(): previous = await conn.fetchrow( """ SELECT wts, version, rts FROM vlobs WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND vlob_id = $2 ORDER BY version DESC LIMIT 1 """, organization_id, id, ) if not previous: raise VlobNotFoundError() elif previous[0] != wts: raise VlobTrustSeedError() elif previous[1] != version - 1: raise VlobVersionError() rts = previous[2] try: result = await conn.execute( """ INSERT INTO vlobs ( organization, vlob_id, rts, wts, version, blob, author ) SELECT _id, $2, $3, $4, $5, $6, ( SELECT _id FROM devices WHERE device_id = $7 AND organization = organizations._id ) FROM organizations WHERE organization_id = $1 """, organization_id, id, rts, wts, version, blob, author, ) except UniqueViolationError: # Should not occurs in theory given we are in a transaction raise VlobVersionError() if result != "INSERT 0 1": raise VlobError(f"Insertion error: {result}") if notify_beacon: await self.beacon_component.ll_update( conn, organization_id, notify_beacon, id, version, author)
async def read(self, organization_id: OrganizationID, id: UUID, rts: str, version: int = None) -> Tuple[int, bytes]: async with self.dbh.pool.acquire() as conn: async with conn.transaction(): if version is None: data = await conn.fetchrow( """ SELECT rts, version, blob FROM vlobs WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND vlob_id = $2 ORDER BY version DESC LIMIT 1 """, organization_id, id, ) if not data: raise VlobNotFoundError() else: data = await conn.fetchrow( """ SELECT rts, version, blob FROM vlobs WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND vlob_id = $2 AND version = $3 """, organization_id, id, version, ) if not data: # TODO: not cool to need 2nd request to know the error... exists = await conn.fetchrow( """ SELECT true FROM vlobs WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND vlob_id = $2 """, organization_id, id, ) if exists: raise VlobVersionError() else: raise VlobNotFoundError() if data["rts"] != rts: raise VlobTrustSeedError() return data[1:]
async def update( self, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: UUID, version: int, timestamp: pendulum.Pendulum, blob: bytes, ) -> None: async with self.dbh.pool.acquire() as conn, conn.transaction(): realm_id = await _get_realm_id_from_vlob_id(conn, organization_id, vlob_id) await _check_realm_and_write_access( conn, organization_id, author, realm_id, encryption_revision ) query = """ SELECT version, created_on FROM vlob_atom WHERE organization = ({}) AND vlob_id = $2 ORDER BY version DESC LIMIT 1 """.format( q_organization_internal_id(Parameter("$1")) ) previous = await conn.fetchrow(query, organization_id, vlob_id) if not previous: raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist") elif previous["version"] != version - 1: raise VlobVersionError() elif previous["created_on"] > timestamp: raise VlobTimestampError() query = """ INSERT INTO vlob_atom ( organization, vlob_encryption_revision, vlob_id, version, blob, size, author, created_on ) SELECT ({}), ({}), $5, $9, $6, $7, ({}), $8 RETURNING _id """.format( q_organization_internal_id(Parameter("$1")), q_vlob_encryption_revision_internal_id( organization_id=Parameter("$1"), realm_id=Parameter("$3"), encryption_revision=Parameter("$4"), ), q_device_internal_id(organization_id=Parameter("$1"), device_id=Parameter("$2")), ) try: vlob_atom_internal_id = await conn.fetchval( query, organization_id, author, realm_id, encryption_revision, vlob_id, blob, len(blob), timestamp, version, ) except UniqueViolationError: # Should not occurs in theory given we are in a transaction raise VlobVersionError() await _vlob_updated( conn, vlob_atom_internal_id, organization_id, author, realm_id, vlob_id, version )
async def read( self, organization_id: OrganizationID, author: DeviceID, encryption_revision: int, vlob_id: UUID, version: Optional[int] = None, timestamp: Optional[pendulum.Pendulum] = None, ) -> Tuple[int, bytes, DeviceID, pendulum.Pendulum]: async with self.dbh.pool.acquire() as conn, conn.transaction(): realm_id = await _get_realm_id_from_vlob_id(conn, organization_id, vlob_id) await _check_realm_and_read_access( conn, organization_id, author, realm_id, encryption_revision ) if version is None: if timestamp is None: query = """ SELECT version, blob, ({}) as author, created_on FROM vlob_atom WHERE vlob_encryption_revision = ({}) AND vlob_id = $4 ORDER BY version DESC LIMIT 1 """.format( q_device(_id=Parameter("author")).select("device_id"), q_vlob_encryption_revision_internal_id( organization_id=Parameter("$1"), realm_id=Parameter("$2"), encryption_revision=Parameter("$3"), ), ) data = await conn.fetchrow( query, organization_id, realm_id, encryption_revision, vlob_id ) assert data # _get_realm_id_from_vlob_id checks vlob presence else: query = """ SELECT version, blob, ({}) as author, created_on FROM vlob_atom WHERE vlob_encryption_revision = ({}) AND vlob_id = $4 AND created_on <= $5 ORDER BY version DESC LIMIT 1 """.format( q_device(_id=Parameter("author")).select("device_id"), q_vlob_encryption_revision_internal_id( organization_id=Parameter("$1"), realm_id=Parameter("$2"), encryption_revision=Parameter("$3"), ), ) data = await conn.fetchrow( query, organization_id, realm_id, encryption_revision, vlob_id, timestamp ) if not data: raise VlobVersionError() else: query = """ SELECT version, blob, ({}) as author, created_on FROM vlob_atom WHERE vlob_encryption_revision = ({}) AND vlob_id = $4 AND version = $5 """.format( q_device(_id=Parameter("author")).select("device_id"), q_vlob_encryption_revision_internal_id( organization_id=Parameter("$1"), realm_id=Parameter("$2"), encryption_revision=Parameter("$3"), ), ) data = await conn.fetchrow( query, organization_id, realm_id, encryption_revision, vlob_id, version ) if not data: raise VlobVersionError() return list(data)