Metadata-Version: 2.4
Name: trovesuite
Version: 1.0.36
Summary: TroveSuite services package providing authentication, authorization, notifications, Azure Storage, and other enterprise services for TroveSuite applications
Home-page: https://dev.azure.com/brightgclt/trovesuite/_git/packages
Author: Bright Debrah Owusu
Author-email: Bright Debrah Owusu <owusu.debrah@deladetech.com>
Maintainer-email: Bright Debrah Owusu <owusu.debrah@deladetech.com>
License: MIT
Project-URL: Homepage, https://dev.azure.com/brightgclt/trovesuite/_git/packages
Project-URL: Repository, https://dev.azure.com/brightgclt/trovesuite/_git/packages
Project-URL: Documentation, https://dev.azure.com/brightgclt/trovesuite/_git/packages
Project-URL: Bug Tracker, https://dev.azure.com/brightgclt/trovesuite/_workitems/create
Keywords: authentication,authorization,notifications,jwt,trovesuite,fastapi,security,tenant,permissions,enterprise,services,azure,storage,blob,cloud-storage
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Security
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.116.1
Requires-Dist: pydantic>=2.11.9
Requires-Dist: psycopg2-binary>=2.9.9
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: python-multipart>=0.0.6
Requires-Dist: python-jose[cryptography]>=3.3.0
Requires-Dist: passlib[bcrypt]>=1.7.4
Requires-Dist: passlib[argon2]<2.0.0,>=1.7.4
Requires-Dist: uvicorn<0.39.0,>=0.38.0
Requires-Dist: pyjwt<3.0.0,>=2.10.1
Requires-Dist: azure-storage-blob>=12.19.0
Requires-Dist: azure-identity>=1.15.0
Provides-Extra: dev
Requires-Dist: pytest>=8.4.2; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
Requires-Dist: factory-boy>=3.3.0; extra == "dev"
Requires-Dist: faker>=20.1.0; extra == "dev"
Requires-Dist: black>=23.11.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# trovesuite

[![PyPI version](https://img.shields.io/pypi/v/trovesuite.svg)](https://pypi.org/project/trovesuite/)
[![Python versions](https://img.shields.io/pypi/pyversions/trovesuite.svg)](https://pypi.org/project/trovesuite/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

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_platform` PostgreSQL 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

```bash
pip install trovesuite
```

Or with Poetry:

```bash
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`](.env.example) for the full list. The essentials:

```bash
# 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

```python
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

```python
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

```python
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

```python
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.

```python
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]`:

```python
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. 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`](.env.example).

Schemas must include the columns referenced in [`src/trovesuite/auth/auth_service.py`](src/trovesuite/auth/auth_service.py) (notably `delete_status`, `is_active`, `is_system`, `is_suspended`, `working_days`, `login_on`, `logout_on`, `resource_type`).

## Development

```bash
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

1. Bump `version` in [`pyproject.toml`](pyproject.toml) (both `[tool.poetry]` and `[project]` blocks) and [`setup.py`](setup.py).
2. Commit, then tag: `git tag v$(new_version) && git push --tags`.
3. The [`publish`](.github/workflows/publish.yml) GitHub Actions workflow builds an sdist + wheel and uploads to PyPI using the `PYPI_API_TOKEN` secret from the `pypi` environment.

## License

MIT — see [LICENSE](LICENSE).
