Beispiel #1
0
async def test_legacy_login_flow_validates(legacy_data, opp):
    """Test in legacy mode login flow."""
    legacy_data.add_auth("test-user", "test-pass")
    await legacy_data.async_save()

    provider = opp_auth.OppAuthProvider(opp, auth_store.AuthStore(opp),
                                        {"type": "openpeerpower"})
    flow = await provider.async_login_flow({})
    result = await flow.async_step_init()
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

    result = await flow.async_step_init({
        "username": "******",
        "password": "******"
    })
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["errors"]["base"] == "invalid_auth"

    result = await flow.async_step_init({
        "username": "******",
        "password": "******"
    })
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["errors"]["base"] == "invalid_auth"

    result = await flow.async_step_init({
        "username": "******",
        "password": "******"
    })
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["data"]["username"] == "test-user"
Beispiel #2
0
async def test_saving_loading(opp, opp_storage):
    """Test storing and saving data.

    Creates one of each type that we store to test we restore correctly.
    """
    manager = await auth.auth_manager_from_config(
        opp,
        [{
            "type": "insecure_example",
            "users": [{
                "username": "******",
                "password": "******"
            }],
        }],
        [],
    )

    step = await manager.login_flow.async_init(("insecure_example", None))
    step = await manager.login_flow.async_configure(step["flow_id"], {
        "username": "******",
        "password": "******"
    })
    credential = step["result"]
    user = await manager.async_get_or_create_user(credential)

    await manager.async_activate_user(user)
    # the first refresh token will be used to create access token
    refresh_token = await manager.async_create_refresh_token(
        user, CLIENT_ID, credential=credential)
    manager.async_create_access_token(refresh_token, "192.168.0.1")
    # the second refresh token will not be used
    await manager.async_create_refresh_token(user,
                                             "dummy-client",
                                             credential=credential)

    await flush_store(manager._store._store)

    store2 = auth_store.AuthStore(opp)
    users = await store2.async_get_users()
    assert len(users) == 1
    assert users[0].permissions == user.permissions
    assert users[0] == user
    assert len(users[0].refresh_tokens) == 2
    for r_token in users[0].refresh_tokens.values():
        if r_token.client_id == CLIENT_ID:
            # verify the first refresh token
            assert r_token.last_used_at is not None
            assert r_token.last_used_ip == "192.168.0.1"
        elif r_token.client_id == "dummy-client":
            # verify the second refresh token
            assert r_token.last_used_at is None
            assert r_token.last_used_ip is None
        else:
            assert False, "Unknown client_id: %s" % r_token.client_id
async def test_system_groups_store_id_and_name(opp, opp_storage):
    """Test that for system groups we store the ID and name.

    Name is stored so that we remain backwards compat with < 0.82.
    """
    store = auth_store.AuthStore(opp)
    await store._async_load()
    data = store._data_to_save()
    assert len(data["users"]) == 0
    assert data["groups"] == [
        {"id": auth_store.GROUP_ID_ADMIN, "name": auth_store.GROUP_NAME_ADMIN},
        {"id": auth_store.GROUP_ID_USER, "name": auth_store.GROUP_NAME_USER},
        {"id": auth_store.GROUP_ID_READ_ONLY, "name": auth_store.GROUP_NAME_READ_ONLY},
    ]
async def test_loading_race_condition(opp):
    """Test only one storage load called when concurrent loading occurred ."""
    store = auth_store.AuthStore(opp)
    with asynctest.patch(
        "openpeerpower.helpers.entity_registry.async_get_registry"
    ) as mock_ent_registry, asynctest.patch(
        "openpeerpower.helpers.device_registry.async_get_registry"
    ) as mock_dev_registry, asynctest.patch(
        "openpeerpower.helpers.storage.Store.async_load"
    ) as mock_load:
        results = await asyncio.gather(store.async_get_users(), store.async_get_users())

        mock_ent_registry.assert_called_once_with(opp)
        mock_dev_registry.assert_called_once_with(opp)
        mock_load.assert_called_once_with()
        assert results[0] == results[1]
async def test_loading_empty_data(opp, opp_storage):
    """Test we correctly load with no existing data."""
    store = auth_store.AuthStore(opp)
    groups = await store.async_get_groups()
    assert len(groups) == 3
    admin_group = groups[0]
    assert admin_group.name == auth_store.GROUP_NAME_ADMIN
    assert admin_group.system_generated
    assert admin_group.id == auth_store.GROUP_ID_ADMIN
    user_group = groups[1]
    assert user_group.name == auth_store.GROUP_NAME_USER
    assert user_group.system_generated
    assert user_group.id == auth_store.GROUP_ID_USER
    read_group = groups[2]
    assert read_group.name == auth_store.GROUP_NAME_READ_ONLY
    assert read_group.system_generated
    assert read_group.id == auth_store.GROUP_ID_READ_ONLY

    users = await store.async_get_users()
    assert len(users) == 0
