simple-module-users 0.0.2__tar.gz → 0.0.4__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.
Files changed (84) hide show
  1. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/.gitignore +4 -0
  2. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/PKG-INFO +5 -5
  3. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/pyproject.toml +5 -5
  4. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/endpoints/views.py +5 -1
  5. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/module.py +10 -1
  6. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Login.tsx +5 -3
  7. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/settings.py +22 -10
  8. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/LICENSE +0 -0
  9. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/README.md +0 -0
  10. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/package.json +0 -0
  11. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/.gitkeep +0 -0
  12. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/_middleware_support.py +0 -0
  13. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/conftest.py +0 -0
  14. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_access_token_model.py +0 -0
  15. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_api_admin.py +0 -0
  16. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_api_admin_filters.py +0 -0
  17. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_api_auth.py +0 -0
  18. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_backend.py +0 -0
  19. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_bootstrap.py +0 -0
  20. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_cli.py +0 -0
  21. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_constants.py +0 -0
  22. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_db_adapter.py +0 -0
  23. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_invite_flow.py +0 -0
  24. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_mailer.py +0 -0
  25. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_rate_limit.py +0 -0
  26. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_role_model.py +0 -0
  27. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_service_admin.py +0 -0
  28. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_settings.py +0 -0
  29. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_user_manager.py +0 -0
  30. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_user_model.py +0 -0
  31. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_user_role_model.py +0 -0
  32. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_user_service.py +0 -0
  33. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_users_deps.py +0 -0
  34. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_users_middleware.py +0 -0
  35. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_users_middleware_public_paths.py +0 -0
  36. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_views.py +0 -0
  37. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tests/test_views_admin.py +0 -0
  38. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/tsconfig.json +0 -0
  39. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/__init__.py +0 -0
  40. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/backend.py +0 -0
  41. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/bootstrap.py +0 -0
  42. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/cli.py +0 -0
  43. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/components/IndexFilters.tsx +0 -0
  44. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/components/RolesTab.tsx +0 -0
  45. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/constants.py +0 -0
  46. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/contracts/__init__.py +0 -0
  47. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/contracts/events.py +0 -0
  48. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/contracts/schemas.py +0 -0
  49. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/db_adapter.py +0 -0
  50. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/deps.py +0 -0
  51. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/endpoints/__init__.py +0 -0
  52. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/endpoints/api.py +0 -0
  53. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/endpoints/api_admin.py +0 -0
  54. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/exceptions.py +0 -0
  55. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/__init__.py +0 -0
  56. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/console.py +0 -0
  57. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/smtp.py +0 -0
  58. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/templates/.gitkeep +0 -0
  59. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/templates/invite.txt +0 -0
  60. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/templates/reset_password.txt +0 -0
  61. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/mailer/templates/verify_email.txt +0 -0
  62. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/manager.py +0 -0
  63. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/middleware.py +0 -0
  64. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/__init__.py +0 -0
  65. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/_base.py +0 -0
  66. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/access_token.py +0 -0
  67. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/role.py +0 -0
  68. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/user.py +0 -0
  69. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/models/user_role.py +0 -0
  70. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/.gitkeep +0 -0
  71. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/AcceptInvite.tsx +0 -0
  72. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/ForgotPassword.tsx +0 -0
  73. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Profile.tsx +0 -0
  74. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Register.tsx +0 -0
  75. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/ResetPassword.tsx +0 -0
  76. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Users/Edit.tsx +0 -0
  77. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Users/Index.tsx +0 -0
  78. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/Users/Invite.tsx +0 -0
  79. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/pages/VerifyEmail.tsx +0 -0
  80. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/py.typed +0 -0
  81. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/rate_limit.py +0 -0
  82. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/roles_cache.py +0 -0
  83. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/service.py +0 -0
  84. {simple_module_users-0.0.2 → simple_module_users-0.0.4}/users/state.py +0 -0
@@ -36,6 +36,10 @@ uploads/
36
36
  # Vite
37
37
  host/static/dist/
38
38
 
39
+ # VitePress (docs)
40
+ docs/.vitepress/cache/
41
+ docs/.vitepress/dist/
42
+
39
43
  # Auto-generated frontend module manifest (regenerated by the host at boot
