Files
saas_backend/tests/test_entitlements.py
T
2026-08-31 20:39:41 -04:00

144 lines
5.3 KiB
Python

"""What a user is actually allowed to do.
This is the SaaS's most important behaviour and the one that was most wrong: a
subscription plan *added* permissions to every user in a workspace instead of
capping what their role could grant, so roles were decorative — a "Viewer" on an
enterprise plan held the enterprise plan's entire permission set (finding S-2).
These tests pin the corrected model down so it cannot drift back.
"""
from __future__ import annotations
from datetime import date, timedelta
import pytest
from app.services.auth.subscription_entitlement_service import (
UNBOUNDED,
SubscriptionEntitlementService as Entitlements,
)
from .conftest import requires_db, utc_today
pytestmark = requires_db
def test_a_plan_caps_the_role_rather_than_adding_to_it(
db, tenant_factory, plan_factory, role_factory, user_factory
):
"""The core of S-2.
A viewer on a plan that includes admin permissions must not receive them.
Before the fix this returned the union, so they did.
"""
plan = plan_factory(accesses=["admin.user.read", "admin.user.delete", "reports.view"])
tenant = tenant_factory(plan_id=plan.id)
role = role_factory(tenant=tenant, name="Viewer", accesses=["reports.view"])
user = user_factory(tenant=tenant, role=role)
assert Entitlements.get_effective_access_codes(db, user) == {"reports.view"}
def test_a_role_cannot_exceed_the_plan(
db, tenant_factory, plan_factory, role_factory, user_factory
):
"""The bound holds in the other direction too.
A role granted something the plan does not include gets nothing extra — the
plan is a ceiling, not a suggestion.
"""
plan = plan_factory(accesses=["reports.view"])
tenant = tenant_factory(plan_id=plan.id)
role = role_factory(tenant=tenant, accesses=["reports.view", "admin.user.delete"])
user = user_factory(tenant=tenant, role=role)
assert Entitlements.get_effective_access_codes(db, user) == {"reports.view"}
def test_no_plan_means_the_role_decides_not_that_nothing_is_permitted(
db, tenant_factory, role_factory, user_factory
):
"""A workspace with no plan is a legitimate state, not a locked-out one."""
tenant = tenant_factory()
role = role_factory(tenant=tenant, accesses=["reports.view"])
user = user_factory(tenant=tenant, role=role)
assert Entitlements.get_plan_bound(db, tenant.id) is UNBOUNDED
assert Entitlements.get_effective_access_codes(db, user) == {"reports.view"}
def test_a_user_with_no_role_has_no_permissions_however_rich_the_plan(
db, tenant_factory, plan_factory, user_factory
):
"""The union bug's most visible symptom: plan permissions arriving unearned.
Someone with no role assigned held everything the plan carried.
"""
plan = plan_factory(accesses=["admin.user.delete", "reports.view"])
tenant = tenant_factory(plan_id=plan.id)
user = user_factory(tenant=tenant, role=None)
assert Entitlements.get_effective_access_codes(db, user) == set()
@pytest.mark.parametrize(
"overrides, live",
[
({}, True),
({"is_active": False}, False),
({"status": "EXPIRED"}, False),
({"status": "INACTIVE"}, False),
({"status": "SUSPENDED"}, False),
({"end_date": utc_today() - timedelta(days=1)}, False),
({"end_date": utc_today() + timedelta(days=1)}, True),
({"end_date": utc_today()}, True),
],
)
def test_subscription_liveness(db, tenant_factory, overrides, live):
"""Expiry is inclusive of the last day — a subscription ending today still works."""
tenant = tenant_factory(**overrides)
assert Entitlements.is_subscription_live(tenant) is live
def test_a_lapsed_subscription_withdraws_plan_permissions(
db, tenant_factory, plan_factory, role_factory, user_factory
):
"""Entitlement used to ignore dates entirely.
Only the request middleware stopped an expired workspace, and only for users
that had a workspace at all — so the entitlement layer itself kept answering
yes long after the subscription ended.
"""
plan = plan_factory(accesses=["reports.view"])
tenant = tenant_factory(plan_id=plan.id, end_date=utc_today() - timedelta(days=1))
role = role_factory(tenant=tenant, accesses=["reports.view"])
user = user_factory(tenant=tenant, role=role)
assert Entitlements.get_plan_bound(db, tenant.id) == set()
assert Entitlements.get_effective_access_codes(db, user) == set()
def test_a_superadmin_is_not_bounded_by_a_plan(db, role_factory, user_factory):
"""A platform superadmin is not a customer of anything, so nothing caps them."""
role = role_factory(accesses=["superadmin.tenant.read", "superadmin.tenant.delete"])
user = user_factory(tenant=None, role=role, is_superadmin=True)
assert Entitlements.get_effective_access_codes(db, user) == {
"superadmin.tenant.read",
"superadmin.tenant.delete",
}
def test_the_subscription_summary_reports_liveness(db, tenant_factory, plan_factory):
plan = plan_factory(max_users_allowed=5)
live = tenant_factory(plan_id=plan.id)
lapsed = tenant_factory(plan_id=plan.id, status="EXPIRED")
assert Entitlements.get_subscription_summary(db, live.id)["is_live"] is True
assert Entitlements.get_subscription_summary(db, lapsed.id)["is_live"] is False
def test_no_workspace_has_no_summary(db):
assert Entitlements.get_subscription_summary(db, None) is None