Beispiel #1
0
    async def list(self, organization_id: OrganizationID,
                   greeter: UserID) -> List[Invitation]:
        async with self.dbh.pool.acquire() as conn:
            rows = await conn.fetch(*_q_list_invitations(
                organization_id=organization_id, greeter_user_id=greeter))

        invitations_with_claimer_online = self._claimers_ready[organization_id]
        invitations = []
        for (
                token,
                type,
                greeter,
                greeter_human_handle_email,
                greeter_human_handle_label,
                claimer_email,
                created_on,
                deleted_on,
                deleted_reason,
        ) in rows:
            if greeter_human_handle_email:
                greeter_human_handle = HumanHandle(
                    email=greeter_human_handle_email,
                    label=greeter_human_handle_label)
            else:
                greeter_human_handle = None

            if deleted_on:
                status = InvitationStatus.DELETED
            elif token in invitations_with_claimer_online:
                status = InvitationStatus.READY
            else:
                status = InvitationStatus.IDLE

            if type == InvitationType.USER.value:
                invitation = UserInvitation(
                    greeter_user_id=UserID(greeter),
                    greeter_human_handle=greeter_human_handle,
                    claimer_email=claimer_email,
                    token=token,
                    created_on=created_on,
                    status=status,
                )
            else:  # Device
                invitation = DeviceInvitation(
                    greeter_user_id=UserID(greeter),
                    greeter_human_handle=greeter_human_handle,
                    token=token,
                    created_on=created_on,
                    status=status,
                )
            invitations.append(invitation)
        return invitations
Beispiel #2
0
    def _from_url_parse_and_consume_params(cls, params):
        kwargs = super()._from_url_parse_and_consume_params(params)

        value = params.pop("action", ())
        if len(value) != 1:
            raise ValueError("Missing mandatory `action` param")
        if value[0] != "claim_user":
            raise ValueError("Expected `action=claim_user` value")

        value = params.pop("user_id", ())
        if len(value) != 1:
            raise ValueError("Missing mandatory `user_id` param")
        try:
            kwargs["user_id"] = UserID(value[0])
        except ValueError as exc:
            raise ValueError("Invalid `user_id` param value") from exc

        value = params.pop("token", ())
        if len(value) > 0:
            try:
                kwargs["token"] = value[0]
            except ValueError:
                raise ValueError("Invalid `token` param value")

        return kwargs
Beispiel #3
0
def test_bad_organization_id_user_id_and_device_name(raw):
    with pytest.raises(ValueError):
        OrganizationID(raw)
    with pytest.raises(ValueError):
        UserID(raw)
    with pytest.raises(ValueError):
        DeviceName(raw)
Beispiel #4
0
async def query_retrieve_active_human_by_email(conn,
                                               organization_id: OrganizationID,
                                               email: str) -> Optional[UserID]:
    result = await conn.fetchrow(*_q_retrieve_active_human_by_email(
        organization_id=organization_id, now=pendulum_now(), email=email))
    if result:
        return UserID(result["user_id"])
Beispiel #5
0
 def validate(self, string, pos):
     try:
         if len(string) == 0:
             return QValidator.Intermediate, string, pos
         UserID(string)
         return QValidator.Acceptable, string, pos
     except ValueError:
         return QValidator.Invalid, string, pos
Beispiel #6
0
    async def info(self, organization_id: OrganizationID,
                   token: UUID) -> Invitation:
        async with self.dbh.pool.acquire() as conn:
            row = await conn.fetchrow(*_q_info_invitation(
                organization_id=organization_id, token=token))
        if not row:
            raise InvitationNotFoundError(token)

        (
            type,
            greeter,
            greeter_human_handle_email,
            greeter_human_handle_label,
            claimer_email,
            created_on,
            deleted_on,
            deleted_reason,
        ) = row

        if deleted_on:
            raise InvitationAlreadyDeletedError(token)

        if greeter_human_handle_email:
            greeter_human_handle = HumanHandle(
                email=greeter_human_handle_email,
                label=greeter_human_handle_label)
        else:
            greeter_human_handle = None
        if type == InvitationType.USER.value:
            return UserInvitation(
                greeter_user_id=UserID(greeter),
                greeter_human_handle=greeter_human_handle,
                claimer_email=claimer_email,
                token=token,
                created_on=created_on,
                status=InvitationStatus.READY,
            )
        else:  # Device
            return DeviceInvitation(
                greeter_user_id=UserID(greeter),
                greeter_human_handle=greeter_human_handle,
                token=token,
                created_on=created_on,
                status=InvitationStatus.READY,
            )
