Example #1
0
def test_invalid_permission_for_agreement_bases(read_only_client, mocker,
                                                field):
    # verify missing base:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["transfer_agreement:read"])
    query = f"query {{ transferAgreement(id: 1) {{ {field} {{ id }} }} }}"
    assert_forbidden_request(read_only_client, query, value={field: None})
Example #2
0
def test_shipment_mutations_cancel(client, mocker, default_shipment,
                                   another_marked_for_shipment_box,
                                   another_shipment):
    # Test case 3.2.7
    shipment_id = str(default_shipment["id"])
    mutation = f"""mutation {{ cancelShipment(id: {shipment_id}) {{
                    id
                    state
                    canceledBy {{ id }}
                    canceledOn
                    details {{ id }}
                }} }}"""
    shipment = assert_successful_request(client, mutation)
    assert shipment.pop("canceledOn").startswith(date.today().isoformat())
    assert shipment == {
        "id": shipment_id,
        "state": ShipmentState.Canceled.name,
        "canceledBy": {
            "id": "8"
        },
        "details": [],
    }

    identifier = another_marked_for_shipment_box["label_identifier"]
    query = f"""query {{ box(labelIdentifier: "{identifier}") {{ state }} }}"""
    box = assert_successful_request(client, query)
    assert box == {"state": BoxState.InStock.name}

    # Shipment does not have any details assigned
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        base_ids=[3], organisation_id=2, user_id=2)
    shipment_id = str(another_shipment["id"])
    mutation = f"""mutation {{ cancelShipment(id: {shipment_id}) {{ state }} }}"""
    shipment = assert_successful_request(client, mutation)
    assert shipment == {"state": ShipmentState.Canceled.name}
Example #3
0
def test_shipment_mutations_cancel_as_member_of_neither_org(
        read_only_client, mocker, default_shipment):
    # Test case 3.2.10
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        organisation_id=3, user_id=2)
    mutation = f"mutation {{ cancelShipment(id: {default_shipment['id']}) {{ id }} }}"
    assert_forbidden_request(read_only_client, mutation)
Example #4
0
def test_invalid_permission_for_qr_code_box(read_only_client, mocker,
                                            default_qr_code):
    # verify missing stock:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["qr:read"])
    code = default_qr_code["code"]
    query = f"""query {{ qrCode(qrCode: "{code}") {{ box {{ id }} }} }}"""
    assert_forbidden_request(read_only_client, query, value={"box": None})
Example #5
0
def test_invalid_permission_for_resource_base(read_only_client, mocker,
                                              default_product, resource):
    # verify missing base:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=[f"{resource}:read"])
    query = f"""query {{ {resource}(id: 1)
                {{ base {{ id }} }} }}"""
    assert_forbidden_request(read_only_client, query, value={"base": None})
Example #6
0
def test_invalid_permission_for_box_field(read_only_client, mocker,
                                          default_box, field):
    # verify missing field:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["stock:read"])
    query = f"""query {{ box(labelIdentifier: "{default_box["label_identifier"]}")
                {{ {field} {{ id }} }} }}"""
    assert_forbidden_request(read_only_client, query, value={field: None})
Example #7
0
def test_invalid_permission_for_base_locations(read_only_client, mocker):
    # verify missing location:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["base:read"])
    query = "query { base(id: 1) { locations { id } } }"
    assert_forbidden_request(read_only_client,
                             query,
                             value={"locations": None})
Example #8
0
def test_invalid_permission_for_beneficiary_tokens(read_only_client, mocker,
                                                   default_beneficiary):
    # verify missing transaction:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["beneficiary:read"])
    id = default_beneficiary["id"]
    query = f"query {{ beneficiary(id: {id}) {{ tokens }} }}"
    assert_forbidden_request(read_only_client, query, value={"tokens": None})
Example #9
0
def test_invalid_permission_for_given_resource_id(read_only_client, mocker,
                                                  query):
    """Verify missing resource:read permission, or missing permission to access
    specified resource (base or organisation).
    """
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["base_1/base:read"], organisation_id=1)
    assert_forbidden_request(read_only_client, f"query {{ {query} }}")
