コード例 #1
0
ファイル: vlob.py プロジェクト: stjordanis/parsec-cloud
    def _check_realm_access(
        self,
        organization_id,
        realm_id,
        user_id,
        encryption_revision,
        allowed_roles,
        expected_maintenance=False,
    ):
        try:
            realm = self._realm_component._get_realm(organization_id, realm_id)
        except RealmNotFoundError:
            raise VlobNotFoundError(f"Realm `{realm_id}` doesn't exist")

        if realm.roles.get(user_id) not in allowed_roles:
            raise VlobAccessError()

        if expected_maintenance is False:
            if realm.status.in_maintenance:
                raise VlobInMaintenanceError(
                    f"Realm `{realm_id}` is currently under maintenance")
        elif expected_maintenance is True:
            if not realm.status.in_maintenance:
                raise VlobNotInMaintenanceError(
                    f"Realm `{realm_id}` not under maintenance")

        if encryption_revision not in (None, realm.status.encryption_revision):
            raise VlobEncryptionRevisionError()
コード例 #2
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)
コード例 #3
0
ファイル: vlob.py プロジェクト: Scille/parsec-cloud
    def _get_vlob(self, organization_id: OrganizationID,
                  vlob_id: VlobID) -> Vlob:
        try:
            return self._vlobs[(organization_id, vlob_id)]

        except KeyError:
            raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")
コード例 #4
0
ファイル: vlob.py プロジェクト: anthrax3/parsec-cloud
    async def list_versions(
        self, organization_id: OrganizationID, author: DeviceID, vlob_id: UUID
    ) -> Dict[int, Tuple[pendulum.Pendulum, DeviceID]]:

        async with self.dbh.pool.acquire() as conn:
            async with 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, None)

                query = """
SELECT
    version,
    ({}) as author,
    created_on
FROM vlob_atom
WHERE
    organization = ({})
    AND vlob_id = $2
ORDER BY version DESC
""".format(
                    q_device(_id=Parameter("author")).select("device_id"),
                    q_organization_internal_id(Parameter("$1")),
                )
                rows = await conn.fetch(query, organization_id, vlob_id)
                assert rows
        if not rows:
            raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")

        return {row["version"]: (row["created_on"], row["author"]) for row in rows}
コード例 #5
0
ファイル: utils.py プロジェクト: Scille/parsec-cloud
async def _get_realm_id_from_vlob_id(conn, organization_id: OrganizationID,
                                     vlob_id: VlobID) -> RealmID:
    realm_id_uuid = await conn.fetchval(*_q_get_realm_id_from_vlob_id(
        organization_id=organization_id.str, vlob_id=vlob_id.uuid))
    if not realm_id_uuid:
        raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")
    return RealmID(realm_id_uuid)
コード例 #6
0
ファイル: vlob.py プロジェクト: admariner/parsec-cloud
    def save_batch(self, batch):
        for vlob_id, version, data in batch:
            key = (vlob_id, version)
            if key in self._done:
                continue
            try:
                del self._todo[key]
            except KeyError:
                raise VlobNotFoundError()
            self._done[key] = data

        return self._total, len(self._done)
コード例 #7
0
async def _check_realm_access(conn, organization_id, realm_id, author,
                              allowed_roles):
    rep = await conn.fetchrow(
        *_q_check_realm_access(organization_id=organization_id,
                               realm_id=realm_id,
                               user_id=author.user_id))

    if not rep:
        raise VlobNotFoundError(f"User `{author.user_id}` doesn't exist")

    if STR_TO_REALM_ROLE.get(rep[0]) not in allowed_roles:
        raise VlobAccessError()
コード例 #8
0
ファイル: read.py プロジェクト: admariner/parsec-cloud
async def query_list_versions(
    conn, organization_id: OrganizationID, author: DeviceID, vlob_id: UUID
) -> Dict[int, Tuple[pendulum.DateTime, DeviceID]]:
    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, None)

    rows = await conn.fetch(*_q_list_versions(organization_id=organization_id, vlob_id=vlob_id))
    assert rows

    if not rows:
        raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")

    return {row["version"]: (row["created_on"], row["author"]) for row in rows}