Beispiel #7
0
async def query_find(conn, organization_id: OrganizationID, query: str,
                     page: int, per_page: int,
                     omit_revoked: bool) -> Tuple[List[UserID], int]:
    if page > 1:
        offset = ((page - 1) * per_page) - 1
    else:
        offset = 0
    if query:
        try:
            UserID(query)
        except ValueError:
            # Contains invalid caracters, no need to go further
            return ([], 0)

        if omit_revoked:
            q = _q_factory(query=True,
                           omit_revoked=True,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id,
                     query="%" + query + "%",
                     now=pendulum_now())

        else:
            q = _q_factory(query=True,
                           omit_revoked=False,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id, query="%" + query + "%")

    else:

        if omit_revoked:
            q = _q_factory(query=False,
                           omit_revoked=True,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id, now=pendulum_now())

        else:
            q = _q_factory(query=False,
                           omit_revoked=False,
                           limit=per_page,
                           offset=offset)
            args = q(organization_id=organization_id)

    all_results = [user["user_id"] for user in await conn.fetch(*args)]
    q = _q_count_total_human(query,
                             omit_revoked=omit_revoked,
                             omit_non_human=True,
                             in_find=True)
    if omit_revoked:
        total = await conn.fetchrow(
            *q(organization_id=organization_id, now=pendulum_now()))
    else:
        total = await conn.fetchrow(*q(organization_id=organization_id))
    return all_results, total[0]
Beispiel #8
0
def test_organization_id_user_id_and_device_name(raw):
    organization_id = OrganizationID(raw)
    assert organization_id == raw

    user_id = UserID(raw)
    assert user_id == raw

    device_name = DeviceName(raw)
    assert device_name == raw
Beispiel #9
0
async def query_get_current_roles(conn, organization_id: OrganizationID,
                                  realm_id: UUID) -> Dict[UserID, RealmRole]:
    ret = await conn.fetch(*_q_get_current_roles(
        organization_id=organization_id, realm_id=realm_id))

    if not ret:
        # Existing group must have at least one owner user
        raise RealmNotFoundError(f"Realm `{realm_id}` doesn't exist")

    return {
        UserID(user_id): STR_TO_REALM_ROLE[role]
        for user_id, role in ret if role is not None
    }
Beispiel #10
0
    async def find(
        self,
        organization_id: OrganizationID,
        query: str = None,
        page: int = 0,
        per_page: int = 100,
        omit_revoked: bool = False,
    ):
        org = self._organizations[organization_id]
        users = org.users

        if query:
            try:
                UserID(query)
            except ValueError:
                # Contains invalid caracters, no need to go further
                return ([], 0)

            results = [
                user_id for user_id in users.keys()
                if user_id.lower().find(query.lower()) != -1
            ]

        else:
            results = users.keys()

        if omit_revoked:
            now = pendulum.now()

            def _user_is_revoked(user_id):
                revoked_on = org.users[user_id].revoked_on
                return revoked_on is not None and revoked_on <= now

            results = [
                user_id for user_id in results if not _user_is_revoked(user_id)
            ]

        # PostgreSQL does case insensitive sort
        sorted_results = sorted(results, key=lambda s: s.lower())
        return sorted_results[(page - 1) * per_page:page *
                              per_page], len(results)
 def next_user_id(self):
     nonlocal name_count
     name_count += 1
     return UserID(f"user{name_count}")
