trovesuite 1.0.0__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 (30) hide show
  1. trovesuite-1.0.0/LICENSE +21 -0
  2. trovesuite-1.0.0/MANIFEST.in +9 -0
  3. trovesuite-1.0.0/PKG-INFO +572 -0
  4. trovesuite-1.0.0/README.md +521 -0
  5. trovesuite-1.0.0/pyproject.toml +176 -0
  6. trovesuite-1.0.0/requirements.txt +10 -0
  7. trovesuite-1.0.0/setup.cfg +4 -0
  8. trovesuite-1.0.0/setup.py +68 -0
  9. trovesuite-1.0.0/src/trovesuite/__init__.py +16 -0
  10. trovesuite-1.0.0/src/trovesuite/auth/__init__.py +16 -0
  11. trovesuite-1.0.0/src/trovesuite/auth/auth_base.py +4 -0
  12. trovesuite-1.0.0/src/trovesuite/auth/auth_controller.py +10 -0
  13. trovesuite-1.0.0/src/trovesuite/auth/auth_read_dto.py +18 -0
  14. trovesuite-1.0.0/src/trovesuite/auth/auth_service.py +334 -0
  15. trovesuite-1.0.0/src/trovesuite/auth/auth_write_dto.py +10 -0
  16. trovesuite-1.0.0/src/trovesuite/configs/__init__.py +16 -0
  17. trovesuite-1.0.0/src/trovesuite/configs/database.py +221 -0
  18. trovesuite-1.0.0/src/trovesuite/configs/logging.py +261 -0
  19. trovesuite-1.0.0/src/trovesuite/configs/settings.py +153 -0
  20. trovesuite-1.0.0/src/trovesuite/entities/__init__.py +11 -0
  21. trovesuite-1.0.0/src/trovesuite/entities/health.py +84 -0
  22. trovesuite-1.0.0/src/trovesuite/entities/sh_response.py +61 -0
  23. trovesuite-1.0.0/src/trovesuite/utils/__init__.py +11 -0
  24. trovesuite-1.0.0/src/trovesuite/utils/helper.py +36 -0
  25. trovesuite-1.0.0/src/trovesuite.egg-info/PKG-INFO +572 -0
  26. trovesuite-1.0.0/src/trovesuite.egg-info/SOURCES.txt +28 -0
  27. trovesuite-1.0.0/src/trovesuite.egg-info/dependency_links.txt +1 -0
  28. trovesuite-1.0.0/src/trovesuite.egg-info/not-zip-safe +1 -0
  29. trovesuite-1.0.0/src/trovesuite.egg-info/requires.txt +19 -0
  30. trovesuite-1.0.0/src/trovesuite.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Bright Debrah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ include requirements.txt
