示例#1
0
async def _apiv1_process_anonymous_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    organization_id = handshake.answer_data["organization_id"]
    expected_rvk = handshake.answer_data["rvk"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": APIV1_HandshakeType.ANONYMOUS,
            "organization_id": organization_id,
        }

    try:
        organization = await backend.organization.get(organization_id)

    except OrganizationNotFoundError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad organization")

    if organization.expiration_date is not None and organization.expiration_date <= pendulum_now():
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    if expected_rvk and organization.root_verify_key != expected_rvk:
        result_req = handshake.build_rvk_mismatch_result_req()
        return None, result_req, _make_error_infos("Bad root verify key")

    context = APIV1_AnonymousClientContext(transport, handshake, organization_id=organization_id)
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#2
0
async def _apiv1_process_administration_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    if handshake.answer_data["token"] != backend.config.administration_token:
        result_req = handshake.build_bad_administration_token_result_req()
        error_infos = {"reason": "Bad token", "handshake_type": APIV1_HandshakeType.ADMINISTRATION}
        return None, result_req, error_infos

    context = APIV1_AdministrationClientContext(transport, handshake)
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#3
0
    async def poorly_serve_client(stream):
        nonlocal client_answered

        transport = await Transport.init_for_server(stream)
        handshake = ServerHandshake()
        await transport.send(handshake.build_challenge_req())
        await transport.recv()
        # Close connection during handshake
        await stream.aclose()

        client_answered = True