Beispiel #12
0
async def test_organization_bootstrap_bad_data(
    backend_data_binder,
    apiv1_backend_sock_factory,
    organization_factory,
    local_device_factory,
    backend,
    coolorg,
):
    neworg = organization_factory("NewOrg")
    newalice = local_device_factory("alice@dev1", neworg)
    await backend_data_binder.bind_organization(neworg)

    bad_organization_id = coolorg.organization_id
    good_organization_id = neworg.organization_id

    root_signing_key = neworg.root_signing_key
    bad_root_signing_key = coolorg.root_signing_key

    good_bootstrap_token = neworg.bootstrap_token
    bad_bootstrap_token = coolorg.bootstrap_token

    good_rvk = neworg.root_verify_key
    bad_rvk = coolorg.root_verify_key

    good_device_id = newalice.device_id

    good_user_id = newalice.user_id
    bad_user_id = UserID("dummy")

    public_key = newalice.public_key
    verify_key = newalice.verify_key

    now = pendulum.now()
    bad_now = now.subtract(seconds=1)

    good_cu = UserCertificateContent(
        author=None,
        timestamp=now,
        user_id=good_user_id,
        public_key=public_key,
        profile=UserProfile.ADMIN,
        human_handle=newalice.human_handle,
    )
    good_redacted_cu = good_cu.evolve(human_handle=None)
    good_cd = DeviceCertificateContent(
        author=None,
        timestamp=now,
        device_id=good_device_id,
        device_label=newalice.device_label,
        verify_key=verify_key,
    )
    good_redacted_cd = good_cd.evolve(device_label=None)
    bad_now_cu = good_cu.evolve(timestamp=bad_now)
    bad_now_cd = good_cd.evolve(timestamp=bad_now)
    bad_now_redacted_cu = good_redacted_cu.evolve(timestamp=bad_now)
    bad_now_redacted_cd = good_redacted_cd.evolve(timestamp=bad_now)
    bad_id_cu = good_cu.evolve(user_id=bad_user_id)
    bad_not_admin_cu = good_cu.evolve(profile=UserProfile.STANDARD)

    bad_key_cu = good_cu.dump_and_sign(bad_root_signing_key)
    bad_key_cd = good_cd.dump_and_sign(bad_root_signing_key)

    good_cu = good_cu.dump_and_sign(root_signing_key)
    good_redacted_cu = good_redacted_cu.dump_and_sign(root_signing_key)
    good_cd = good_cd.dump_and_sign(root_signing_key)
    good_redacted_cd = good_redacted_cd.dump_and_sign(root_signing_key)
    bad_now_cu = bad_now_cu.dump_and_sign(root_signing_key)
    bad_now_cd = bad_now_cd.dump_and_sign(root_signing_key)
    bad_now_redacted_cu = bad_now_redacted_cu.dump_and_sign(root_signing_key)
    bad_now_redacted_cd = bad_now_redacted_cd.dump_and_sign(root_signing_key)
    bad_id_cu = bad_id_cu.dump_and_sign(root_signing_key)
    bad_not_admin_cu = bad_not_admin_cu.dump_and_sign(root_signing_key)

    for i, (status, organization_id, *params) in enumerate([
        ("not_found", good_organization_id, bad_bootstrap_token, good_cu,
         good_cd, good_rvk),
        (
            "already_bootstrapped",
            bad_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
        ),
        (
            "invalid_certification",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            bad_rvk,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            bad_now_cu,
            good_cd,
            good_rvk,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            bad_id_cu,
            good_cd,
            good_rvk,
        ),
        (
            "invalid_certification",
            good_organization_id,
            good_bootstrap_token,
            bad_key_cu,
            good_cd,
            good_rvk,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            bad_now_cd,
            good_rvk,
        ),
        (
            "invalid_certification",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            bad_key_cd,
            good_rvk,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            bad_not_admin_cu,
            good_cd,
            good_rvk,
        ),
            # Tests with redacted certificates
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_cu,  # Not redacted !
            good_redacted_cd,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_redacted_cu,
            good_cd,  # Not redacted !
        ),
        (
            "bad_message",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            None,  # None not allowed
            good_redacted_cd,
        ),
        (
            "bad_message",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_redacted_cu,
            None,  # None not allowed
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            bad_now_redacted_cu,
            good_redacted_cd,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_redacted_cu,
            bad_now_redacted_cd,
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_redacted_cu,
            _missing,  # Must proved redacted_device if redacted user is present
        ),
        (
            "invalid_data",
            good_organization_id,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            _missing,  # Must proved redacted_device if redacted user is present
            good_redacted_cd,
        ),
    ]):
        print(f"sub test {i}")
        async with apiv1_backend_sock_factory(backend,
                                              organization_id) as sock:
            rep = await organization_bootstrap(sock, *params)
        assert rep["status"] == status

    # Finally cheap test to make sure our "good" data were really good
    async with apiv1_backend_sock_factory(backend,
                                          good_organization_id) as sock:
        rep = await organization_bootstrap(
            sock,
            good_bootstrap_token,
            good_cu,
            good_cd,
            good_rvk,
            good_redacted_cu,
            good_redacted_cd,
        )
    assert rep["status"] == "ok"
