コード例 #1
0
    def check_endpoint(
        self,
        client: FlaskClient,
        method: str,
        endpoint: str,
        headers: Optional[Dict[str, str]],
        expected_authorized: bool,
        paths: List[str],
    ) -> List[str]:

        assert method in (
            "GET",
            "POST",
            "PUT",
            "PATCH",
            "DELETE",
        )

        path = self.get_path(method, endpoint)

        assert path in paths

        # SERVER_URI because api and auth are already included in endpoint
        full_endpoint = f"{SERVER_URI}/{endpoint}"

        if method == "GET":
            r = client.get(full_endpoint, headers=headers)
        elif method == "POST":
            r = client.post(full_endpoint, headers=headers)
        elif method == "PUT":
            r = client.put(full_endpoint, headers=headers)
        elif method == "PATCH":
            r = client.patch(full_endpoint, headers=headers)
        elif method == "DELETE":
            r = client.delete(full_endpoint, headers=headers)
        else:  # pragma: no cover
            pytest.fail("Unknown method")

        if expected_authorized:
            assert r.status_code != 401
        else:
            assert r.status_code != 400

        paths.remove(path)
        return paths
コード例 #2
0
    def test_03_change_profile(self, client: FlaskClient,
                               faker: Faker) -> None:

        # Always enable during core tests
        if not Env.get_bool("MAIN_LOGIN_ENABLE"):  # pragma: no cover
            log.warning("Profile is disabled, skipping tests")
            return

        headers, _ = self.do_login(client, None, None)

        # update profile, no auth
        r = client.put(f"{AUTH_URI}/profile")
        assert r.status_code == 401
        # update profile, no auth
        r = client.patch(f"{AUTH_URI}/profile")
        assert r.status_code == 401

        # update profile, no data
        r = client.patch(f"{AUTH_URI}/profile", data={}, headers=headers)
        assert r.status_code == 204

        events = self.get_last_events(1)
        assert events[0].event == Events.modify.value
        assert events[0].user == BaseAuthentication.default_user
        assert events[0].target_type == "User"
        # It is true in the core, but projects may introduce additional values
        # and expand the input dictionary even if initially empty
        # e.g. meteohub adds here the requests_expiration_days parameter
        # assert len(events[0].payload) == 0

        newname = faker.name()
        newuuid = faker.pystr()

        r = client.get(f"{AUTH_URI}/profile", headers=headers)
        assert r.status_code == 200
        c = self.get_content(r)
        assert c.get("name") is not None
        assert c.get("name") != newname
        assert c.get("uuid") is not None
        assert c.get("uuid") != newuuid

        # update profile
        data = {"name": newname, "uuid": newuuid}
        r = client.patch(f"{AUTH_URI}/profile", data=data, headers=headers)
        # uuid cannot be modified and will raise an unknown field
        assert r.status_code == 400
        data = {"name": newname}
        r = client.patch(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 204

        events = self.get_last_events(1)
        assert events[0].event == Events.modify.value
        assert events[0].user == BaseAuthentication.default_user
        assert events[0].target_type == "User"
        # It is true in the core, but projects may introduce additional values
        # and expand the input dictionary even if initially empty
        # e.g. meteohub adds here the requests_expiration_days parameter
        # assert len(events[0].payload) == 1
        assert "name" in events[0].payload

        r = client.get(f"{AUTH_URI}/profile", headers=headers)
        assert r.status_code == 200
        c = self.get_content(r)
        assert c.get("name") == newname
        assert c.get("uuid") != newuuid

        # change password, no data
        r = client.put(f"{AUTH_URI}/profile", data={}, headers=headers)
        assert r.status_code == 400
        # Sending a new_password and/or password_confirm without a password
        newpassword = faker.password()
        data = {"new_password": newpassword}
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400
        data = {"password_confirm": newpassword}
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400
        data = {"new_password": newpassword, "password_confirm": newpassword}
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        data = {}
        data["password"] = faker.password(length=5)
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        data["new_password"] = faker.password(length=5)
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        data["password_confirm"] = faker.password(length=5)
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        data["password"] = BaseAuthentication.default_password
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        # Passwords are too short
        data["password_confirm"] = data["new_password"]
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 400

        # Trying to set new password == password... it is not permitted!
        data["password_confirm"] = data["password"]
        data["new_password"] = data["password"]

        if Env.get_bool("AUTH_SECOND_FACTOR_AUTHENTICATION"):
            data["totp_code"] = BaseTests.generate_totp(
                BaseAuthentication.default_user)

        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 409

        # Change the password
        data["new_password"] = faker.password(strong=True)
        data["password_confirm"] = data["new_password"]
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 204

        # After a change password a spam of delete Token is expected
        # Reverse the list and skip all delete tokens to find the change password event
        events = self.get_last_events(100)
        events.reverse()
        for event in events:
            if event.event == Events.delete.value:
                assert event.target_type == "Token"
                continue

            assert event.event == Events.change_password.value
            assert event.user == BaseAuthentication.default_user
            break

        # verify the new password
        headers, _ = self.do_login(client, BaseAuthentication.default_user,
                                   data["new_password"])

        # restore the previous password
        data["password"] = data["new_password"]
        data["new_password"] = BaseAuthentication.default_password
        data["password_confirm"] = BaseAuthentication.default_password
        if Env.get_bool("AUTH_SECOND_FACTOR_AUTHENTICATION"):
            data["totp_code"] = BaseTests.generate_totp(
                BaseAuthentication.default_user)
        r = client.put(f"{AUTH_URI}/profile", data=data, headers=headers)
        assert r.status_code == 204

        # After a change password a spam of delete Token is expected
        # Reverse the list and skip all delete tokens to find the change password event
        events = self.get_last_events(100)
        events.reverse()
        for event in events:
            if event.event == Events.delete.value:
                assert event.target_type == "Token"
                continue

            assert event.event == Events.change_password.value
            assert event.user == BaseAuthentication.default_user
            break

        # verify the new password
        headers, _ = self.do_login(client, BaseAuthentication.default_user,
                                   BaseAuthentication.default_password)

        self.save("auth_header", headers)
コード例 #3
0
    def test_users_custom_fields(self, client: FlaskClient) -> None:

        output_fields = mem.customizer.get_custom_output_fields(None)

        profile_inputs = mem.customizer.get_custom_input_fields(
            request=None, scope=mem.customizer.PROFILE)
        registration_inputs = mem.customizer.get_custom_input_fields(
            request=None, scope=mem.customizer.REGISTRATION)
        admin_inputs = mem.customizer.get_custom_input_fields(
            request=None, scope=mem.customizer.ADMIN)
        uuid, data = self.create_user(client)
        headers, _ = self.do_login(client, data["email"], data["password"])

        # Verify custom output fields (if defined) included in the profile response
        r = client.get(f"{AUTH_URI}/profile", headers=headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)

        for field in output_fields:
            assert field in response

        # Verify custom input fields (if defined) included in the profile input schema
        r = client.patch(f"{AUTH_URI}/profile",
                         json={"get_schema": 1},
                         headers=headers)
        response = self.get_content(r)
        assert isinstance(response, list)
        for field in profile_inputs.keys():
            for expected in response:
                if expected["key"] == field:
                    break
            else:  # pragma: no cover
                pytest.fail(
                    f"Input field {field} not found in profile input schema")

        # Verify custom registration fields (if defined) included in the reg. schema
        r = client.post(f"{AUTH_URI}/profile", json={"get_schema": 1})
        response = self.get_content(r)
        assert isinstance(response, list)
        for field in registration_inputs.keys():
            for expected in response:
                if expected["key"] == field:
                    break
            else:  # pragma: no cover
                pytest.fail(
                    f"Input field {field} not found in registration input schema"
                )

        headers, _ = self.do_login(client, None, None)
        # Verify custom admin input fields (if defined) included in admin users schema
        r = client.post(f"{API_URI}/admin/users",
                        json={"get_schema": 1},
                        headers=headers)
        response = self.get_content(r)
        assert isinstance(response, list)
        for field in admin_inputs.keys():
            for expected in response:
                if expected["key"] == field:
                    break
            else:  # pragma: no cover
                pytest.fail(
                    f"Input field {field} not found in admin users input schema"
                )

        # Verify custom admin output fields (if defined) included in admin users output
        r = client.get(f"{API_URI}/admin/users/{uuid}", headers=headers)
        response = self.get_content(r)
        assert isinstance(response, dict)
        for field in output_fields:
            # This will fail
            assert field in response
コード例 #4
0
    def test_sendmail(self, client: FlaskClient, faker: Faker) -> None:

        headers, _ = self.do_login(client, None, None)

        r = client.get(f"{API_URI}/admin/mail", headers=headers)
        assert r.status_code == 405

        r = client.put(f"{API_URI}/admin/mail", headers=headers)
        assert r.status_code == 405

        r = client.patch(f"{API_URI}/admin/mail", headers=headers)
        assert r.status_code == 405

        r = client.delete(f"{API_URI}/admin/mail", headers=headers)
        assert r.status_code == 405

        data: Dict[str, Any] = {"dry_run": False}
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["subject"] = faker.pystr()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["body"] = faker.text()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["to"] = faker.pystr()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["to"] = faker.ascii_email()
        data["body"] = "TEST EMAIL BODY"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 204

        mail = self.read_mock_email()
        body = mail.get("body", "")
        assert "TEST EMAIL BODY" in body

        data["dry_run"] = True
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 200

        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "html_body" in response
        assert "plain_body" in response
        assert "subject" in response
        assert "to" in response
        assert "cc" in response
        assert "bcc" in response

        data["dry_run"] = False

        data["body"] = "TEST EMAIL <b>HTML</b> BODY"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 204
        mail = self.read_mock_email()
        body = mail.get("body", "")
        assert "TEST EMAIL <b>HTML</b> BODY" in body

        data["dry_run"] = True
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 200

        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "html_body" in response
        assert "plain_body" in response
        assert "subject" in response
        assert "to" in response
        assert "cc" in response
        assert "bcc" in response

        data["dry_run"] = False

        data["body"] = faker.text()
        data["cc"] = faker.pystr()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["cc"] = faker.ascii_email()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 204

        data["cc"] = f"{faker.ascii_email()},{faker.pystr()}"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["cc"] = f"{faker.ascii_email()},{faker.ascii_email()}"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 204

        data["bcc"] = faker.pystr()
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["bcc"] = f"{faker.ascii_email()},{faker.pystr()}"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 400

        data["bcc"] = f"{faker.ascii_email()},{faker.ascii_email()}"
        r = client.post(f"{API_URI}/admin/mail", json=data, headers=headers)
        assert r.status_code == 204

        mail = self.read_mock_email()

        body = mail.get("body", "")
        email_headers = mail.get("headers", "")
        assert body is not None
        assert email_headers is not None
        # Subject: is a key in the MIMEText
        assert f"Subject: {data['subject']}" in email_headers
        ccs = mail.get("cc", [])
        assert ccs[0] == data["to"]
        assert ccs[1] == data["cc"].split(",")
        assert ccs[2] == data["bcc"].split(",")
コード例 #5
0
    def test_api_dataset(self, client: FlaskClient, faker: Faker) -> None:
        # setup the test env
        (
            admin_headers,
            uuid_group_A,
            user_A1_uuid,
            user_A1_headers,
            uuid_group_B,
            user_B1_uuid,
            user_B1_headers,
            user_B2_uuid,
            user_B2_headers,
            study1_uuid,
            study2_uuid,
        ) = create_test_env(client, faker, study=True)

        # create a new dataset
        dataset1 = {"name": faker.pystr(), "description": faker.pystr()}
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset1,
        )
        assert r.status_code == 200
        dataset1_uuid = self.get_content(r)
        assert isinstance(dataset1_uuid, str)
        # check the directory exists
        dir_path = INPUT_ROOT.joinpath(uuid_group_B, study1_uuid,
                                       dataset1_uuid)
        assert dir_path.is_dir()

        # create a new dataset in a study of an other group
        r = client.post(
            f"{API_URI}/study/{study2_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset1,
        )
        assert r.status_code == 404

        # create a technical
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/technicals",
            headers=user_B1_headers,
            data={"name": faker.pystr()},
        )
        assert r.status_code == 200
        technical_uuid = self.get_content(r)
        assert isinstance(technical_uuid, str)
        # create a phenotype
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/phenotypes",
            headers=user_B1_headers,
            data={
                "name": faker.pystr(),
                "sex": "male"
            },
        )
        assert r.status_code == 200
        phenotype_uuid = self.get_content(r)
        assert isinstance(phenotype_uuid, str)

        # create a new dataset as admin not belonging to study group
        dataset2 = {
            "name": faker.pystr(),
            "description": faker.pystr(),
            "phenotype": phenotype_uuid,
            "technical": technical_uuid,
        }
        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=admin_headers,
            data=dataset2,
        )
        assert r.status_code == 404

        r = client.post(
            f"{API_URI}/study/{study1_uuid}/datasets",
            headers=user_B1_headers,
            data=dataset2,
        )
        assert r.status_code == 200
        dataset2_uuid = self.get_content(r)
        assert isinstance(dataset2_uuid, str)

        # test dataset access
        # test dataset list response
        r = client.get(f"{API_URI}/study/{study1_uuid}/datasets",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test dataset list response for a study you don't have access
        r = client.get(f"{API_URI}/study/{study2_uuid}/datasets",
                       headers=user_B1_headers)
        assert r.status_code == 404

        # test dataset list response for admin
        r = client.get(f"{API_URI}/study/{study1_uuid}/datasets",
                       headers=admin_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert len(response) == 2

        # test empty list of datasets in a study
        r = client.get(f"{API_URI}/study/{study2_uuid}/datasets",
                       headers=user_A1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, list)
        assert not response

        # dataset owner
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        # same group of the owner
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        # dataset owned by an other group
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_A1_headers)
        assert r.status_code == 404
        not_authorized_message = self.get_content(r)
        assert isinstance(not_authorized_message, str)

        # admin access
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=admin_headers)
        assert r.status_code == 200

        # test technical and phenoype assignation when a new dataset is created
        r = client.get(f"{API_URI}/dataset/{dataset2_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "technical" in response
        assert "phenotype" in response
        assert response["technical"]["uuid"] == technical_uuid
        # check phenotype was correctly assigned
        assert response["phenotype"]["uuid"] == phenotype_uuid

        # test dataset changing status
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 204
        # check new status in get response
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "status" in response

        # delete status
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "-1"},
        )
        assert r.status_code == 204
        # check status has been removed
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert not response["status"]

        # try to modify a status when the dataset is running
        graph = neo4j.get_instance()
        dataset = graph.Dataset.nodes.get_or_none(uuid=dataset1_uuid)
        dataset.status = "RUNNING"
        dataset.save()
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 400

        # admin tries to modify a status when the dataset is running
        r = client.patch(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=admin_headers,
            data={"status": "UPLOAD COMPLETED"},
        )
        assert r.status_code == 204

        # test dataset modification

        # modify a dataset you do not own
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_A1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 404
        # modify a dataset you own
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B1_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 204
        # modify a dataset of your group assigning a technical and a phenotype
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B2_headers,
            data={
                "name": faker.pystr(),
                "technical": technical_uuid,
                "phenotype": phenotype_uuid,
            },
        )
        assert r.status_code == 204
        # check technical was correctly assigned
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert "technical" in response
        assert "phenotype" in response
        assert response["technical"]["uuid"] == technical_uuid
        # check phenotype was correctly assigned
        assert response["phenotype"]["uuid"] == phenotype_uuid

        # modify a dataset of your group removing a technical and a phenotype
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=user_B2_headers,
            data={
                "technical": "-1",
                "phenotype": "-1"
            },
        )
        assert r.status_code == 204
        # check technical was correctly removed
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B2_headers)
        assert r.status_code == 200
        response = self.get_content(r)
        assert isinstance(response, dict)
        assert response["technical"] is None
        # check phenotype was correctly removed
        assert response["phenotype"] is None

        # admin modify a dataset of a group he don't belongs
        r = client.put(
            f"{API_URI}/dataset/{dataset1_uuid}",
            headers=admin_headers,
            data={"description": faker.pystr()},
        )
        assert r.status_code == 404
        # simulate the dataset has an output directory
        # create the output directory in the same way is created in launch pipeline task
        output_path = OUTPUT_ROOT.joinpath(dir_path.relative_to(INPUT_ROOT))
        output_path.mkdir(parents=True)
        assert output_path.is_dir()

        # delete a dataset
        # delete a dataset you do not own
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=user_A1_headers)
        assert r.status_code == 404
        # admin delete a dataset of a group he don't belong
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=admin_headers)
        assert r.status_code == 404
        # delete a dataset you own
        r = client.delete(f"{API_URI}/dataset/{dataset1_uuid}",
                          headers=user_B1_headers)
        assert r.status_code == 204
        assert not dir_path.is_dir()
        # delete a study own by your group
        r = client.delete(f"{API_URI}/dataset/{dataset2_uuid}",
                          headers=user_B2_headers)
        assert r.status_code == 204
        # check dataset deletion
        r = client.get(f"{API_URI}/dataset/{dataset1_uuid}",
                       headers=user_B1_headers)
        assert r.status_code == 404
        not_existent_message = self.get_content(r)
        assert isinstance(not_existent_message, str)
        assert not_existent_message == not_authorized_message
        # check directory deletion
        assert not dir_path.is_dir()
        assert not output_path.is_dir()

        # delete all the elements used by the test
        delete_test_env(
            client,
            user_A1_headers,
            user_B1_headers,
            user_B1_uuid,
            user_B2_uuid,
            user_A1_uuid,
            uuid_group_A,
            uuid_group_B,
            study1_uuid=study1_uuid,
            study2_uuid=study2_uuid,
        )