Metadata-Version: 2.4
Name: juntai-iam
Version: 1.1.0
Summary: Side-effect-free Juntai identity and authorization integration library
Author: Juntai Platform Team
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/zephytiju/JuntaiIAMPython
Project-URL: Repository, https://github.com/zephytiju/JuntaiIAMPython.git
Project-URL: Issues, https://github.com/zephytiju/JuntaiIAMPython/issues
Keywords: iam,oidc,casdoor,casbin,kingbasees,kubernetes
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: casbin<2.0.0,>=1.43.0
Requires-Dist: httpx<1.0.0,>=0.27.0
Requires-Dist: juntai-iam-contracts==1.1.1
Requires-Dist: PyJWT[crypto]<3.0.0,>=2.9.0
Provides-Extra: test
Requires-Dist: build==1.3.0; extra == "test"
Requires-Dist: jsonschema<5.0.0,>=4.23.0; extra == "test"
Requires-Dist: psycopg[binary]<4.0.0,>=3.2.0; extra == "test"
Requires-Dist: pytest<9.0.0,>=8.3.0; extra == "test"
Requires-Dist: ruff<1.0.0,>=0.12.0; extra == "test"
Provides-Extra: kes
Requires-Dist: ksycopg2==2.9.1; platform_system != "Darwin" and extra == "kes"
Dynamic: license-file

# Juntai IAM Python

`juntai-iam` is the side-effect-free Python integration library for Juntai
identity and domain authorization. It validates Casdoor API access tokens and
agent Application proofs, resolves current human, agent, and service principals,
validates Kubernetes projected workload tokens, evaluates the reviewed Casbin
policy and peer-principal intersections, installs trusted transaction-local
KingbaseES context, and enforces field-read and field-write decisions.

This repository does **not** contain an IAM service, HTTP listener, browser
session, tenant administration API, secret backend, policy store, audit chain,
database migration owner, provider runtime, deployment image, OpenAPI artifact,
or generated TypeScript client. Casdoor remains authoritative for organizations,
Users, Applications, OIDC tokens, Permissions, models, and Casbin records.
Domain repositories remain authoritative for their resources, actions, fields,
tables, ACLs, and RLS migrations.

## Install

```bash
python -m pip install "juntai-iam>=1,<2"
```

The stable import root is `juntai.iam`. Importing it performs no discovery,
network request, token validation, database connection, migration, policy
synchronization, listener startup, or global middleware installation.

## Compose a service boundary

```python
from juntai.iam import (
    JUNTAI_POLICY_MODEL_V1,
    AuthorizationRequest,
    CasdoorAccessTokenVerifier,
    CasdoorPolicyEvaluator,
    IamMiddleware,
    iam_transaction_context,
    project_allowed_fields,
)

verifier = CasdoorAccessTokenVerifier.from_discovery(
    issuer=settings.oidc_issuer,
    audiences={settings.api_audience},
    required_scopes={"juntai.api"},
)
evaluator = CasdoorPolicyEvaluator(
    model=JUNTAI_POLICY_MODEL_V1,
    policy_source=policy_source,
)
iam = IamMiddleware(verifier=verifier, evaluator=evaluator)
```

Construct these objects once in the service composition root and inject them.
The policy source reads a revisioned snapshot from the configured Casdoor policy
integration; this package does not persist or mutate policy.

For a protected operation:

```python
identity = iam.require_human_or_delegated(request)
decision = await iam.authorize(
    identity,
    AuthorizationRequest(
        tenant=identity.tenant_id,
        resource=f"axiom/workflows/{workflow_id}",
        action="read",
        requested_fields=("id", "name", "created_at", "secret_notes"),
    ),
)

with database.transaction() as transaction:
    with iam_transaction_context(transaction, identity, decision):
        row = repository.get(transaction, workflow_id)

return project_allowed_fields(row, decision)
```

Build tenant, resource, action, and requested fields from verified identity and
the domain route. Never accept an authorization decision, tenant, group, role,
resource, action, or field grant from an untrusted request.

## Peer principals, bindings, and token rules

`IdentityContext/v2` is immutable and uses exactly `human`, `agent`, or
`service`. Authentication mechanism, session, workload, delegated authority,
and MCP/OpenAPI channel remain separate. The durable issuer-qualified subject is
always the actor; in delegated agent execution it remains the agent, while the
human grantor and exact live grant are separate references. `PrincipalPresentation/v1`
is safe user-facing identity data and never authorizes an operation.

Human API validation checks signature, issuer, audience, authorized party,
expiry, not-before, scopes, organization, key identity, and the exact
`tokenType=access-token` profile. Callers forward Casdoor's OAuth `access_token`
response field. Casdoor 3.125 aliases `id_token` to the same JWT bytes, so
downstream code cannot infer response-field provenance; any future distinct
ID-token profile is rejected as an API credential.

