mirror of
https://github.com/langgenius/dify.git
synced 2026-05-22 18:08:40 +08:00
127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
"""Tests for the mint-policy validator.
|
|
|
|
Cross-checks the (subject_type, prefix, scopes) triple a caller intends
|
|
to mint against ``MINTABLE_PROFILES``. The validator's defense-in-depth
|
|
value kicks in when a caller wires scopes or prefix from a non-canonical
|
|
source — the well-formed canonical path is the no-violation case.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from libs.oauth_bearer import MINTABLE_PROFILES, Scope, SubjectType
|
|
from services.openapi.mint_policy import MintPolicyViolation, validate_mint_policy
|
|
|
|
|
|
def test_canonical_account_profile_passes():
|
|
profile = MINTABLE_PROFILES[SubjectType.ACCOUNT]
|
|
validate_mint_policy(
|
|
subject_type=profile.subject_type,
|
|
prefix=profile.prefix,
|
|
scopes=profile.scopes,
|
|
)
|
|
|
|
|
|
def test_canonical_external_sso_profile_passes():
|
|
profile = MINTABLE_PROFILES[SubjectType.EXTERNAL_SSO]
|
|
validate_mint_policy(
|
|
subject_type=profile.subject_type,
|
|
prefix=profile.prefix,
|
|
scopes=profile.scopes,
|
|
)
|
|
|
|
|
|
def test_wrong_prefix_rejected():
|
|
with pytest.raises(MintPolicyViolation) as exc:
|
|
validate_mint_policy(
|
|
subject_type=SubjectType.ACCOUNT,
|
|
prefix="dfoe_", # SSO prefix on an account subject
|
|
scopes=frozenset({Scope.FULL}),
|
|
)
|
|
assert "prefix" in str(exc.value)
|
|
|
|
|
|
def test_wrong_scopes_rejected():
|
|
with pytest.raises(MintPolicyViolation) as exc:
|
|
validate_mint_policy(
|
|
subject_type=SubjectType.ACCOUNT,
|
|
prefix="dfoa_",
|
|
scopes=frozenset({Scope.APPS_RUN}), # account should be {FULL}
|
|
)
|
|
assert "scopes" in str(exc.value)
|
|
|
|
|
|
def test_external_sso_with_full_scope_rejected():
|
|
with pytest.raises(MintPolicyViolation):
|
|
validate_mint_policy(
|
|
subject_type=SubjectType.EXTERNAL_SSO,
|
|
prefix="dfoe_",
|
|
scopes=frozenset({Scope.FULL}), # FULL never applies to dfoe_
|
|
)
|
|
|
|
|
|
def test_message_carries_both_drift_reasons():
|
|
"""Mismatched prefix AND mismatched scopes both surface in one error."""
|
|
with pytest.raises(MintPolicyViolation) as exc:
|
|
validate_mint_policy(
|
|
subject_type=SubjectType.ACCOUNT,
|
|
prefix="dfoe_",
|
|
scopes=frozenset({Scope.APPS_RUN}),
|
|
)
|
|
msg = str(exc.value)
|
|
assert "prefix" in msg
|
|
assert "scopes" in msg
|
|
|
|
|
|
def test_license_required_decorator_skips_on_ce():
|
|
from unittest.mock import patch
|
|
|
|
from services.openapi.license_gate import license_required
|
|
|
|
@license_required
|
|
def view():
|
|
return "ok"
|
|
|
|
with patch("services.openapi.license_gate.dify_config") as cfg:
|
|
cfg.ENTERPRISE_ENABLED = False
|
|
assert view() == "ok"
|
|
|
|
|
|
def test_license_required_decorator_403_on_invalid_ee_license():
|
|
from unittest.mock import patch
|
|
|
|
from werkzeug.exceptions import Forbidden
|
|
|
|
from services.openapi.license_gate import license_required
|
|
|
|
@license_required
|
|
def view():
|
|
return "ok"
|
|
|
|
with (
|
|
patch("services.openapi.license_gate.dify_config") as cfg,
|
|
patch("services.openapi.license_gate._is_license_valid", return_value=False),
|
|
):
|
|
cfg.ENTERPRISE_ENABLED = True
|
|
with pytest.raises(Forbidden) as exc:
|
|
view()
|
|
assert "license_required" in exc.value.description
|
|
|
|
|
|
def test_license_required_decorator_passes_on_valid_ee_license():
|
|
from unittest.mock import patch
|
|
|
|
from services.openapi.license_gate import license_required
|
|
|
|
@license_required
|
|
def view():
|
|
return "ok"
|
|
|
|
with (
|
|
patch("services.openapi.license_gate.dify_config") as cfg,
|
|
patch("services.openapi.license_gate._is_license_valid", return_value=True),
|
|
):
|
|
cfg.ENTERPRISE_ENABLED = True
|
|
assert view() == "ok"
|