simple-module-auth 0.0.17__tar.gz → 0.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/PKG-INFO +3 -3
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/middleware.py +11 -1
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/pyproject.toml +3 -3
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_auth_middleware.py +47 -3
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/.gitignore +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/LICENSE +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/README.md +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/__init__.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/contracts/__init__.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/contracts/provider.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/contracts/resolver.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/contracts/schemas.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/deps.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/locales/en.json +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/locales/es.json +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/module.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/py.typed +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/auth/state.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/package.json +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_auth_provider_protocol.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_deps.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_module.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_resolver_registry.py +0 -0
- {simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_user_context.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simple_module_auth
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.18
|
|
4
4
|
Summary: Session-cookie authentication primitives — middleware, login/logout, redirect helpers for simple_module
|
|
5
5
|
Project-URL: Homepage, https://github.com/antosubash/simple_module_python
|
|
6
6
|
Project-URL: Repository, https://github.com/antosubash/simple_module_python
|
|
@@ -22,8 +22,8 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.12
|
|
24
24
|
Requires-Dist: itsdangerous>=2.2
|
|
25
|
-
Requires-Dist: simple-module-core==0.0.
|
|
26
|
-
Requires-Dist: simple-module-db==0.0.
|
|
25
|
+
Requires-Dist: simple-module-core==0.0.18
|
|
26
|
+
Requires-Dist: simple-module-db==0.0.18
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# simple_module_auth
|
|
@@ -47,7 +47,9 @@ class AuthMiddleware:
|
|
|
47
47
|
return
|
|
48
48
|
|
|
49
49
|
path: str = scope["path"]
|
|
50
|
-
|
|
50
|
+
method: str = scope.get("method", "GET")
|
|
51
|
+
app_state = scope["app"].state
|
|
52
|
+
auth_state = app_state.auth
|
|
51
53
|
provider = auth_state.auth_provider
|
|
52
54
|
|
|
53
55
|
if provider is None:
|
|
@@ -58,6 +60,14 @@ class AuthMiddleware:
|
|
|
58
60
|
any(path.startswith(p) for p in _FRAMEWORK_PUBLIC_PREFIXES)
|
|
59
61
|
or path in _FRAMEWORK_PUBLIC_EXACT
|
|
60
62
|
)
|
|
63
|
+
# Module-contributed public routes (register_public_routes hook). Method
|
|
64
|
+
# -aware, so a GET read route can be exempted without opening sibling
|
|
65
|
+
# POST/PATCH mutations under the same prefix.
|
|
66
|
+
if not is_public:
|
|
67
|
+
public_routes = getattr(app_state, "public_routes", None)
|
|
68
|
+
is_public = public_routes is not None and public_routes.matches(method, path)
|
|
69
|
+
# Legacy provider-declared paths (prefix-only, method-agnostic). Kept for
|
|
70
|
+
# back-compat with AuthProvider implementations.
|
|
61
71
|
if not is_public:
|
|
62
72
|
prefix_paths, exact_paths = provider.get_public_paths()
|
|
63
73
|
is_public = any(path.startswith(p) for p in prefix_paths) or path in exact_paths
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "simple_module_auth"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.18"
|
|
4
4
|
description = "Session-cookie authentication primitives — middleware, login/logout, redirect helpers for simple_module"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -22,8 +22,8 @@ classifiers = [
|
|
|
22
22
|
]
|
|
23
23
|
dependencies = [
|
|
24
24
|
"itsdangerous>=2.2",
|
|
25
|
-
"simple_module_core==0.0.
|
|
26
|
-
"simple_module_db==0.0.
|
|
25
|
+
"simple_module_core==0.0.18",
|
|
26
|
+
"simple_module_db==0.0.18",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.entry-points.simple_module]
|
|
@@ -44,15 +44,16 @@ class _StubProvider:
|
|
|
44
44
|
return auth.startswith("Bearer ")
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def _build_app(provider, *, principal_resolvers=None):
|
|
47
|
+
def _build_app(provider, *, principal_resolvers=None, public_routes=None):
|
|
48
48
|
app = FastAPI()
|
|
49
49
|
app.state.auth = AuthState(
|
|
50
50
|
auth_provider=provider,
|
|
51
51
|
principal_resolvers=list(principal_resolvers or []),
|
|
52
52
|
)
|
|
53
|
+
if public_routes is not None:
|
|
54
|
+
app.state.public_routes = public_routes
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
async def catch_all(request: Request, path: str = ""):
|
|
56
|
+
async def _handler(request: Request, path: str = ""):
|
|
56
57
|
user = getattr(request.state, "user", None)
|
|
57
58
|
return JSONResponse(
|
|
58
59
|
{
|
|
@@ -60,6 +61,8 @@ def _build_app(provider, *, principal_resolvers=None):
|
|
|
60
61
|
}
|
|
61
62
|
)
|
|
62
63
|
|
|
64
|
+
app.add_api_route("/{path:path}", _handler, methods=["GET", "POST", "PATCH"])
|
|
65
|
+
|
|
63
66
|
app.add_middleware(AuthMiddleware)
|
|
64
67
|
app.add_middleware(SessionMiddleware, secret_key=SECRET)
|
|
65
68
|
return app
|
|
@@ -129,6 +132,47 @@ async def test_root_is_public(unauthenticated_app):
|
|
|
129
132
|
assert resp.status_code == 200
|
|
130
133
|
|
|
131
134
|
|
|
135
|
+
async def test_registry_public_route_skips_auth():
|
|
136
|
+
"""A module-contributed public route lets an unauthenticated GET through."""
|
|
137
|
+
from simple_module_core.public_routes import PublicRouteRegistry
|
|
138
|
+
|
|
139
|
+
registry = PublicRouteRegistry()
|
|
140
|
+
registry.add_prefix("/api/gis/stac")
|
|
141
|
+
app = _build_app(_StubProvider(user=None), public_routes=registry)
|
|
142
|
+
|
|
143
|
+
transport = httpx.ASGITransport(app=app)
|
|
144
|
+
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
|
145
|
+
resp = await c.get("/api/gis/stac/collections")
|
|
146
|
+
assert resp.status_code == 200
|
|
147
|
+
assert resp.json()["user"] is None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
async def test_registry_method_scoping_gates_other_verbs():
|
|
151
|
+
"""A GET-scoped public rule exempts GET but still gates PATCH on the same path."""
|
|
152
|
+
from simple_module_core.public_routes import PublicRouteRegistry
|
|
153
|
+
|
|
154
|
+
registry = PublicRouteRegistry()
|
|
155
|
+
registry.add_regex(r"/api/gis/datasets/[^/]+/tilejson$", methods={"GET"})
|
|
156
|
+
app = _build_app(_StubProvider(user=None), public_routes=registry)
|
|
157
|
+
|
|
158
|
+
transport = httpx.ASGITransport(app=app)
|
|
159
|
+
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
|
160
|
+
ok = await c.get("/api/gis/datasets/42/tilejson")
|
|
161
|
+
gated = await c.patch("/api/gis/datasets/42/tilejson")
|
|
162
|
+
assert ok.status_code == 200
|
|
163
|
+
assert gated.status_code == 401
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def test_no_registry_falls_back_to_provider_paths(unauthenticated_app):
|
|
167
|
+
"""Apps built without a public-routes registry still honor provider paths."""
|
|
168
|
+
transport = httpx.ASGITransport(app=unauthenticated_app)
|
|
169
|
+
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
|
170
|
+
public = await c.get("/stub/public/data")
|
|
171
|
+
gated = await c.get("/api/protected")
|
|
172
|
+
assert public.status_code == 200
|
|
173
|
+
assert gated.status_code == 401
|
|
174
|
+
|
|
175
|
+
|
|
132
176
|
async def test_resolver_chain_fallback():
|
|
133
177
|
"""When provider returns None, fall through to principal resolvers."""
|
|
134
178
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simple_module_auth-0.0.17 → simple_module_auth-0.0.18}/tests/test_auth_provider_protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|