Each agent is a distinct non-interactive Casdoor User owned by `juntai-system`.
An Agent-category Application is only an `AuthenticatorBinding/v1`. For
client credentials, `verify_agent()` verifies standard `sub` as the Application,
`azp` and standard `aud` as its exact client ID, plus six signed JWT-Custom
claims for binding ID, principal ID, binding revision, authority epoch, logical
audience, and logical resource. `resolve_agent_identity()` then requires the
current Application and User to reference the same principal, binding, epoch,
and revisions, and verifies the running application-version reference and every
selected profile against the principal's current eligibility lists. A custom
claim never substitutes for standard JWT validation or current-state resolution.
Raw credentials are never returned or logged.

Discovery and JWKS are fetched lazily and cached. Unknown keys trigger at most
one bounded refresh and fail closed. Previously validated keys may survive a
configured short issuer outage; `invalidate_keys()` supplies the rotation and
emergency invalidation path.

Services use audience-bound projected ServiceAccount tokens validated through
the live Kubernetes TokenReview API. Namespace and ServiceAccount are mapped by
trusted service configuration. The TokenReview client may authenticate with a
projected reviewer token or an explicit Kubernetes client-certificate/key pair
supplied by the composition root; credential files are never read at import
time.

`PeerPrincipalEvaluator` is the shared fail-closed path. Self mode evaluates
direct grants; delegated mode replaces direct grants with the current grantor
authority intersected with the exact `DelegationGrant/v1`. It never unions self
grants. Both modes then intersect the principal/agent ceiling, application
installation, profile, invocation/session/task, and final domain policy. The
exact equations are:

```text
A_self = G_self ∩ C_principal ∩ C_install ∩ C_profile ∩ C_invocation_session_task ∩ P_domain
A_delegated = A_grantor(current) ∩ G_delegation ∩ C_principal ∩ C_install ∩ C_profile ∩ C_invocation_session_task ∩ P_domain
```

`G_self` is deliberately absent from `A_delegated`. The
default current-state staleness is at most 30 seconds; high-risk operations can
require a live check. Revocation, expiry, lost grantor authority, stale epochs,
or any tenant/application/audience/resource/purpose/profile/task/session mismatch
denies without fallback. MCP and OpenAPI select audit channel only.

## Casbin conventions

The reviewed logical policy tuple is:

```text
p = subject_or_role, tenant, resource_pattern, action_pattern, field_pattern, effect
g = subject, role, tenant
```

Evaluation is default deny and any matching deny overrides allows. Tenant is an
exact verified organization identifier and cannot be wildcarded. Resource,
action, and field patterns use literal slash segments, `*` for one segment, and
terminal `**` for descendants. Regular expressions and executable matcher text
are rejected. Operation policies use field pattern `*`; requested fields are
evaluated separately through `field.read` or `field.write` policies.

`AuthorizationDecision` binds the result to the exact tenant, resource, action,
allowed fields, matched policy identifiers, revision, correlation-derived
decision identifier, and audit reason. Call `invalidate()` on the evaluator for
policy revocation, emergency deny, or revision notifications; cache TTL is the
bounded fallback.

## KingbaseES integration

`iam_transaction_context` accepts a DB-API connection or cursor with autocommit
disabled. It uses parameterized `set_config(..., true)` calls for tenant,
subject, groups, roles, workload, delegation, policy revision, correlation, and
decision values. KingbaseES clears them at transaction end, including pooled
connection reuse.

Domain migrations—not this package—must:

- add `tenant_id` to every protected table;
- enable and force RLS;
- define tenant policy using
  `tenant_id = current_setting('juntai.iam.tenant_id', true)`;
- grant runtime access to a dedicated non-superuser, non-owner role without
  `BYPASSRLS`; and
- add domain ACL tables for ownership and high-cardinality sharing.

`assert_forced_rls()` and `assert_least_privileged_runtime_role()` provide
acceptance checks. Missing transaction context yields `NULL` in the recommended
predicate and therefore denies every row.

## Contracts

This package pins `juntai-iam-contracts==1.1.1`. That separate, language-neutral
package owns the wire schemas, deterministic fixtures, canonicalization, and
cross-language conformance matrix. `juntai-iam` owns Python validation and
enforcement only. It does not copy the schemas, issue credentials, persist a
principal or grant, or expose a network service. A schema-valid request still
grants no authority until current proof and every evaluation layer allow it.

## Verification

```bash
python -m pip install -e '.[test]'
ruff check .
pytest -m 'not kes and not kubernetes and not casdoor'
python -m build
```

Real-dependency suites are opt-in and never substitute SQLite, in-memory
authority, or fixture platform APIs:

- `tests/casdoor` validates a native Casdoor-issued access token through live
  discovery and JWKS. Platform's official-image acceptance additionally proves
  two distinct agent Applications, signed JWT-Custom binding claims, supported
  User/Application/Permission convergence, and absence of the historical patch.
- `tests/kubernetes` validates a projected ServiceAccount token with live
  TokenReview.
- `tests/kes` proves forced RLS, missing-context denial, cross-tenant isolation,
  and pooled-connection cleanup against KingbaseES.

The deterministic HTTP/JWT and TokenReview test doubles under `tests/unit` are
unit evidence only.