Beispiel #6
0
async def test_race_condition_in_data_loading(opp):
    """Test race condition in the opp_auth.Data loading.

    Ref issue: https://github.com/openpeerpower/core/issues/21569
    """
    counter = 0

    async def mock_load(_):
        """Mock of openpeerpower.helpers.storage.Store.async_load."""
        nonlocal counter
        counter += 1
        await asyncio.sleep(0)

    provider = opp_auth.OppAuthProvider(opp, auth_store.AuthStore(opp),
                                        {"type": "openpeerpower"})
    with patch("openpeerpower.helpers.storage.Store.async_load",
               new=mock_load):
        task1 = provider.async_validate_login("user", "pass")
        task2 = provider.async_validate_login("user", "pass")
        results = await asyncio.gather(task1, task2, return_exceptions=True)
        assert counter == 1
        assert isinstance(results[0], opp_auth.InvalidAuth)
        # results[1] will be a TypeError if race condition occurred
        assert isinstance(results[1], opp_auth.InvalidAuth)
Beispiel #7
0
async def async_test_open_peer_power(loop, load_registries=True):
    """Return a Open Peer Power object pointing at test config dir."""
    opp = ha.OpenPeerPower()
    store = auth_store.AuthStore(opp)
    opp.auth = auth.AuthManager(opp, store, {}, {})
    ensure_auth_manager_loaded(opp.auth)
    INSTANCES.append(opp)

    orig_async_add_job = opp.async_add_job
    orig_async_add_executor_job = opp.async_add_executor_job
    orig_async_create_task = opp.async_create_task

    def async_add_job(target, *args):
        """Add job."""
        check_target = target
        while isinstance(check_target, ft.partial):
            check_target = check_target.func

        if isinstance(check_target,
                      Mock) and not isinstance(target, AsyncMock):
            fut = asyncio.Future()
            fut.set_result(target(*args))
            return fut

        return orig_async_add_job(target, *args)

    def async_add_executor_job(target, *args):
        """Add executor job."""
        check_target = target
        while isinstance(check_target, ft.partial):
            check_target = check_target.func

        if isinstance(check_target, Mock):
            fut = asyncio.Future()
            fut.set_result(target(*args))
            return fut

        return orig_async_add_executor_job(target, *args)

    def async_create_task(coroutine):
        """Create task."""
        if isinstance(coroutine,
                      Mock) and not isinstance(coroutine, AsyncMock):
            fut = asyncio.Future()
            fut.set_result(None)
            return fut

        return orig_async_create_task(coroutine)

    async def async_wait_for_task_count(self,
                                        max_remaining_tasks: int = 0) -> None:
        """Block until at most max_remaining_tasks remain.

        Based on OpenPeerPower.async_block_till_done
        """
        # To flush out any call_soon_threadsafe
        await asyncio.sleep(0)
        start_time: float | None = None

        while len(self._pending_tasks) > max_remaining_tasks:
            pending: Collection[Awaitable[Any]] = [
                task for task in self._pending_tasks if not task.done()
            ]
            self._pending_tasks.clear()
            if len(pending) > max_remaining_tasks:
                remaining_pending = await self._await_count_and_log_pending(
                    pending, max_remaining_tasks=max_remaining_tasks)
                self._pending_tasks.extend(remaining_pending)

                if start_time is None:
                    # Avoid calling monotonic() until we know
                    # we may need to start logging blocked tasks.
                    start_time = 0
                elif start_time == 0:
                    # If we have waited twice then we set the start
                    # time
                    start_time = monotonic()
                elif monotonic() - start_time > BLOCK_LOG_TIMEOUT:
                    # We have waited at least three loops and new tasks
                    # continue to block. At this point we start
                    # logging all waiting tasks.
                    for task in pending:
                        _LOGGER.debug("Waiting for task: %s", task)
            else:
                self._pending_tasks.extend(pending)
                await asyncio.sleep(0)

    async def _await_count_and_log_pending(
            self,
            pending: Collection[Awaitable[Any]],
            max_remaining_tasks: int = 0) -> Collection[Awaitable[Any]]:
        """Block at most max_remaining_tasks remain and log tasks that take a long time.

        Based on OpenPeerPower._await_and_log_pending
        """
        wait_time = 0

        return_when = asyncio.ALL_COMPLETED
        if max_remaining_tasks:
            return_when = asyncio.FIRST_COMPLETED

        while len(pending) > max_remaining_tasks:
            _, pending = await asyncio.wait(pending,
                                            timeout=BLOCK_LOG_TIMEOUT,
                                            return_when=return_when)
            if not pending or max_remaining_tasks:
                return pending
            wait_time += BLOCK_LOG_TIMEOUT
            for task in pending:
                _LOGGER.debug("Waited %s seconds for task: %s", wait_time,
                              task)

        return []

    opp.async_add_job = async_add_job
    opp.async_add_executor_job = async_add_executor_job
    opp.async_create_task = async_create_task
    opp.async_wait_for_task_count = types.MethodType(async_wait_for_task_count,
                                                     opp)
    opp._await_count_and_log_pending = types.MethodType(
        _await_count_and_log_pending, opp)

    opp.data[loader.DATA_CUSTOM_COMPONENTS] = {}

    opp.config.location_name = "test home"
    opp.config.config_dir = get_test_config_dir()
    opp.config.latitude = 32.87336
    opp.config.longitude = -117.22743
    opp.config.elevation = 0
    opp.config.time_zone = "US/Pacific"
    opp.config.units = METRIC_SYSTEM
    opp.config.media_dirs = {"local": get_test_config_dir("media")}
    opp.config.skip_pip = True

    opp.config_entries = config_entries.ConfigEntries(opp, {})
    opp.config_entries._entries = {}
    opp.config_entries._store._async_ensure_stop_listener = lambda: None

    # Load the registries
    if load_registries:
        await asyncio.gather(
            device_registry.async_load(opp),
            entity_registry.async_load(opp),
            area_registry.async_load(opp),
        )
        await opp.async_block_till_done()

    opp.state = ha.CoreState.running

    # Mock async_start
    orig_start = opp.async_start

    async def mock_async_start():
        """Start the mocking."""
        # We only mock time during tests and we want to track tasks
        with patch("openpeerpower.core._async_create_timer"), patch.object(
                opp, "async_stop_track_tasks"):
            await orig_start()

    opp.async_start = mock_async_start

    @ha.callback
    def clear_instance(event):
        """Clear global instance."""
        INSTANCES.remove(opp)

    opp.bus.async_listen_once(EVENT_OPENPEERPOWER_CLOSE, clear_instance)

    return opp