40
44
  # or via `make gen-pages`).
41
45
  host/client_app/modules.manifest.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_module_users
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Email + password user management, admin invites, RBAC-ready — replaces Keycloak for simple_module apps
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -24,10 +24,10 @@ Requires-Python: >=3.12
24
24
  Requires-Dist: aiosmtplib>=3.0
25
25
  Requires-Dist: cachetools>=5.3
26
26
  Requires-Dist: fastapi-users[sqlalchemy]<16,>=15
27
- Requires-Dist: simple-module-auth==0.0.2
28
- Requires-Dist: simple-module-core==0.0.2
29
- Requires-Dist: simple-module-db==0.0.2
30
- Requires-Dist: simple-module-hosting==0.0.2
27
+ Requires-Dist: simple-module-auth==0.0.4
28
+ Requires-Dist: simple-module-core==0.0.4
29
+ Requires-Dist: simple-module-db==0.0.4
30
+ Requires-Dist: simple-module-hosting==0.0.4
31
31
  Requires-Dist: typer>=0.12
32
32
  Description-Content-Type: text/markdown
33
33
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_users"
3
- version = "0.0.2"
3
+ version = "0.0.4"
4
4
  description = "Email + password user management, admin invites, RBAC-ready — replaces Keycloak for simple_module apps"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -21,10 +21,10 @@ classifiers = [
21
21
  "Typing :: Typed",
22
22
  ]
23
23
  dependencies = [
24
- "simple_module_core==0.0.2",
25
- "simple_module_db==0.0.2",
26
- "simple_module_hosting==0.0.2",
27
- "simple_module_auth==0.0.2", # workspace module — contracts
24
+ "simple_module_core==0.0.4",
25
+ "simple_module_db==0.0.4",
26
+ "simple_module_hosting==0.0.4",
27
+ "simple_module_auth==0.0.4", # workspace module — contracts
28
28
  # Pinned to a narrow range: `deps.py` relies on mutating CookieTransport
29
29
  # fields after construction (see reconfigure_cookie_transport in backend.py).
30
30
  # Bumping the major version requires re-checking those field names.
@@ -68,7 +68,11 @@ async def login_page(request: Request, inertia: InertiaDep) -> InertiaResponse:
68
68
  dev_accounts.append({"label": "User", "email": user_email, "password": user_password})
69
69
  return await inertia.render(
70
70
  _PAGE_LOGIN,
71
- {"allow_signup": users_settings.allow_signup, "dev_accounts": dev_accounts},
71
+ {
72
+ "allow_signup": users_settings.allow_signup,
73
+ "dev_accounts": dev_accounts,
74
+ "login_redirect_url": users_settings.login_redirect_url,
75
+ },
72
76
  )
73
77
 
74
78
 
@@ -82,9 +82,10 @@ class UsersModule(ModuleBase):
82
82
  label="Users",
83
83
  url=_URL_USERS_ADMIN,
84
84
  icon=_ICON_USERS,
85
- order=30,
85
+ order=100,
86
86
  section=MenuSection.SIDEBAR,
87
87
  roles=[ADMIN_ROLE_NAME],
88
+ group="Administration",
88
89
  )
89
90
  )
90
91
  # Self-service: profile + logout live in the user dropdown.
@@ -147,6 +148,14 @@ class UsersModule(ModuleBase):
147
148
  window_seconds=s.auth_rate_limit_window_seconds,
148
149
  )
149
150
 
151
+ # Auto-fall-back from the default ``/dashboard/`` to ``/`` when the
152
+ # Dashboard module isn't installed, so ``--preset minimal`` doesn't
153
+ # 404 on login. Operator-set overrides are preserved.
154
+ if s.login_redirect_url == "/dashboard/" and not any(
155
+ m.meta.name == "Dashboard" for m in app.state.sm.modules
156
+ ):
157
+ s.login_redirect_url = "/"
158
+
150
159
  reconfigure_cookie_transport(auth_backend, s)
151
160
 
