Skip to content

[change] Enforce disabled-organization rules in RADIUS #729

Description

@pandafy
Image

Blockers

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"]
Loading

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 Start requests should be blocked;
  • accounting Stop and Interim-Update requests 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 OrganizationRadiusSettings cache entries;
  • invalidate only RadiusToken objects belonging to the disabled organization.

Disabling organization B must never invalidate authentication state belonging to organization A.

Goals

  • Make Organization.is_active a first-class enforcement mechanism throughout openwisp-radius.
  • Align RADIUS behavior with the disabled-organization policy introduced by [change] Make disabled organizations readonly but deletable 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_active guard to FreeRADIUS authentication.
  • Add an Organization.is_active guard to authorization flows.
  • Invalidate organization authentication caches when an organization is disabled.
  • Invalidate organization-specific RadiusToken state 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 Start requests for disabled organizations.
  • Allow accounting Stop requests.
  • Allow accounting Interim-Update requests.
  • 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

Reference: OpenWISP Device Operation Analysis

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No fields configured for Task.

    Projects

    Status
    Backlog
    Status
    To do (general)
    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions