Policy stores¶
Policy sources implement a single protocol:
from typing import Any, Dict, Optional, Protocol, Awaitable
class PolicySource(Protocol):
def load(self) -> Dict[str, Any] | Awaitable[Dict[str, Any]]: ...
def etag(self) -> Optional[str] | Awaitable[Optional[str]]: ...
Out of the box, three stores are available: File, HTTP, S3.
FilePolicySource (local file)¶
Module: rbacx.store.file_store (also re-exported from rbacx.store).
Import & basic usage¶
from rbacx.store import FilePolicySource
source = FilePolicySource("policy.json") # path to a JSON file
doc = source.load() # policy as a dict
tag = source.etag() # string or None
Behavior¶
- Loads JSON from a local file.
etag()reflects the file content (suitable for change detection).- If validation is enabled, it performs a schema check via
rbacx.dsl.validate.
Constructor options (core)¶
validate_schema: bool = False— enable schema validation onload().include_mtime_in_etag: bool- if needed "touch" changes detected (defaultFalse).
Safe write utility¶
from rbacx.store import atomic_write
atomic_write("policy.json", data='{"rules": []}', encoding="utf-8")
HTTPPolicySource (HTTP/HTTPS)¶
Module: rbacx.store.http_store.
Extra dependency: pip install "rbacx[http]".
Import & basic usage¶
from rbacx.store.http_store import HTTPPolicySource # also re-exported from `rbacx.store`.
source = HTTPPolicySource("https://example.com/rbac/policy.json")
doc = source.load() # dict; if server returns 304 Not Modified — returns {}
tag = source.etag() # last ETag (if provided by the server)
Behavior¶
- Issues a GET request and, if a previous ETag is known, sends
If-None-Match. - On
304 Not Modified, returns empty dict{}— a signal that applying can be skipped. - If schema validation is enabled, the loaded policy is validated against the built-in schema before being returned.
Constructor options¶
headers: dict[str, str] | None = None— additional HTTP headers.validate_schema: bool = False— whenTrue, the store validates the parsed policy duringload().
Security parameters (all keyword-only, default values maintain backward compatibility):
verify_ssl: bool = True— TLS certificate verification, passed asverify=torequests.get.timeout: float = 5.0— request timeout in seconds.allow_redirects: bool = True— whether to follow HTTP redirects.allowed_schemes: tuple[str, ...] = ("http", "https")— URL scheme whitelist; raisesValueErrorat construction for any other scheme. Pass("https",)to enforce HTTPS-only.block_private_ips: bool = False— whenTrue, raisesValueErrorif the URL host is a numeric private, loopback, or link-local IP address (SSRF guard). Hostname literals are not resolved.
S3PolicySource¶
Module: rbacx.store.s3_store (also re-exported from rbacx.store).
Requires: boto3 (and optionally botocore for advanced client tuning).
Import & basic usage¶
from rbacx.store import S3PolicySource
source = S3PolicySource("s3://my-bucket/policies/rbac.json")
doc = source.load() # JSON document from S3 as a dict
tag = source.etag() # string or None (depending on the strategy)
Change-detection strategies¶
The change_detector parameter selects the source of the "change tag":
"etag"(default) — uses ETag fromHeadObject."version_id"— usesVersionId(bucket versioning must be enabled)."checksum"— usesGetObjectAttributes(..., ObjectAttributes=['Checksum'])if object checksums are enabled.
If a strategy is unavailable for a particular bucket/object, use "etag" (the most compatible option).
Options (core)¶
validate_schema: bool = False— validate the policy against the schema onload().
Network/client parameters:
- You can pass a prepared
boto3.Session. - Timeouts/retries can be tuned via
botocore.config.Config. - Additional client parameters are accepted (e.g.,
endpoint_url,region_name).
(Argument names match the S3PolicySource constructor; use the simple form from the example if you don't need advanced tuning.)
Using with HotReloader¶
Any store can be connected to HotReloader:
from rbacx import Guard
from rbacx import HotReloader
from rbacx.store import FilePolicySource, S3PolicySource
from rbacx.store.http_store import HTTPPolicySource
guard = Guard(policy={})
# Local file
file_src = FilePolicySource("policy.json")
HotReloader(guard, file_src, poll_interval=2.0).start()
# HTTP
http_src = HTTPPolicySource("https://example.com/rbac/policy.json")
HotReloader(guard, http_src, poll_interval=5.0).start()
# S3
s3_src = S3PolicySource("s3://my-bucket/policies/rbac.json")
HotReloader(guard, s3_src, poll_interval=5.0).start()
It is recommended to begin with an explicit check_and_reload() when the process starts, and then either enable background polling with start() or call check_and_reload() at request boundaries (see RbacxMiddleware).
HTTPPolicySource security parameters¶
All parameters are keyword-only and fully backward-compatible with existing
code that only passes url.
| Parameter | Type | Default | Description |
|---|---|---|---|
verify_ssl |
bool |
True |
Enable TLS certificate verification (verify= in requests.get). Set to False only in dev environments. |
timeout |
float |
5.0 |
Request timeout in seconds. |
allow_redirects |
bool |
True |
Whether to follow HTTP redirects. Set to False to prevent open-redirect abuse. |
allowed_schemes |
tuple[str, ...] |
("http", "https") |
URL scheme whitelist. Raise ValueError at construction for any other scheme. Pass ("https",) to enforce HTTPS-only. |
block_private_ips |
bool |
False |
When True, raise ValueError if the URL's host is a numeric private, loopback, or link-local IP address (SSRF guard). Hostname literals are not resolved. |
from rbacx.store import HTTPPolicySource
# HTTPS-only, strict TLS, no redirects, SSRF guard enabled
src = HTTPPolicySource(
"https://policy-server.internal/policy.json",
allowed_schemes=("https",),
verify_ssl=True,
allow_redirects=False,
block_private_ips=True,
timeout=10.0,
)
Note on
block_private_ipsand hostnames: only numeric IP literals are checked (e.g.192.168.1.1). Hostname literals such aslocalhostare not resolved at construction time to avoid DNS TOCTOU races. For full hostname- based SSRF protection use network-level controls or a customrequestssession.