openwisp / openwisp/openwisp-radius
[change] Enforce disabled-organization rules in RADIUS
@pandafy is already working on this.
Since Aug 18, 2026.
- Dominant language
- Python
- Stars
- 445
- Forks
- 234
- Avg merge
- 20h 21m
- Merged PRs (30d)
- 19
Description
Blockers
- openwisp/openwisp-users#522
Is your feature request related to a problem? Please describe.
openwisp-radius currently does not enforce Organization.is_active.
As a result, disabling an organization has little practical effect on RADIUS operations. A disabled organization can continue to:
- authenticate users through FreeRADIUS;
- create and update accounting sessions;
- register users and memberships;
- issue and validate tokens;
- send SMS verification messages;
- perform social and SAML registration flows;
- emit CoA/Disconnect requests;
- execute asynchronous tasks that were queued before the organization was disabled.
This behavior is inconsistent with the disabled-organization policy being introduced in openwisp/openwisp-users#522, whose goal is to make disabled organizations effectively read-only while still allowing auditing, cleanup, and deletion of existing records.
Describe the solution you'd like
flowchart TD
A["Disabled Organization"]
A --> B["BLOCK access-producing operations"]
B --> B1["Authentication"]
B1 --> B2["Authorization"]
B2 --> B3["Registration"]
B3 --> B4["Tokens"]
B4 --> B5["SMS / Social / SAML"]
B5 --> B6["CoA / Disconnect"]
A --> C["ACCOUNTING exception"]
C --> C1["Start → BLOCK"]
C1 --> C2["Stop → ALLOW"]
C2 --> C3["Interim-Update → ALLOW"]
C3 --> C4["Session cleanup → ALLOW"]
A --> D["CRUD policy"]
D --> D1["Create / Update → BLOCK"]
D1 --> D2["Read / Delete → ALLOW"]
A --> E["Defense in depth"]
E --> E1["Invalidate auth cache"]
E1 --> E2["Invalidate organization RadiusTokens only"]
E2 --> E3["Revalidate queued Celery tasks"]
Adopt the disabled-organization policy defined in openwisp/openwisp-users#522 and extend it to cover RADIUS-specific operations.
Write protection
Reuse the admin, DRF, model-validation, and active-organization helpers introduced by openwisp/openwisp-users#522 so that objects belonging to disabled organizations cannot be created or modified.
Read and delete operations must remain available.
Authentication and credential issuance
Authentication and credential-related operations must be denied when the organization is disabled, including:
- FreeRADIUS API authentication;
- user authorization;
- token issuance and validation;
- registration flows;
- SMS verification flows;
- social authentication;
- SAML authentication;
- CoA generation.
Cache invalidation
The current FreeRADIUS authentication flow relies on cached organization credentials that remain valid after an organization is disabled.
Disabling an organization must immediately invalidate all authentication-related cache entries and credentials so that authentication stops taking effect without waiting for cache expiration.
Authentication code should also explicitly verify Organization.is_active as a defense-in-depth measure.
Asynchronous operations
Operations executed through Celery must revalidate organization state when they start executing.
This prevents work that was queued while an organization was active from continuing after the organization has been disabled.
Accounting exceptions
Some accounting operations represent cleanup of already established sessions rather than creation of new activity.
To preserve network correctness:
- accounting
Startrequests should be blocked; - accounting
StopandInterim-Updaterequests should remain allowed; - session cleanup and maintenance tasks should continue to operate normally.
Scope of the block: per-organization context, not per-user
The guard is scoped to the organization context of each request, not to the user.
A user who belongs to two organizations — organization A (active) and organization B (disabled) — is not locked out globally. Requests resolved to organization A continue to succeed, while requests resolved to organization B are rejected.
FreeRADIUS uuid/token authentication path
In api/freeradius_views.py, the organization is determined from the token or NAS that issued the request. Each request is evaluated independently, therefore blocking organization B must not affect authentication requests that belong to organization A.
REST API path
For endpoints such as /api/v1/radius/<org_slug>/..., the organization is explicitly determined from the URL. The guard therefore applies naturally on a per-organization basis.
RADIUS token authentication path
The radius-token authentication flow behaves differently.
RadiusToken is currently a one-per-user resource and the cache["rt-{username}"] entry maps a username to the organization associated with that token.
As a consequence, radius-token authentication is effectively bound to the user's current organization. If that token currently belongs to a disabled organization, authentication is rejected until a new token is obtained through an active organization, which updates both the token and the cache entry.
This is therefore a current-organization authentication model rather than a simultaneous multi-organization authentication model.
For this reason, cache invalidation performed during organization disablement must be scoped to the affected organization only:
- invalidate that organization's
OrganizationRadiusSettingscache entries; - invalidate only
RadiusTokenobjects belonging to the disabled organization.
Disabling organization B must never invalidate authentication state belonging to organization A.
Goals
- Make
Organization.is_activea first-class enforcement mechanism throughoutopenwisp-radius. - Align RADIUS behavior with the disabled-organization policy introduced by openwisp/openwisp-users#522.
- Prevent authentication, registration, token issuance, SMS delivery, and CoA operations for disabled organizations.
- Keep existing data readable and deletable.
- Allow cleanup and maintenance operations to continue running.
- Ensure asynchronous tasks and cached authentication state cannot bypass organization disablement.
- Provide consistent behavior for both regular users and superusers.
- Ensure organization disablement only affects operations executed in the context of the disabled organization.
Deliverables / Work items
A. Authentication & credentials — BLOCK
- Add an
Organization.is_activeguard to FreeRADIUS authentication. - Add an
Organization.is_activeguard to authorization flows. - Invalidate organization authentication caches when an organization is disabled.
- Invalidate organization-specific
RadiusTokenstate when an organization is disabled. - Verify MAC authentication and roaming flows cannot bypass the guard.
- Verify disposable-token and identity-verification flows cannot bypass the guard.
B. Accounting
- Block accounting
Startrequests for disabled organizations. - Allow accounting
Stoprequests. - Allow accounting
Interim-Updaterequests. - Keep session-cleanup paths operational.
- Keep accounting history readable for audit purposes.
- Review Accounting-On handling and document expected behavior.
C. Registration, token and SMS flows
- Block user registration.
- Block token issuance.
- Block token validation and refresh operations.
- Block SMS verification flows.
- Block phone-number update flows.
- Block social authentication flows.
- Block SAML authentication flows.
- Revalidate organization state in asynchronous registration tasks.
D. CoA and asynchronous tasks
- Add organization-state validation to CoA tasks.
- Add organization-state validation to CoA trigger paths.
- Additional monitoring-related deliverables identified during analysis:
- Revalidate organization state at task execution time for queued Celery jobs.
E. Admin and CRUD protections
- Adopt the reusable admin protections introduced in openwisp/openwisp-users#522.
- Adopt the reusable DRF protections introduced in openwisp/openwisp-users#522.
- Exclude disabled organizations from organization selectors and autocomplete widgets.
- Ensure group and NAS management follows the disabled-organization policy.
- Keep read and delete operations available.
Reference: OpenWISP Device Operation Analysis
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.