示例#4
0
async def _do_process_authenticated_answer(
    backend, transport: Transport, handshake: ServerHandshake, handshake_type
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:

    organization_id = cast(OrganizationID,
                           handshake.answer_data["organization_id"])
    device_id = cast(DeviceID, handshake.answer_data["device_id"])
    expected_rvk = handshake.answer_data["rvk"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": handshake_type,
            "organization_id": organization_id,
            "device_id": device_id,
        }

    try:
        organization = await backend.organization.get(organization_id)
        user, device = await backend.user.get_user_with_device(
            organization_id, device_id)

    except (OrganizationNotFoundError, UserNotFoundError, KeyError) as exc:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos(str(exc))

    if organization.root_verify_key != expected_rvk:
        result_req = handshake.build_rvk_mismatch_result_req()
        return None, result_req, _make_error_infos("Bad root verify key")

    if organization.is_expired:
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    if user.revoked_on and user.revoked_on <= pendulum_now():
        result_req = handshake.build_revoked_device_result_req()
        return None, result_req, _make_error_infos("Revoked device")

    context = AuthenticatedClientContext(
        transport=transport,
        api_version=handshake.backend_api_version,
        organization_id=organization_id,
        device_id=device_id,
        human_handle=user.human_handle,
        device_label=device.device_label,
        profile=user.profile,
        public_key=user.public_key,
        verify_key=device.verify_key,
    )
    result_req = handshake.build_result_req(device.verify_key)
    return context, result_req, None
示例#5
0
async def _process_invited_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    organization_id = handshake.answer_data["organization_id"]
    invitation_type = handshake.answer_data["invitation_type"]
    token = handshake.answer_data["token"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": HandshakeType.INVITED,
            "organization_id": organization_id,
            "invitation_type": invitation_type,
            "token": token,
        }

    try:
        organization = await backend.organization.get(organization_id)

    except OrganizationNotFoundError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad organization")

    if organization.expiration_date is not None and organization.expiration_date <= pendulum_now():
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    try:
        invitation = await backend.invite.info(
            organization_id, token=handshake.answer_data["token"]
        )
    except InvitationError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad invitation")

    if handshake.answer_data["invitation_type"] == InvitationType.USER:
        expected_invitation_type = UserInvitation
    else:  # Device
        expected_invitation_type = DeviceInvitation
    if not isinstance(invitation, expected_invitation_type):
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad invitation")

    context = InvitedClientContext(
        transport, handshake, organization_id=organization_id, invitation=invitation
    )
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#6
0
async def _apiv1_process_anonymous_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    organization_id = cast(OrganizationID,
                           handshake.answer_data["organization_id"])
    expected_rvk = handshake.answer_data["rvk"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": APIV1_HandshakeType.ANONYMOUS,
            "organization_id": organization_id,
        }

    try:
        organization = await backend.organization.get(organization_id)

    except OrganizationNotFoundError:
        if backend.config.organization_spontaneous_bootstrap:
            # Lazy creation of the organization with always the same empty token
            try:
                await backend.organization.create(id=organization_id,
                                                  bootstrap_token="")
            except OrganizationAlreadyExistsError:
                pass
            organization = await backend.organization.get(organization_id)

        else:
            result_req = handshake.build_bad_identity_result_req()
            return None, result_req, _make_error_infos("Bad organization")

    if organization.is_expired:
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    if expected_rvk and organization.root_verify_key != expected_rvk:
        result_req = handshake.build_rvk_mismatch_result_req()
        return None, result_req, _make_error_infos("Bad root verify key")

    context = APIV1_AnonymousClientContext(
        transport,
        api_version=handshake.backend_api_version,
        organization_id=organization_id)
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#7
0
async def do_handshake(
    backend, transport: Transport
) -> Tuple[Optional[BaseClientContext], Optional[Dict]]:
    try:
        handshake = ServerHandshake()
        challenge_req = handshake.build_challenge_req()
        await transport.send(challenge_req)
        answer_req = await transport.recv()

        handshake.process_answer_req(answer_req)
        if handshake.answer_type == HandshakeType.AUTHENTICATED:
            context, result_req, error_infos = await _process_authenticated_answer(
                backend, transport, handshake)

        elif handshake.answer_type == HandshakeType.INVITED:
            context, result_req, error_infos = await _process_invited_answer(
                backend, transport, handshake)

        else:
            assert handshake.answer_type == APIV1_HandshakeType.ANONYMOUS
            context, result_req, error_infos = await _apiv1_process_anonymous_answer(
                backend, transport, handshake)

    except ProtocolError as exc:
        context = None
        result_req = handshake.build_bad_protocol_result_req(str(exc))
        error_infos = {
            "reason": str(exc),
            "handshake_type": handshake.answer_type
        }

    await transport.send(result_req)

    return context, error_infos
示例#8
0
def test_good_invited_handshake(coolorg, invitation_type):
    organization_id = OrganizationID("Org")
    token = uuid4()

    sh = ServerHandshake()
    ch = InvitedClientHandshake(
        organization_id=organization_id, invitation_type=invitation_type, token=token
    )
    assert sh.state == "stalled"

    challenge_req = sh.build_challenge_req()
    assert sh.state == "challenge"

    answer_req = ch.process_challenge_req(challenge_req)

    sh.process_answer_req(answer_req)
    assert sh.state == "answer"
    assert sh.answer_type == HandshakeType.INVITED
    assert sh.answer_data == {
        "client_api_version": API_V2_VERSION,
        "organization_id": organization_id,
        "invitation_type": invitation_type,
        "token": token,
    }

    result_req = sh.build_result_req()
    assert sh.state == "result"

    ch.process_result_req(result_req)
    assert sh.client_api_version == API_V2_VERSION
示例#9
0
def test_good_authenticated_handshake(alice):
    sh = ServerHandshake()

    ch = AuthenticatedClientHandshake(
        alice.organization_id, alice.device_id, alice.signing_key, alice.root_verify_key
    )
    assert sh.state == "stalled"

    challenge_req = sh.build_challenge_req()
    assert sh.state == "challenge"

    answer_req = ch.process_challenge_req(challenge_req)

    sh.process_answer_req(answer_req)
    assert sh.state == "answer"
    assert sh.answer_type == HandshakeType.AUTHENTICATED
    assert sh.answer_data == {
        "answer": ANY,
        "client_api_version": API_V2_VERSION,
        "organization_id": alice.organization_id,
        "device_id": alice.device_id,
        "rvk": alice.root_verify_key,
    }
    result_req = sh.build_result_req(alice.verify_key)
    assert sh.state == "result"

    ch.process_result_req(result_req)
    assert sh.client_api_version == API_V2_VERSION
示例#10
0
def test_build_result_req_bad_challenge(alice):
    sh = ServerHandshake()
    sh.build_challenge_req()
    answer = {
        "handshake": "answer",
        "type": HandshakeType.AUTHENTICATED.value,
        "client_api_version": API_V2_VERSION,
        "organization_id": alice.organization_id,
        "device_id": alice.device_id,
        "rvk": alice.root_verify_key.encode(),
        "answer": alice.signing_key.sign(sh.challenge + b"-dummy"),
    }
    sh.process_answer_req(packb(answer))
    with pytest.raises(HandshakeFailedChallenge):
        sh.build_result_req(alice.verify_key)
示例#11
0
def test_process_answer_req_bad_format(req, alice):
    for key, good_value in [
        ("organization_id", alice.organization_id),
        ("device_id", alice.device_id),
        ("rvk", alice.root_verify_key.encode()),
        ("token", uuid4()),
    ]:
        if req.get(key) == "<good>":
            req[key] = good_value
    req["client_api_version"] = API_V2_VERSION
    sh = ServerHandshake()
    sh.build_challenge_req()
    with pytest.raises(InvalidMessageError):
        sh.process_answer_req(packb(req))
示例#12
0
def test_build_bad_outcomes(alice, method, expected_result):
    sh = ServerHandshake()
    sh.build_challenge_req()
    answer = {
        "handshake": "answer",
        "type": HandshakeType.AUTHENTICATED.value,
        "client_api_version": API_V2_VERSION,
        "organization_id": alice.organization_id,
        "device_id": alice.device_id,
        "rvk": alice.root_verify_key.encode(),
        "answer": alice.signing_key.sign(sh.challenge),
    }
    sh.process_answer_req(packb(answer))
    req = getattr(sh, method)()
    assert unpackb(req) == {"handshake": "result", "result": expected_result, "help": ANY}
示例#13
0
async def _process_invited_answer(
    backend, transport: Transport, handshake: ServerHandshake
) -> Tuple[Optional[BaseClientContext], bytes, Optional[Dict]]:
    organization_id = cast(OrganizationID, handshake.answer_data["organization_id"])
    invitation_type = cast(InvitationType, handshake.answer_data["invitation_type"])
    token = handshake.answer_data["token"]

    def _make_error_infos(reason):
        return {
            "reason": reason,
            "handshake_type": HandshakeType.INVITED,
            "organization_id": organization_id,
            "invitation_type": invitation_type,
            "token": token,
        }

    try:
        organization = await backend.organization.get(organization_id)

    except OrganizationNotFoundError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad organization")

    if organization.is_expired:
        result_req = handshake.build_organization_expired_result_req()
        return None, result_req, _make_error_infos("Expired organization")

    try:
        invitation = await backend.invite.info(
            organization_id, token=handshake.answer_data["token"]
        )
    except InvitationAlreadyDeletedError:
        result_req = handshake.build_bad_identity_result_req(
            help="Invalid handshake: Invitation already deleted"
        )
        return None, result_req, _make_error_infos("Bad invitation")

    except InvitationNotFoundError:
        result_req = handshake.build_bad_identity_result_req(
            help="Invalid handshake: Invitation not found"
        )
        return None, result_req, _make_error_infos("Bad invitation")

    except InvitationError:
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad invitation")

    expected_invitation_type: Type = (
        UserInvitation
        if handshake.answer_data["invitation_type"] == InvitationType.USER
        else DeviceInvitation
    )
    if not isinstance(invitation, expected_invitation_type):
        result_req = handshake.build_bad_identity_result_req()
        return None, result_req, _make_error_infos("Bad invitation")

    context = InvitedClientContext(
        transport, handshake, organization_id=organization_id, invitation=invitation
    )
    result_req = handshake.build_result_req()
    return context, result_req, None
示例#14
0
    async def _do_handshake(self, transport):
        context = None
        error_infos = None
        try:
            handshake = transport.handshake = ServerHandshake()
            challenge_req = handshake.build_challenge_req()
            await transport.send(challenge_req)
            answer_req = await transport.recv()

            handshake.process_answer_req(answer_req)

            if handshake.answer_type == "authenticated":
                organization_id = handshake.answer_data["organization_id"]
                device_id = handshake.answer_data["device_id"]
                expected_rvk = handshake.answer_data["rvk"]
                try:
                    organization = await self.organization.get(organization_id)
                    user, device = await self.user.get_user_with_device(
                        organization_id, device_id)

                except (OrganizationNotFoundError, UserNotFoundError,
                        KeyError) as exc:
                    result_req = handshake.build_bad_identity_result_req()
                    error_infos = {
                        "reason": str(exc),
                        "handshake_type": "authenticated",
                        "organization_id": organization_id,
                        "device_id": device_id,
                    }

                else:
                    if organization.root_verify_key != expected_rvk:
                        result_req = handshake.build_rvk_mismatch_result_req()
                        error_infos = {
                            "reason": "Bad root verify key",
                            "handshake_type": "authenticated",
                            "organization_id": organization_id,
                            "device_id": device_id,
                        }

                    elif (organization.expiration_date is not None
                          and organization.expiration_date <= pendulum_now()):
                        result_req = handshake.build_organization_expired_result_req(
                        )
                        error_infos = {
                            "reason": "Expired organization",
                            "handshake_type": "authenticated",
                            "organization_id": organization_id,
                            "device_id": device_id,
                        }

                    elif user.revoked_on and user.revoked_on <= pendulum_now():
                        result_req = handshake.build_revoked_device_result_req(
                        )
                        error_infos = {
                            "reason": "Revoked device",
                            "handshake_type": "authenticated",
                            "organization_id": organization_id,
                            "device_id": device_id,
                        }

                    else:
                        context = LoggedClientContext(
                            transport,
                            organization_id,
                            user.is_admin,
                            device_id,
                            user.public_key,
                            device.verify_key,
                        )
                        result_req = handshake.build_result_req(
                            device.verify_key)

            elif handshake.answer_type == "anonymous":
                organization_id = handshake.answer_data["organization_id"]
                expected_rvk = handshake.answer_data["rvk"]
                try:
                    organization = await self.organization.get(organization_id)

                except OrganizationNotFoundError:
                    result_req = handshake.build_bad_identity_result_req()
                    error_infos = {
                        "reason": "Bad organization",
                        "handshake_type": "anonymous",
                        "organization_id": organization_id,
                    }

                else:
                    if expected_rvk and organization.root_verify_key != expected_rvk:
                        result_req = handshake.build_rvk_mismatch_result_req()
                        error_infos = {
                            "reason": "Bad root verify key",
                            "handshake_type": "anonymous",
                            "organization_id": organization_id,
                        }

                    elif (organization.expiration_date is not None
                          and organization.expiration_date <= pendulum_now()):
                        result_req = handshake.build_organization_expired_result_req(
                        )
                        error_infos = {
                            "reason": "Expired organization",
                            "handshake_type": "anonymous",
                            "organization_id": organization_id,
                        }

                    else:
                        context = AnonymousClientContext(
                            transport, organization_id)
                        result_req = handshake.build_result_req()

            elif handshake.answer_type == "administration":
                if handshake.answer_data[
                        "token"] == self.config.administration_token:
                    context = AdministrationClientContext(transport)
                    result_req = handshake.build_result_req()
                else:
                    result_req = handshake.build_bad_administration_token_result_req(
                    )
                    error_infos = {
                        "reason": "Bad token",
                        "handshake_type": "administration"
                    }

            else:
                assert False

        except ProtocolError as exc:
            result_req = handshake.build_bad_protocol_result_req(str(exc))
            error_infos = {
                "reason": str(exc),
                "handshake_type": handshake.answer_type
            }

        await transport.send(result_req)
        return context, error_infos