5
+ recursive-include auth_service *.py
6
+ recursive-include auth_service *.pyi
7
+ recursive-exclude * __pycache__
8
+ recursive-exclude * *.py[co]
9
+ recursive-exclude * .DS_Store
@@ -0,0 +1,572 @@
1
+ Metadata-Version: 2.4
2
+ Name: trovesuite
3
+ Version: 1.0.0
4
+ Summary: A comprehensive authentication and authorization service for ERP systems
5
+ Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
6
+ Author: Bright Debrah Owusu
7
+ Author-email: Bright Debrah Owusu <owusu.debrah@deladetech.com>
8
+ Maintainer-email: Bright Debrah Owusu <owusu.debrah@deladetech.com>
9
+ License: MIT
10
+ Project-URL: Homepage, https://dev.azure.com/brightgclt/trovesuite/_git/packages
11
+ Project-URL: Repository, https://dev.azure.com/brightgclt/trovesuite/_git/packages
12
+ Project-URL: Documentation, https://dev.azure.com/brightgclt/trovesuite/_git/packages
13
+ Project-URL: Bug Tracker, https://dev.azure.com/brightgclt/trovesuite/_workitems/create
14
+ Keywords: authentication,authorization,jwt,erp,fastapi,security
15
+ Classifier: Development Status :: 5 - Production/Stable
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
25
+ Classifier: Topic :: Security
26
+ Requires-Python: >=3.12
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: fastapi>=0.116.1
30
+ Requires-Dist: pydantic>=2.11.9
31
+ Requires-Dist: psycopg2-binary>=2.9.9
32
+ Requires-Dist: python-dotenv>=1.0.0
33
+ Requires-Dist: python-multipart>=0.0.6
34
+ Requires-Dist: python-jose[cryptography]>=3.3.0
35
+ Requires-Dist: passlib[bcrypt]>=1.7.4
36
+ Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=8.4.2; extra == "dev"
39
+ Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
40
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
41
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
42
+ Requires-Dist: factory-boy>=3.3.0; extra == "dev"
43
+ Requires-Dist: faker>=20.1.0; extra == "dev"
44
+ Requires-Dist: black>=23.11.0; extra == "dev"
45
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
46
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
47
+ Dynamic: author
48
+ Dynamic: home-page
49
+ Dynamic: license-file
50
+ Dynamic: requires-python
51
+
52
+ # TroveSuite Auth Service
53
+
54
+ A comprehensive authentication and authorization service for ERP systems. This package provides JWT token validation, user authorization, and permission checking capabilities.
55
+
56
+ ## Features
57
+
58
+ - **JWT Token Validation**: Secure token decoding and validation
59
+ - **User Authorization**: Multi-level authorization with tenant verification
60
+ - **Permission Checking**: Hierarchical permission system (organization > business > app > location > resource)
61
+ - **Database Integration**: PostgreSQL support with connection pooling
62
+ - **Logging**: Comprehensive logging with multiple output formats
63
+ - **Azure Integration**: Support for Azure Storage Queues and Managed Identity
64
+ - **FastAPI Ready**: Built for FastAPI applications
65
+
66
+ ## Installation
67
+
68
+ ### From Azure DevOps Artifacts
69
+
70
+ #### Using pip
71
+ ```bash
72
+ pip install trovesuite --index-url https://pypi.org/simple/ --extra-index-url https://pkgs.dev.azure.com/brightgclt/trovesuite/_packaging/packages/pypi/simple/
73
+ ```
74
+
75
+ #### Using Poetry
76
+ ```bash
77
+ # Add Azure DevOps Artifacts as a source
78
+ poetry source add --priority=supplemental azure https://pkgs.dev.azure.com/brightgclt/trovesuite/_packaging/packages/pypi/simple/
79
+
80
+ # Install the package
81
+ poetry add trovesuite
82
+ ```
83
+
84
+ ### From Source
85
+
86
+ #### Using pip
87
+ ```bash
88
+ git clone https://brightgclt@dev.azure.com/brightgclt/trovesuite/_git/packages
89
+ cd packages
90
+ pip install -e .
91
+ ```
92
+
93
+ #### Using Poetry
94
+ ```bash
95
+ git clone https://brightgclt@dev.azure.com/brightgclt/trovesuite/_git/packages
96
+ cd packages
97
+ poetry install
98
+ ```
99
+
100
+ ### Development Installation
101
+
102
+ #### Using pip
103
+ ```bash
104
+ git clone https://brightgclt@dev.azure.com/brightgclt/trovesuite/_git/packages
105
+ cd packages
106
+ pip install -e ".[dev]"
107
+ ```
108
+
109
+ #### Using Poetry
110
+ ```bash
111
+ git clone https://brightgclt@dev.azure.com/brightgclt/trovesuite/_git/packages
112
+ cd packages
113
+ poetry install --with dev
114
+ ```
115
+
116
+ ## Quick Start
117
+
118
+ ### Basic Usage
119
+
120
+ ```python
121
+ from trovesuite import AuthService
122
+ from trovesuite.configs.settings import db_settings
123
+
124
+ # Configure your database settings
125
+ db_settings.DB_HOST = "localhost"
126
+ db_settings.DB_PORT = 5432
127
+ db_settings.DB_NAME = "your_database"
128
+ db_settings.DB_USER = "your_user"
129
+ db_settings.DB_PASSWORD = "your_password"
130
+ db_settings.SECRET_KEY = "your-secret-key"
131
+
132
+ # Initialize the auth service
133
+ auth_service = AuthService()
134
+
135
+ # Authorize a user
136
+ from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
137
+ auth_data = AuthServiceWriteDto(user_id="user123", tenant="tenant456")
138
+ result = AuthService.authorize(auth_data)
139
+
140
+ if result.success:
141
+ print("User authorized successfully")
142
+ for role in result.data:
143
+ print(f"Role: {role.role_id}, Permissions: {role.permissions}")
144
+ else:
145
+ print(f"Authorization failed: {result.detail}")
146
+ ```
147
+
148
+ ### JWT Token Decoding
149
+
150
+ ```python
151
+ from trovesuite import AuthService
152
+ from fastapi import Depends
153
+ from fastapi.security import OAuth2PasswordBearer
154
+
155
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
156
+
157
+ @app.get("/protected")
158
+ async def protected_route(token: str = Depends(oauth2_scheme)):
159
+ # Decode and validate token
160
+ user_data = AuthService.decode_token(token)
161
+ user_id = user_data["user_id"]
162
+ tenant_id = user_data["tenant_id"]
163
+
164
+ # Authorize user
165
+ from trovesuite.auth.auth_write_dto import AuthServiceWriteDto
166
+ auth_data = AuthServiceWriteDto(user_id=user_id, tenant=tenant_id)
167
+ auth_result = AuthService.authorize(auth_data)
168
+ return auth_result
169
+ ```
170
+
171
+ ### Convenience Methods
172
+
173
+ ```python
174
+ from trovesuite import AuthService
175
+
176
+ # Get user info directly from token
177
+ user_info = AuthService.get_user_info_from_token(token)
178
+ print(f"User: {user_info['user_id']}, Tenant: {user_info['tenant_id']}")
179
+
180
+ # Authorize user directly from token (combines decode + authorize)
181
+ auth_result = AuthService.authorize_user_from_token(token)
182
+
183
+ if auth_result.success:
184
+ # Get all user permissions
185
+ all_permissions = AuthService.get_user_permissions(auth_result.data)
186
+ print(f"User has permissions: {all_permissions}")
187
+
188
+ # Check if user has any of the required permissions
189
+ has_any = AuthService.has_any_permission(
190
+ auth_result.data,
191
+ ["read", "write", "admin"]
192
+ )
193
+
194
+ # Check if user has all required permissions
195
+ has_all = AuthService.has_all_permissions(
196
+ auth_result.data,
197
+ ["read", "write"]
198
+ )
199
+ ```
200
+
201
+ ### Permission Checking
202
+
203
+ ```python
204
+ from trovesuite import AuthService
205
+
206
+ # After getting user roles from authorization
207
+ user_roles = auth_result.data
208
+
209
+ # Check specific permission
210
+ has_permission = AuthService.check_permission(
211
+ user_roles=user_roles,
212
+ action="read",
213
+ org_id="org123",
214
+ bus_id="bus456",
215
+ app_id="app789"
216
+ )
217
+
218
+ if has_permission:
219
+ print("User has permission to read from this resource")
220
+ ```
221
+
222
+
223
+ ## Configuration
224
+
225
+ ### Quick Configuration Check
226
+
227
+ ```python
228
+ from trovesuite.configs.settings import db_settings
229
+
230
+ # Check your configuration
231
+ config_summary = db_settings.get_configuration_summary()
232
+ print("Current configuration:")
233
+ for key, value in config_summary.items():
234
+ print(f" {key}: {value}")
235
+
236
+ # The service will automatically validate configuration on import
237
+ # and show warnings for potential issues
238
+ ```
239
+
240
+ ### Environment Variables
241
+
242
+ The service uses environment variables for configuration. Set these in your environment or `.env` file:
243
+
244
+ ```bash
245
+ # Database Configuration
246
+ DB_HOST=localhost
247
+ DB_PORT=5432
248
+ DB_NAME=your_database
249
+ DB_USER=your_user
250
+ DB_PASSWORD=your_password
251
+ DATABASE_URL=postgresql://user:password@localhost:5432/database
252
+
253
+ # Security
254
+ SECRET_KEY=your-secret-key-here
255
+ ALGORITHM=HS256
256
+ ACCESS_TOKEN_EXPIRE_MINUTES=60
257
+
258
+ # Application
259
+ APP_NAME=Auth Service
260
+ ENVIRONMENT=production
261
+ DEBUG=false
262
+
263
+ # Logging
264
+ LOG_LEVEL=INFO
265
+ LOG_FORMAT=detailed
266
+ LOG_TO_FILE=true
267
+
268
+ # Table Names (customize as needed)
269
+ MAIN_TENANTS_TABLE=tenants
270
+ TENANT_LOGIN_SETTINGS_TABLE=login_settings
271
+ USER_GROUPS_TABLE=user_groups
272
+ ASSIGN_ROLES_TABLE=assign_roles
273
+ ROLE_PERMISSIONS_TABLE=role_permissions
274
+
275
+ # Azure (optional - for queue functionality)
276
+ STORAGE_ACCOUNT_NAME=your-storage-account
277
+ USER_ASSIGNED_MANAGED_IDENTITY=your-managed-identity
278
+ ```
279
+
280
+ ### Database Schema
281
+
282
+ The service expects the following database tables:
283
+
284
+ #### Main Schema Tables
285
+ - `tenants` - Tenant information and verification status
286
+ - `role_permissions` - Role-permission mappings
287
+
288
+ #### Tenant Schema Tables (per tenant)
289
+ - `login_settings` - User login configurations (working days, suspension status, etc.)
290
+ - `user_groups` - User-group memberships
291
+ - `assign_roles` - Role assignments to users/groups with resource hierarchy
292
+
293
+ ## API Reference
294
+
295
+ ### AuthService
296
+
297
+ #### `authorize(user_id: str, tenant_id: str) -> Respons[AuthServiceReadDto]`
298
+
299
+ Authorizes a user and returns their roles and permissions.
300
+
301
+ **Parameters:**
302
+ - `user_id`: The user identifier (must be a non-empty string)
303
+ - `tenant_id`: The tenant identifier (must be a non-empty string)
304
+
305
+ **Returns:**
306
+ - `Respons[AuthServiceReadDto]`: Authorization result with user roles and permissions
307
+
308
+ **Error Codes:**
309
+ - `INVALID_USER_ID`: Invalid or empty user_id
310
+ - `INVALID_TENANT_ID`: Invalid or empty tenant_id
311
+ - `TENANT_NOT_FOUND`: Tenant doesn't exist or is deleted
312
+ - `TENANT_NOT_VERIFIED`: Tenant exists but is not verified
313
+ - `USER_NOT_FOUND`: User doesn't exist in tenant or is inactive
314
+ - `USER_SUSPENDED`: User account is suspended
315
+ - `LOGIN_TIME_RESTRICTED`: Login not allowed at current time
316
+
317
+ #### `decode_token(token: str) -> dict`
318
+
319
+ Decodes and validates a JWT token.
320
+
321
+ **Parameters:**
322
+ - `token`: The JWT token to decode
323
+
324
+ **Returns:**
325
+ - `dict`: Token payload with user_id and tenant_id
326
+
327
+ **Raises:**
328
+ - `HTTPException`: If token is invalid
329
+
330
+ #### `check_permission(user_roles: list, action: str, **kwargs) -> bool`
331
+
332
+ Checks if a user has a specific permission for a resource.
333
+
334
+ **Parameters:**
335
+ - `user_roles`: List of user roles from authorization
336
+ - `action`: The permission action to check
337
+ - `org_id`, `bus_id`, `app_id`, `resource_id`, `shared_resource_id`: Resource identifiers
338
+
339
+ **Returns:**
340
+ - `bool`: True if user has permission, False otherwise
341
+
342
+ #### `get_user_info_from_token(token: str) -> dict`
343
+
344
+ Convenience method to get user information from a JWT token.
345
+
346
+ **Parameters:**
347
+ - `token`: JWT token string
348
+
349
+ **Returns:**
350
+ - `dict`: User information including user_id and tenant_id
351
+
352
+ #### `authorize_user_from_token(token: str) -> Respons[AuthServiceReadDto]`
353
+
354
+ Convenience method to authorize a user directly from a JWT token.
355
+
356
+ **Parameters:**
357
+ - `token`: JWT token string
358
+
359
+ **Returns:**
360
+ - `Respons[AuthServiceReadDto]`: Authorization result with user roles and permissions
361
+
362
+ #### `get_user_permissions(user_roles: list) -> list`
363
+
364
+ Get all unique permissions for a user across all their roles.
365
+
366
+ **Parameters:**
367
+ - `user_roles`: List of user roles from authorization
368
+
369
+ **Returns:**
370
+ - `list`: Unique list of permissions
371
+
372
+ #### `has_any_permission(user_roles: list, required_permissions: list) -> bool`
373
+
374
+ Check if user has any of the required permissions.
375
+
376
+ **Parameters:**
377
+ - `user_roles`: List of user roles from authorization
378
+ - `required_permissions`: List of permissions to check for
379
+
380
+ **Returns:**
381
+ - `bool`: True if user has any of the required permissions
382
+
383
+ #### `has_all_permissions(user_roles: list, required_permissions: list) -> bool`
384
+
385
+ Check if user has all of the required permissions.
386
+
387
+ **Parameters:**
388
+ - `user_roles`: List of user roles from authorization
389
+ - `required_permissions`: List of permissions to check for
390
+
391
+ **Returns:**
392
+ - `bool`: True if user has all of the required permissions
393
+
394
+ ### Data Models
395
+
396
+ #### `AuthServiceReadDto`
397
+
398
+ ```python
399
+ class AuthServiceReadDto(BaseModel):
400
+ org_id: Optional[str] = None
401
+ bus_id: Optional[str] = None
402
+ app_id: Optional[str] = None
403
+ shared_resource_id: Optional[str] = None
404
+ user_id: Optional[str] = None
405
+ group_id: Optional[str] = None
406
+ role_id: Optional[str] = None
407
+ tenant_id: Optional[str] = None
408
+ permissions: Optional[List[str]] = None
409
+ resource_id: Optional[str] = None
410
+ ```
411
+
412
+ #### `Respons[T]`
413
+
414
+ ```python
415
+ class Respons[T](BaseModel):
416
+ detail: Optional[str] = None
417
+ error: Optional[str] = None
418
+ data: Optional[List[T]] = None
419
+ status_code: int = 200
420
+ success: bool = True
421
+ pagination: Optional[PaginationMeta] = None
422
+ ```
423
+
424
+ ## Error Handling
425
+
426
+ The service provides comprehensive error handling with specific error codes and user-friendly messages:
427
+
428
+ ### Common Error Scenarios
429
+
430
+ ```python
431
+ from trovesuite import AuthService
432
+
433
+ # Example: Handle authorization errors
434
+ result = AuthService.authorize("user123", "tenant456")
435
+
436
+ if not result.success:
437
+ if result.error == "TENANT_NOT_FOUND":
438
+ print("Tenant doesn't exist")
439
+ elif result.error == "USER_SUSPENDED":
440
+ print("User account is suspended")
441
+ elif result.error == "LOGIN_TIME_RESTRICTED":
442
+ print("Login not allowed at this time")
443
+ else:
444
+ print(f"Authorization failed: {result.detail}")
445
+ else:
446
+ print("Authorization successful!")
447
+ ```
448
+
449
+ ### Best Practices
450
+
451
+ 1. **Always check the `success` field** before accessing `data`
452
+ 2. **Use specific error codes** for programmatic error handling
453
+ 3. **Display user-friendly messages** from the `detail` field
454
+ 4. **Log errors** for debugging purposes
455
+ 5. **Validate input parameters** before calling service methods
456
+
457
+ ### Configuration Validation
458
+
459
+ The service automatically validates configuration on import and shows warnings for potential issues:
460
+
461
+ ```python
462
+ # Configuration validation happens automatically
463
+ from trovesuite_auth_service.configs.settings import db_settings
464
+
465
+ # Check configuration summary
466
+ config = db_settings.get_configuration_summary()
467
+ print("Configuration loaded successfully")
468
+
469
+ # Common warnings you might see:
470
+ # - Default SECRET_KEY in production
471
+ # - Missing database configuration
472
+ # - Inconsistent environment settings
473
+ ```
474
+
475
+ ## Development
476
+
477
+ ### Running Tests
478
+
479
+ #### Using pip
480
+ ```bash
481
+ pytest
482
+ ```
483
+
484
+ #### Using Poetry
485
+ ```bash
486
+ poetry run pytest
487
+ ```
488
+
489
+ ### Code Formatting
490
+
491
+ #### Using pip
492
+ ```bash
493
+ black trovesuite/
494
+ ```
495
+
496
+ #### Using Poetry
497
+ ```bash
498
+ poetry run black trovesuite/
499
+ ```
500
+
501
+ ### Type Checking
502
+
503
+ #### Using pip
504
+ ```bash
505
+ mypy trovesuite/
506
+ ```
507
+
508
+ #### Using Poetry
509
+ ```bash
510
+ poetry run mypy trovesuite/
511
+ ```
512
+
513
+ ### Linting
514
+
515
+ #### Using pip
516
+ ```bash
517
+ flake8 trovesuite/
518
+ ```
519
+
520
+ #### Using Poetry
521
+ ```bash
522
+ poetry run flake8 trovesuite/
523
+ ```
524
+
525
+ ### Poetry Configuration
526
+
527
+ If you're using Poetry in your project, you can add this package to your `pyproject.toml`:
528
+
529
+ ```toml
530
+ [tool.poetry.dependencies]
531
+ trovesuite = "^1.0.0"
532
+
533
+ [[tool.poetry.source]]
534
+ name = "azure"
535
+ url = "https://pkgs.dev.azure.com/brightgclt/trovesuite/_packaging/packages/pypi/simple/"
536
+ priority = "supplemental"
537
+ ```
538
+
539
+ Then run:
540
+ ```bash
541
+ poetry install
542
+ ```
543
+
544
+ ## Contributing
545
+
546
+ 1. Fork the repository
547
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
548
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
549
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
550
+ 5. Open a Pull Request
551
+
552
+ ## License
553
+
554
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
555
+
556
+ ## Support
557
+
558
+ For support, email brightgclt@gmail.com or create a work item in the [Azure DevOps repository](https://dev.azure.com/brightgclt/trovesuite/_workitems/create).
559
+
560
+ ## Changelog
561
+
562
+ ### 1.0.8
563
+ - Restructured package for direct service imports
564
+ - Added notification services
565
+ - Excluded controllers from package build
566
+ - Updated import paths for better usability
567
+ - JWT token validation
568
+ - User authorization with tenant verification
569
+ - Hierarchical permission checking
570
+ - PostgreSQL database integration
571
+ - Comprehensive logging
572
+ - Azure integration support