Пример #1
0
    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()
Пример #2
0
    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()
Пример #3
0
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
Пример #4
0
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
Пример #5
0
    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)
Пример #6
0
    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)
Пример #7
0
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)
Пример #8
0
    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()
Пример #9
0
    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)
Пример #10
0
    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:]
Пример #11
0
    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
            )
Пример #12
0
    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)