コード例 #9
0
ファイル: write.py プロジェクト: Scille/parsec-cloud
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)
コード例 #10
0
ファイル: vlob.py プロジェクト: anthrax3/parsec-cloud
async def _check_realm(
    conn, organization_id, realm_id, encryption_revision, expected_maintenance=False
):
    try:
        rep = await get_realm_status(conn, organization_id, realm_id)

    except RealmNotFoundError as exc:
        raise VlobNotFoundError(*exc.args) from exc

    if expected_maintenance is False:
        if rep["maintenance_type"]:
            raise VlobInMaintenanceError("Data realm is currently under maintenance")
    elif expected_maintenance is True:
        if not rep["maintenance_type"]:
            raise VlobNotInMaintenanceError(f"Realm `{realm_id}` not under maintenance")

    if encryption_revision is not None and rep["encryption_revision"] != encryption_revision:
        raise VlobEncryptionRevisionError()
コード例 #11
0
ファイル: vlob.py プロジェクト: anthrax3/parsec-cloud
async def _get_realm_id_from_vlob_id(conn, organization_id, vlob_id):
    query = """
SELECT
    realm.realm_id
FROM vlob_atom
INNER JOIN vlob_encryption_revision
ON  vlob_atom.vlob_encryption_revision = vlob_encryption_revision._id
INNER JOIN realm
ON vlob_encryption_revision.realm = realm._id
WHERE vlob_atom._id = ({})
LIMIT 1
    """.format(
        q_vlob_atom(organization_id=Parameter("$1"), vlob_id=Parameter("$2")).select("_id").limit(1)
    )

    realm_id = await conn.fetchval(query, organization_id, vlob_id)
    if not realm_id:
        raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")
    return realm_id
コード例 #12
0
ファイル: vlob.py プロジェクト: admariner/parsec-cloud
    def get_reencrypted_vlobs(self):
        assert self.is_finished()
        vlobs = {}
        for (vlob_id, version), data in sorted(self._done.items()):
            try:
                (_, author,
                 timestamp) = self._original_vlobs[vlob_id].data[version - 1]

            except KeyError:
                raise VlobNotFoundError()

            if vlob_id not in vlobs:
                vlobs[vlob_id] = Vlob(self.realm_id,
                                      [(data, author, timestamp)])
            else:
                vlobs[vlob_id].data.append((data, author, timestamp))
            assert len(vlobs[vlob_id].data) == version

        return vlobs
コード例 #13
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()
コード例 #14
0
ファイル: utils.py プロジェクト: Scille/parsec-cloud
async def _check_realm_access(
    conn,
    organization_id: OrganizationID,
    realm_id: RealmID,
    author: DeviceID,
    allowed_roles: Tuple[RealmRole, ...],
) -> DateTime:
    rep = await conn.fetchrow(
        *_q_check_realm_access(organization_id=organization_id.str,
                               realm_id=realm_id.uuid,
                               user_id=author.user_id.str))

    if not rep:
        raise VlobNotFoundError(f"User `{author.user_id}` doesn't exist")

    role = RealmRole(rep[0]) if rep[0] is not None else None
    if role not in allowed_roles:
        raise VlobAccessError()

    role_granted_on = rep[1]
    return role_granted_on
