- infrasynth.configs: typed multi-tenant config store (registry, service, secrets, cache) + public config_changed/config_reset signals and API - emit the declared-but-dead signals (features flags/overrides, scheduler task_completed/task_failed, tenancy tenant_updated, audit model_changed) and per-model audit field exclusions - security: permission catalog (security_permission), Django-style model-derived AutoPermission, PermissionRegistry, RoleAssignment, global-or-tenant Grant/Revoke, catalog API - consolidate the permission surface: PermissionRegistry only (drop the settings dict), IsAuthenticatedAndPermitted aliases HybridPermission, require_permission replaced by required_permissions + require_all - packaging: add [build-system]; add Forgejo publish workflow (.forgejo)
128 lines
8.1 KiB
Markdown
128 lines
8.1 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to `infrasynth-base` are documented here.
|
|
|
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
Versions are derived from Conventional Commits by `python-semantic-release`; do
|
|
not hand-pick a version (see `../AGENTS.backend-packages.md` §8).
|
|
|
|
## [Unreleased]
|
|
|
|
### Added
|
|
- **Automatic permission management.** Every concrete model contributes
|
|
Django-style `view`/`add`/`change`/`delete` codenames
|
|
(`{app}.{verb}_{model}`) to a kit-owned permission catalog
|
|
(`security.Permission`), and `infrasynth.security.permissions.AutoPermission`
|
|
derives and enforces the right codename per DRF action with no per-view
|
|
configuration. Consumers register custom codenames in their own code via
|
|
`PermissionRegistry`, kit model
|
|
viewsets enforce automatically, and `manage.py sync_permissions` +
|
|
`post_migrate` keep the catalog in sync. `INFRASYNTH_SECURITY["AUTO_PERMISSIONS"]`
|
|
selects `global` (default) / `opt_in` / `off`. New `GET /api/v1/auth/permissions/`
|
|
catalog endpoint for assignment UIs.
|
|
- **Multiple roles per user per tenant.** `security.RoleAssignment` (tenant-scoped)
|
|
complements the global `Role.users` M2M; `AuthorizationService` resolves both.
|
|
Global roles (`tenant IS NULL`) require `platform.roles.manage` to create/edit;
|
|
tenant roles remain under `security.manage_roles`.
|
|
- **Global grants/revokes.** `Grant`/`Revoke` are now global-or-tenant: a normal
|
|
write is tenant-scoped, `{"scope": "global"}` (requires `platform.roles.manage`)
|
|
creates a platform-wide override that applies in every tenant.
|
|
- Built-in custom permissions for the kit (`security.*`, `audit.*`, `configs.*`,
|
|
`tenancy.*`, and the `platform.*` cross-tenant set).
|
|
- **`infrasynth.configs` — typed, multi-tenant configuration store.** New app
|
|
(`configs` feature flag) with a code/settings registry
|
|
(`ConfigRegistry`/`INFRASYNTH_CONFIGS["DEFINITIONS"]`), typed coercion
|
|
(string/int/float/bool/decimal/json/choice/duration), precedence
|
|
tenant override → global default → registry default, per-tenant caching, and
|
|
Fernet-encrypted secrets that are masked in the API/signals/audit. API under
|
|
`/api/v1/configs/` (`GET` values/definitions, `PUT`/`DELETE` override,
|
|
`PUT .../global/<key>/`), gated by `configs.manage`/`configs.manage_global`.
|
|
Public signals `config_changed`/`config_reset`.
|
|
- **Emitted signals that were previously declared but never fired.**
|
|
`features.flag_created`/`flag_toggled`/`flag_deleted` and
|
|
`override_created`/`override_deleted` now fire from the flag viewsets (and flag
|
|
mutations bust the feature cache); `scheduler.task_completed`/`task_failed` fire
|
|
exactly once on terminal `TaskExecution` transitions (new `scheduler/receivers.py`);
|
|
`tenancy.tenant_updated` fires from the tenant edit path (`TenantService.update_tenant`);
|
|
`audit.model_changed` fires alongside each `ModelChangeLog` row.
|
|
- `INFRASYNTH_AUDIT["EXCLUDED_MODEL_FIELDS"]` — per-model excluded fields so
|
|
`ConfigValue` is audited without ever logging its (possibly encrypted) value.
|
|
- **Uniform extensibility across every module.** Storage backends can be
|
|
registered (`register_storage_backend` or `STORAGE_BACKENDS[name]["CLASS"]`),
|
|
pipeline steps via `PipelineStepRegistry`/`@pipeline_step`, the pipeline
|
|
executor via `PIPELINE_EXECUTOR`, the invoice PDF via `INVOICE_PDF_BUILDER`,
|
|
and the 2FA method via `TWO_FACTOR_SERVICE`/`TWO_FACTOR_RECOVERY_SERVICE` — all
|
|
through settings/registries, with no kit edits. The other modules already
|
|
resolved extensions through dotted paths (gateways, channels, tasks, inbound
|
|
handlers, scanner) and are now documented as such.
|
|
- **Lazy public API per app package.** `from infrasynth.security import
|
|
AuthorizationService`, `from infrasynth.billing import EntitlementService`,
|
|
etc. resolve via PEP 562 without importing models before the app registry is
|
|
ready; `infrasynth.shared` re-exports its primitives eagerly.
|
|
- **Composable per-endpoint gates** (`infrasynth.gates`): declare
|
|
`infrasynth_gates = [...]` (and/or `@gated(...)` on a viewset action) with
|
|
`TwoFactorGate`, `AltchaGate`, `EntitlementGate`, `FeatureGate`,
|
|
`PermissionGate`, or a custom `Gate`. Access is evaluated per endpoint, the
|
|
default is "gate nothing", and denials raise the correct namespaced error
|
|
(`AUTH_2FA_REQUIRED`, `ENTITLEMENT_PLAN_UPGRADE_REQUIRED`,
|
|
`VALIDATION_ALTCHA_REQUIRED`, …). `GatePermission` is a default permission
|
|
class and the kit's `HybridPermission` evaluates declared gates too.
|
|
- **`2fa` JWT claim** minted only after successful verification and preserved
|
|
across workspace selection, so `TwoFactorGate` works for multi-workspace users.
|
|
- **Verified inbound webhooks.** `InboundReceiveView` now enforces the shared
|
|
HMAC signature (or a provider-specific `BaseInboundHandler.verify`), payload
|
|
size limits, timestamp tolerance, and idempotent re-delivery via
|
|
`InboundEvent.external_id`; verified events are dispatched to the endpoint's
|
|
handler through `process_inbound_event` and marked `is_verified`/`is_processed`.
|
|
- **Working 2FA login flow.** Login now challenges users with a configured
|
|
second factor (pre-auth session + cookie) and only mints JWT cookies after
|
|
`2fa/verify/` (or `2fa/recovery/`) succeeds; `TwoFactorMiddleware` guards the
|
|
session-authenticated surface.
|
|
- **Permission enforcement.** `HybridPermission` (any-of `required_permissions`, or all-of with `require_all`) is now
|
|
wired into security and audit viewsets with documented codenames and a
|
|
tenant-owner bypass; `HybridPermission` takes tenant ownership into account.
|
|
- **API-key rotation** (`/api/v1/auth/api-keys/<id>/rotate/`) and
|
|
**user permission/role endpoints** (`/api/v1/auth/users/<id>/permissions/`,
|
|
`/users/<id>/roles/`).
|
|
- **Billing webhook processing.** Verified events are applied idempotently to
|
|
subscriptions, entitlements, invoices, and `PaymentTransaction` rows; replay
|
|
protection via `assert_fresh_webhook`.
|
|
- **Scheduled billing lifecycle** (`sync_subscriptions`,
|
|
`advance_entitlement_lifecycle`, `expire_entitlements`,
|
|
`generate_renewal_invoices`) and **notification retries** + log retention, all
|
|
wired into `CELERY_BEAT_SCHEDULE`.
|
|
- **Audit update diffs** are captured automatically via a `pre_save` snapshot;
|
|
**audit retention purge** task added.
|
|
- **Feature rollout** (`rollout_percentage`, `environments`,
|
|
`ROLLOUT_HASH_ALGORITHM`) and settings-driven flag registration.
|
|
- **Login brute-force guard** (per-credential rate limit + IP blacklist),
|
|
**configurable password policy** (`PasswordPolicyValidator`), and a correctly
|
|
enforced **ALTCHA** proof-of-work.
|
|
- **File hardening:** global upload-size limit, processing-pipeline toggle, and
|
|
a pluggable virus scanner (`noop`/`clamav`/custom) with `REQUIRE_VIRUS_SCAN`.
|
|
- **Workflow guards:** `MAX_INSTANCES_PER_WORKFLOW`, `ROUTE_MAX_DEPTH`,
|
|
`ALLOW_SELF_ASSIGNMENT`, `AUTO_CLONE_ASSIGNEES_ON_REENTRY`.
|
|
- **MercadoPago** webhook signature verification.
|
|
- `README.md`, `CHANGELOG.md`, and a CI format/coverage gate.
|
|
|
|
### Changed
|
|
- **Permission surface consolidated.** `IsAuthenticatedAndPermitted` is now an
|
|
alias of `HybridPermission` (it was a no-op subclass), and the
|
|
`require_permission(...)` class factory was removed: use
|
|
`required_permissions` (any-of) plus `require_all = True` on the view for
|
|
all-of. Custom permissions are declared only through `PermissionRegistry`
|
|
(the `INFRASYNTH_SECURITY["CUSTOM_PERMISSIONS"]` settings path was dropped).
|
|
- `TenantRateThrottle` and `RateLimitHeadersMiddleware` are active by default,
|
|
producing `X-RateLimit-*` headers on API responses.
|
|
- `EntitlementService` treats `past_due` as within grace (entitled) and merges
|
|
entitlement-level feature overrides over `plan.features`; `require_limit`
|
|
raises `ENTITLEMENT_LIMIT_REACHED`.
|
|
- `FeatureService` resolves the current tenant automatically and honors rollout
|
|
and environment targeting.
|
|
|
|
### Fixed
|
|
- API-key authentication no longer leaks tenant context.
|
|
- `EventRegistry.emit` no longer uses `__import__` and honors
|
|
`DELIVERY_BACKEND`.
|
|
- Test media artifacts no longer accumulate in the repository tree.
|