trovesuite 1.0.2__tar.gz → 1.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.
- {trovesuite-1.0.2/src/trovesuite.egg-info → trovesuite-1.0.4}/PKG-INFO +3 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/pyproject.toml +5 -3
- {trovesuite-1.0.2 → trovesuite-1.0.4}/setup.py +1 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/auth_controller.py +2 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/auth_read_dto.py +0 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/auth_service.py +7 -7
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/auth_write_dto.py +1 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/configs/database.py +29 -17
- trovesuite-1.0.4/src/trovesuite/configs/settings.py +57 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4/src/trovesuite.egg-info}/PKG-INFO +3 -1
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite.egg-info/requires.txt +2 -0
- trovesuite-1.0.2/src/trovesuite/configs/settings.py +0 -153
- {trovesuite-1.0.2 → trovesuite-1.0.4}/LICENSE +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/MANIFEST.in +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/README.md +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/requirements.txt +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/setup.cfg +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/__init__.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/__init__.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/auth/auth_base.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/configs/__init__.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/configs/logging.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/entities/__init__.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/entities/health.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/entities/sh_response.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/utils/__init__.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite/utils/helper.py +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite.egg-info/SOURCES.txt +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite.egg-info/dependency_links.txt +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite.egg-info/not-zip-safe +0 -0
- {trovesuite-1.0.2 → trovesuite-1.0.4}/src/trovesuite.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trovesuite
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications
|
|
5
5
|
Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
|
|
6
6
|
Author: Bright Debrah Owusu
|
|
@@ -34,6 +34,8 @@ Requires-Dist: python-multipart>=0.0.6
|
|
|
34
34
|
Requires-Dist: python-jose[cryptography]>=3.3.0
|
|
35
35
|
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
36
36
|
Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
|
|
37
|
+
Requires-Dist: uvicorn<0.39.0,>=0.38.0
|
|
38
|
+
Requires-Dist: pyjwt<3.0.0,>=2.10.1
|
|
37
39
|
Provides-Extra: dev
|
|
38
40
|
Requires-Dist: pytest>=8.4.2; extra == "dev"
|
|
39
41
|
Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "trovesuite"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.4"
|
|
8
8
|
description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
|
|
9
9
|
authors = ["brightgclt <brightgclt@gmail.com>"]
|
|
10
10
|
license = "MIT"
|
|
@@ -56,7 +56,7 @@ Documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
|
|
|
56
56
|
|
|
57
57
|
[project]
|
|
58
58
|
name = "trovesuite"
|
|
59
|
-
version = "1.0.
|
|
59
|
+
version = "1.0.4"
|
|
60
60
|
description = "TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications"
|
|
61
61
|
readme = "README.md"
|
|
62
62
|
license = {text = "MIT"}
|
|
@@ -89,7 +89,9 @@ dependencies = [
|
|
|
89
89
|
"python-multipart>=0.0.6",
|
|
90
90
|
"python-jose[cryptography]>=3.3.0",
|
|
91
91
|
"passlib[bcrypt]>=1.7.4",
|
|
92
|
-
"passlib[argon2] (>=1.7.4,<2.0.0)"
|
|
92
|
+
"passlib[argon2] (>=1.7.4,<2.0.0)",
|
|
93
|
+
"uvicorn (>=0.38.0,<0.39.0)",
|
|
94
|
+
"pyjwt (>=2.10.1,<3.0.0)"
|
|
93
95
|
|
|
94
96
|
]
|
|
95
97
|
|
|
@@ -15,7 +15,7 @@ with open("pyproject.toml", "r", encoding="utf-8") as fh:
|
|
|
15
15
|
|
|
16
16
|
setup(
|
|
17
17
|
name="trovesuite",
|
|
18
|
-
version="1.0.
|
|
18
|
+
version="1.0.4",
|
|
19
19
|
author="Bright Debrah Owusu",
|
|
20
20
|
author_email="owusu.debrah@deladetech.com",
|
|
21
21
|
description="TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications",
|
|
@@ -2,9 +2,10 @@ from fastapi import APIRouter
|
|
|
2
2
|
from src.trovesuite.auth.auth_write_dto import AuthControllerWriteDto
|
|
3
3
|
from src.trovesuite.auth.auth_read_dto import AuthControllerReadDto
|
|
4
4
|
from src.trovesuite.auth.auth_service import AuthService
|
|
5
|
+
from src.trovesuite.entities.sh_response import Respons
|
|
5
6
|
|
|
6
7
|
auth_router = APIRouter()
|
|
7
8
|
|
|
8
|
-
@auth_router.post("/auth", response_model=AuthControllerReadDto)
|
|
9
|
+
@auth_router.post("/auth", response_model=Respons[AuthControllerReadDto])
|
|
9
10
|
async def authorize(data: AuthControllerWriteDto):
|
|
10
11
|
return AuthService.authorize(data=data)
|
|
@@ -11,7 +11,6 @@ class AuthControllerReadDto(BaseModel):
|
|
|
11
11
|
role_id: Optional[str] = None
|
|
12
12
|
tenant_id: Optional[str] = None
|
|
13
13
|
permissions: Optional[List[str]] = None
|
|
14
|
-
shared_resource_id: Optional[str] = None
|
|
15
14
|
resource_id: Optional[str] = None
|
|
16
15
|
|
|
17
16
|
class AuthServiceReadDto(AuthControllerReadDto):
|
|
@@ -48,7 +48,7 @@ class AuthService:
|
|
|
48
48
|
|
|
49
49
|
user_id: str = data.user_id
|
|
50
50
|
tenant_id: str = data.tenant_id
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
"""Check if a user is authorized based on login settings and roles"""
|
|
53
53
|
# Input validation
|
|
54
54
|
if not user_id or not isinstance(user_id, str):
|
|
@@ -68,14 +68,14 @@ class AuthService:
|
|
|
68
68
|
status_code=400,
|
|
69
69
|
error="INVALID_TENANT_ID"
|
|
70
70
|
)
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
try:
|
|
73
73
|
|
|
74
74
|
is_tenant_verified = DatabaseManager.execute_query(
|
|
75
75
|
f"SELECT is_verified FROM {db_settings.MAIN_TENANTS_TABLE} WHERE delete_status = 'NOT_DELETED' AND id = %s",
|
|
76
76
|
(tenant_id,),
|
|
77
77
|
)
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
if not is_tenant_verified or len(is_tenant_verified) == 0:
|
|
80
80
|
logger.warning("Login failed - tenant not found: %s", tenant_id)
|
|
81
81
|
return Respons[AuthServiceReadDto](
|
|
@@ -156,7 +156,7 @@ class AuthService:
|
|
|
156
156
|
|
|
157
157
|
# 1️⃣ Get all groups the user belongs to
|
|
158
158
|
user_groups = DatabaseManager.execute_query(
|
|
159
|
-
f"""SELECT group_id FROM "{tenant_id}".{db_settings.
|
|
159
|
+
f"""SELECT group_id FROM "{tenant_id}".{db_settings.TENANT_USER_GROUPS_TABLE}
|
|
160
160
|
WHERE delete_status = 'NOT_DELETED' AND is_active = true AND user_id = %s""",(user_id,),
|
|
161
161
|
)
|
|
162
162
|
|
|
@@ -169,7 +169,7 @@ class AuthService:
|
|
|
169
169
|
f"""
|
|
170
170
|
SELECT DISTINCT ON (org_id, group_id, bus_id, app_id, shared_resource_id, resource_id, user_id, role_id)
|
|
171
171
|
org_id, group_id, bus_id, app_id, shared_resource_id, resource_id, user_id, role_id
|
|
172
|
-
FROM "{tenant_id}".{db_settings.
|
|
172
|
+
FROM "{tenant_id}".{db_settings.TENANT_ASSIGN_ROLES_TABLE}
|
|
173
173
|
WHERE delete_status = 'NOT_DELETED'
|
|
174
174
|
AND is_active = true
|
|
175
175
|
AND (user_id = %s OR group_id = ANY(%s))
|
|
@@ -183,7 +183,7 @@ class AuthService:
|
|
|
183
183
|
f"""
|
|
184
184
|
SELECT DISTINCT ON (org_id, bus_id, app_id, shared_resource_id, resource_id, user_id, role_id)
|
|
185
185
|
org_id, bus_id, app_id, shared_resource_id, resource_id, user_id, role_id
|
|
186
|
-
FROM "{tenant_id}".{db_settings.
|
|
186
|
+
FROM "{tenant_id}".{db_settings.TENANT_ASSIGN_ROLES_TABLE}
|
|
187
187
|
WHERE delete_status = 'NOT_DELETED'
|
|
188
188
|
AND is_active = true
|
|
189
189
|
AND user_id = %s
|
|
@@ -196,7 +196,7 @@ class AuthService:
|
|
|
196
196
|
get_user_roles_with_tenant_and_permissions = []
|
|
197
197
|
for role in get_user_roles:
|
|
198
198
|
permissions = DatabaseManager.execute_query(
|
|
199
|
-
f"""SELECT permission_id FROM {db_settings.
|
|
199
|
+
f"""SELECT permission_id FROM {db_settings.MAIN_ROLE_PERMISSIONS_TABLE} WHERE role_id = %s""",
|
|
200
200
|
params=(role["role_id"],),)
|
|
201
201
|
|
|
202
202
|
role_dict = {**role, "tenant_id": tenant_id, "permissions": [p['permission_id'] for p in permissions]}
|
|
@@ -13,7 +13,6 @@ logger = get_logger("database")
|
|
|
13
13
|
|
|
14
14
|
# Database connection pool
|
|
15
15
|
_connection_pool: Optional[psycopg2.pool.ThreadedConnectionPool] = None
|
|
16
|
-
_sqlmodel_engine = None
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class DatabaseConfig:
|
|
@@ -21,16 +20,20 @@ class DatabaseConfig:
|
|
|
21
20
|
|
|
22
21
|
def __init__(self):
|
|
23
22
|
self.settings = db_settings
|
|
23
|
+
self.database_url = self.settings.database_url
|
|
24
24
|
self.pool_size = 5
|
|
25
25
|
self.max_overflow = 10
|
|
26
|
-
|
|
27
|
-
@property
|
|
28
|
-
def database_url(self):
|
|
29
|
-
"""Get database URL (lazy evaluation)"""
|
|
30
|
-
return self.settings.database_url
|
|
31
26
|
|
|
32
27
|
def get_connection_params(self) -> dict:
|
|
33
28
|
"""Get database connection parameters"""
|
|
29
|
+
if self.settings.DATABASE_URL:
|
|
30
|
+
# Use full DATABASE_URL if available
|
|
31
|
+
return {
|
|
32
|
+
"dsn": self.settings.DATABASE_URL,
|
|
33
|
+
"cursor_factory": RealDictCursor
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# fallback to individual DB_* variables
|
|
34
37
|
return {
|
|
35
38
|
"host": self.settings.DB_HOST,
|
|
36
39
|
"port": self.settings.DB_PORT,
|
|
@@ -98,18 +101,19 @@ def get_connection_pool() -> psycopg2.pool.ThreadedConnectionPool:
|
|
|
98
101
|
"""Get the database connection pool"""
|
|
99
102
|
global _connection_pool
|
|
100
103
|
if _connection_pool is None:
|
|
101
|
-
|
|
104
|
+
error_msg = (
|
|
105
|
+
"Database not initialized. This usually means:\n"
|
|
106
|
+
"1. Missing or incorrect .env file in app/ directory\n"
|
|
107
|
+
"2. Database credentials are wrong\n"
|
|
108
|
+
"3. Database container is not running\n"
|
|
109
|
+
"4. Database initialization failed during startup\n"
|
|
110
|
+
"Please check the startup logs for more details."
|
|
111
|
+
)
|
|
112
|
+
logger.error(error_msg)
|
|
113
|
+
raise Exception(error_msg)
|
|
102
114
|
return _connection_pool
|
|
103
115
|
|
|
104
116
|
|
|
105
|
-
def get_sqlmodel_engine():
|
|
106
|
-
"""Get the SQLModel engine"""
|
|
107
|
-
global _sqlmodel_engine
|
|
108
|
-
if _sqlmodel_engine is None:
|
|
109
|
-
raise Exception("Database not initialized. Call initialize_database() first.")
|
|
110
|
-
return _sqlmodel_engine
|
|
111
|
-
|
|
112
|
-
|
|
113
117
|
@contextmanager
|
|
114
118
|
def get_db_connection():
|
|
115
119
|
"""Get a database connection from the pool (context manager)"""
|
|
@@ -217,5 +221,13 @@ class DatabaseManager:
|
|
|
217
221
|
}
|
|
218
222
|
|
|
219
223
|
|
|
220
|
-
# Database initialization
|
|
221
|
-
|
|
224
|
+
# Database initialization on module import
|
|
225
|
+
try:
|
|
226
|
+
initialize_database()
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.error(f"Failed to initialize database on startup: {str(e)}")
|
|
229
|
+
logger.error("⚠️ CRITICAL: Application started without database connection!")
|
|
230
|
+
logger.error("⚠️ Please check your .env file and database configuration")
|
|
231
|
+
logger.error("⚠️ The application will not function properly without a database connection")
|
|
232
|
+
# Don't raise here to allow the app to start even if DB is unavailable
|
|
233
|
+
# But log it clearly so it's obvious what's wrong
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import os
|
|
2
|
+
class Settings:
|
|
3
|
+
|
|
4
|
+
# Database URL
|
|
5
|
+
DATABASE_URL: str = os.getenv("DATABASE_URL")
|
|
6
|
+
|
|
7
|
+
DB_USER: str = os.getenv("DB_USER")
|
|
8
|
+
DB_HOST: str = os.getenv("DB_HOST")
|
|
9
|
+
DB_NAME: str = os.getenv("DB_NAME")
|
|
10
|
+
DB_PORT: str = os.getenv("DB_PORT")
|
|
11
|
+
DB_PASSWORD: str = os.getenv("DB_PASSWORD")
|
|
12
|
+
|
|
13
|
+
# Application settings
|
|
14
|
+
APP_NAME: str = os.getenv("APP_NAME", "Python Template API")
|
|
15
|
+
DEBUG: bool = os.getenv("DEBUG", "True").lower() in ("true",1)
|
|
16
|
+
APP_VERSION: str = os.getenv("APP_VERSION", "1.0.0")
|
|
17
|
+
|
|
18
|
+
# Logging settings
|
|
19
|
+
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
|
20
|
+
LOG_FORMAT: str = os.getenv("LOG_FORMAT", "detailed") # detailed, json, simple
|
|
21
|
+
LOG_TO_FILE: bool = os.getenv("LOG_TO_FILE", "False").lower() in ("true", 1)
|
|
22
|
+
LOG_MAX_SIZE: int = int(os.getenv("LOG_MAX_SIZE", "10485760")) # 10MB
|
|
23
|
+
LOG_BACKUP_COUNT: int = int(os.getenv("LOG_BACKUP_COUNT", "5"))
|
|
24
|
+
LOG_DIR: str = os.getenv("LOG_DIR", "logs")
|
|
25
|
+
|
|
26
|
+
# Security settings
|
|
27
|
+
ENVIRONMENT: str = os.getenv("ENVIRONMENT")
|
|
28
|
+
ALGORITHM: str = os.getenv("ALGORITHM")
|
|
29
|
+
SECRET_KEY: str = os.getenv("SECRET_KEY")
|
|
30
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "120"))
|
|
31
|
+
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# SHARED TABLES (main schema)
|
|
34
|
+
# =============================================================================
|
|
35
|
+
MAIN_TENANTS_TABLE = os.getenv("MAIN_TENANTS_TABLE")
|
|
36
|
+
MAIN_ROLE_PERMISSIONS_TABLE = os.getenv("MAIN_ROLE_PERMISSIONS_TABLE")
|
|
37
|
+
MAIN_USER_SUBSCRIPTIONS_TABLE = os.getenv("MAIN_USER_SUBSCRIPTIONS_TABLE")
|
|
38
|
+
MAIN_USER_SUBSCRIPTION_HISTORY_TABLE = os.getenv("MAIN_USER_SUBSCRIPTION_HISTORY_TABLE")
|
|
39
|
+
MAIN_SUBSCRIPTIONS_TABLE = os.getenv("MAIN_SUBSCRIPTIONS_TABLE")
|
|
40
|
+
|
|
41
|
+
# =============================================================================
|
|
42
|
+
# TENANT-SPECIFIC TABLES (tenant schemas)
|
|
43
|
+
# =============================================================================
|
|
44
|
+
TENANT_LOGIN_SETTINGS_TABLE = os.getenv("TENANT_LOGIN_SETTINGS_TABLE")
|
|
45
|
+
TENANT_ASSIGN_ROLES_TABLE = os.getenv("TENANT_ASSIGN_ROLES_TABLE")
|
|
46
|
+
TENANT_USER_GROUPS_TABLE = os.getenv("TENANT_USER_GROUPS_TABLE")
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def database_url(self) -> str:
|
|
50
|
+
if self.DATABASE_URL:
|
|
51
|
+
return self.DATABASE_URL
|
|
52
|
+
|
|
53
|
+
port = int(self.DB_PORT) if self.DB_PORT else 5432
|
|
54
|
+
return f"postgresql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{port}/{self.DB_NAME}"
|
|
55
|
+
|
|
56
|
+
# Global settings instance
|
|
57
|
+
db_settings = Settings()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trovesuite
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, and other enterprise services for TroveSuite applications
|
|
5
5
|
Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
|
|
6
6
|
Author: Bright Debrah Owusu
|
|
@@ -34,6 +34,8 @@ Requires-Dist: python-multipart>=0.0.6
|
|
|
34
34
|
Requires-Dist: python-jose[cryptography]>=3.3.0
|
|
35
35
|
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
36
36
|
Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
|
|
37
|
+
Requires-Dist: uvicorn<0.39.0,>=0.38.0
|
|
38
|
+
Requires-Dist: pyjwt<3.0.0,>=2.10.1
|
|
37
39
|
Provides-Extra: dev
|
|
38
40
|
Requires-Dist: pytest>=8.4.2; extra == "dev"
|
|
39
41
|
Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import warnings
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
class Settings:
|
|
6
|
-
"""Settings configuration for TroveSuite Auth Service"""
|
|
7
|
-
|
|
8
|
-
# =============================================================================
|
|
9
|
-
# DATABASE CONFIGURATION
|
|
10
|
-
# =============================================================================
|
|
11
|
-
DATABASE_URL: str = os.getenv(
|
|
12
|
-
"DATABASE_URL",
|
|
13
|
-
"postgresql://username:password@localhost:5432/database_name"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
# Alternative database configuration
|
|
17
|
-
DB_USER: Optional[str] = os.getenv("DB_USER")
|
|
18
|
-
DB_HOST: Optional[str] = os.getenv("DB_HOST")
|
|
19
|
-
DB_NAME: Optional[str] = os.getenv("DB_NAME")
|
|
20
|
-
DB_PORT: int = int(os.getenv("DB_PORT", "5432"))
|
|
21
|
-
DB_PASSWORD: Optional[str] = os.getenv("DB_PASSWORD")
|
|
22
|
-
ENVIRONMENT: str = os.getenv("ENVIRONMENT", "development")
|
|
23
|
-
|
|
24
|
-
# =============================================================================
|
|
25
|
-
# APPLICATION SETTINGS
|
|
26
|
-
# =============================================================================
|
|
27
|
-
APP_NAME: str = os.getenv("APP_NAME", "TroveSuite Auth Service")
|
|
28
|
-
DEBUG: bool = os.getenv("DEBUG", "False").lower() == "true"
|
|
29
|
-
|
|
30
|
-
# =============================================================================
|
|
31
|
-
# SECURITY SETTINGS
|
|
32
|
-
# =============================================================================
|
|
33
|
-
ALGORITHM: str = os.getenv("ALGORITHM", "HS256")
|
|
34
|
-
SECRET_KEY: str = os.getenv("SECRET_KEY", "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7")
|
|
35
|
-
ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "60"))
|
|
36
|
-
|
|
37
|
-
# =============================================================================
|
|
38
|
-
# LOGGING SETTINGS
|
|
39
|
-
# =============================================================================
|
|
40
|
-
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
|
41
|
-
LOG_FORMAT: str = os.getenv("LOG_FORMAT", "detailed") # detailed, json, simple
|
|
42
|
-
LOG_TO_FILE: bool = os.getenv("LOG_TO_FILE", "False").lower() == "false"
|
|
43
|
-
LOG_MAX_SIZE: int = int(os.getenv("LOG_MAX_SIZE", "10485760")) # 10MB
|
|
44
|
-
LOG_BACKUP_COUNT: int = int(os.getenv("LOG_BACKUP_COUNT", "5"))
|
|
45
|
-
LOG_DIR: str = os.getenv("LOG_DIR", "logs")
|
|
46
|
-
|
|
47
|
-
# =============================================================================
|
|
48
|
-
# DATABASE TABLE NAMES
|
|
49
|
-
# =============================================================================
|
|
50
|
-
# Main schema tables
|
|
51
|
-
MAIN_TENANTS_TABLE: str = os.getenv("MAIN_TENANTS_TABLE", "tenants")
|
|
52
|
-
ROLE_PERMISSIONS_TABLE: str = os.getenv("ROLE_PERMISSIONS_TABLE", "role_permissions")
|
|
53
|
-
|
|
54
|
-
# Tenant-specific tables (used in queries with tenant schema)
|
|
55
|
-
TENANT_LOGIN_SETTINGS_TABLE: str = os.getenv("TENANT_LOGIN_SETTINGS_TABLE", "login_settings")
|
|
56
|
-
USER_GROUPS_TABLE: str = os.getenv("USER_GROUPS_TABLE", "user_groups")
|
|
57
|
-
ASSIGN_ROLES_TABLE: str = os.getenv("ASSIGN_ROLES_TABLE", "assign_roles")
|
|
58
|
-
|
|
59
|
-
# =============================================================================
|
|
60
|
-
# AZURE CONFIGURATION (Optional - for queue functionality)
|
|
61
|
-
# =============================================================================
|
|
62
|
-
STORAGE_ACCOUNT_NAME: str = os.getenv("STORAGE_ACCOUNT_NAME", "")
|
|
63
|
-
USER_ASSIGNED_MANAGED_IDENTITY: str = os.getenv("USER_ASSIGNED_MANAGED_IDENTITY", "")
|
|
64
|
-
|
|
65
|
-
@property
|
|
66
|
-
def database_url(self) -> str:
|
|
67
|
-
"""Get the database URL, either from DATABASE_URL or constructed from individual components"""
|
|
68
|
-
if self.DATABASE_URL != "postgresql://username:password@localhost:5432/database_name":
|
|
69
|
-
return self.DATABASE_URL
|
|
70
|
-
|
|
71
|
-
# Validate individual components
|
|
72
|
-
if not all([self.DB_USER, self.DB_HOST, self.DB_NAME, self.DB_PASSWORD]):
|
|
73
|
-
missing = []
|
|
74
|
-
if not self.DB_USER:
|
|
75
|
-
missing.append("DB_USER")
|
|
76
|
-
if not self.DB_HOST:
|
|
77
|
-
missing.append("DB_HOST")
|
|
78
|
-
if not self.DB_NAME:
|
|
79
|
-
missing.append("DB_NAME")
|
|
80
|
-
if not self.DB_PASSWORD:
|
|
81
|
-
missing.append("DB_PASSWORD")
|
|
82
|
-
|
|
83
|
-
raise ValueError(
|
|
84
|
-
f"Database configuration incomplete. Missing environment variables: {', '.join(missing)}. "
|
|
85
|
-
f"Please set these variables or provide a complete DATABASE_URL."
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
return f"postgresql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
|
|
89
|
-
|
|
90
|
-
def validate_configuration(self) -> None:
|
|
91
|
-
"""Validate the current configuration and warn about potential issues"""
|
|
92
|
-
warnings_list = []
|
|
93
|
-
|
|
94
|
-
# Check for default secret key
|
|
95
|
-
if self.SECRET_KEY == "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7":
|
|
96
|
-
warnings_list.append(
|
|
97
|
-
"SECRET_KEY is using the default value. This is insecure for production. "
|
|
98
|
-
"Please set a strong, unique SECRET_KEY environment variable."
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
# Check for development environment in production-like settings
|
|
102
|
-
if self.ENVIRONMENT == "development" and self.DEBUG is False:
|
|
103
|
-
warnings_list.append(
|
|
104
|
-
"ENVIRONMENT is set to 'development' but DEBUG is False. "
|
|
105
|
-
"Consider setting ENVIRONMENT to 'production' for production deployments."
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# Check database configuration
|
|
109
|
-
try:
|
|
110
|
-
self.database_url
|
|
111
|
-
except ValueError as e:
|
|
112
|
-
warnings_list.append(f"Database configuration issue: {str(e)}")
|
|
113
|
-
|
|
114
|
-
# Check for missing Azure configuration if needed
|
|
115
|
-
if self.ENVIRONMENT == "production" and not self.STORAGE_ACCOUNT_NAME:
|
|
116
|
-
warnings_list.append(
|
|
117
|
-
"STORAGE_ACCOUNT_NAME is not set. Azure queue functionality may not work properly."
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# Emit warnings
|
|
121
|
-
for warning in warnings_list:
|
|
122
|
-
warnings.warn(warning, UserWarning)
|
|
123
|
-
|
|
124
|
-
def get_configuration_summary(self) -> dict:
|
|
125
|
-
"""Get a summary of the current configuration (excluding sensitive data)"""
|
|
126
|
-
return {
|
|
127
|
-
"app_name": self.APP_NAME,
|
|
128
|
-
"environment": self.ENVIRONMENT,
|
|
129
|
-
"debug": self.DEBUG,
|
|
130
|
-
"database_host": self.DB_HOST,
|
|
131
|
-
"database_port": self.DB_PORT,
|
|
132
|
-
"database_name": self.DB_NAME,
|
|
133
|
-
"database_user": self.DB_USER,
|
|
134
|
-
"log_level": self.LOG_LEVEL,
|
|
135
|
-
"log_format": self.LOG_FORMAT,
|
|
136
|
-
"log_to_file": self.LOG_TO_FILE,
|
|
137
|
-
"algorithm": self.ALGORITHM,
|
|
138
|
-
"access_token_expire_minutes": self.ACCESS_TOKEN_EXPIRE_MINUTES,
|
|
139
|
-
"MAIN_TENANTS_TABLE": self.MAIN_TENANTS_TABLE,
|
|
140
|
-
"role_permissions_table": self.ROLE_PERMISSIONS_TABLE,
|
|
141
|
-
"TENANT_LOGIN_SETTINGS_TABLE": self.TENANT_LOGIN_SETTINGS_TABLE,
|
|
142
|
-
"user_groups_table": self.USER_GROUPS_TABLE,
|
|
143
|
-
"assign_roles_table": self.ASSIGN_ROLES_TABLE,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
# Global settings instance
|
|
147
|
-
db_settings = Settings()
|
|
148
|
-
|
|
149
|
-
# Validate configuration on import
|
|
150
|
-
try:
|
|
151
|
-
db_settings.validate_configuration()
|
|
152
|
-
except Exception as e:
|
|
153
|
-
warnings.warn("Configuration validation failed: %s", str(e), UserWarning)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|