def store(opp):
    """Mock store."""
    return auth_store.AuthStore(opp)
async def test_loading_all_access_group_data_format(opp, opp_storage):
    """Test we correctly load old data with single group."""
    opp_storage[auth_store.STORAGE_KEY] = {
        "version": 1,
        "data": {
            "credentials": [],
            "users": [
                {
                    "id": "user-id",
                    "is_active": True,
                    "is_owner": True,
                    "name": "Paulus",
                    "system_generated": False,
                    "group_ids": ["abcd-all-access"],
                },
                {
                    "id": "system-id",
                    "is_active": True,
                    "is_owner": True,
                    "name": "Opp.io",
                    "system_generated": True,
                },
            ],
            "groups": [{"id": "abcd-all-access", "name": "All Access"}],
            "refresh_tokens": [
                {
                    "access_token_expiration": 1800.0,
                    "client_id": "http://localhost:8123/",
                    "created_at": "2018-10-03T13:43:19.774637+00:00",
                    "id": "user-token-id",
                    "jwt_key": "some-key",
                    "last_used_at": "2018-10-03T13:43:19.774712+00:00",
                    "token": "some-token",
                    "user_id": "user-id",
                },
                {
                    "access_token_expiration": 1800.0,
                    "client_id": None,
                    "created_at": "2018-10-03T13:43:19.774637+00:00",
                    "id": "system-token-id",
                    "jwt_key": "some-key",
                    "last_used_at": "2018-10-03T13:43:19.774712+00:00",
                    "token": "some-token",
                    "user_id": "system-id",
                },
                {
                    "access_token_expiration": 1800.0,
                    "client_id": "http://localhost:8123/",
                    "created_at": "2018-10-03T13:43:19.774637+00:00",
                    "id": "hidden-because-no-jwt-id",
                    "last_used_at": "2018-10-03T13:43:19.774712+00:00",
                    "token": "some-token",
                    "user_id": "user-id",
                },
            ],
        },
    }

    store = auth_store.AuthStore(opp)
    groups = await store.async_get_groups()
    assert len(groups) == 3
    admin_group = groups[0]
    assert admin_group.name == auth_store.GROUP_NAME_ADMIN
    assert admin_group.system_generated
    assert admin_group.id == auth_store.GROUP_ID_ADMIN
    read_group = groups[1]
    assert read_group.name == auth_store.GROUP_NAME_READ_ONLY
    assert read_group.system_generated
    assert read_group.id == auth_store.GROUP_ID_READ_ONLY
    user_group = groups[2]
    assert user_group.name == auth_store.GROUP_NAME_USER
    assert user_group.system_generated
    assert user_group.id == auth_store.GROUP_ID_USER

    users = await store.async_get_users()
    assert len(users) == 2

    owner, system = users

    assert owner.system_generated is False
    assert owner.groups == [admin_group]
    assert len(owner.refresh_tokens) == 1
    owner_token = list(owner.refresh_tokens.values())[0]
    assert owner_token.id == "user-token-id"

    assert system.system_generated is True
    assert system.groups == []
    assert len(system.refresh_tokens) == 1
    system_token = list(system.refresh_tokens.values())[0]
    assert system_token.id == "system-token-id"