test: 补充权限、消息与会话服务回归用例
会话读取、接管、重分配与 Socket 订阅统一校验角色、组织与可见性。
This commit is contained in:
1 parent
8e3a4d95da
commit
164f5f9615
97 files changed
+37281
No files matched your search
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Copyright 2024-2026 ReplyPier
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
What a widget token may do to the customer it names.
|
||||
"""
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models.customer import Customer
|
||||
from app.models.session_to_agent import SessionStatus, SessionToAgent
|
||||
from app.services.conversation_token import VERIFIED_SCOPE, account_conversation_key
|
||||
from app.services.widget_identity import (
|
||||
can_act_as,
|
||||
may_stand_for,
|
||||
trusts_identity_in_conversation,
|
||||
trusts_identity_in_session,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def customer(db, test_organization) -> Customer:
|
||||
"""A customer the widget created for the browser holding "browser-a"."""
|
||||
c = Customer(email="known@acme.com", full_name="Known", organization_id=test_organization.id,
|
||||
conversation_key="browser-a")
|
||||
db.add(c)
|
||||
db.commit()
|
||||
db.refresh(c)
|
||||
return c
|
||||
|
||||
|
||||
def _session(db, customer, key) -> SessionToAgent:
|
||||
session = SessionToAgent(session_id=uuid4(), customer_id=customer.id,
|
||||
organization_id=customer.organization_id,
|
||||
status=SessionStatus.OPEN, conversation_key=key)
|
||||
db.add(session)
|
||||
db.commit()
|
||||
return session
|
||||
|
||||
|
||||
def test_a_vouched_for_token_may_act_as_anyone_it_names(db, customer):
|
||||
_session(db, customer, "browser-a")
|
||||
assert can_act_as(db, customer, VERIFIED_SCOPE)
|
||||
|
||||
|
||||
def test_a_widget_token_may_act_as_a_customer_whose_conversations_are_all_its_own(db, customer):
|
||||
_session(db, customer, "browser-a")
|
||||
assert can_act_as(db, customer, "browser-a")
|
||||
|
||||
|
||||
def test_a_widget_token_may_not_act_as_a_customer_with_someone_elses_conversation(db, customer):
|
||||
_session(db, customer, "browser-a")
|
||||
_session(db, customer, "browser-b")
|
||||
assert not can_act_as(db, customer, "browser-a")
|
||||
|
||||
|
||||
def test_a_keyless_conversation_belongs_to_the_vouched_for_scope(db, customer):
|
||||
_session(db, customer, None)
|
||||
assert not can_act_as(db, customer, "browser-a")
|
||||
|
||||
|
||||
def test_a_customer_another_browser_created_is_not_owned_even_without_conversations(db, customer):
|
||||
assert not can_act_as(db, customer, "browser-b")
|
||||
|
||||
|
||||
def test_a_legacy_token_owns_only_an_anonymous_customer_from_before_keys(db, test_organization):
|
||||
anonymous = Customer(email="1738000000000@noemail.com", organization_id=test_organization.id)
|
||||
known = Customer(email="someone@acme.com", organization_id=test_organization.id)
|
||||
db.add_all([anonymous, known])
|
||||
db.commit()
|
||||
|
||||
assert can_act_as(db, anonymous, "jti:abc")
|
||||
assert not can_act_as(db, known, "jti:abc")
|
||||
assert not can_act_as(db, anonymous, "browser-a")
|
||||
|
||||
|
||||
def test_a_widget_token_may_not_act_as_a_signed_in_customer(db, customer):
|
||||
customer.is_authenticated = True
|
||||
db.commit()
|
||||
assert not can_act_as(db, customer, "browser-a")
|
||||
assert not may_stand_for(customer, "browser-a")
|
||||
assert may_stand_for(customer, VERIFIED_SCOPE)
|
||||
|
||||
|
||||
def test_no_scope_or_no_customer_grants_nothing(db, customer):
|
||||
assert not can_act_as(db, customer, None)
|
||||
assert not can_act_as(db, None, VERIFIED_SCOPE)
|
||||
assert not may_stand_for(customer, None)
|
||||
|
||||
|
||||
def test_what_we_know_is_only_recited_inside_the_customers_own_conversation(db, customer):
|
||||
own = _session(db, customer, "browser-a")
|
||||
|
||||
assert trusts_identity_in_session(db, customer, own.session_id)
|
||||
assert not trusts_identity_in_session(db, customer, uuid4())
|
||||
|
||||
claimed = _session(db, customer, "browser-b")
|
||||
assert not trusts_identity_in_session(db, customer, claimed.session_id)
|
||||
# And once someone else is in, not even the original browser is trusted:
|
||||
# the customer is no longer theirs alone.
|
||||
assert not trusts_identity_in_session(db, customer, own.session_id)
|
||||
|
||||
|
||||
def test_a_vouched_for_conversation_is_trusted(db, customer):
|
||||
verified = _session(db, customer, None)
|
||||
|
||||
assert trusts_identity_in_session(db, customer, verified.session_id)
|
||||
|
||||
|
||||
def test_a_signed_in_account_may_stand_for_and_act_as_its_customer(db, customer):
|
||||
"""The in-app support chat: our backend vouched for the account, so its key is
|
||||
as trusted as the embedding app's - for the conversations it started."""
|
||||
customer.is_authenticated = True
|
||||
db.commit()
|
||||
scope = account_conversation_key("user-1")
|
||||
|
||||
assert may_stand_for(customer, scope)
|
||||
assert can_act_as(db, customer, scope)
|
||||
assert trusts_identity_in_conversation(db, customer, _session(db, customer, scope))
|
||||
|
||||
|
||||
def test_no_conversation_means_no_trust(db, customer):
|
||||
assert not trusts_identity_in_conversation(db, customer, None)
|
||||
Reference in new issue
Block a user