Beispiel #13
0
async def query_find_humans(
    conn,
    organization_id: OrganizationID,
    query: str,
    page: int,
    per_page: int,
    omit_revoked: bool,
    omit_non_human: bool,
) -> Tuple[List[HumanFindResultItem], int]:
    if page >= 1:
        offset = (page - 1) * per_page
    else:
        return ([], 0)
    if query:

        if omit_revoked:
            q = _q_human_factory(
                query=True,
                omit_revoked=True,
                omit_non_human=omit_non_human,
                limit=per_page,
                offset=offset,
            )
            args = q(organization_id=organization_id,
                     now=pendulum_now(),
                     query="%" + query + "%")

        else:
            q = _q_human_factory(
                query=True,
                omit_revoked=False,
                omit_non_human=omit_non_human,
                offset=offset,
                limit=per_page,
            )
            args = q(organization_id=organization_id,
                     now=pendulum_now(),
                     query="%" + query + "%")

    else:

        if omit_revoked:
            q = _q_human_factory(
                query=False,
                omit_revoked=True,
                omit_non_human=omit_non_human,
                limit=per_page,
                offset=offset,
            )
            args = q(organization_id=organization_id, now=pendulum_now())

        else:
            q = _q_human_factory(
                query=False,
                omit_revoked=False,
                omit_non_human=omit_non_human,
                offset=offset,
                limit=per_page,
            )
            args = q(organization_id=organization_id, now=pendulum_now())

    raw_results = await conn.fetch(*args)

    humans = [
        HumanFindResultItem(
            user_id=UserID(user_id),
            human_handle=HumanHandle(email=email, label=label)
            if email is not None else None,
            revoked=revoked,
        ) for user_id, email, label, revoked in raw_results
    ]
    q = _q_count_total_human(query,
                             omit_revoked=omit_revoked,
                             omit_non_human=omit_non_human)
    if omit_revoked:
        total = await conn.fetchrow(
            *q(organization_id=organization_id, now=pendulum_now()))
    else:
        total = await conn.fetchrow(*q(organization_id=organization_id))
    return (humans, total[0])
Beispiel #14
0
)
def test_backend_organization_bootstrap_addr_bad_value(url, exc_msg):
    with pytest.raises(ValueError) as exc:
        BackendOrganizationBootstrapAddr.from_url(url)
    assert str(exc.value) == exc_msg


@pytest.fixture(scope="session")
def organization_addr(exported_verify_key):
    url = "parsec://foo/org?rvk=<rvk>".replace("<rvk>", exported_verify_key)
    return BackendOrganizationAddr.from_url(url)


@pytest.mark.parametrize(
    "user_id,token",
    [(UserID("alice"), "123"),
     (UserID("alice"), None)]  # Token is not mandatory
)
def test_backend_organization_claim_user_addr_good(organization_addr, user_id,
                                                   token):
    addr = BackendOrganizationClaimUserAddr.build(organization_addr, user_id,
                                                  token)

    assert addr.hostname == organization_addr.hostname
    assert addr.port == organization_addr.port
    assert addr.use_ssl == organization_addr.use_ssl
    assert addr.organization_id == organization_addr.organization_id
    assert addr.root_verify_key == organization_addr.root_verify_key

    assert isinstance(addr.user_id, UserID)
    assert addr.user_id == user_id