mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 04:55:07 +00:00
feat(service): add wait_for_approval and return the cache for a login with no prompt
A pair code shown in a log line never answers a prompt, so the server read that login as anonymous and kept the session cache.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from unshackle.core.api.input_bridge import InputBridge
|
||||
from unshackle.core.service import Service
|
||||
|
||||
|
||||
def _service(bridge=None):
|
||||
return SimpleNamespace(log=logging.getLogger("test"), _input_bridge=bridge)
|
||||
|
||||
|
||||
def test_returns_the_first_value_check_gives():
|
||||
answers = iter([None, None, "token"])
|
||||
assert Service.wait_for_approval(_service(), "enter code", lambda: next(answers), timeout=5, interval=0) == "token"
|
||||
|
||||
|
||||
def test_times_out_when_check_never_gives_a_value():
|
||||
with pytest.raises(TimeoutError):
|
||||
Service.wait_for_approval(_service(), "enter code", lambda: None, timeout=0.05, interval=0.01)
|
||||
|
||||
|
||||
def test_stops_when_the_remote_session_is_cancelled():
|
||||
bridge = InputBridge()
|
||||
calls = []
|
||||
|
||||
def check():
|
||||
calls.append(1)
|
||||
bridge.cancel()
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
Service.wait_for_approval(_service(bridge), "enter code", check, timeout=5, interval=0)
|
||||
assert len(calls) == 1
|
||||
@@ -101,7 +101,7 @@ def test_the_client_refuses_a_challenge_for_the_other_drm_system() -> None:
|
||||
assert "not a playready device" in answer["error"]
|
||||
|
||||
|
||||
def test_a_cdm_call_after_authentication_leaves_the_status_and_client_auth_alone() -> None:
|
||||
def test_a_cdm_call_after_authentication_leaves_the_status_alone() -> None:
|
||||
bridge = InputBridge()
|
||||
bridge.status = AuthStatus.AUTHENTICATED
|
||||
result: list[dict] = []
|
||||
@@ -118,7 +118,6 @@ def test_a_cdm_call_after_authentication_leaves_the_status_and_client_auth_alone
|
||||
|
||||
assert result == [{"session": "s", "challenge": "Yw=="}]
|
||||
assert bridge.status is AuthStatus.AUTHENTICATED
|
||||
assert bridge.answered is False
|
||||
assert bridge.get_pending_cdm_call() is None
|
||||
|
||||
|
||||
|
||||
@@ -96,19 +96,6 @@ def test_status_setter() -> None:
|
||||
assert bridge.status is AuthStatus.AUTHENTICATED
|
||||
|
||||
|
||||
def test_answered_only_after_an_accepted_response() -> None:
|
||||
bridge = InputBridge()
|
||||
assert bridge.submit_response("early") is False
|
||||
assert bridge.answered is False
|
||||
worker = threading.Thread(target=bridge.request_input, args=("code?",))
|
||||
worker.start()
|
||||
while bridge.status != AuthStatus.PENDING_INPUT:
|
||||
time.sleep(0.01)
|
||||
assert bridge.submit_response("CODE") is True
|
||||
worker.join(timeout=2)
|
||||
assert bridge.answered is True
|
||||
|
||||
|
||||
def test_request_input_after_authentication_fails_fast() -> None:
|
||||
"""The client stops polling for prompts once authenticated, so a later prompt must not wait for an answer."""
|
||||
bridge = InputBridge()
|
||||
|
||||
@@ -221,11 +221,12 @@ def _run_session(monkeypatch, tmp_path, data, answer, server_account=False, fail
|
||||
@pytest.mark.parametrize(
|
||||
"data, answer, server_account, fail, expected",
|
||||
[
|
||||
({}, None, False, False, False), # anonymous login through the server: stays the server's
|
||||
({}, None, False, False, True), # no prompt: a pair code shown in the log, approved on another device
|
||||
({}, "CODE", False, False, True), # device code the client approved
|
||||
({}, "CODE", False, True, False), # answered, but the login failed
|
||||
({}, "CODE", True, False, False), # server account OTP answered by the client
|
||||
({"cache": "tokens"}, None, False, False, True), # client uploaded its own earlier login
|
||||
({}, None, True, False, False), # server account
|
||||
],
|
||||
)
|
||||
def test_client_auth_follows_login_provenance(monkeypatch, tmp_path, data, answer, server_account, fail, expected):
|
||||
|
||||
Reference in New Issue
Block a user