diff --git a/.bumpversion.cfg b/.bumpversion.cfg index bc014f67e4a..d7b3861300f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.8.2-beta.31 +current_version = 0.8.2-beta.32 tag = False tag_name = {new_version} commit = True diff --git a/.github/workflows/pr-tests-enclave.yml b/.github/workflows/pr-tests-enclave.yml index 7c6ee71cb6d..16921240a39 100644 --- a/.github/workflows/pr-tests-enclave.yml +++ b/.github/workflows/pr-tests-enclave.yml @@ -31,6 +31,13 @@ jobs: steps: - uses: actions/checkout@v3 + # free 10GB of space + - name: Remove unnecessary files + if: matrix.os == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Check for file changes uses: dorny/paths-filter@v2 id: changes diff --git a/.github/workflows/pr-tests-frontend.yml b/.github/workflows/pr-tests-frontend.yml index a63c4e44f42..aa75efbffe3 100644 --- a/.github/workflows/pr-tests-frontend.yml +++ b/.github/workflows/pr-tests-frontend.yml @@ -108,6 +108,13 @@ jobs: steps: - uses: actions/checkout@v3 + # free 10GB of space + - name: Remove unnecessary files + if: matrix.os == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Check for file changes uses: dorny/paths-filter@v2 id: changes diff --git a/.github/workflows/pr-tests-stack-arm64.yml b/.github/workflows/pr-tests-stack-arm64.yml index 847b63cd413..98406271f38 100644 --- a/.github/workflows/pr-tests-stack-arm64.yml +++ b/.github/workflows/pr-tests-stack-arm64.yml @@ -30,6 +30,13 @@ jobs: - uses: actions/checkout@v3 + # free 10GB of space + - name: Remove unnecessary files + if: matrix.os == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Check for file changes uses: dorny/paths-filter@v2 id: changes diff --git a/.github/workflows/pr-tests-stack.yml b/.github/workflows/pr-tests-stack.yml index 3c87775d1cb..a42a2cec76a 100644 --- a/.github/workflows/pr-tests-stack.yml +++ b/.github/workflows/pr-tests-stack.yml @@ -50,6 +50,13 @@ jobs: - uses: actions/checkout@v3 + # free 10GB of space + - name: Remove unnecessary files + if: matrix.os == 'ubuntu-latest' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Check for file changes uses: dorny/paths-filter@v2 id: changes diff --git a/VERSION b/VERSION index e8c2873959e..8d742e21c7c 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ # Mono Repo Global Version -__version__ = "0.8.2-beta.31" +__version__ = "0.8.2-beta.32" # elsewhere we can call this file: `python VERSION` and simply take the stdout # stdlib diff --git a/packages/grid/VERSION b/packages/grid/VERSION index e8c2873959e..8d742e21c7c 100644 --- a/packages/grid/VERSION +++ b/packages/grid/VERSION @@ -1,5 +1,5 @@ # Mono Repo Global Version -__version__ = "0.8.2-beta.31" +__version__ = "0.8.2-beta.32" # elsewhere we can call this file: `python VERSION` and simply take the stdout # stdlib diff --git a/packages/grid/devspace.yaml b/packages/grid/devspace.yaml index 7a58a689228..df7f24aa907 100644 --- a/packages/grid/devspace.yaml +++ b/packages/grid/devspace.yaml @@ -15,13 +15,13 @@ pipelines: run: |- run_dependencies --all ensure_pull_secrets --all - build_images --all -t $(git rev-parse --short=6 HEAD) -t 0.8.2-beta.31 -t dev-latest + build_images --all -t $(git rev-parse --short=6 HEAD) -t 0.8.2-beta.32 -t dev-latest create_deployments --all vars: DEVSPACE_ENV_FILE: "default.env" CONTAINER_REGISTRY: "docker.io" - VERSION: "0.8.2-beta.31" + VERSION: "0.8.2-beta.32" # This is a list of `images` that DevSpace can build for this project # We recommend to skip image building during development (devspace dev) as much as possible diff --git a/packages/grid/frontend/package.json b/packages/grid/frontend/package.json index bae96f042d3..ee4bcfbd642 100644 --- a/packages/grid/frontend/package.json +++ b/packages/grid/frontend/package.json @@ -1,6 +1,6 @@ { "name": "pygrid-ui", - "version": "0.8.2-beta.31", + "version": "0.8.2-beta.32", "private": true, "scripts": { "dev": "pnpm i && vite dev --host --port 80", diff --git a/packages/grid/helm/repo/index.yaml b/packages/grid/helm/repo/index.yaml index fa4958e1359..6db9025ad63 100644 --- a/packages/grid/helm/repo/index.yaml +++ b/packages/grid/helm/repo/index.yaml @@ -1,9 +1,25 @@ apiVersion: v1 entries: syft: + - apiVersion: v2 + appVersion: 0.8.2-beta.32 + created: "2023-09-25T08:46:19.495962412Z" + dependencies: + - name: component-chart + repository: https://charts.devspace.sh + version: 0.8.6 + description: Perform numpy-like analysis on data that remains in someone elses + server + digest: a2a5e157e5d9fe60f85388c955829e4b890bdbce7d89885318e3622db997b39e + icon: https://raw.githubusercontent.com/OpenMined/PySyft/dev/docs/img/title_syft_light.png + name: syft + type: application + urls: + - https://openmined.github.io/PySyft/helm/syft-0.8.2-beta.32.tgz + version: 0.8.2-beta.32 - apiVersion: v2 appVersion: 0.8.2-beta.31 - created: "2023-09-22T06:33:05.972438781Z" + created: "2023-09-25T08:46:19.495473951Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -19,7 +35,7 @@ entries: version: 0.8.2-beta.31 - apiVersion: v2 appVersion: 0.8.2-beta.30 - created: "2023-09-22T06:33:05.97194548Z" + created: "2023-09-25T08:46:19.494994105Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -35,7 +51,7 @@ entries: version: 0.8.2-beta.30 - apiVersion: v2 appVersion: 0.8.2-beta.29 - created: "2023-09-22T06:33:05.970951154Z" + created: "2023-09-25T08:46:19.494020408Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -51,7 +67,7 @@ entries: version: 0.8.2-beta.29 - apiVersion: v2 appVersion: 0.8.2-beta.28 - created: "2023-09-22T06:33:05.970430332Z" + created: "2023-09-25T08:46:19.493495228Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -67,7 +83,7 @@ entries: version: 0.8.2-beta.28 - apiVersion: v2 appVersion: 0.8.2-beta.27 - created: "2023-09-22T06:33:05.969838718Z" + created: "2023-09-25T08:46:19.492990336Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -83,7 +99,7 @@ entries: version: 0.8.2-beta.27 - apiVersion: v2 appVersion: 0.8.2-beta.26 - created: "2023-09-22T06:33:05.968583044Z" + created: "2023-09-25T08:46:19.492461048Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -99,7 +115,7 @@ entries: version: 0.8.2-beta.26 - apiVersion: v2 appVersion: 0.8.2-beta.25 - created: "2023-09-22T06:33:05.968013221Z" + created: "2023-09-25T08:46:19.49184557Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -115,7 +131,7 @@ entries: version: 0.8.2-beta.25 - apiVersion: v2 appVersion: 0.8.2-beta.24 - created: "2023-09-22T06:33:05.967506125Z" + created: "2023-09-25T08:46:19.490894425Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -131,7 +147,7 @@ entries: version: 0.8.2-beta.24 - apiVersion: v2 appVersion: 0.8.2-beta.23 - created: "2023-09-22T06:33:05.967022231Z" + created: "2023-09-25T08:46:19.490398149Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -147,7 +163,7 @@ entries: version: 0.8.2-beta.23 - apiVersion: v2 appVersion: 0.8.2-beta.22 - created: "2023-09-22T06:33:05.966541814Z" + created: "2023-09-25T08:46:19.489912062Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -163,7 +179,7 @@ entries: version: 0.8.2-beta.22 - apiVersion: v2 appVersion: 0.8.2-beta.21 - created: "2023-09-22T06:33:05.966060807Z" + created: "2023-09-25T08:46:19.489394065Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -179,7 +195,7 @@ entries: version: 0.8.2-beta.21 - apiVersion: v2 appVersion: 0.8.2-beta.20 - created: "2023-09-22T06:33:05.965568708Z" + created: "2023-09-25T08:46:19.488879245Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -195,7 +211,7 @@ entries: version: 0.8.2-beta.20 - apiVersion: v2 appVersion: 0.8.2-beta.19 - created: "2023-09-22T06:33:05.964536071Z" + created: "2023-09-25T08:46:19.487911468Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -211,7 +227,7 @@ entries: version: 0.8.2-beta.19 - apiVersion: v2 appVersion: 0.8.2-beta.18 - created: "2023-09-22T06:33:05.963997516Z" + created: "2023-09-25T08:46:19.487420092Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -227,7 +243,7 @@ entries: version: 0.8.2-beta.18 - apiVersion: v2 appVersion: 0.8.2-beta.17 - created: "2023-09-22T06:33:05.963462206Z" + created: "2023-09-25T08:46:19.486921932Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -243,7 +259,7 @@ entries: version: 0.8.2-beta.17 - apiVersion: v2 appVersion: 0.8.2-beta.16 - created: "2023-09-22T06:33:05.962883947Z" + created: "2023-09-25T08:46:19.48641708Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -259,7 +275,7 @@ entries: version: 0.8.2-beta.16 - apiVersion: v2 appVersion: 0.8.2-beta.15 - created: "2023-09-22T06:33:05.961941004Z" + created: "2023-09-25T08:46:19.485808284Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -275,7 +291,7 @@ entries: version: 0.8.2-beta.15 - apiVersion: v2 appVersion: 0.8.2-beta.14 - created: "2023-09-22T06:33:05.961219287Z" + created: "2023-09-25T08:46:19.484687848Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -291,7 +307,7 @@ entries: version: 0.8.2-beta.14 - apiVersion: v2 appVersion: 0.8.2-beta.13 - created: "2023-09-22T06:33:05.960689047Z" + created: "2023-09-25T08:46:19.484174169Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -307,7 +323,7 @@ entries: version: 0.8.2-beta.13 - apiVersion: v2 appVersion: 0.8.2-beta.12 - created: "2023-09-22T06:33:05.960176621Z" + created: "2023-09-25T08:46:19.483686279Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -323,7 +339,7 @@ entries: version: 0.8.2-beta.12 - apiVersion: v2 appVersion: 0.8.2-beta.11 - created: "2023-09-22T06:33:05.95957576Z" + created: "2023-09-25T08:46:19.483189642Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -339,7 +355,7 @@ entries: version: 0.8.2-beta.11 - apiVersion: v2 appVersion: 0.8.2-beta.10 - created: "2023-09-22T06:33:05.959011136Z" + created: "2023-09-25T08:46:19.482695349Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -355,7 +371,7 @@ entries: version: 0.8.2-beta.10 - apiVersion: v2 appVersion: 0.8.2-beta.9 - created: "2023-09-22T06:33:05.97540098Z" + created: "2023-09-25T08:46:19.499466231Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -371,7 +387,7 @@ entries: version: 0.8.2-beta.9 - apiVersion: v2 appVersion: 0.8.2-beta.8 - created: "2023-09-22T06:33:05.974908541Z" + created: "2023-09-25T08:46:19.498953404Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -387,7 +403,7 @@ entries: version: 0.8.2-beta.8 - apiVersion: v2 appVersion: 0.8.2-beta.7 - created: "2023-09-22T06:33:05.974425088Z" + created: "2023-09-25T08:46:19.498422413Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -403,7 +419,7 @@ entries: version: 0.8.2-beta.7 - apiVersion: v2 appVersion: 0.8.2-beta.6 - created: "2023-09-22T06:33:05.973945163Z" + created: "2023-09-25T08:46:19.497412528Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -419,7 +435,7 @@ entries: version: 0.8.2-beta.6 - apiVersion: v2 appVersion: 0.8.2-beta.5 - created: "2023-09-22T06:33:05.973457753Z" + created: "2023-09-25T08:46:19.496931651Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -435,7 +451,7 @@ entries: version: 0.8.2-beta.5 - apiVersion: v2 appVersion: 0.8.2-beta.4 - created: "2023-09-22T06:33:05.972965895Z" + created: "2023-09-25T08:46:19.496449942Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -451,7 +467,7 @@ entries: version: 0.8.2-beta.4 - apiVersion: v2 appVersion: 0.8.2-beta.3 - created: "2023-09-22T06:33:05.971448132Z" + created: "2023-09-25T08:46:19.494507688Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -467,7 +483,7 @@ entries: version: 0.8.2-beta.3 - apiVersion: v2 appVersion: 0.8.2-beta.2 - created: "2023-09-22T06:33:05.965069436Z" + created: "2023-09-25T08:46:19.488396554Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -483,7 +499,7 @@ entries: version: 0.8.2-beta.2 - apiVersion: v2 appVersion: 0.8.2-beta.1 - created: "2023-09-22T06:33:05.958499771Z" + created: "2023-09-25T08:46:19.482188674Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -499,7 +515,7 @@ entries: version: 0.8.2-beta.1 - apiVersion: v2 appVersion: 0.8.1 - created: "2023-09-22T06:33:05.957976755Z" + created: "2023-09-25T08:46:19.481669164Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -515,7 +531,7 @@ entries: version: 0.8.1 - apiVersion: v2 appVersion: 0.8.1-beta.21 - created: "2023-09-22T06:33:05.957463557Z" + created: "2023-09-25T08:46:19.481137692Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -531,7 +547,7 @@ entries: version: 0.8.1-beta.21 - apiVersion: v2 appVersion: 0.8.1-beta.20 - created: "2023-09-22T06:33:05.956910635Z" + created: "2023-09-25T08:46:19.480619155Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -547,7 +563,7 @@ entries: version: 0.8.1-beta.20 - apiVersion: v2 appVersion: 0.8.1-beta.19 - created: "2023-09-22T06:33:05.956290227Z" + created: "2023-09-25T08:46:19.480103142Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -563,7 +579,7 @@ entries: version: 0.8.1-beta.19 - apiVersion: v2 appVersion: 0.8.1-beta.15 - created: "2023-09-22T06:33:05.955080759Z" + created: "2023-09-25T08:46:19.479570949Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -579,7 +595,7 @@ entries: version: 0.8.1-beta.15 - apiVersion: v2 appVersion: 0.8.1-beta.12 - created: "2023-09-22T06:33:05.954540721Z" + created: "2023-09-25T08:46:19.478989504Z" dependencies: - name: component-chart repository: https://charts.devspace.sh @@ -593,4 +609,4 @@ entries: urls: - https://openmined.github.io/PySyft/helm/syft-0.8.1-beta.12.tgz version: 0.8.1-beta.12 -generated: "2023-09-22T06:33:05.953845634Z" +generated: "2023-09-25T08:46:19.477733318Z" diff --git a/packages/grid/helm/repo/syft-0.8.2-beta.32.tgz b/packages/grid/helm/repo/syft-0.8.2-beta.32.tgz new file mode 100644 index 00000000000..c3d1bba0964 Binary files /dev/null and b/packages/grid/helm/repo/syft-0.8.2-beta.32.tgz differ diff --git a/packages/grid/helm/syft/Chart.yaml b/packages/grid/helm/syft/Chart.yaml index 91db2a35105..5f97ec4593d 100644 --- a/packages/grid/helm/syft/Chart.yaml +++ b/packages/grid/helm/syft/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: syft description: Perform numpy-like analysis on data that remains in someone elses server type: application -version: "0.8.2-beta.31" -appVersion: "0.8.2-beta.31" +version: "0.8.2-beta.32" +appVersion: "0.8.2-beta.32" icon: https://raw.githubusercontent.com/OpenMined/PySyft/dev/docs/img/title_syft_light.png dependencies: diff --git a/packages/grid/podman/podman-kube/podman-syft-kube.yaml b/packages/grid/podman/podman-kube/podman-syft-kube.yaml index 03286ac7cfd..374bbbf8bbb 100644 --- a/packages/grid/podman/podman-kube/podman-syft-kube.yaml +++ b/packages/grid/podman/podman-kube/podman-syft-kube.yaml @@ -41,7 +41,7 @@ spec: - configMapRef: name: podman-syft-config - image: docker.io/openmined/grid-backend:0.8.2-beta.31 + image: docker.io/openmined/grid-backend:0.8.2-beta.32 imagePullPolicy: IfNotPresent resources: {} tty: true @@ -57,7 +57,7 @@ spec: envFrom: - configMapRef: name: podman-syft-config - image: docker.io/openmined/grid-frontend:0.8.2-beta.31 + image: docker.io/openmined/grid-frontend:0.8.2-beta.32 imagePullPolicy: IfNotPresent resources: {} tty: true diff --git a/packages/hagrid/hagrid/deps.py b/packages/hagrid/hagrid/deps.py index bc778dafcf6..d3f7d04b88a 100644 --- a/packages/hagrid/hagrid/deps.py +++ b/packages/hagrid/hagrid/deps.py @@ -42,7 +42,7 @@ from .version import __version__ LATEST_STABLE_SYFT = "0.8.1" -LATEST_BETA_SYFT = "0.8.2-beta.31" +LATEST_BETA_SYFT = "0.8.2-beta.32" DOCKER_ERROR = """ You are running an old version of docker, possibly on Linux. You need to install v2. diff --git a/packages/hagrid/hagrid/manifest_template.yml b/packages/hagrid/hagrid/manifest_template.yml index 03d0fef03cd..92eb5cd9c93 100644 --- a/packages/hagrid/hagrid/manifest_template.yml +++ b/packages/hagrid/hagrid/manifest_template.yml @@ -1,9 +1,9 @@ manifestVersion: 0.1 hagrid_version: 0.3.72 -syft_version: 0.8.2-beta.31 -dockerTag: 0.8.2-beta.31 +syft_version: 0.8.2-beta.32 +dockerTag: 0.8.2-beta.32 baseUrl: https://raw.githubusercontent.com/OpenMined/PySyft/ -hash: 00d4fb50c26820ed0fc84bb8c3047b6243f1473c +hash: ba2289479262a742b1df2b7c8548e024b237594a target_dir: ~/.hagrid/PySyft/ files: grid: diff --git a/packages/syft/setup.cfg b/packages/syft/setup.cfg index 79435cdaf96..e5905e3627a 100644 --- a/packages/syft/setup.cfg +++ b/packages/syft/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = syft -version = attr: "0.8.2-beta.31" +version = attr: "0.8.2-beta.32" description = Perform numpy-like analysis on data that remains in someone elses server author = OpenMined author_email = info@openmined.org diff --git a/packages/syft/src/syft/VERSION b/packages/syft/src/syft/VERSION index e8c2873959e..8d742e21c7c 100644 --- a/packages/syft/src/syft/VERSION +++ b/packages/syft/src/syft/VERSION @@ -1,5 +1,5 @@ # Mono Repo Global Version -__version__ = "0.8.2-beta.31" +__version__ = "0.8.2-beta.32" # elsewhere we can call this file: `python VERSION` and simply take the stdout # stdlib diff --git a/packages/syft/src/syft/__init__.py b/packages/syft/src/syft/__init__.py index ad126ba01d3..72c46ee1d06 100644 --- a/packages/syft/src/syft/__init__.py +++ b/packages/syft/src/syft/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.8.2-beta.31" +__version__ = "0.8.2-beta.32" # stdlib import pathlib diff --git a/packages/syft/src/syft/service/user/user_service.py b/packages/syft/src/syft/service/user/user_service.py index f488147bc61..6b9c14a3944 100644 --- a/packages/syft/src/syft/service/user/user_service.py +++ b/packages/syft/src/syft/service/user/user_service.py @@ -229,13 +229,12 @@ def update( # Get user to be updated by its UID result = self.stash.get_by_uid(credentials=context.credentials, uid=uid) - # TODO: ADD Email Validation - # check if the email already exists + # check if the email already exists (with root's key) if user_update.email is not Empty: - user_with_email = self.stash.get_by_email( - credentials=context.credentials, email=user_update.email + user_with_email_exists: bool = self.stash.email_exists( + email=user_update.email ) - if user_with_email.ok() is not None: + if user_with_email_exists: return SyftError( message=f"A user with the email {user_update.email} already exists." ) diff --git a/packages/syft/src/syft/service/user/user_stash.py b/packages/syft/src/syft/service/user/user_stash.py index e3fd20bd179..8535acea25d 100644 --- a/packages/syft/src/syft/service/user/user_stash.py +++ b/packages/syft/src/syft/service/user/user_stash.py @@ -76,6 +76,13 @@ def get_by_email( qks = QueryKeys(qks=[EmailPartitionKey.with_obj(email)]) return self.query_one(credentials=credentials, qks=qks) + def email_exists(self, email: str) -> bool: + res = self.get_by_email(credentials=self.admin_verify_key().ok(), email=email) + if res.ok() is None: + return False + else: + return True + def get_by_role( self, credentials: SyftVerifyKey, role: ServiceRole ) -> Result[Optional[User], str]: diff --git a/packages/syft/src/syft/store/kv_document_store.py b/packages/syft/src/syft/store/kv_document_store.py index d7131f52be2..bc1c6d2ea37 100644 --- a/packages/syft/src/syft/store/kv_document_store.py +++ b/packages/syft/src/syft/store/kv_document_store.py @@ -450,6 +450,7 @@ def _delete( ActionObjectWRITE(uid=qk.value, credentials=credentials) ): _obj = self.data.pop(qk.value) + self.permissions.pop(qk.value) self._delete_unique_keys_for(_obj) self._delete_search_keys_for(_obj) return Ok(SyftSuccess(message="Deleted")) diff --git a/packages/syft/src/syft/store/mongo_client.py b/packages/syft/src/syft/store/mongo_client.py index f5c8ea47b05..c20b056777f 100644 --- a/packages/syft/src/syft/store/mongo_client.py +++ b/packages/syft/src/syft/store/mongo_client.py @@ -73,7 +73,7 @@ class MongoStoreClientConfig(StoreClientConfig): Controls how long (in milliseconds) the driver will wait to find an available, appropriate server to carry out a database operation; while it is waiting, multiple server monitoring operations may be carried out, each controlled by `connectTimeoutMS`. - Defaults to ``30000`` (30 seconds). + Defaults to ``120000`` (120 seconds). `waitQueueTimeoutMS`: (integer or None) How long (in milliseconds) a thread will wait for a socket from the pool if the pool has no free sockets. Defaults to ``None`` (no timeout). @@ -108,7 +108,7 @@ class MongoStoreClientConfig(StoreClientConfig): timeoutMS: int = 0 socketTimeoutMS: int = 0 connectTimeoutMS: int = 20000 - serverSelectionTimeoutMS: int = 30000 + serverSelectionTimeoutMS: int = 120000 waitQueueTimeoutMS: Optional[int] = None heartbeatFrequencyMS: int = 10000 appname: str = "pysyft" diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index 46664512647..05b5a94d16e 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -3,6 +3,7 @@ from typing import Dict from typing import List from typing import Optional +from typing import Set from typing import Type # third party @@ -229,6 +230,8 @@ def _set( add_permissions: Optional[List[ActionObjectPermission]] = None, ignore_duplicates: bool = False, ) -> Result[SyftObject, str]: + # TODO: Refactor this function since now it's doing both set and + # update at the same time write_permission = ActionObjectWRITE(uid=obj.id, credentials=credentials) can_write = self.has_permission(write_permission) @@ -379,21 +382,37 @@ def _get_all_from_store( def _delete( self, credentials: SyftVerifyKey, qk: QueryKey, has_permission: bool = False ) -> Result[SyftSuccess, Err]: + if not ( + has_permission + or self.has_permission( + ActionObjectWRITE(uid=qk.value, credentials=credentials) + ) + ): + return Err(f"You don't have permission to delete object with qk: {qk}") + collection_status = self.collection if collection_status.is_err(): return collection_status collection: MongoCollection = collection_status.ok() - if has_permission or self.has_permission( - ActionObjectWRITE(uid=qk.value, credentials=credentials) - ): - qks = QueryKeys(qks=qk) - result = collection.delete_one(filter=qks.as_dict_mongo) - - if result.deleted_count == 1: - return Ok(SyftSuccess(message="Deleted")) + collection_permissions_status = self.permissions + if collection_permissions_status.is_err(): + return collection_permissions_status + collection_permissions: MongoCollection = collection_permissions_status.ok() - return Err(f"Failed to delete object with qk: {qk}") + qks = QueryKeys(qks=qk) + # delete the object + result = collection.delete_one(filter=qks.as_dict_mongo) + # delete the object's permission + result_permission = collection_permissions.delete_one(filter=qks.as_dict_mongo) + if result.deleted_count == 1 and result_permission.deleted_count == 1: + return Ok(SyftSuccess(message="Object and its permission are deleted")) + elif result.deleted_count == 0: + return Err(f"Failed to delete object with qk: {qk}") + else: + return Err( + f"Object with qk: {qk} was deleted, but failed to delete its corresponding permission" + ) def has_permission(self, permission: ActionObjectPermission) -> bool: """Check if the permission is inside the permission collection""" @@ -402,10 +421,6 @@ def has_permission(self, permission: ActionObjectPermission) -> bool: return False collection_permissions: MongoCollection = collection_permissions_status.ok() - # TODO: fix for other admins - if self.root_verify_key.verify == permission.credentials.verify: - return True - permissions: Optional[Dict] = collection_permissions.find_one( {"_id": permission.uid} ) @@ -413,6 +428,10 @@ def has_permission(self, permission: ActionObjectPermission) -> bool: if permissions is None: return False + # TODO: fix for other admins + if self.root_verify_key.verify == permission.credentials.verify: + return True + if permission.permission_string in permissions["permissions"]: return True @@ -441,7 +460,7 @@ def add_permission(self, permission: ActionObjectPermission) -> Result[None, Err {"_id": permission.uid} ) if permissions is None: - # Permission doesn't exits, add a new one + # Permission doesn't exist, add a new one collection_permissions.insert_one( { "_id": permission.uid, @@ -450,7 +469,7 @@ def add_permission(self, permission: ActionObjectPermission) -> Result[None, Err ) else: # update the permissions with the new permission string - permission_strings: set = permissions["permissions"] + permission_strings: Set = permissions["permissions"] permission_strings.add(permission.permission_string) collection_permissions.update_one( {"_id": permission.uid}, {"$set": {"permissions": permission_strings}} @@ -472,11 +491,18 @@ def remove_permission( ) if permissions is None: return Err(f"permission with UID {permission.uid} not found!") - permissions_strings: set = permissions["permissions"] - permissions_strings.remove(permission.permission_string) - collection_permissions.update_one( - {"_id": permission.uid}, {"$set": {"permissions": permissions_strings}} - ) + permissions_strings: Set = permissions["permissions"] + if permission.permission_string in permissions_strings: + permissions_strings.remove(permission.permission_string) + if len(permissions_strings) > 0: + collection_permissions.update_one( + {"_id": permission.uid}, + {"$set": {"permissions": permissions_strings}}, + ) + else: + collection_permissions.delete_one({"_id": permission.uid}) + else: + return Err(f"the permission {permission.permission_string} does not exist!") def take_ownership( self, uid: UID, credentials: SyftVerifyKey diff --git a/packages/syft/tests/conftest.py b/packages/syft/tests/conftest.py index 53bc1c2260b..734faf9d5a5 100644 --- a/packages/syft/tests/conftest.py +++ b/packages/syft/tests/conftest.py @@ -46,6 +46,11 @@ def guest_client(worker): return worker.guest_client +@pytest.fixture(autouse=True) +def guest_verify_key(worker): + return worker.guest_client.credentials.verify_key + + @pytest.fixture(autouse=True) def guest_domain_client(root_domain_client): return root_domain_client.guest() diff --git a/packages/syft/tests/syft/stores/kv_document_store_test.py b/packages/syft/tests/syft/stores/kv_document_store_test.py index 956a5077a7e..3e66d753a96 100644 --- a/packages/syft/tests/syft/stores/kv_document_store_test.py +++ b/packages/syft/tests/syft/stores/kv_document_store_test.py @@ -101,7 +101,7 @@ def test_kv_store_partition_delete( assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - # random object + # can't delete a random object since it was not added obj = MockSyftObject(data="bogus") key = kv_store_partition.settings.store_key.with_obj(obj) res = kv_store_partition.delete(root_verify_key, key) @@ -114,10 +114,13 @@ def test_kv_store_partition_delete( res = kv_store_partition.delete(root_verify_key, key) assert res.is_ok() assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - idx - 1 + # check that the corresponding permissions were also deleted + assert len(kv_store_partition.data) == len(kv_store_partition.permissions) res = kv_store_partition.delete(root_verify_key, key) assert res.is_err() assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - idx - 1 + assert len(kv_store_partition.data) == len(kv_store_partition.permissions) assert len(kv_store_partition.all(root_verify_key).ok()) == 0 diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 968a94ce0fa..599d387bb9e 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -1,15 +1,27 @@ # stdlib import sys from threading import Thread +from typing import List +from typing import Set from typing import Tuple # third party from joblib import Parallel from joblib import delayed +from pymongo.collection import Collection as MongoCollection import pytest +from result import Err # syft absolute +from syft.node.credentials import SyftVerifyKey +from syft.service.action.action_permissions import ActionObjectPermission +from syft.service.action.action_permissions import ActionPermission +from syft.service.action.action_store import ActionObjectEXECUTE +from syft.service.action.action_store import ActionObjectOWNER +from syft.service.action.action_store import ActionObjectREAD +from syft.service.action.action_store import ActionObjectWRITE from syft.store.document_store import PartitionSettings +from syft.store.document_store import QueryKey from syft.store.document_store import QueryKeys from syft.store.mongo_client import MongoStoreClientConfig from syft.store.mongo_document_store import MongoStoreConfig @@ -17,12 +29,20 @@ # relative from .store_constants_test import generate_db_name +from .store_constants_test import test_verify_key_string_hacker from .store_fixtures_test import mongo_store_partition_fn from .store_mocks_test import MockObjectType from .store_mocks_test import MockSyftObject REPEATS = 20 +PERMISSIONS = [ + ActionObjectOWNER, + ActionObjectREAD, + ActionObjectWRITE, + ActionObjectEXECUTE, +] + @pytest.mark.skipif( sys.platform != "linux", reason="pytest_mock_resources + docker issues on Windows" @@ -34,6 +54,7 @@ def test_mongo_store_partition_sanity( assert res.is_ok() assert hasattr(mongo_store_partition, "_collection") + assert hasattr(mongo_store_partition, "_permissions") def test_mongo_store_partition_init_failed(root_verify_key) -> None: @@ -231,7 +252,7 @@ def test_mongo_store_partition_update( res = mongo_store_partition.update(root_verify_key, key, obj_new) assert res.is_ok() - # The ID should stay the same on update, unly the values are updated. + # The ID should stay the same on update, only the values are updated. assert ( len( mongo_store_partition.all( @@ -599,3 +620,434 @@ def _kv_cbk(tid: int) -> None: ).ok() ) assert stored_cnt == 0 + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_collection( + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + + collection_permissions_status = mongo_store_partition.permissions + assert not collection_permissions_status.is_err() + collection_permissions = collection_permissions_status.ok() + assert isinstance(collection_permissions, MongoCollection) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_add_remove_permission( + root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition +) -> None: + """ + Test the add_permission and remove_permission functions of MongoStorePartition + """ + # setting up + res = mongo_store_partition.init_store() + assert res.is_ok() + permissions_collection: MongoCollection = mongo_store_partition.permissions.ok() + obj = MockSyftObject(data=1) + + # add the first permission + obj_read_permission = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.READ, credentials=root_verify_key + ) + mongo_store_partition.add_permission(obj_read_permission) + find_res_1 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert find_res_1 is not None + assert len(find_res_1["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } + + # add the second permission + obj_write_permission = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + mongo_store_partition.add_permission(obj_write_permission) + + find_res_2 = permissions_collection.find_one({"_id": obj.id}) + assert find_res_2 is not None + assert len(find_res_2["permissions"]) == 2 + assert find_res_2["permissions"] == { + obj_read_permission.permission_string, + obj_write_permission.permission_string, + } + + # add duplicated permission + mongo_store_partition.add_permission(obj_write_permission) + find_res_3 = permissions_collection.find_one({"_id": obj.id}) + assert len(find_res_3["permissions"]) == 2 + assert find_res_3["permissions"] == find_res_2["permissions"] + + # remove the write permission + mongo_store_partition.remove_permission(obj_write_permission) + find_res_4 = permissions_collection.find_one({"_id": obj.id}) + assert len(find_res_4["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } + + # remove a non-existent permission + remove_res = mongo_store_partition.remove_permission( + ActionObjectPermission( + uid=obj.id, permission=ActionPermission.OWNER, credentials=root_verify_key + ) + ) + assert isinstance(remove_res, Err) + find_res_5 = permissions_collection.find_one({"_id": obj.id}) + assert len(find_res_5["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } + + # there is only one permission object + assert permissions_collection.count_documents({}) == 1 + + # add permissions in a loop + new_permissions = [] + for idx in range(1, REPEATS + 1): + new_obj = MockSyftObject(data=idx) + new_obj_read_permission = ActionObjectPermission( + uid=new_obj.id, + permission=ActionPermission.READ, + credentials=root_verify_key, + ) + new_permissions.append(new_obj_read_permission) + mongo_store_partition.add_permission(new_obj_read_permission) + assert permissions_collection.count_documents({}) == 1 + idx + + # remove all the permissions added in the loop + for permission in new_permissions: + mongo_store_partition.remove_permission(permission) + + assert permissions_collection.count_documents({}) == 1 + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_add_permissions( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + permissions_collection: MongoCollection = mongo_store_partition.permissions.ok() + obj = MockSyftObject(data=1) + + # add multiple permissions for the first object + permission_1 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + permission_2 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.OWNER, credentials=root_verify_key + ) + permission_3 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.READ, credentials=guest_verify_key + ) + permissions: List[ActionObjectPermission] = [ + permission_1, + permission_2, + permission_3, + ] + mongo_store_partition.add_permissions(permissions) + + # check if the permissions have been added properly + assert permissions_collection.count_documents({}) == 1 + find_res = permissions_collection.find_one({"_id": obj.id}) + assert find_res is not None + assert len(find_res["permissions"]) == 3 + + # add permissions for the second object + obj_2 = MockSyftObject(data=2) + permission_4 = ActionObjectPermission( + uid=obj_2.id, permission=ActionPermission.READ, credentials=root_verify_key + ) + permission_5 = ActionObjectPermission( + uid=obj_2.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + mongo_store_partition.add_permissions([permission_4, permission_5]) + + assert permissions_collection.count_documents({}) == 2 + find_res_2 = permissions_collection.find_one({"_id": obj_2.id}) + assert find_res_2 is not None + assert len(find_res_2["permissions"]) == 2 + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +@pytest.mark.parametrize("permission", PERMISSIONS) +def test_mongo_store_partition_has_permission( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, + permission: ActionObjectPermission, +) -> None: + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + + res = mongo_store_partition.init_store() + assert res.is_ok() + + # root permission + obj = MockSyftObject(data=1) + permission_root = permission(uid=obj.id, credentials=root_verify_key) + permission_client = permission(uid=obj.id, credentials=guest_verify_key) + permission_hacker = permission(uid=obj.id, credentials=hacker_verify_key) + mongo_store_partition.add_permission(permission_root) + # only the root user has access to this permission + assert mongo_store_partition.has_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_client) + assert not mongo_store_partition.has_permission(permission_hacker) + + # client permission for another object + obj_2 = MockSyftObject(data=2) + permission_client_2 = permission(uid=obj_2.id, credentials=guest_verify_key) + permission_root_2 = permission(uid=obj_2.id, credentials=root_verify_key) + permisson_hacker_2 = permission(uid=obj_2.id, credentials=hacker_verify_key) + mongo_store_partition.add_permission(permission_client_2) + # the root (admin) and guest client should have this permission + assert mongo_store_partition.has_permission(permission_root_2) + assert mongo_store_partition.has_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permisson_hacker_2) + + # remove permissions + mongo_store_partition.remove_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_client) + assert not mongo_store_partition.has_permission(permission_hacker) + + mongo_store_partition.remove_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permission_root_2) + assert not mongo_store_partition.has_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permisson_hacker_2) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +@pytest.mark.parametrize("permission", PERMISSIONS) +def test_mongo_store_partition_take_ownership( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, + permission: ActionObjectPermission, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + obj = MockSyftObject(data=1) + + # the guest client takes ownership of obj + mongo_store_partition.take_ownership(uid=obj.id, credentials=guest_verify_key) + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=guest_verify_key) + ) + # the root client will also has the permission + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=hacker_verify_key) + ) + + # hacker or root try to take ownership of the obj and will fail + res = mongo_store_partition.take_ownership( + uid=obj.id, credentials=hacker_verify_key + ) + res_2 = mongo_store_partition.take_ownership( + uid=obj.id, credentials=root_verify_key + ) + assert res.is_err() + assert res_2.is_err() + assert res.value == res_2.value == f"UID: {obj.id} already owned." + + # another object + obj_2 = MockSyftObject(data=2) + # root client takes ownership + mongo_store_partition.take_ownership(uid=obj_2.id, credentials=root_verify_key) + assert mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=root_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=guest_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=hacker_verify_key) + ) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_set( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + """ + Test the permissions functionalities when using MongoStorePartition._set function + """ + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + res = mongo_store_partition.init_store() + assert res.is_ok() + + # set the object to mongo_store_partition.collection + obj = MockSyftObject(data=1) + res = mongo_store_partition.set(root_verify_key, obj, ignore_duplicates=False) + assert res.is_ok() + assert res.ok() == obj + + # check if the corresponding permissions has been added to the permissions + # collection after the root client claim it + pemissions_collection = mongo_store_partition.permissions.ok() + assert isinstance(pemissions_collection, MongoCollection) + permissions = pemissions_collection.find_one({"_id": obj.id}) + assert permissions is not None + assert isinstance(permissions["permissions"], Set) + assert len(permissions["permissions"]) == 4 + for permission in PERMISSIONS: + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) + + # the hacker tries to set duplicated object but should not be able to claim it + res_2 = mongo_store_partition.set(guest_verify_key, obj, ignore_duplicates=True) + assert res_2.is_ok() + for permission in PERMISSIONS: + assert not mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=hacker_verify_key) + ) + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_get_all( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + # set several objects for the root and guest client + num_root_objects: int = 5 + num_guest_objects: int = 3 + for i in range(num_root_objects): + obj = MockSyftObject(data=i) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + for i in range(num_guest_objects): + obj = MockSyftObject(data=i) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj, ignore_duplicates=False + ) + + assert ( + len(mongo_store_partition.all(root_verify_key).ok()) + == num_root_objects + num_guest_objects + ) + assert len(mongo_store_partition.all(guest_verify_key).ok()) == num_guest_objects + assert len(mongo_store_partition.all(hacker_verify_key).ok()) == 0 + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_delete( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + collection: MongoCollection = mongo_store_partition.collection.ok() + pemissions_collection: MongoCollection = mongo_store_partition.permissions.ok() + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + + # the root client set an object + obj = MockSyftObject(data=1) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + qk: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj) + # guest or hacker can't delete it + assert not mongo_store_partition.delete(guest_verify_key, qk).is_ok() + assert not mongo_store_partition.delete(hacker_verify_key, qk).is_ok() + # only the root client can delete it + assert mongo_store_partition.delete(root_verify_key, qk).is_ok() + # check if the object and its permission have been deleted + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 + + # the guest client set an object + obj_2 = MockSyftObject(data=2) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj_2, ignore_duplicates=False + ) + qk_2: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_2) + # the hacker can't delete it + assert not mongo_store_partition.delete(hacker_verify_key, qk_2).is_ok() + # the guest client can delete it + assert mongo_store_partition.delete(guest_verify_key, qk_2).is_ok() + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 + + # the guest client set another object + obj_3 = MockSyftObject(data=3) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj_3, ignore_duplicates=False + ) + qk_3: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_3) + # the root client also has the permission to delete it + assert mongo_store_partition.delete(root_verify_key, qk_3).is_ok() + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_update( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + # the root client set an object + obj = MockSyftObject(data=1) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + assert len(mongo_store_partition.all(credentials=root_verify_key).ok()) == 1 + + qk: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj) + permsissions: MongoCollection = mongo_store_partition.permissions.ok() + + for v in range(REPEATS): + # the guest client should not have permission to update obj + obj_new = MockSyftObject(data=v) + res = mongo_store_partition.update( + credentials=guest_verify_key, qk=qk, obj=obj_new + ) + assert res.is_err() + # the root client has the permission to update obj + res = mongo_store_partition.update( + credentials=root_verify_key, qk=qk, obj=obj_new + ) + assert res.is_ok() + # the id of the object in the permission collection should not be changed + assert permsissions.find_one(qk.as_dict_mongo)["_id"] == obj.id diff --git a/packages/syft/tests/syft/users/user_test.py b/packages/syft/tests/syft/users/user_test.py index 5729c643166..b9bfac92a7d 100644 --- a/packages/syft/tests/syft/users/user_test.py +++ b/packages/syft/tests/syft/users/user_test.py @@ -8,6 +8,7 @@ from syft import SyftSuccess from syft.client.api import SyftAPICall from syft.client.domain_client import DomainClient +from syft.node.node import get_default_root_email from syft.node.worker import Worker from syft.service.context import AuthedServiceContext from syft.service.user.user import ServiceRole @@ -32,7 +33,7 @@ def get_users(worker): ) -def get_mock_client(root_client, role): +def get_mock_client(root_client, role) -> DomainClient: worker = root_client.api.connection.node client = worker.guest_client mail = Faker().email() @@ -66,17 +67,17 @@ def manually_call_service(worker, client, service, args=None, kwargs=None): @pytest.fixture -def guest_client(worker): +def guest_client(worker) -> DomainClient: return get_mock_client(worker.root_client, ServiceRole.GUEST) @pytest.fixture -def ds_client(worker): +def ds_client(worker) -> DomainClient: return get_mock_client(worker.root_client, ServiceRole.DATA_SCIENTIST) @pytest.fixture -def do_client(worker): +def do_client(worker) -> DomainClient: return get_mock_client(worker.root_client, ServiceRole.DATA_OWNER) @@ -233,6 +234,24 @@ def test_user_update(root_client): ) +def test_guest_user_update_to_root_email_failed( + root_client: DomainClient, + do_client: DomainClient, + guest_client: DomainClient, + ds_client: DomainClient, +) -> None: + default_root_email: str = get_default_root_email() + user_update_to_root_email = UserUpdate(email=default_root_email) + for client in [root_client, do_client, guest_client, ds_client]: + res = client.api.services.user.update( + uid=client.me.id, user_update=user_update_to_root_email + ) + assert isinstance(res, SyftError) + assert ( + res.message == f"A user with the email {default_root_email} already exists." + ) + + def test_user_view_set_password(worker: Worker, root_client: DomainClient) -> None: root_client.me.set_password("123", confirm=False) email = root_client.me.email @@ -260,7 +279,6 @@ def test_user_view_set_invalid_email( [ ("syft@gmail.com", "syft_ds@gmail.com"), ("syft@openmined.com", "syft_ds@openmined.com"), - ("info@openmined.org", "info_ds@openmined.org"), ], ) def test_user_view_set_email_success( @@ -275,6 +293,22 @@ def test_user_view_set_email_success( assert isinstance(result2, SyftSuccess) +def test_user_view_set_default_admin_email_failed( + ds_client: DomainClient, guest_client: DomainClient +) -> None: + default_root_email = get_default_root_email() + result = ds_client.me.set_email(default_root_email) + assert isinstance(result, SyftError) + assert ( + result.message == f"A user with the email {default_root_email} already exists." + ) + result_2 = guest_client.me.set_email(default_root_email) + assert isinstance(result_2, SyftError) + assert ( + result.message == f"A user with the email {default_root_email} already exists." + ) + + def test_user_view_set_duplicated_email( root_client: DomainClient, ds_client: DomainClient, guest_client: DomainClient ) -> None: diff --git a/packages/syftcli/manifest.yml b/packages/syftcli/manifest.yml index 82a43fb667e..6fa94b67187 100644 --- a/packages/syftcli/manifest.yml +++ b/packages/syftcli/manifest.yml @@ -1,11 +1,11 @@ manifestVersion: 1.0 -syftVersion: 0.8.2-beta.31 -dockerTag: 0.8.2-beta.31 +syftVersion: 0.8.2-beta.32 +dockerTag: 0.8.2-beta.32 images: - - docker.io/openmined/grid-frontend:0.8.2-beta.31 - - docker.io/openmined/grid-backend:0.8.2-beta.31 + - docker.io/openmined/grid-frontend:0.8.2-beta.32 + - docker.io/openmined/grid-backend:0.8.2-beta.32 - docker.io/library/mongo:latest - docker.io/traefik:v2.10