trovesuite
Shared service layer for TroveSuite applications. Bundles the internal building blocks — authentication, authorization, notifications, and Azure blob storage — behind a small, consistent API so downstream FastAPI services don't reimplement them.
This package is built for the TroveSuite platform and assumes TroveSuite's
core_platformPostgreSQL schema (tenants, users, groups, roles, permissions). It is published to PyPI as a convenience for TroveSuite services; it is not a general-purpose auth library.
What's inside
| Service | Purpose |
|---|---|
AuthService |
JWT decode, multi-tenant user authorization, role/permission resolution, permission checks. |
NotificationService |
Send email via Gmail SMTP (plain + HTML). SMS is stubbed. |
StorageService |
Azure Blob Storage: container creation, upload/update/delete, download, SAS URL generation. |
Helper |
OTP/ID generation, JWT encode, tenant-aware email dispatch, activity logging. |
Install
pip install trovesuite
Or with Poetry:
poetry add trovesuite
Requires Python 3.13.
Configure
The package reads configuration from environment variables (a .env file is loaded automatically via python-dotenv). See .env.example for the full list. The essentials:
# Postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=trovesuite
DB_USER=postgres
DB_PASSWORD=...
# JWT
SECRET_KEY=change-me
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
# Email (optional — used by Helper.send_notification)
MAIL_SENDER_EMAIL=alerts@yourdomain.com
MAIL_SENDER_PWD=gmail-app-password
# Azure Storage (optional — used by StorageService)
STORAGE_ACCOUNT_NAME=yourstorageaccount
USER_ASSIGNED_MANAGED_IDENTITY=<client-id> # omit for system-assigned
Usage
Authorize a user from a JWT
from trovesuite import AuthService
result = AuthService.authorize_user_from_token(token)
if result.success:
for role_entry in result.data:
print(role_entry.role_id, role_entry.permissions)
else:
print(result.error, result.detail)
Authorize by IDs
from trovesuite import AuthService
from trovesuite.auth import AuthServiceWriteDto
result = AuthService.authorize(
AuthServiceWriteDto(user_id="usr_...", tenant_id="tnt_...")
)
authorize verifies the tenant exists and is verified, checks login-window restrictions (working days or custom time period), merges tenant-level and system-level role assignments, and returns a Respons[AuthServiceReadDto] with each role's permissions attached.
Possible result.error codes: INVALID_USER_ID, INVALID_TENANT_ID, TENANT_NOT_FOUND, TENANT_NOT_VERIFIED, USER_NOT_FOUND, USER_SUSPENDED, LOGIN_TIME_RESTRICTED, LOGIN_DAY_RESTRICTED.
Check permissions
from trovesuite import AuthService
# Does the user have 'permission-user-create'?
can_create = AuthService.check_permission(
users_data=result.data,
action="permission-user-create",
)
# Same check, but only against roles scoped to a specific resource type
can_create_in_group = AuthService.check_permission(
users_data=result.data,
action="permission-user-create",
resource_type="rt-group",
)
# Aggregate helpers
all_perms = AuthService.get_user_permissions(result.data)
AuthService.has_any_permission(result.data, ["p-read", "p-write"])
AuthService.has_all_permissions(result.data, ["p-read", "p-write"])
Send an email
from trovesuite import NotificationService
from trovesuite.notification import NotificationEmailServiceWriteDto
result = NotificationService.send_email(NotificationEmailServiceWriteDto(
sender_email="alerts@yourdomain.com",
receiver_email=["user@example.com"], # str or list[str]
password="gmail-app-password",
subject="Welcome",
text_message="Plain text body",
html_message="<h1>HTML body</h1>", # optional
))
For tenant-aware sending that pulls credentials from the platform's cp_notification_email_credentials table (and falls back to MAIL_SENDER_*), use Helper.send_notification(...) with a template.
Azure Blob Storage
StorageService authenticates via Managed Identity (user-assigned if managed_identity_client_id is provided, otherwise falls back to DefaultAzureCredential for local dev). SAS URLs are generated via user-delegation keys — no storage account key required.
from trovesuite import StorageService
from trovesuite.storage import (
StorageFileUploadServiceWriteDto,
StorageFileUrlServiceWriteDto,
)
upload = StorageService.upload_file(StorageFileUploadServiceWriteDto(
storage_account_url="https://myaccount.blob.core.windows.net",
container_name="documents",
blob_name="invoice.pdf",
directory_path="tenants/tnt_abc", # optional prefix
file_content=pdf_bytes,
content_type="application/pdf",
))
url = StorageService.get_file_url(StorageFileUrlServiceWriteDto(
storage_account_url="https://myaccount.blob.core.windows.net",
container_name="documents",
blob_name="tenants/tnt_abc/invoice.pdf",
expiry_hours=2,
))
Also available: create_container, update_file, delete_file, delete_multiple_files, download_file.
Response shape
Every service method returns a generic Respons[T]:
class Respons[T](BaseModel):
detail: Optional[str] # human-readable message
error: Optional[str] # machine error code (None on success)
data: Optional[List[T]] # payload; always a list, possibly empty
status_code: int = 200
success: bool = True
pagination: Optional[PaginationMeta] = None
Always branch on result.success before reading result.data.
Database expectations
AuthService and Helper read from the TroveSuite core_platform schema. This package does NOT create or migrate the database — schema provisioning is owned by tvs-sqlscript (EF Core migrations). Run it once per environment before starting any consumer of this package:
cd ../tvs-sqlscript
dotnet run --project src/Trovesuite.Database.Runner -- deploy --module all
Table names are configurable via environment variables (CORE_PLATFORM_TENANTS_TABLE, CORE_PLATFORM_USER_GROUPS_TABLE, CORE_PLATFORM_LOGIN_SETTINGS_TABLE, CORE_PLATFORM_ASSIGN_ROLES_TABLE, CORE_PLATFORM_ROLE_PERMISSIONS_TABLE, CORE_PLATFORM_ROLES_TABLE, CORE_PLATFORM_USERS_TABLE, CORE_PLATFORM_ACTIVITY_LOGS_TABLE, CORE_PLATFORM_RESOURCE_ID_TABLE, CORE_PLATFORM_NOTIFICATION_EMAIL_CREDENTIALS_TABLE) — see .env.example.
Schemas must include the columns referenced in src/trovesuite/auth/auth_service.py (notably delete_status, is_active, is_system, is_suspended, working_days, login_on, logout_on, resource_type).
Development
git clone <repo>
cd tvs-package
poetry install --with dev
poetry run pytest
poetry run black src/
poetry run mypy src/
poetry run flake8 src/
Releasing
- Bump
versioninpyproject.toml(both[tool.poetry]and[project]blocks) andsetup.py. - Commit, then tag:
git tag v$(new_version) && git push --tags. - The
publishGitHub Actions workflow builds an sdist + wheel and uploads to PyPI using thePYPI_API_TOKENsecret from thepypienvironment.
License
MIT — see LICENSE.
Release files for trovesuite 1.0.40
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| trovesuite-1.0.40.tar.gz | 51.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| trovesuite-1.0.40-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 106.1 kB
Release files / trovesuite-1.0.40.tar.gz
| Download URL | trovesuite-1.0.40.tar.gz |
|---|---|
| Size | 51.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f0179082ee6eae7f8ac32ebfc81bf8ca0f7a55d9080058b1127712e53cf3863
|
|
BLAKE2b-256 checksum How to use checksums |
ace23994be9ef713662ba744c5ab29c00a9b281a611cb83b4e270372a02c0764
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / trovesuite-1.0.40-py3-none-any.whl
| Download URL | trovesuite-1.0.40-py3-none-any.whl |
|---|---|
| Size | 54.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
26ef49ab6903d8bf5cd46cf8c23a956a63c76069ff77ac5f1055130b5db627f2
|
|
BLAKE2b-256 checksum How to use checksums |
4e1d4f04323fb3d40be1c8d35fe53a311b8400337614db549fa634bbb01474f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|