Example #10
0
def test_transfer_agreement_mutations_cancel_as_member_of_neither_org(
        read_only_client, mocker, default_transfer_agreement):
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        organisation_id=3, user_id=2)
    # Test case 2.2.20
    agreement_id = default_transfer_agreement["id"]
    mutation = f"mutation {{ cancelTransferAgreement(id: {agreement_id}) {{ id }} }}"
    assert_forbidden_request(read_only_client, mutation)
Example #11
0
def test_base_specific_permissions(client, mocker):
    """Verify that a user can only create beneficiary if base-specific permission
    available. QR codes can be created regardless of any base but for the front-end the
    base-specific distinction is relevant.
    """
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        organisation_id=2,
        user_id=1,
        permissions=[
            "base_2/qr:create",
            "stock:write",
            "base_3/beneficiary:create",
        ],
    )

    create_beneficiary_for_base2_mutation = """createBeneficiary(
                creationInput : {
                    firstName: "First",
                    lastName: "Last",
                    dateOfBirth: "1990-09-01",
                    baseId: 2,
                    groupIdentifier: "1312",
                    gender: Male,
                    languages: [de],
                    isVolunteer: true,
                    registered: false
                }) {
                id
            }"""
    create_beneficiary_for_base3_mutation = (
        create_beneficiary_for_base2_mutation.replace("baseId: 2", "baseId: 3")
    )
    data = {
        "query": f"""mutation {{
            bene2: {create_beneficiary_for_base2_mutation}
            bene3: {create_beneficiary_for_base3_mutation}
        }}"""
    }

    response = client.post("/graphql", json=data)
    assert response.status_code == 200
    assert response.json["data"]["bene2"] is None
    assert response.json["data"]["bene3"] is not None
    assert len(response.json["errors"]) == 1
    assert response.json["errors"][0]["extensions"]["code"] == "FORBIDDEN"
    assert response.json["errors"][0]["path"] == ["bene2"]

    data = {
        "query": """mutation {
            qr2: createQrCode { code }
            qr3: createQrCode { code }
        }"""
    }
    response = client.post("/graphql", json=data)
    assert response.status_code == 200
    assert response.json["data"]["qr2"] is not None
    assert response.json["data"]["qr3"] is not None
    assert "errors" not in response.json
Example #12
0
def test_shipment_mutations_create_as_member_of_neither_org(
        read_only_client, mocker, default_transfer_agreement):
    # Test case 3.2.4b
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        organisation_id=3, user_id=2)
    mutation = _generate_create_shipment_mutation(
        source_base={"id": 0},
        target_base={"id": 0},
        agreement=default_transfer_agreement,
    )
    assert_forbidden_request(read_only_client, mutation)
Example #13
0
def test_transfer_agreement_mutations_invalid_state(read_only_client, mocker,
                                                    expired_transfer_agreement,
                                                    action):
    # The client has to be permitted to perform the action in general
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        organisation_id=expired_transfer_agreement["target_organisation"],
        user_id=2)
    # Test cases 2.2.11, 2.2.12, 2.2.13
    agreement_id = expired_transfer_agreement["id"]
    mutation = f"mutation {{ {action}TransferAgreement(id: {agreement_id}) {{ id }} }}"
    assert_bad_user_input(read_only_client, mutation)
Example #14
0
def test_invalid_permission_for_shipment_details_field(read_only_client,
                                                       mocker, default_box,
                                                       field):
    # verify missing field:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["shipment:read"])
    query = f"""query {{ shipment(id: 1) {{ details
                {{ {field} {{ id }} }} }} }}"""
    assert_forbidden_request(read_only_client,
                             query,
                             value={"details": [{
                                 field: None
                             }]})
Example #15
0
def auth_service(module_mocker):
    """Patch any interaction with the Auth0 service for the scope of the
    `endpoint_tests` test module.
    Mimick the requesting user in an appropriate way.

    This helps to run the module's tests offline, i.e. without the requirement to
    establish an actual connection to the Auth0 web service. Also the tests are
    decoupled from any changes of user attributes in Auth0.
    """
    module_mocker.patch("boxtribute_server.auth.get_auth_string_from_header"
                        ).return_value = "Bearer Some.Token"
    module_mocker.patch(
        "boxtribute_server.auth.get_public_key").return_value = None
    module_mocker.patch("jose.jwt.decode").return_value = create_jwt_payload()