152
161
  await asyncio.gather(
@@ -21,10 +21,12 @@ interface DevAccount {
21
21
  interface Props {
22
22
  allow_signup: boolean;
23
23
  dev_accounts: DevAccount[];
24
+ login_redirect_url: string;
24
25
  }
25
26
 
26
27
  function Login() {
27
- const { allow_signup, dev_accounts } = usePage<{ props: Props }>().props as unknown as Props;
28
+ const { allow_signup, dev_accounts, login_redirect_url } = usePage<{ props: Props }>()
29
+ .props as unknown as Props;
28
30
 
29
31
  const [email, setEmail] = useState('');
30
32
  const [password, setPassword] = useState('');
@@ -34,8 +36,8 @@ function Login() {
34
36
 
35
37
  const nextUrl =
36
38
  typeof window !== 'undefined'
37
- ? new URLSearchParams(window.location.search).get('next') || '/dashboard/'
38
- : '/dashboard/';
39
+ ? new URLSearchParams(window.location.search).get('next') || login_redirect_url
40
+ : login_redirect_url;
39
41
 
40
42
  const submitLogin = (username: string, pwd: string) => {
41
43
  setError(null);
@@ -1,13 +1,11 @@
1
1
  """Users module settings — DB-backed via ``register_module_settings``.
2
2
 
3
- Construction no longer reads ``SM_USERS_*`` environment variables. Values
4
- come from pydantic defaults at boot, then get hydrated from the DB by the
5
- hosting lifespan before module ``on_startup`` runs. Runtime changes go
6
- through ``settings.reload.apply_changes_and_reload``.
7
-
8
- The one remaining env read is ``SM_ENVIRONMENT``, consulted by the
9
- ``@model_validator`` to refuse placeholder token secrets in production —
10
- that's a host-level setting, not a users-module field.
3
+ Most fields hydrate from the DB after boot, with pydantic defaults filling in
4
+ until then. Runtime changes go through ``settings.reload.apply_changes_and_reload``.
5
+
6
+ The two token-secret fields read ``SM_USERS_*`` at import time as a bootstrap
7
+ path: a fresh production deploy needs to clear the validator below before any
8
+ DB-backed settings can be seeded, otherwise the two paths deadlock.
11
9
  """
12
10
 
13
11
  from __future__ import annotations
@@ -16,6 +14,7 @@ import os
16
14
 
17
15
  from pydantic import Field, model_validator
18
16
  from pydantic_settings import BaseSettings, SettingsConfigDict
17
+ from simple_module_core.dotenv import env_str
19
18
  from simple_module_core.environments import NON_PROD_ENVIRONMENTS
20
19
 
21
20
  _PLACEHOLDER_RESET_SECRET = "dev-reset-token-secret-change-me"
@@ -31,10 +30,23 @@ class UsersSettings(BaseSettings):
31
30
  allow_signup: bool = False
32
31
  require_verification: bool = True
33
32
 
33
+ # Where the login page sends a successful sign-in. Sites without the
34
+ # bundled ``dashboard`` module (``sm new --preset minimal``) override
35
+ # this to wherever their post-login landing lives.
36
+ login_redirect_url: str = "/dashboard/"
37
+
34
38
  # Token secrets — MUST be set in production. Dev default is a deterministic
35
39
  # placeholder that's obvious in logs so it can't be mistaken for a real key.
36
- reset_password_token_secret: str = "dev-reset-token-secret-change-me"
37
- verification_token_secret: str = "dev-verify-token-secret-change-me"
40
+ # Resolved at module-import time so ``info.default`` stays serializable
41
+ # for the settings admin UI.
42
+ reset_password_token_secret: str = env_str(
43
+ "SM_USERS_RESET_PASSWORD_TOKEN_SECRET",
44
+ _PLACEHOLDER_RESET_SECRET,
45
+ )
46
+ verification_token_secret: str = env_str(
47
+ "SM_USERS_VERIFICATION_TOKEN_SECRET",
48
+ _PLACEHOLDER_VERIFY_SECRET,
49
+ )
38
50
  reset_password_token_lifetime_seconds: int = 60 * 60 # 1 hour
39
51
  verification_token_lifetime_seconds: int = 60 * 60 * 24 * 7 # 7 days
40
52