ferrox-py-auth 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.
- ferrox_py_auth-1.0.0/PKG-INFO +51 -0
- ferrox_py_auth-1.0.0/README.md +41 -0
- ferrox_py_auth-1.0.0/docs/auth_service.md +35 -0
- ferrox_py_auth-1.0.0/docs/gdpr_service.md +36 -0
- ferrox_py_auth-1.0.0/docs/models.md +44 -0
- ferrox_py_auth-1.0.0/docs/overview.md +39 -0
- ferrox_py_auth-1.0.0/docs/rbac.md +32 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/__init__.py +1 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/controllers/auth_controller.py +52 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/models/user.py +16 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/security/rbac.py +26 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/services/auth_service.py +52 -0
- ferrox_py_auth-1.0.0/ferrox_py_auth/services/gdpr_service.py +31 -0
- ferrox_py_auth-1.0.0/pyproject.toml +15 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ferrox-py-auth
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: IAM, SSO, GDPR and RBAC suite for Ferrox ecosystem.
|
|
5
|
+
Author: AI-Autistic-Intelligence
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: ferrox-py>=1.0.0
|
|
8
|
+
Requires-Dist: pydantic>=2.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Ferrox-Py-Auth
|
|
12
|
+
|
|
13
|
+
## 1. Overview (What does this do?)
|
|
14
|
+
The `ferrox-py-auth` module is a specialized extension for the Ferrox ecosystem that provides a comprehensive Identity & Access Management (IAM) suite. It delivers pre-built functionalities for managing user registrations, Single Sign-On (SSO), Role-Based Access Control (RBAC), and compliance with European data privacy laws (GDPR).
|
|
15
|
+
|
|
16
|
+
## 2. Philosophy (Why does it exist?)
|
|
17
|
+
Authentication and authorization are complex and highly sensitive domains. Re-implementing secure login flows, password hashing, SSO integration, and GDPR-compliant data deletion mechanisms for every new project is not only inefficient but heavily prone to critical security vulnerabilities. This module exists to provide a battle-tested, standardized "plug-and-play" identity solution that adheres to strict zero-trust principles.
|
|
18
|
+
|
|
19
|
+
## 3. Target Audience (Who is it for?)
|
|
20
|
+
This module is intended for backend engineers and security architects building Enterprise SaaS, financial platforms, or any consumer-facing application where data privacy (like GDPR compliance) and secure access controls are legal or operational requirements.
|
|
21
|
+
|
|
22
|
+
## 4. Architecture (How does it work?)
|
|
23
|
+
`ferrox-py-auth` plugs directly into the **7-Layer Onion Request Pipeline** of the core `ferrox-py` framework, specifically occupying Layers 3 (Threat Engine) and 4 (Auth Guards). It registers a set of domain Services (`AuthService`, `GdprService`) into the central IoC Container and exposes abstract Data Models (`User`, `UserIdentity`) that can be persisted via MongoDB or SQLAlchemy adapters provided by the `ferrox-py` Data Component.
|
|
24
|
+
|
|
25
|
+
## 5. Installation / Setup
|
|
26
|
+
This package requires the core `ferrox-py` framework to function. Install it via pip:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ferrox-py-auth
|
|
30
|
+
```
|
|
31
|
+
Ensure you have configured a persistent database layer (either SQL or NoSQL) in your core application to store the user identities.
|
|
32
|
+
|
|
33
|
+
## 6. Quickstart (Usage)
|
|
34
|
+
```python
|
|
35
|
+
from ferrox_py.core.app import FerroxApp
|
|
36
|
+
from ferrox_py.core.container import Container
|
|
37
|
+
from ferrox_py_auth import AuthModule
|
|
38
|
+
|
|
39
|
+
# Initialize the IoC container
|
|
40
|
+
container = Container()
|
|
41
|
+
|
|
42
|
+
# The AuthModule automatically registers the AuthService, GdprService,
|
|
43
|
+
# and Auth Guards into the container and application lifecycle.
|
|
44
|
+
container.register_module(AuthModule)
|
|
45
|
+
|
|
46
|
+
app = FerroxApp(container)
|
|
47
|
+
app.start()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 7. Ecosystem Integration
|
|
51
|
+
This module relies heavily on the **Security** component of the core `ferrox-py` framework for generating and parsing PASETO/JWT tokens. It also serves as a foundational dependency for `ferrox-py-commerce`, as a validated User context is required before initiating billing or subscription processes.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Ferrox-Py-Auth
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The `ferrox-py-auth` module is a specialized extension for the Ferrox ecosystem that provides a comprehensive Identity & Access Management (IAM) suite. It delivers pre-built functionalities for managing user registrations, Single Sign-On (SSO), Role-Based Access Control (RBAC), and compliance with European data privacy laws (GDPR).
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
Authentication and authorization are complex and highly sensitive domains. Re-implementing secure login flows, password hashing, SSO integration, and GDPR-compliant data deletion mechanisms for every new project is not only inefficient but heavily prone to critical security vulnerabilities. This module exists to provide a battle-tested, standardized "plug-and-play" identity solution that adheres to strict zero-trust principles.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This module is intended for backend engineers and security architects building Enterprise SaaS, financial platforms, or any consumer-facing application where data privacy (like GDPR compliance) and secure access controls are legal or operational requirements.
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
`ferrox-py-auth` plugs directly into the **7-Layer Onion Request Pipeline** of the core `ferrox-py` framework, specifically occupying Layers 3 (Threat Engine) and 4 (Auth Guards). It registers a set of domain Services (`AuthService`, `GdprService`) into the central IoC Container and exposes abstract Data Models (`User`, `UserIdentity`) that can be persisted via MongoDB or SQLAlchemy adapters provided by the `ferrox-py` Data Component.
|
|
14
|
+
|
|
15
|
+
## 5. Installation / Setup
|
|
16
|
+
This package requires the core `ferrox-py` framework to function. Install it via pip:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install ferrox-py-auth
|
|
20
|
+
```
|
|
21
|
+
Ensure you have configured a persistent database layer (either SQL or NoSQL) in your core application to store the user identities.
|
|
22
|
+
|
|
23
|
+
## 6. Quickstart (Usage)
|
|
24
|
+
```python
|
|
25
|
+
from ferrox_py.core.app import FerroxApp
|
|
26
|
+
from ferrox_py.core.container import Container
|
|
27
|
+
from ferrox_py_auth import AuthModule
|
|
28
|
+
|
|
29
|
+
# Initialize the IoC container
|
|
30
|
+
container = Container()
|
|
31
|
+
|
|
32
|
+
# The AuthModule automatically registers the AuthService, GdprService,
|
|
33
|
+
# and Auth Guards into the container and application lifecycle.
|
|
34
|
+
container.register_module(AuthModule)
|
|
35
|
+
|
|
36
|
+
app = FerroxApp(container)
|
|
37
|
+
app.start()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 7. Ecosystem Integration
|
|
41
|
+
This module relies heavily on the **Security** component of the core `ferrox-py` framework for generating and parsing PASETO/JWT tokens. It also serves as a foundational dependency for `ferrox-py-commerce`, as a validated User context is required before initiating billing or subscription processes.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# AuthService (Authentication & SSO)
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The `AuthService` is a singleton service registered within the Ferrox IoC container that handles all aspects of user registration, login, and identity verification. It provides out-of-the-box support for traditional email/password flows as well as external Single Sign-On (SSO) integrations.
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
Modern web applications rarely rely solely on basic username/password authentication anymore. Users expect seamless logins via Google, Apple, or GitHub. The `AuthService` exists to abstract the complexities of linking multiple external identities to a single user account and to enforce secure registration flows (like Double Opt-In) by default.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This service is for backend developers who need to implement a secure user registration and login system without dealing with the low-level mechanics of hashing passwords, generating secure tokens, or verifying OAuth2 payloads manually.
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
- **Standard Registration**: Handles traditional credentials. It automatically initiates a "Double Opt-In" process, meaning the account remains in a locked, unverified state until a confirmation email link is clicked.
|
|
14
|
+
- **SSO Integration (OIDC/OAuth2)**: Handles login/registration via external Identity Providers (IdPs). The identity (e.g., a Google ID) is saved inside an `identities` sub-document associated with the user. If a user registers via SSO, the "email verified" status is automatically inherited from the trusted IdP, bypassing the email opt-in step.
|
|
15
|
+
|
|
16
|
+
## 5. Installation / Setup
|
|
17
|
+
The `AuthService` is automatically registered in your IoC container when you import and register the `AuthModule` from `ferrox-py-auth`. You may need to configure external OAuth2 credentials (Client IDs and Secrets) in your environment variables for SSO functionality.
|
|
18
|
+
|
|
19
|
+
## 6. Quickstart (Usage)
|
|
20
|
+
```python
|
|
21
|
+
from ferrox_py_auth.services.auth_service import AuthService
|
|
22
|
+
|
|
23
|
+
# Example: Resolving the service from the container and registering via SSO
|
|
24
|
+
async def handle_google_callback(auth_service: AuthService, google_payload: dict):
|
|
25
|
+
# Registration via SSO (Bypasses Email Validation automatically)
|
|
26
|
+
user = await auth_service.register_via_sso(
|
|
27
|
+
email=google_payload["email"],
|
|
28
|
+
provider="google",
|
|
29
|
+
provider_id=google_payload["sub"]
|
|
30
|
+
)
|
|
31
|
+
return user
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 7. Ecosystem Integration
|
|
35
|
+
The `AuthService` leverages the core **Data Component** to persist the `User` models to the database. Upon successful login, it interacts with the **Security Component** to generate a secure PASETO or JWT token, which is then returned to the client to be used in the Authorization header of subsequent requests.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# GdprService
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The `GdprService` is a specialized service that abstracts complex commands required to comply with modern privacy laws (like the GDPR in Europe and the CCPA in California). It provides automated mechanisms for data portability (exporting user data) and the Right to be Forgotten (deleting or anonymizing user data).
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
Compliance with data privacy laws is a strict legal requirement, not a feature. Leaving developers to manually write cascade deletions for user data is highly dangerous; it often results in orphaned records, corrupted relational constraints, or, worse, incomplete deletions that violate privacy laws. This service exists to centralize and automate secure data anonymization and extraction.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This service is essential for Data Protection Officers (DPOs), compliance engineers, and backend developers who are legally required to provide users with the ability to download their data or permanently delete their accounts from the SaaS platform.
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
- **Right to be Forgotten (Deletion)**: The `delete_user_data` method securely masks, anonymizes, or hard-deletes Personally Identifiable Information (PII) across the database. It triggers an internal event that other modules can listen to, ensuring data is scrubbed across all bounded contexts.
|
|
14
|
+
- **Data Portability**: The `export_user_data` method aggregates the entire user state and preferences into a readable JSON or CSV format, ready to be compressed into a `.zip` file and delivered to the user.
|
|
15
|
+
|
|
16
|
+
## 5. Installation / Setup
|
|
17
|
+
The `GdprService` is available out of the box when using `ferrox-py-auth`. No additional installation is required, but you must ensure your specific data repositories implement the required anonymization hooks if you have custom tables containing PII.
|
|
18
|
+
|
|
19
|
+
## 6. Quickstart (Usage)
|
|
20
|
+
```python
|
|
21
|
+
from ferrox_py_auth.services.gdpr_service import GdprService
|
|
22
|
+
|
|
23
|
+
async def handle_account_deletion(gdpr_service: GdprService, user_id: str):
|
|
24
|
+
# Perform a compliant soft delete or hard delete of PII
|
|
25
|
+
await gdpr_service.delete_user_data(user_id=user_id)
|
|
26
|
+
return {"message": "Account successfully anonymized."}
|
|
27
|
+
|
|
28
|
+
async def handle_data_export(gdpr_service: GdprService, user_id: str):
|
|
29
|
+
# Generate a full export of the user's data footprint
|
|
30
|
+
dump = await gdpr_service.export_user_data(user_id=user_id)
|
|
31
|
+
# Returns a comprehensive dict: { "email": "...", "identities": [...], "created_at": "..." }
|
|
32
|
+
return dump
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 7. Ecosystem Integration
|
|
36
|
+
The `GdprService` integrates with the **CQRS and Event Dispatcher** from the core framework. When `delete_user_data` is called, it emits a `UserDeletedEvent`. Other modules (like `ferrox-py-commerce`) subscribe to this event to safely cancel active subscriptions and anonymize billing records without creating tightly coupled dependencies.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Models (Pydantic / Mongo)
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The Models module exports the base Data Transfer Objects (DTOs) and Database Schemas used to represent Users and their authentication states. These models are statically validated using Pydantic and are designed to be natively serialized/deserialized into MongoDB or adapted for SQLAlchemy ORMs.
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
A fragmented definition of what constitutes a "User" leads to bugs across different microservices. By providing strict, centrally defined Pydantic models for `User` and `UserIdentity`, `ferrox-py-auth` ensures that every part of the application agrees on the structure of the identity data, ensuring data integrity and fast validation at the API boundaries.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This module is for developers integrating the Auth module who need to understand the underlying data structure of the User object, or those who need to extend the base models with custom application-specific fields (like `company_name` or `avatar_url`).
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
- **UserIdentity**: A sub-document representing an external Identity Provider (IdP) link. It stores the `provider` (e.g., "google", "apple", "facebook", "local"), the `provider_id` (the alphanumeric unique identifier from the IdP), and auditing timestamps like `last_login`.
|
|
14
|
+
- **User**: The primary aggregate root model. It contains the main `email` address, manages critical boolean flags (`is_email_verified`, `is_active`, `is_locked`), holds a list of `UserIdentity` objects, and contains an array of `roles` (strings) used by the RBAC engine.
|
|
15
|
+
|
|
16
|
+
## 5. Installation / Setup
|
|
17
|
+
These models require `pydantic` (installed by default with the core framework). If you intend to use them directly with a NoSQL database, the `motor` driver from the core Data Component is recommended.
|
|
18
|
+
|
|
19
|
+
## 6. Quickstart (Usage)
|
|
20
|
+
```python
|
|
21
|
+
from ferrox_py_auth.models.user import User, UserIdentity
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
# Creating an instance of a User manually (usually handled by AuthService)
|
|
25
|
+
identity = UserIdentity(
|
|
26
|
+
provider="google",
|
|
27
|
+
provider_id="sub_123456789",
|
|
28
|
+
last_login=datetime.utcnow()
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
new_user = User(
|
|
32
|
+
email="test@example.com",
|
|
33
|
+
is_email_verified=True,
|
|
34
|
+
is_active=True,
|
|
35
|
+
roles=["user", "editor"],
|
|
36
|
+
identities=[identity]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# The model will automatically validate its schema upon instantiation
|
|
40
|
+
print(new_user.model_dump_json())
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 7. Ecosystem Integration
|
|
44
|
+
These models are utilized by the **Validation Pipes** (Layer 5) to validate incoming request bodies containing user data. They are also intrinsically linked to the **Data Component**, where they act as the primary interface between the `AuthService` and the database repository.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Ferrox-Py-Auth Overview
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The `ferrox-py-auth` package provides a unified, enterprise-ready Identity, Access Management (IAM), and Data Privacy (GDPR) solution designed specifically to plug into the core `ferrox-py` framework.
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
In complex distributed systems and modern SaaS architectures, authentication is rarely as simple as a single "Users" SQL table. It requires dynamic extension to support Single Sign-On (Google, Apple, Microsoft), Multi-Factor Authentication, and a decoupled data architecture to scale securely. This package exists to encapsulate all that complexity into a secure, reusable module so developers can focus on business logic rather than writing boilerplate auth code.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This package is built for application architects and backend engineers tasked with building secure platforms that require strict user identity tracking, varied permission levels (RBAC), and legal compliance with international privacy laws.
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
The Auth package seamlessly hooks into Layer 3 (Threat Engine) and Layer 4 (Auth Guards) of the `ferrox-py` Onion Request Pipeline. It provides the practical implementation for parsing tokens, validating user state, and persisting user data.
|
|
14
|
+
Integrated features include:
|
|
15
|
+
- Management of multiple users with SSO identities linked to the same aggregate entity.
|
|
16
|
+
- Granular Role-Based Access Control (RBAC).
|
|
17
|
+
- Ready-to-use GDPR tools (Right to be Forgotten, Data Portability).
|
|
18
|
+
|
|
19
|
+
## 5. Installation / Setup
|
|
20
|
+
Install the package alongside the core framework:
|
|
21
|
+
```bash
|
|
22
|
+
pip install ferrox-py-auth
|
|
23
|
+
```
|
|
24
|
+
Ensure that cryptographic keys used for token generation (JWT/PASETO) are injected securely via environment variables.
|
|
25
|
+
|
|
26
|
+
## 6. Quickstart (Usage)
|
|
27
|
+
To enable IAM across your application, register the module in your root configuration:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from ferrox_py.core.container import Container
|
|
31
|
+
from ferrox_py_auth import AuthModule
|
|
32
|
+
|
|
33
|
+
container = Container()
|
|
34
|
+
container.register_module(AuthModule)
|
|
35
|
+
# Authentication and RBAC layers are now active globally.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 7. Ecosystem Integration
|
|
39
|
+
The Auth module is a fundamental prerequisite for many other ecosystem packages. For instance, `ferrox-py-commerce` requires a guaranteed, authenticated `User` context to bind payment methods and track subscription ownership securely.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# RBAC (Role-Based Access Control)
|
|
2
|
+
|
|
3
|
+
## 1. Overview (What does this do?)
|
|
4
|
+
The RBAC (Role-Based Access Control) module provides a granular mechanism for restricting access to specific API endpoints or Service methods based on the permissions assigned to the currently authenticated user. It exposes the `@require_roles` decorator for declarative access control.
|
|
5
|
+
|
|
6
|
+
## 2. Philosophy (Why does it exist?)
|
|
7
|
+
Hardcoding `if user.role == "admin"` statements inside controller logic leads to scattered, unmaintainable, and highly insecure code. The philosophy of this component is to extract access control entirely out of the business logic and enforce it at the pipeline boundary. If a user does not have the required role, the request is terminated before the domain logic is ever invoked.
|
|
8
|
+
|
|
9
|
+
## 3. Target Audience (Who is it for?)
|
|
10
|
+
This module is for developers building multi-tenant SaaS applications, administrative dashboards, or any system where different users (e.g., standard users, editors, superadmins) require different levels of access to the system resources.
|
|
11
|
+
|
|
12
|
+
## 4. Architecture (How does it work?)
|
|
13
|
+
When an HTTP or gRPC endpoint is called, the authorization token (JWT or PASETO) is validated at Layer 3 of the Onion Pipeline. The token payload contains the user's "claims", which include their list of `roles`. The `@require_roles` decorator operates at Layer 4. It intercepts the call *before* executing the controller, checks the local context for the extracted roles, and compares them against the required roles defined in the decorator.
|
|
14
|
+
|
|
15
|
+
## 5. Installation / Setup
|
|
16
|
+
RBAC functionalities are automatically available when you install `ferrox-py-auth`. No additional database setup is required for basic RBAC, as the roles are inherently expected to be embedded directly within the cryptographic token payload.
|
|
17
|
+
|
|
18
|
+
## 6. Quickstart (Usage)
|
|
19
|
+
```python
|
|
20
|
+
from ferrox_py_auth.security.rbac import require_roles
|
|
21
|
+
|
|
22
|
+
class AdminController:
|
|
23
|
+
|
|
24
|
+
# Restrict this endpoint to users who have at least one of these roles
|
|
25
|
+
@require_roles("superadmin", "editor")
|
|
26
|
+
async def delete_article(self, request, article_id: str):
|
|
27
|
+
# Execution only reaches this point if the token contains a matching role
|
|
28
|
+
return {"status": "Article deleted successfully"}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 7. Ecosystem Integration
|
|
32
|
+
The RBAC decorator is tightly integrated with the core **Web & Transports Component**. By operating entirely on the Request context generated by the **Security Component**'s Auth Guards, it remains agnostic to the underlying web framework (e.g., FastAPI), allowing it to secure both HTTP REST endpoints and WebSocket connections uniformly.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# init
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from fastapi import Request
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from ferrox_py.core.controllers import BaseController
|
|
4
|
+
from ferrox_py.core.container import Container
|
|
5
|
+
from ..services.auth_service import AuthService
|
|
6
|
+
from ..services.gdpr_service import GdprService
|
|
7
|
+
from ..security.rbac import require_roles
|
|
8
|
+
|
|
9
|
+
class LoginPayload(BaseModel):
|
|
10
|
+
email: str
|
|
11
|
+
password_hash: str
|
|
12
|
+
|
|
13
|
+
class SsoPayload(BaseModel):
|
|
14
|
+
provider: str
|
|
15
|
+
provider_id: str
|
|
16
|
+
email: str
|
|
17
|
+
|
|
18
|
+
class AuthController(BaseController):
|
|
19
|
+
def __init__(self, container: Container):
|
|
20
|
+
super().__init__(prefix="/auth", tags=["Auth & IAM"])
|
|
21
|
+
|
|
22
|
+
self.auth = AuthService(container.resolve("JwtService"))
|
|
23
|
+
self.gdpr = GdprService(self.auth)
|
|
24
|
+
|
|
25
|
+
@self.router.get("/config")
|
|
26
|
+
async def get_config():
|
|
27
|
+
return self.ok(self.auth.get_config(), "Auth settings retrieved")
|
|
28
|
+
|
|
29
|
+
@self.router.post("/register")
|
|
30
|
+
async def register(payload: LoginPayload):
|
|
31
|
+
user = await self.auth.register_local(payload.email, payload.password_hash)
|
|
32
|
+
return self.created(user.model_dump(), "User registered. Please check your email.")
|
|
33
|
+
|
|
34
|
+
@self.router.post("/sso")
|
|
35
|
+
async def sso_login(payload: SsoPayload):
|
|
36
|
+
token = await self.auth.login_sso(payload.provider, payload.provider_id, payload.email)
|
|
37
|
+
return self.ok({"token": token}, "SSO Login successful")
|
|
38
|
+
|
|
39
|
+
@self.router.get("/gdpr/export")
|
|
40
|
+
@require_roles("user", "admin")
|
|
41
|
+
async def export_my_data(request: Request):
|
|
42
|
+
# Extract user_id from the authenticated request state
|
|
43
|
+
user_id = request.state.user["sub"]
|
|
44
|
+
data = await self.gdpr.export_data(user_id)
|
|
45
|
+
return self.ok(data, "GDPR Export ready")
|
|
46
|
+
|
|
47
|
+
@self.router.delete("/gdpr/forget")
|
|
48
|
+
@require_roles("user", "admin")
|
|
49
|
+
async def delete_my_account(request: Request):
|
|
50
|
+
user_id = request.state.user["sub"]
|
|
51
|
+
success = await self.gdpr.forget_me(user_id)
|
|
52
|
+
return self.ok({"deleted": success}, "Account permanently deleted")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
class Identity(BaseModel):
|
|
6
|
+
provider: str # e.g., 'local', 'google', 'facebook', 'apple'
|
|
7
|
+
provider_id: str # ID from the external provider or hash of password
|
|
8
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
9
|
+
|
|
10
|
+
class User(BaseModel):
|
|
11
|
+
id: str
|
|
12
|
+
email: str
|
|
13
|
+
email_verified: bool = False
|
|
14
|
+
roles: List[str] = ["user"]
|
|
15
|
+
identities: List[Identity] = []
|
|
16
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from typing import List
|
|
3
|
+
from fastapi import Request
|
|
4
|
+
from ferrox_py.core.errors import FerroxError
|
|
5
|
+
|
|
6
|
+
def require_roles(*roles: str):
|
|
7
|
+
"""
|
|
8
|
+
RBAC Decorator.
|
|
9
|
+
Extracts the user 'roles' claim from the JWT (attached to request.state.user)
|
|
10
|
+
and validates it against the required roles.
|
|
11
|
+
"""
|
|
12
|
+
def decorator(func):
|
|
13
|
+
@wraps(func)
|
|
14
|
+
async def wrapper(request: Request, *args, **kwargs):
|
|
15
|
+
user_data = getattr(request.state, "user", None)
|
|
16
|
+
if not user_data:
|
|
17
|
+
raise FerroxError("Unauthorized - No JWT token found", 401)
|
|
18
|
+
|
|
19
|
+
user_roles = user_data.get("roles", [])
|
|
20
|
+
|
|
21
|
+
if not any(role in user_roles for role in roles):
|
|
22
|
+
raise FerroxError(f"Forbidden - Requires one of roles: {roles}", 403)
|
|
23
|
+
|
|
24
|
+
return await func(request, *args, **kwargs)
|
|
25
|
+
return wrapper
|
|
26
|
+
return decorator
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from ferrox_py.core.provider import injectable
|
|
2
|
+
from ferrox_py.core.errors import FerroxError
|
|
3
|
+
from ferrox_py.security.jwt import JwtService
|
|
4
|
+
from ..models.user import User, Identity
|
|
5
|
+
from typing import Dict, Any
|
|
6
|
+
|
|
7
|
+
@injectable()
|
|
8
|
+
class AuthService:
|
|
9
|
+
def __init__(self, jwt_service: JwtService):
|
|
10
|
+
self.jwt = jwt_service
|
|
11
|
+
self.enforce_sso_only = False # Feature flag configurable by the application
|
|
12
|
+
|
|
13
|
+
# Mock DB
|
|
14
|
+
self._users_db: Dict[str, User] = {}
|
|
15
|
+
|
|
16
|
+
def get_config(self) -> dict:
|
|
17
|
+
"""Returns auth settings for the frontend (e.g. to hide password fields)."""
|
|
18
|
+
return {"enforce_sso_only": self.enforce_sso_only}
|
|
19
|
+
|
|
20
|
+
async def register_local(self, email: str, password_hash: str) -> User:
|
|
21
|
+
if self.enforce_sso_only:
|
|
22
|
+
raise FerroxError("Local registration is disabled by SSO policy", 403)
|
|
23
|
+
|
|
24
|
+
if email in self._users_db:
|
|
25
|
+
raise FerroxError("Email already in use", 400)
|
|
26
|
+
|
|
27
|
+
user = User(
|
|
28
|
+
id=email,
|
|
29
|
+
email=email,
|
|
30
|
+
identities=[Identity(provider="local", provider_id=password_hash)]
|
|
31
|
+
)
|
|
32
|
+
self._users_db[email] = user
|
|
33
|
+
|
|
34
|
+
# Here we would trigger the MailerService for email_verified=True
|
|
35
|
+
print(f"AuthService: Emitting Email Confirmation for {email}")
|
|
36
|
+
return user
|
|
37
|
+
|
|
38
|
+
async def login_sso(self, provider: str, provider_id: str, email: str) -> str:
|
|
39
|
+
"""Handles SSO login or account linkage if user already exists."""
|
|
40
|
+
user = self._users_db.get(email)
|
|
41
|
+
|
|
42
|
+
if not user:
|
|
43
|
+
# Create new user, auto-verify email since it comes from trusted SSO
|
|
44
|
+
user = User(id=email, email=email, email_verified=True, identities=[])
|
|
45
|
+
self._users_db[email] = user
|
|
46
|
+
|
|
47
|
+
# Link identity if not present
|
|
48
|
+
if not any(i.provider == provider for i in user.identities):
|
|
49
|
+
user.identities.append(Identity(provider=provider, provider_id=provider_id))
|
|
50
|
+
|
|
51
|
+
# Issue JWT
|
|
52
|
+
return self.jwt.sign({"sub": user.id, "roles": user.roles})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from ferrox_py.core.provider import injectable
|
|
2
|
+
from ferrox_py.core.errors import FerroxError
|
|
3
|
+
from .auth_service import AuthService
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
@injectable()
|
|
7
|
+
class GdprService:
|
|
8
|
+
def __init__(self, auth_service: AuthService):
|
|
9
|
+
self.auth = auth_service
|
|
10
|
+
|
|
11
|
+
async def export_data(self, user_id: str) -> Dict:
|
|
12
|
+
"""Returns all PII (Personally Identifiable Information) for GDPR export."""
|
|
13
|
+
user = self.auth._users_db.get(user_id)
|
|
14
|
+
if not user:
|
|
15
|
+
raise FerroxError("User not found", 404)
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
"account": user.model_dump(),
|
|
19
|
+
"consent_logs": [],
|
|
20
|
+
"activity_logs": []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async def forget_me(self, user_id: str) -> bool:
|
|
24
|
+
"""Right to be forgotten: Hard deletes the user and all associated identities."""
|
|
25
|
+
if user_id in self.auth._users_db:
|
|
26
|
+
del self.auth._users_db[user_id]
|
|
27
|
+
# In a real scenario, this would emit an event to soft-delete or anonymize
|
|
28
|
+
# related records across the entire microservice ecosystem.
|
|
29
|
+
print(f"GdprService: User {user_id} completely deleted from system.")
|
|
30
|
+
return True
|
|
31
|
+
return False
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ferrox-py-auth"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "IAM, SSO, GDPR and RBAC suite for Ferrox ecosystem."
|
|
9
|
+
authors = [{ name = "AI-Autistic-Intelligence" }]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"ferrox-py>=1.0.0",
|
|
14
|
+
"pydantic>=2.0"
|
|
15
|
+
]
|