Example #16
0
def test_metrics_query_for_god_user(
    read_only_client,
    mocker,
    organisation_id,
    number_of_families_served,
    number_of_sales,
):
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["*"], organisation_id=None)
    query = f"""query {{ metrics(organisationId: {organisation_id}) {{
                numberOfFamiliesServed numberOfSales }} }}"""
    response = assert_successful_request(read_only_client,
                                         query,
                                         field="metrics")
    assert response == {
        "numberOfFamiliesServed": number_of_families_served,
        "numberOfSales": number_of_sales,
    }
Example #17
0
def test_shipment_mutations_update_checked_in_boxes_when_shipment_in_non_sent_state(
    read_only_client,
    mocker,
    default_shipment,
    prepared_shipment_detail,
    another_location,
    another_product,
):
    # Test case 3.2.36
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        base_ids=[3], organisation_id=2, user_id=2)
    assert_bad_user_input_when_updating_shipment(
        read_only_client,
        shipment=default_shipment,
        received_details=[prepared_shipment_detail],
        target_location=another_location,
        target_product=another_product,
    )
Example #18
0
def test_transfer_agreement_mutations(
    client,
    default_organisation,
    another_organisation,
    mocker,
):
    def _create_mutation(creation_input):
        return f"""mutation {{ createTransferAgreement(
                    creationInput: {{ {creation_input} }}
                    ) {{
                        id
                        sourceOrganisation {{ id }}
                        targetOrganisation {{ id }}
                        state
                        type
                        requestedBy {{ id }}
                        validFrom
                        validUntil
                        sourceBases {{ id }}
                        targetBases {{ id }}
                        shipments {{ id }}
                    }}
                }}"""

    # Leave all optional fields empty in input
    # Test case 2.2.1
    creation_input = f"""targetOrganisationId: {another_organisation['id']},
        type: {TransferAgreementType.Bidirectional.name}"""
    agreement = assert_successful_request(client,
                                          _create_mutation(creation_input))
    first_agreement_id = agreement.pop("id")
    assert agreement.pop("validFrom").startswith(date.today().isoformat())
    assert agreement == {
        "sourceOrganisation": {
            "id": str(default_organisation["id"])
        },
        "targetOrganisation": {
            "id": str(another_organisation["id"])
        },
        "state": TransferAgreementState.UnderReview.name,
        "type": TransferAgreementType.Bidirectional.name,
        "requestedBy": {
            "id": "8"
        },
        "validUntil": None,
        "sourceBases": [{
            "id": "1"
        }, {
            "id": "2"
        }],
        "targetBases": [{
            "id": "3"
        }, {
            "id": "4"
        }],
        "shipments": [],
    }

    # Provide all available fields in input
    # Test case 2.2.2
    valid_from = "2021-12-15"
    valid_until = "2022-06-30"
    creation_input = f"""targetOrganisationId: {another_organisation['id']},
        type: {TransferAgreementType.Bidirectional.name},
        validFrom: "{valid_from}",
        validUntil: "{valid_until}",
        timezone: "Europe/London",
        sourceBaseIds: [1],
        targetBaseIds: [3]"""
    agreement = assert_successful_request(client,
                                          _create_mutation(creation_input))
    second_agreement_id = agreement.pop("id")
    assert agreement.pop("validFrom").startswith(valid_from)
    assert agreement.pop("validUntil").startswith(valid_until)
    assert agreement == {
        "sourceOrganisation": {
            "id": str(default_organisation["id"])
        },
        "targetOrganisation": {
            "id": str(another_organisation["id"])
        },
        "state": TransferAgreementState.UnderReview.name,
        "type": TransferAgreementType.Bidirectional.name,
        "requestedBy": {
            "id": "8"
        },
        "sourceBases": [{
            "id": "1"
        }],
        "targetBases": [{
            "id": "3"
        }],
        "shipments": [],
    }

    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        base_ids=[3], organisation_id=2, user_id=2)
    # Test case 2.2.3
    mutation = f"""mutation {{ acceptTransferAgreement(id: {first_agreement_id}) {{
                    state
                    acceptedBy {{ id }}
                    acceptedOn
                }}
            }}"""
    agreement = assert_successful_request(client, mutation)
    assert agreement.pop("acceptedOn").startswith(date.today().isoformat())
    assert agreement == {
        "state": TransferAgreementState.Accepted.name,
        "acceptedBy": {
            "id": "2"
        },
    }

    # Test case 2.2.7
    mutation = f"""mutation {{ cancelTransferAgreement(id: {first_agreement_id}) {{
                    state
                    terminatedBy {{ id }}
                    terminatedOn
                }}
            }}"""
    agreement = assert_successful_request(client, mutation)
    assert agreement.pop("terminatedOn").startswith(date.today().isoformat())
    assert agreement == {
        "state": TransferAgreementState.Canceled.name,
        "terminatedBy": {
            "id": "2"
        },
    }

    # Test case 2.2.5
    mutation = f"""mutation {{ rejectTransferAgreement(id: {second_agreement_id}) {{
                    state
                    terminatedBy {{ id }}
                    terminatedOn
                }}
            }}"""
    agreement = assert_successful_request(client, mutation)
    assert agreement.pop("terminatedOn").startswith(date.today().isoformat())
    assert agreement == {
        "state": TransferAgreementState.Rejected.name,
        "terminatedBy": {
            "id": "2"
        },
    }