コード例 #15
0
ファイル: vlob.py プロジェクト: anthrax3/parsec-cloud
async def _check_realm_access(conn, organization_id, realm_id, author, allowed_roles):
    query = """
WITH cte_current_realm_roles AS (
    SELECT DISTINCT ON(user_) user_, role
    FROM  realm_user_role
    WHERE realm = ({})
    ORDER BY user_, certified_on DESC
)
SELECT role
FROM user_
LEFT JOIN cte_current_realm_roles
ON user_._id = cte_current_realm_roles.user_
WHERE user_._id = ({})
        """.format(
        q_realm_internal_id(organization_id=Parameter("$1"), realm_id=Parameter("$2")),
        q_user_internal_id(organization_id=Parameter("$1"), user_id=Parameter("$3")),
    )
    rep = await conn.fetchrow(query, organization_id, realm_id, author.user_id)

    if not rep:
        raise VlobNotFoundError(f"User `{author.user_id}` doesn't exist")

    if STR_TO_REALM_ROLE.get(rep[0]) not in allowed_roles:
        raise VlobAccessError()
コード例 #16
0
ファイル: vlob.py プロジェクト: anthrax3/parsec-cloud
    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
            )
コード例 #17
0
ファイル: vlob.py プロジェクト: admariner/parsec-cloud
    def _check_realm_access(self, organization_id, realm_id, user_id,
                            encryption_revision, operation_kind):
        try:
            realm = self._realm_component._get_realm(organization_id, realm_id)
        except RealmNotFoundError:
            raise VlobNotFoundError(f"Realm `{realm_id}` doesn't exist")

        # Only an owner can perform maintenance operation
        if operation_kind == OperationKind.MAINTENANCE:
            allowed_roles = (RealmRole.OWNER, )
        # All roles can do read-only operation
        elif operation_kind == OperationKind.DATA_READ:
            allowed_roles = (
                RealmRole.OWNER,
                RealmRole.MANAGER,
                RealmRole.CONTRIBUTOR,
                RealmRole.READER,
            )
        # All roles except reader can do write operation
        elif operation_kind == OperationKind.DATA_WRITE:
            allowed_roles = (RealmRole.OWNER, RealmRole.MANAGER,
                             RealmRole.CONTRIBUTOR)
        else:
            assert False, f"Operation kind {operation_kind} not supported"

        # Check the role
        if realm.roles.get(user_id) not in allowed_roles:
            raise VlobAccessError()

        # Special case of reading while in reencryption
        if operation_kind == OperationKind.DATA_READ and realm.status.in_reencryption:
            # Starting a reencryption maintenance bumps the encryption revision.
            # Hence if we are currently in reencryption maintenance, last encryption revision is not ready
            # to be used (it will be once the reencryption is over !).
            # So during this intermediary state, we allow read access to the previous encryption revision instead.

            # Note that `encryption_revision` might also be `None` in the case of `poll_changes` and `list_versions`
            # requests, which should also be allowed during a reencryption

            # The vlob is not available yet for the current revision
            if (encryption_revision is not None and encryption_revision
                    == realm.status.encryption_revision):
                raise VlobInMaintenanceError(
                    f"Realm `{realm_id}` is currently under maintenance")

            # The vlob is only available at the previous revision
            if (encryption_revision is not None and encryption_revision !=
                    realm.status.encryption_revision - 1):
                raise VlobEncryptionRevisionError()

        # In all other cases
        else:
            # Writing during maintenance is forbidden
            if operation_kind != OperationKind.MAINTENANCE and realm.status.in_maintenance:
                raise VlobInMaintenanceError(
                    f"Realm `{realm_id}` is currently under maintenance")

            # A maintenance state was expected
            if operation_kind == OperationKind.MAINTENANCE and not realm.status.in_maintenance:
                raise VlobNotInMaintenanceError(
                    f"Realm `{realm_id}` not under maintenance")

            # Otherwise, simply check that the revisions match
            if (encryption_revision is not None and
                    encryption_revision != realm.status.encryption_revision):
                raise VlobEncryptionRevisionError()
コード例 #18
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)
コード例 #19
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:]
コード例 #20
0
async def _get_realm_id_from_vlob_id(conn, organization_id, vlob_id):
    realm_id = await conn.fetchval(*_q_get_realm_id_from_vlob_id(
        organization_id=organization_id, vlob_id=vlob_id))
    if not realm_id:
        raise VlobNotFoundError(f"Vlob `{vlob_id}` doesn't exist")
    return realm_id