cardo-python-utils 0.5.dev32__tar.gz → 0.5.dev33__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.
- {cardo_python_utils-0.5.dev32/cardo_python_utils.egg-info → cardo_python_utils-0.5.dev33}/PKG-INFO +1 -1
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33/cardo_python_utils.egg-info}/PKG-INFO +1 -1
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/pyproject.toml +1 -1
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/README.md +11 -8
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/utils.py +1 -2
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/middleware/tenant_aware_http_middleware.py +6 -16
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/oidc_settings.py +1 -7
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/settings.py +7 -1
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tenant_context.py +2 -2
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/LICENSE +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/MANIFEST.in +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/README.rst +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/cardo_python_utils.egg-info/SOURCES.txt +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/cardo_python_utils.egg-info/dependency_links.txt +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/cardo_python_utils.egg-info/requires.txt +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/cardo_python_utils.egg-info/top_level.txt +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/choices.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/data_structures.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/db.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/auth.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/templates/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/templates/user_groups_changelist.html +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/user_group.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/views.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/drf.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/ninja.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/apps.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/auth/service.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/celery/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/celery/tenant_aware_task.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/routers.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/transaction.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/utils.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/management/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/management/commands/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/management/commands/migrateall.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/management/commands/tenant_aware_command.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/middleware/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/models/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/models/user_group.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/redis/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/redis/key_function.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/storage/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/storage/tenant_aware_storage.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tests/__init__.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tests/conftest.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django_utils.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/esma_choices.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/exceptions.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/imports.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/math.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/text.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/time.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/types_hinting.py +0 -0
- {cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cardo-python-utils"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.dev33"
|
|
8
8
|
description = "Python library enhanced with a wide range of functions for different scenarios."
|
|
9
9
|
readme = "README.rst"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -2,18 +2,20 @@ This package provides utilities for facilitating IDP communication and multi-ten
|
|
|
2
2
|
|
|
3
3
|
# Usage
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
## Environment variables to set
|
|
7
6
|
|
|
8
7
|
- AWS_STORAGE_TENANT_BUCKET_NAMES
|
|
9
|
-
|
|
8
|
+
- _This variable should be set if separate tenant buckets are needed._
|
|
9
|
+
- A JSON dictionary where each key is the tenant name and the value is the bucket name.
|
|
10
10
|
- DATABASE_CONFIG
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
- A JSON dictionary where each key is the tenant name and the value is a dict with the datase config.
|
|
12
|
+
- If multiple 'DATABASE_CONFIG'-prefixed variables are set, they will be merged into a single dictionary.
|
|
13
13
|
- KEYCLOAK_SERVER_URL
|
|
14
|
-
|
|
14
|
+
- The URL of the Keycloak deployment
|
|
15
15
|
- KEYCLOAK_CONFIDENTIAL_CLIENT_ID
|
|
16
|
-
|
|
16
|
+
- The id of the confidential client of the backend service
|
|
17
|
+
- KEYCLOAK_CONFIDENTIAL_CLIENT_SERVICE_ACCOUNT_TOKEN_FILE_PATHS
|
|
18
|
+
- A JSON dictionary where each key is the tenant name and the value is the file path of the service account token for the confidential client of that tenant
|
|
17
19
|
|
|
18
20
|
## settings.py file
|
|
19
21
|
|
|
@@ -61,7 +63,8 @@ CACHES = {
|
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
# If using Django Storages with S3,
|
|
66
|
+
# If using Django Storages with S3, and separate tenant buckets are needed,
|
|
67
|
+
# configure the storage backends as follows:
|
|
65
68
|
STORAGES = {
|
|
66
69
|
"default": {
|
|
67
70
|
"BACKEND": "python_utils.django.storage.TenantAwarePrivateS3Storage",
|
|
@@ -119,4 +122,4 @@ SESSION_SAVE_EVERY_REQUEST = True # Extend session on each request
|
|
|
119
122
|
|
|
120
123
|
## With django-ninja
|
|
121
124
|
|
|
122
|
-
If using `django-ninja`, apart from the settings configured above, auth utils are provided in the django/api/ninja.py module.
|
|
125
|
+
If using `django-ninja`, apart from the settings configured above, auth utils are provided in the django/api/ninja.py module.
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/utils.py
RENAMED
|
@@ -4,8 +4,7 @@ from django.conf import settings
|
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
5
|
from jwt import decode, PyJWKClient
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
|
|
7
|
+
from ..oidc_settings import get_oidc_op_jwks_endpoint
|
|
9
8
|
from ..tenant_context import TenantContext
|
|
10
9
|
|
|
11
10
|
JWKS_CLIENTS = {} # Cache for PyJWKClient instances
|
|
@@ -5,7 +5,7 @@ from django.conf import settings
|
|
|
5
5
|
from django.core.handlers.wsgi import WSGIRequest
|
|
6
6
|
from django.http import HttpResponse
|
|
7
7
|
|
|
8
|
-
from ..settings import DEVELOPMENT_TENANT, TENANT_AWARE_EXCLUDED_PATHS
|
|
8
|
+
from ..settings import DEVELOPMENT_TENANT, TENANT_AWARE_EXCLUDED_PATHS
|
|
9
9
|
from ..tenant_context import TenantContext
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
@@ -51,9 +51,6 @@ class TenantAwareHttpMiddleware:
|
|
|
51
51
|
if self._is_excluded_path(request.path):
|
|
52
52
|
return self.get_response(request)
|
|
53
53
|
|
|
54
|
-
if TenantContext.is_set():
|
|
55
|
-
raise Exception("Tenant context already set")
|
|
56
|
-
|
|
57
54
|
# In DEBUG mode, use DEVELOPMENT_TENANT directly
|
|
58
55
|
# In production, extract tenant from subdomain
|
|
59
56
|
if settings.DEBUG:
|
|
@@ -64,10 +61,6 @@ class TenantAwareHttpMiddleware:
|
|
|
64
61
|
if tenant is None:
|
|
65
62
|
raise Exception(f"Could not determine tenant from subdomain. Host: {request.get_host()}")
|
|
66
63
|
|
|
67
|
-
# Validate tenant exists in configured databases
|
|
68
|
-
if not self._is_valid_tenant(tenant):
|
|
69
|
-
raise Exception(f"Unknown tenant: {tenant}")
|
|
70
|
-
|
|
71
64
|
# Call the next middleware in the chain until the response is returned.
|
|
72
65
|
# After that, the database alias is removed from the thread local variable.
|
|
73
66
|
with TenantContext(tenant):
|
|
@@ -94,6 +87,11 @@ class TenantAwareHttpMiddleware:
|
|
|
94
87
|
- <app>.tenant-internal.domain.com -> tenant (strips -internal suffix)
|
|
95
88
|
"""
|
|
96
89
|
host = request.get_host().split(":")[0] # Remove port if present
|
|
90
|
+
|
|
91
|
+
if host == "testserver":
|
|
92
|
+
logger.debug("Using 'default' tenant for testserver host.")
|
|
93
|
+
return "default"
|
|
94
|
+
|
|
97
95
|
parts = host.split(".")
|
|
98
96
|
|
|
99
97
|
# Need at least 3 parts: <app>.<tenant>.<domain>
|
|
@@ -103,11 +101,3 @@ class TenantAwareHttpMiddleware:
|
|
|
103
101
|
return tenant
|
|
104
102
|
|
|
105
103
|
return None
|
|
106
|
-
|
|
107
|
-
@staticmethod
|
|
108
|
-
def _is_valid_tenant(tenant: str) -> bool:
|
|
109
|
-
"""
|
|
110
|
-
Validate that the tenant exists in configured databases.
|
|
111
|
-
Skip 'default' as it's typically a placeholder.
|
|
112
|
-
"""
|
|
113
|
-
return tenant in TENANT_DATABASES
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/oidc_settings.py
RENAMED
|
@@ -12,15 +12,9 @@ from .tenant_context import TenantContext
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
KEYCLOAK_SERVER_URL = os.getenv("KEYCLOAK_SERVER_URL", None)
|
|
15
|
-
if not KEYCLOAK_SERVER_URL:
|
|
16
|
-
raise ValueError("KEYCLOAK_SERVER_URL env variable is not set.")
|
|
17
|
-
|
|
18
15
|
KEYCLOAK_CONFIDENTIAL_CLIENT_ID = os.getenv("KEYCLOAK_CONFIDENTIAL_CLIENT_ID", None)
|
|
19
|
-
if not KEYCLOAK_CONFIDENTIAL_CLIENT_ID:
|
|
20
|
-
raise ValueError("KEYCLOAK_CONFIDENTIAL_CLIENT_ID env variable is not set.")
|
|
21
|
-
|
|
22
16
|
KEYCLOAK_CONFIDENTIAL_CLIENT_SERVICE_ACCOUNT_TOKEN_FILE_PATHS: dict[str, str] = json.loads(
|
|
23
|
-
os.getenv("KEYCLOAK_CONFIDENTIAL_CLIENT_SERVICE_ACCOUNT_TOKEN_FILE_PATHS")
|
|
17
|
+
os.getenv("KEYCLOAK_CONFIDENTIAL_CLIENT_SERVICE_ACCOUNT_TOKEN_FILE_PATHS", "{}")
|
|
24
18
|
)
|
|
25
19
|
KEYCLOAK_CLIENT_CREDENTIALS_GRANT_TYPE = "client_credentials"
|
|
26
20
|
KEYCLOAK_CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/settings.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.conf import settings
|
|
2
2
|
|
|
3
3
|
TENANT_KEY = "tenant"
|
|
4
|
+
DATABASES = settings.DATABASES
|
|
4
5
|
TENANT_DATABASES = set(settings.DATABASES.keys()) - {"default"}
|
|
5
6
|
|
|
6
7
|
TENANT_AWARE_EXCLUDED_PATHS = getattr(settings, "TENANT_AWARE_EXCLUDED_PATHS", ())
|
|
@@ -14,4 +15,9 @@ TENANT_AWARE_EXCLUDED_PATHS = (
|
|
|
14
15
|
"/mediafiles",
|
|
15
16
|
)
|
|
16
17
|
|
|
17
|
-
DEVELOPMENT_TENANT = getattr(settings, "DEVELOPMENT_TENANT"
|
|
18
|
+
DEVELOPMENT_TENANT = getattr(settings, "DEVELOPMENT_TENANT")
|
|
19
|
+
if DEVELOPMENT_TENANT is None:
|
|
20
|
+
if TENANT_DATABASES:
|
|
21
|
+
DEVELOPMENT_TENANT = list(TENANT_DATABASES)[0]
|
|
22
|
+
else:
|
|
23
|
+
DEVELOPMENT_TENANT = list(DATABASES.keys())[0]
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tenant_context.py
RENAMED
|
@@ -4,7 +4,7 @@ from contextlib import ContextDecorator
|
|
|
4
4
|
from threading import local
|
|
5
5
|
from typing import Optional
|
|
6
6
|
|
|
7
|
-
from .settings import
|
|
7
|
+
from .settings import DATABASES
|
|
8
8
|
|
|
9
9
|
logger = logging.getLogger(__name__)
|
|
10
10
|
thread_namespace = local()
|
|
@@ -75,7 +75,7 @@ class TenantContext(ContextDecorator):
|
|
|
75
75
|
logger.info("Tenant context already set to %s", tenant)
|
|
76
76
|
return
|
|
77
77
|
|
|
78
|
-
if tenant not in
|
|
78
|
+
if tenant not in DATABASES:
|
|
79
79
|
logger.error(f"Tenant '{tenant}' not found in DATABASES settings.")
|
|
80
80
|
return sys.exit(1)
|
|
81
81
|
|
|
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
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/data_structures.py
RENAMED
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/__init__.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/__init__.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/admin/views.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/__init__.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/drf.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/api/ninja.py
RENAMED
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/auth/service.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/celery/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/__init__.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/routers.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/transaction.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/db/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/models/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/redis/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tests/__init__.py
RENAMED
|
File without changes
|
{cardo_python_utils-0.5.dev32 → cardo_python_utils-0.5.dev33}/python_utils/django/tests/conftest.py
RENAMED
|
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
|