trovesuite 1.0.10__tar.gz → 1.0.12__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.10/src/trovesuite.egg-info → trovesuite-1.0.12}/PKG-INFO +1 -1
- {trovesuite-1.0.10 → trovesuite-1.0.12}/pyproject.toml +2 -2
- {trovesuite-1.0.10 → trovesuite-1.0.12}/setup.py +1 -1
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/auth_read_dto.py +3 -2
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/auth_service.py +93 -64
- {trovesuite-1.0.10 → trovesuite-1.0.12/src/trovesuite.egg-info}/PKG-INFO +1 -1
- {trovesuite-1.0.10 → trovesuite-1.0.12}/LICENSE +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/MANIFEST.in +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/README.md +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/requirements.txt +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/setup.cfg +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/auth_base.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/auth_controller.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/auth/auth_write_dto.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/configs/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/configs/database.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/configs/logging.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/configs/settings.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/entities/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/entities/health.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/entities/sh_response.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_base.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_controller.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_read_dto.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_service.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_write_dto.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/storage_base.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/storage_controller.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/storage_read_dto.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/storage_service.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/storage/storage_write_dto.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/utils/__init__.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/utils/helper.py +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite.egg-info/SOURCES.txt +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite.egg-info/dependency_links.txt +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite.egg-info/not-zip-safe +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite.egg-info/requires.txt +0 -0
- {trovesuite-1.0.10 → trovesuite-1.0.12}/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.12
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, 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
|
|
@@ -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.12"
|
|
8
8
|
description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
|
|
9
9
|
authors = ["brightgclt <brightgclt@gmail.com>"]
|
|
10
10
|
license = "MIT"
|
|
@@ -58,7 +58,7 @@ Documentation = "https://dev.azure.com/brightgclt/trovesuite/_git/packages"
|
|
|
58
58
|
|
|
59
59
|
[project]
|
|
60
60
|
name = "trovesuite"
|
|
61
|
-
version = "1.0.
|
|
61
|
+
version = "1.0.12"
|
|
62
62
|
description = "TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications"
|
|
63
63
|
readme = "README.md"
|
|
64
64
|
license = {text = "MIT"}
|
|
@@ -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.12",
|
|
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",
|
|
@@ -3,8 +3,8 @@ from pydantic import BaseModel
|
|
|
3
3
|
|
|
4
4
|
class AuthControllerReadDto(BaseModel):
|
|
5
5
|
org_id: Optional[str] = None
|
|
6
|
-
bus_id: Optional[str] = None
|
|
7
|
-
app_id: Optional[str] = None
|
|
6
|
+
bus_id: Optional[str] = None
|
|
7
|
+
app_id: Optional[str] = None
|
|
8
8
|
shared_resource_id: Optional[str] = None
|
|
9
9
|
user_id: Optional[str] = None
|
|
10
10
|
group_id: Optional[str] = None
|
|
@@ -12,6 +12,7 @@ class AuthControllerReadDto(BaseModel):
|
|
|
12
12
|
tenant_id: Optional[str] = None
|
|
13
13
|
permissions: Optional[List[str]] = None
|
|
14
14
|
resource_id: Optional[str] = None
|
|
15
|
+
resource_type: Optional[str] = None
|
|
15
16
|
|
|
16
17
|
class AuthServiceReadDto(AuthControllerReadDto):
|
|
17
18
|
pass
|
|
@@ -45,10 +45,10 @@ class AuthService:
|
|
|
45
45
|
|
|
46
46
|
@staticmethod
|
|
47
47
|
def authorize(data: AuthServiceWriteDto) -> Respons[AuthServiceReadDto]:
|
|
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):
|
|
@@ -59,7 +59,7 @@ class AuthService:
|
|
|
59
59
|
status_code=400,
|
|
60
60
|
error="INVALID_USER_ID"
|
|
61
61
|
)
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
if not tenant_id or not isinstance(tenant_id, str):
|
|
64
64
|
return Respons[AuthServiceReadDto](
|
|
65
65
|
detail="Invalid tenant_id: must be a non-empty string",
|
|
@@ -75,9 +75,9 @@ class AuthService:
|
|
|
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
|
-
logger.warning("
|
|
80
|
+
logger.warning("Authorization failed - tenant not found: %s", tenant_id)
|
|
81
81
|
return Respons[AuthServiceReadDto](
|
|
82
82
|
detail=f"Tenant '{tenant_id}' not found or has been deleted",
|
|
83
83
|
data=[],
|
|
@@ -85,9 +85,9 @@ class AuthService:
|
|
|
85
85
|
status_code=404,
|
|
86
86
|
error="TENANT_NOT_FOUND"
|
|
87
87
|
)
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
if not is_tenant_verified[0]['is_verified']:
|
|
90
|
-
logger.warning("
|
|
90
|
+
logger.warning("Authorization failed - tenant not verified for user: %s, tenant: %s", user_id, tenant_id)
|
|
91
91
|
return Respons[AuthServiceReadDto](
|
|
92
92
|
detail=f"Tenant '{tenant_id}' is not verified. Please contact your administrator.",
|
|
93
93
|
data=[],
|
|
@@ -96,14 +96,36 @@ class AuthService:
|
|
|
96
96
|
error="TENANT_NOT_VERIFIED"
|
|
97
97
|
)
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
WHERE (delete_status = 'NOT_DELETED' AND is_active = true ) AND user_id = %s""",
|
|
104
|
-
(user_id,),
|
|
99
|
+
# 1️⃣ Get all groups the user belongs to
|
|
100
|
+
user_groups = DatabaseManager.execute_query(
|
|
101
|
+
f"""SELECT group_id FROM "{tenant_id}".{db_settings.TENANT_USER_GROUPS_TABLE}
|
|
102
|
+
WHERE delete_status = 'NOT_DELETED' AND is_active = true AND user_id = %s""",(user_id,),
|
|
105
103
|
)
|
|
106
104
|
|
|
105
|
+
# 2️⃣ Prepare list of group_ids
|
|
106
|
+
group_ids = [g["group_id"] for g in user_groups] if user_groups else []
|
|
107
|
+
|
|
108
|
+
# 3️⃣ Get login settings - check user-level first, then group-level
|
|
109
|
+
if group_ids:
|
|
110
|
+
login_settings_details = DatabaseManager.execute_query(
|
|
111
|
+
f"""SELECT user_id, group_id, is_suspended, can_always_login,
|
|
112
|
+
is_multi_factor_enabled, is_login_before, working_days,
|
|
113
|
+
login_on, logout_on FROM "{tenant_id}".{db_settings.TENANT_LOGIN_SETTINGS_TABLE}
|
|
114
|
+
WHERE (delete_status = 'NOT_DELETED' AND is_active = true )
|
|
115
|
+
AND (user_id = %s OR group_id = ANY(%s))
|
|
116
|
+
ORDER BY user_id NULLS LAST
|
|
117
|
+
LIMIT 1""",
|
|
118
|
+
(user_id, group_ids),
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
login_settings_details = DatabaseManager.execute_query(
|
|
122
|
+
f"""SELECT user_id, group_id, is_suspended, can_always_login,
|
|
123
|
+
is_multi_factor_enabled, is_login_before, working_days,
|
|
124
|
+
login_on, logout_on FROM "{tenant_id}".{db_settings.TENANT_LOGIN_SETTINGS_TABLE}
|
|
125
|
+
WHERE (delete_status = 'NOT_DELETED' AND is_active = true ) AND user_id = %s""",
|
|
126
|
+
(user_id,),
|
|
127
|
+
)
|
|
128
|
+
|
|
107
129
|
if not login_settings_details or len(login_settings_details) == 0:
|
|
108
130
|
logger.warning("Authorization failed - user not found: %s in tenant: %s", user_id, tenant_id)
|
|
109
131
|
return Respons[AuthServiceReadDto](
|
|
@@ -124,56 +146,67 @@ class AuthService:
|
|
|
124
146
|
error="USER_SUSPENDED"
|
|
125
147
|
)
|
|
126
148
|
|
|
149
|
+
# ✅ UPDATED: Mutually exclusive login restrictions logic
|
|
127
150
|
if not login_settings_details[0]['can_always_login']:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
151
|
+
# Get from database (should already be datetime objects)
|
|
152
|
+
login_on = login_settings_details[0]['login_on']
|
|
153
|
+
logout_on = login_settings_details[0]['logout_on']
|
|
154
|
+
working_days = login_settings_details[0]['working_days']
|
|
155
|
+
|
|
156
|
+
# Only ONE restriction type can be active at a time:
|
|
157
|
+
# 1. working_days restriction (if login_on/logout_on are NULL)
|
|
158
|
+
# 2. time period restriction (if login_on/logout_on are set)
|
|
159
|
+
|
|
160
|
+
if login_on and logout_on:
|
|
161
|
+
# Time period restriction is active
|
|
162
|
+
logger.info(f"Checking time period restriction for user: {user_id}")
|
|
163
|
+
|
|
133
164
|
# Get current datetime (full date and time) with timezone
|
|
134
|
-
current_datetime = datetime.now(timezone.utc).replace(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
login_on = login_settings_details[0]['login_on']
|
|
138
|
-
logout_on = login_settings_details[0]['logout_on']
|
|
139
|
-
|
|
140
|
-
# Set defaults if None (with timezone awareness)
|
|
141
|
-
if not login_on:
|
|
142
|
-
login_on = datetime.min.replace(tzinfo=timezone.utc)
|
|
143
|
-
if not logout_on:
|
|
144
|
-
logout_on = datetime.max.replace(tzinfo=timezone.utc)
|
|
165
|
+
current_datetime = datetime.now(timezone.utc).replace(
|
|
166
|
+
microsecond=0, second=0
|
|
167
|
+
)
|
|
145
168
|
|
|
146
169
|
# Compare full datetime objects (both date and time)
|
|
147
170
|
if not (login_on <= current_datetime <= logout_on):
|
|
148
|
-
logger.warning(
|
|
171
|
+
logger.warning(
|
|
172
|
+
f"Authorization failed - outside allowed period for user: {user_id}"
|
|
173
|
+
)
|
|
149
174
|
return Respons[AuthServiceReadDto](
|
|
150
|
-
detail="
|
|
175
|
+
detail="Access is not allowed at this time. Please check your access schedule.",
|
|
151
176
|
data=[],
|
|
152
177
|
success=False,
|
|
153
178
|
status_code=403,
|
|
154
179
|
error="LOGIN_TIME_RESTRICTED"
|
|
155
180
|
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
WHERE delete_status = 'NOT_DELETED' AND is_active = true AND user_id = %s""",(user_id,),
|
|
161
|
-
)
|
|
181
|
+
elif working_days:
|
|
182
|
+
# Working days restriction is active
|
|
183
|
+
logger.info(f"Checking working days restriction for user: {user_id}")
|
|
184
|
+
current_day = datetime.now().strftime("%A").upper()
|
|
162
185
|
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
if current_day not in working_days:
|
|
187
|
+
logger.warning(
|
|
188
|
+
f"Authorization failed - not a working day for user: {user_id}"
|
|
189
|
+
)
|
|
190
|
+
return Respons[AuthServiceReadDto](
|
|
191
|
+
detail="Access is not allowed on this day. Please contact your administrator.",
|
|
192
|
+
data=[],
|
|
193
|
+
success=False,
|
|
194
|
+
status_code=403,
|
|
195
|
+
error="LOGIN_DAY_RESTRICTED"
|
|
196
|
+
)
|
|
165
197
|
|
|
166
|
-
#
|
|
198
|
+
# 4️⃣ Build query dynamically to include groups (if any) + user
|
|
199
|
+
# ⚠️ CHANGED: Simplified to new schema - only select user_id, group_id, role_id, resource_type
|
|
167
200
|
if group_ids:
|
|
168
201
|
get_user_roles = DatabaseManager.execute_query(
|
|
169
202
|
f"""
|
|
170
|
-
SELECT DISTINCT ON (
|
|
171
|
-
|
|
203
|
+
SELECT DISTINCT ON (group_id, user_id, role_id)
|
|
204
|
+
group_id, user_id, role_id, resource_type
|
|
172
205
|
FROM "{tenant_id}".{db_settings.TENANT_ASSIGN_ROLES_TABLE}
|
|
173
206
|
WHERE delete_status = 'NOT_DELETED'
|
|
174
207
|
AND is_active = true
|
|
175
208
|
AND (user_id = %s OR group_id = ANY(%s))
|
|
176
|
-
ORDER BY
|
|
209
|
+
ORDER BY group_id, user_id, role_id;
|
|
177
210
|
""",
|
|
178
211
|
(user_id, group_ids),
|
|
179
212
|
)
|
|
@@ -181,13 +214,13 @@ class AuthService:
|
|
|
181
214
|
# No groups, just check roles for user
|
|
182
215
|
get_user_roles = DatabaseManager.execute_query(
|
|
183
216
|
f"""
|
|
184
|
-
SELECT DISTINCT ON (
|
|
185
|
-
|
|
217
|
+
SELECT DISTINCT ON (user_id, role_id)
|
|
218
|
+
user_id, role_id, resource_type
|
|
186
219
|
FROM "{tenant_id}".{db_settings.TENANT_ASSIGN_ROLES_TABLE}
|
|
187
220
|
WHERE delete_status = 'NOT_DELETED'
|
|
188
221
|
AND is_active = true
|
|
189
222
|
AND user_id = %s
|
|
190
|
-
ORDER BY
|
|
223
|
+
ORDER BY user_id, role_id;
|
|
191
224
|
""",
|
|
192
225
|
(user_id,),
|
|
193
226
|
)
|
|
@@ -224,31 +257,27 @@ class AuthService:
|
|
|
224
257
|
status_code=500,
|
|
225
258
|
error="Authorization check failed due to an internal error"
|
|
226
259
|
)
|
|
227
|
-
|
|
260
|
+
|
|
228
261
|
@staticmethod
|
|
229
|
-
def check_permission(users_data: list, action=None,
|
|
230
|
-
resource_id=None, shared_resource_id=None) -> bool:
|
|
262
|
+
def check_permission(users_data: list, action=None, resource_type=None) -> bool:
|
|
231
263
|
"""
|
|
232
|
-
Check if user has a given permission (action)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
264
|
+
Check if user has a given permission (action).
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
users_data: List of user authorization data containing roles and permissions
|
|
268
|
+
action: The permission/action to check for
|
|
269
|
+
resource_type: Optional resource type filter (e.g., 'rt-user', 'rt-group')
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
bool: True if user has the permission, False otherwise
|
|
236
273
|
"""
|
|
237
274
|
for user_data in users_data:
|
|
238
|
-
# Check
|
|
239
|
-
if user_data.
|
|
240
|
-
continue
|
|
241
|
-
if user_data.bus_id not in (None, bus_id):
|
|
242
|
-
continue
|
|
243
|
-
if user_data.app_id not in (None, app_id):
|
|
244
|
-
continue
|
|
245
|
-
if user_data.resource_id not in (None, resource_id):
|
|
246
|
-
continue
|
|
247
|
-
if user_data.shared_resource_id not in (None, shared_resource_id):
|
|
275
|
+
# Check resource_type if specified
|
|
276
|
+
if resource_type and user_data.resource_type and user_data.resource_type != resource_type:
|
|
248
277
|
continue
|
|
249
278
|
|
|
250
279
|
# Check if the permission exists
|
|
251
|
-
if action in user_data.permissions:
|
|
280
|
+
if action and action in user_data.permissions:
|
|
252
281
|
return True
|
|
253
282
|
|
|
254
283
|
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trovesuite
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.12
|
|
4
4
|
Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, 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
|
|
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
|
{trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_controller.py
RENAMED
|
File without changes
|
{trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_read_dto.py
RENAMED
|
File without changes
|
|
File without changes
|
{trovesuite-1.0.10 → trovesuite-1.0.12}/src/trovesuite/notification/notification_write_dto.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|