Example #19
0
def unauthorized(mocker):
    """Effectively remove any permissions from current client."""
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=[])
Example #20
0
def test_shipment_mutations_on_target_side(
    client,
    mocker,
    default_transfer_agreement,
    unidirectional_transfer_agreement,
    default_bases,
    sent_shipment,
    default_shipment_detail,
    another_shipment_detail,
    another_location,
    another_product,
    default_product,
    default_location,
    box_without_qr_code,
    marked_for_shipment_box,
):
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        base_ids=[3], organisation_id=2, user_id=2)

    # Test cases 3.2.1b, 3.2.1c
    for agreement in [
            default_transfer_agreement, unidirectional_transfer_agreement
    ]:
        source_base_id = str(default_bases[3]["id"])
        target_base_id = str(default_bases[2]["id"])
        agreement_id = agreement["id"]
        creation_input = f"""sourceBaseId: {source_base_id},
                             targetBaseId: {target_base_id},
                             transferAgreementId: {agreement_id}"""
        mutation = f"""mutation {{ createShipment(creationInput: {{ {creation_input} }})
                    {{
                        sourceBase {{ id }}
                        targetBase {{ id }}
                        state
                    }} }}"""
        shipment = assert_successful_request(client, mutation)
        assert shipment == {
            "sourceBase": {
                "id": source_base_id
            },
            "targetBase": {
                "id": target_base_id
            },
            "state": ShipmentState.Preparing.name,
        }

    target_product_id = str(another_product["id"])
    target_location_id = str(another_location["id"])
    shipment_id = str(sent_shipment["id"])
    detail_id = str(default_shipment_detail["id"])
    another_detail_id = str(another_shipment_detail["id"])

    def _create_mutation(*, detail_id, target_product_id, target_location_id):
        update_input = f"""id: {shipment_id},
                receivedShipmentDetailUpdateInputs: {{
                        id: {detail_id},
                        targetProductId: {target_product_id},
                        targetLocationId: {target_location_id}
                    }}"""
        return f"""mutation {{ updateShipment(updateInput: {{ {update_input} }}) {{
                        id
                        state
                        completedBy {{ id }}
                        completedOn
                        details {{
                            id
                            targetProduct {{ id }}
                            targetLocation {{ id }}
                            box {{
                                state
                            }}
                        }}
                    }} }}"""

    # Test case 3.2.34a
    shipment = assert_successful_request(
        client,
        _create_mutation(
            detail_id=detail_id,
            target_product_id=target_product_id,
            target_location_id=target_location_id,
        ),
    )
    expected_shipment = {
        "id":
        shipment_id,
        "state":
        ShipmentState.Sent.name,
        "completedBy":
        None,
        "completedOn":
        None,
        "details": [
            {
                "id": detail_id,
                "box": {
                    "state": BoxState.Received.name
                },
                "targetProduct": {
                    "id": target_product_id
                },
                "targetLocation": {
                    "id": target_location_id
                },
            },
            {
                "id": another_detail_id,
                "box": {
                    "state": BoxState.MarkedForShipment.name
                },
                "targetProduct": None,
                "targetLocation": None,
            },
        ],
    }
    assert shipment == expected_shipment

    # Verify that another_detail_id is not updated (invalid product)
    # Test cases 3.2.39ab
    for product in [default_product, {"id": 0}]:
        shipment = assert_successful_request(
            client,
            _create_mutation(
                detail_id=another_detail_id,
                target_product_id=product["id"],
                target_location_id=target_location_id,
            ),
        )
        assert shipment == expected_shipment

    # Verify that another_detail_id is not updated (invalid location)
    # Test cases 3.2.38ab
    for location in [default_location, {"id": 0}]:
        shipment = assert_successful_request(
            client,
            _create_mutation(
                detail_id=another_detail_id,
                target_product_id=target_product_id,
                target_location_id=location["id"],
            ),
        )
        assert shipment == expected_shipment

    # Test case 3.2.40, 3.2.34b
    box_label_identifier = marked_for_shipment_box["label_identifier"]
    mutation = f"""mutation {{ updateShipment( updateInput: {{
                id: {shipment_id},
                lostBoxLabelIdentifiers: ["{box_label_identifier}"]
            }} ) {{
                id
                state
                completedBy {{ id }}
                completedOn
                details {{ id }}
            }} }}"""
    shipment = assert_successful_request(client, mutation)
    assert shipment.pop("completedOn").startswith(date.today().isoformat())
    assert shipment == {
        "id": shipment_id,
        "state": ShipmentState.Completed.name,
        "completedBy": {
            "id": "2"
        },
        "details": [],
    }
    box_label_identifier = box_without_qr_code["label_identifier"]
    query = f"""query {{ box(labelIdentifier: "{box_label_identifier}") {{
                    state
                    product {{ id }}
                    location {{ id }}
    }} }}"""
    box = assert_successful_request(client, query)
    assert box == {
        "state": BoxState.InStock.name,
        "product": {
            "id": target_product_id
        },
        "location": {
            "id": target_location_id
        },
    }

    # The box is still registered in the source base, hence any user from the target
    # organisation can't access it
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload()
    box_label_identifier = marked_for_shipment_box["label_identifier"]
    query = f"""query {{ box(labelIdentifier: "{box_label_identifier}") {{
                    state }} }}"""
    box = assert_successful_request(client, query)
    assert box == {"state": BoxState.Lost.name}
Example #21
0
def test_permission_scope(read_only_client, mocker, default_bases, method):
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=[f"base:{method}"])
    query = "query { bases { id } }"
    bases = assert_successful_request(read_only_client, query)
    assert len(bases) == len(default_bases)
Example #22
0
def test_permission_for_god_user(read_only_client, mocker, default_users):
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["*"])
    query = "query { users { id } }"
    users = assert_successful_request(read_only_client, query)
    assert len(users) == len(default_users)
Example #23
0
def test_query_non_existent_resource_for_god_user(read_only_client, mocker, resource):
    # Non-god users would not be authorized to access resource ID 0
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(permissions=["*"])
    query = f"query {{ {resource}(id: 0) {{ id }} }}"
    response = assert_bad_user_input(read_only_client, query, field=resource)
    assert "SQL" not in response.json["errors"][0]["message"]
Example #24
0
def test_invalid_permission_for_location_boxes(read_only_client, mocker):
    # verify missing stock:read permission
    mocker.patch("jose.jwt.decode").return_value = create_jwt_payload(
        permissions=["location:read"])
    query = "query { location(id: 1) { boxes { elements { id } } } }"
    assert_forbidden_request(read_only_client, query, value={"boxes": None})