authtuna 0.1.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.
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: authtuna
3
+ Version: 0.1.0
4
+ Summary: A high-performance, framework-agnostic authorization and session management library for Python
5
+ Home-page: https://github.com/shashstormer/authtuna
6
+ Author: shashstormer
7
+ Author-email: shashanka5398@gmail.com
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: pydantic-settings>=2.0.0
23
+ Requires-Dist: sqlalchemy>=2.0.0
24
+ Requires-Dist: cryptography>=41.0.0
25
+ Requires-Dist: pyotp>=2.8.0
26
+ Requires-Dist: PyJWT>=2.8.0
27
+ Requires-Dist: aiosmtplib>=2.0.0
28
+ Requires-Dist: dkimpy>=1.1.0
29
+ Requires-Dist: slowapi>=0.1.0
30
+ Provides-Extra: fastapi
31
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
32
+ Requires-Dist: starlette>=0.27.0; extra == "fastapi"
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
35
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
36
+ Requires-Dist: black>=23.0.0; extra == "dev"
37
+ Requires-Dist: isort>=5.12.0; extra == "dev"
38
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: description-content-type
45
+ Dynamic: home-page
46
+ Dynamic: provides-extra
47
+ Dynamic: requires-dist
48
+ Dynamic: requires-python
49
+ Dynamic: summary
50
+
51
+ # AuthTuna 🐟
52
+
53
+ A high-performance, framework-agnostic authorization and session management library for Python.
54
+
55
+ AuthTuna provides a robust, multi-layered security foundation for modern web applications. It is designed for developers who need to build complex, multi-tenant systems without compromising on security or performance. The library combines a powerful hierarchical permission model with an advanced, stateful session management system to actively defend against a wide range of modern threats.
56
+
57
+ This project is built on the philosophy that robust security should be accessible, not an afterthought. It provides the tools to manage complex authorization logic in a way that is both intuitive and highly secure.
58
+
59
+ ## Core Concepts
60
+
61
+ AuthTuna is built on a few key architectural principles:
62
+
63
+ - **Hierarchical RBAC (Role-Based Access Control)**: Permissions are not global. They are structured in a logical, multi-level hierarchy perfect for SaaS and collaborative platforms: Organization ➡️ Project ➡️ Team ➡️ Resource. A user's abilities can change depending on their context.
64
+
65
+ - **Object-Level Security**: Go beyond simple roles with fine-grained permissions based on a resource's specific attributes, such as ownership. This allows for intuitive rules like "a user can always edit their own posts."
66
+
67
+ - **Advanced Session Management**: A high-security, dual-state session model that actively detects and prevents session hijacking. It uses a server-side session store as the source of truth, providing full control over session validity.
68
+
69
+ - **Framework-Agnostic Design**: A clean core engine written in pure Python, with dedicated adapters for seamless integration with modern web frameworks like FastAPI.
70
+
71
+ ## Features
72
+
73
+ - **Multi-Level Hierarchical Permissions**: Manage complex user roles across different organizational contexts with a clear and scalable data model.
74
+
75
+ - **Object-Level Ownership Rules**: Easily define and check for permissions based on a user's relationship to a specific resource.
76
+
77
+ - **Dual-State Session Management**: Combines a secure, server-side session store with a rotating JWT cookie for maximum security and control.
78
+
79
+ - **Rotating Tokens**: The core of our session security. Each token is single-use, mitigating replay attacks and providing immediate, real-time breach detection.
80
+
81
+ - **Session Hijack Detection**: Actively monitors sessions by "fingerprinting" the user's IP address and User-Agent on every request and comparing it against the secure server-side record.
82
+
83
+ - **Built-in CSRF Protection**: The SameSite=Lax cookie strategy is the default, protecting all state-changing endpoints from cross-site request forgery.
84
+
85
+ - **SQL-First Design**: Optimized for performance and data integrity with relational databases like PostgreSQL. The data access patterns are designed to leverage the power of SQL for complex, relational queries.
86
+
87
+ ## Installation
88
+
89
+ The library is designed to be modular. You can install the core engine by itself or with framework-specific extras.
90
+
91
+ ```bash
92
+ # Install the core library
93
+ pip install authtuna
94
+
95
+ # Install with FastAPI integration and its dependencies
96
+ pip install authtuna[fastapi]
97
+ ```
98
+
99
+ ## Quick Start
100
+
101
+ Here's a brief example of how to protect a FastAPI route using AuthTuna's core components.
102
+
103
+ ```python
104
+ from fastapi import FastAPI, Depends, HTTPException
105
+ from authtuna.core.authorizer import Authorizer
106
+ from authtuna.integrations.fastapi import get_authorizer, get_current_user
107
+ from authtuna.data.models import User, Post # Your application's models
108
+
109
+ # Assume 'authorizer' is initialized with your data provider in your app's setup
110
+ app = FastAPI()
111
+
112
+ # A protected route to edit a blog post
113
+ @app.put("/posts/{post_id}")
114
+ async def edit_post(
115
+ post_id: int,
116
+ user: User = Depends(get_current_user),
117
+ authorizer: Authorizer = Depends(get_authorizer)
118
+ ):
119
+ """
120
+ This endpoint allows a user to edit a post, but only if they have
121
+ the required permissions as determined by AuthTuna.
122
+ """
123
+ # 1. Fetch the resource from the database
124
+ # This is the object we want to check permissions against.
125
+ post_to_edit = await get_post_by_id(post_id)
126
+ if not post_to_edit:
127
+ raise HTTPException(status_code=404, detail="Post not found")
128
+
129
+ # 2. Check permission using the Authorizer
130
+ # This single call executes the entire multi-stage check:
131
+ # - Is the user the owner of the post?
132
+ # - Does their role in the post's team, project, or org grant permission?
133
+ if not authorizer.can(user, "post:edit", post_to_edit):
134
+ # If the check fails, deny access.
135
+ raise HTTPException(
136
+ status_code=403,
137
+ detail="You do not have permission to perform this action."
138
+ )
139
+
140
+ # 3. If the check passes, proceed with the business logic
141
+ # ... update the post in the database
142
+
143
+ return {"status": "success", "message": "Post updated successfully."}
144
+ ```
145
+
146
+ ## Architectural Diagram
147
+
148
+ This diagram shows the complete data structure for the authorization and session management system, designed for optimal performance and clarity.
149
+
150
+ ```mermaid
151
+ erDiagram
152
+ Users {
153
+ int user_id PK
154
+ string name
155
+ }
156
+ Organizations {
157
+ int org_id PK
158
+ string name
159
+ }
160
+ Projects {
161
+ int project_id PK
162
+ int org_id FK
163
+ }
164
+ Teams {
165
+ int team_id PK
166
+ int project_id FK
167
+ }
168
+ Resources {
169
+ int resource_id PK
170
+ string name
171
+ int team_id FK
172
+ int owner_id FK
173
+ }
174
+ Roles {
175
+ int role_id PK
176
+ string name
177
+ enum scope
178
+ }
179
+ Permissions {
180
+ int permission_id PK
181
+ string codename
182
+ }
183
+ Tools {
184
+ int tool_id PK
185
+ string name
186
+ }
187
+ UserSession {
188
+ string session_id PK
189
+ int user_id FK
190
+ string rotating_token
191
+ string ip_address
192
+ string user_agent
193
+ }
194
+
195
+ Tools ||--|{ Permissions : "defines"
196
+ Roles }o--o{ RolePermissions : "grants"
197
+ Permissions }o--o{ RolePermissions : "is granted by"
198
+
199
+ Users ||--o{ OrganizationMemberships : "member of"
200
+ Organizations ||--o{ OrganizationMemberships : "has member"
201
+ Roles ||--o{ OrganizationMemberships : "with role"
202
+
203
+ Users ||--o{ ProjectMemberships : "member of"
204
+ Projects ||--o{ ProjectMemberships : "has member"
205
+ Roles ||--o{ ProjectMemberships : "with role"
206
+
207
+ Users ||--o{ TeamMemberships : "is member of"
208
+ Teams ||--o{ TeamMemberships : "has member"
209
+ Roles ||--o{ TeamMemberships : "with role"
210
+
211
+ Organizations ||--|{ Projects : "contains"
212
+ Projects ||--|{ Teams : "contains"
213
+ Teams ||--|{ Resources : "contains"
214
+ Users ||--|{ Resources : "owns"
215
+ Users ||--o{ UserSession : "has"
216
+ ```
217
+
218
+ ## Security Overview 🛡️
219
+
220
+ AuthTuna is designed with a defense-in-depth strategy. Security is not a single feature but a result of the entire architecture working together.
221
+
222
+ | Attack Vector | Defense Strategy & Implementation |
223
+ |---------------|-----------------------------------|
224
+ | **Session Hijacking** | IP/User-Agent fingerprinting is performed on every request, comparing against the secure server-side record. The rotating token provides immediate, real-time breach detection if a stolen cookie is used. |
225
+ | **Replay Attacks** | The core session model is built on one-time-use rotating tokens. An intercepted request is useless because the token becomes invalid after its first use. |
226
+ | **CSRF** | The primary defense is the SameSite=Lax cookie attribute, which prevents browsers from sending the session cookie on unauthorized cross-site POST, PUT, etc., requests. |
227
+ | **XSS** | The core session_id is stored in an HttpOnly cookie, preventing direct access from JavaScript. This is combined with a strong Content Security Policy (CSP) and proper output encoding in the application. |
228
+ | **Timing Attacks** | All comparisons of secret values (like the rotating_token) are performed using a constant-time comparison function (e.g., Python's hmac.compare_digest). |
229
+ | **Credential Stuffing** | While outside the library's direct scope, we strongly recommend rate limiting, CAPTCHA, and Multi-Factor Authentication (MFA) on the application's login form. |
230
+ | **Insecure Direct Object Ref.** | The core authorizer.can(user, permission, resource) check is the fundamental defense against IDOR, ensuring a user is authorized for the specific resource they are requesting. |
231
+ | **Session Fixation** | A new, cryptographically secure session_id is generated in the database after every successful login, ensuring a user cannot be forced into a pre-existing session. |
232
+
233
+ ## Contributing
234
+
235
+ We welcome contributions of all kinds! Whether it's reporting a bug, proposing a new feature, or submitting a pull request, your help is valued. Please feel free to open an issue to start a discussion.
236
+
237
+ ## License
238
+
239
+ This project is licensed under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,22 @@
1
+ """
2
+ AuthTuna - A high-performance, framework-agnostic authorization and session management library for Python.
3
+ """
4
+
5
+ __version__ = "0.0.1"
6
+ __author__ = "shashstormer"
7
+ __description__ = "A robust, multi-layered security foundation for modern web applications"
8
+
9
+ from .core.database import DatabaseManager, db_manager, User
10
+ from .core.config import settings
11
+ from .middlewares.session import DatabaseSessionMiddleware
12
+ from .routers import auth_router, social_router
13
+
14
+ __all__ = [
15
+ "DatabaseManager",
16
+ "settings",
17
+ "db_manager",
18
+ "User",
19
+ "DatabaseSessionMiddleware",
20
+ "auth_router",
21
+ "social_router",
22
+ ]
@@ -0,0 +1,98 @@
1
+ import logging
2
+ from pydantic_settings import BaseSettings, SettingsConfigDict
3
+ from pydantic import SecretStr
4
+ from typing import List, Optional
5
+
6
+ logger = logging.getLogger(__name__)
7
+ import os
8
+ import dotenv
9
+
10
+ dotenv.load_dotenv()
11
+ module_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
12
+
13
+
14
+ class Settings(BaseSettings):
15
+ """
16
+ Manages all application configuration using Pydantic.
17
+ Loads settings from environment variables for security and flexibility.
18
+ """
19
+ # Application settings
20
+ APP_NAME: str = "AuthTuna"
21
+ ALGORITHM: str = "HS256" # JWT Encryption algorithm
22
+ API_BASE_URL: str
23
+ # Security settings
24
+ JWT_SECRET_KEY: SecretStr = SecretStr("dev-secret-key-change-in-production")
25
+ ENCRYPTION_PRIMARY_KEY: SecretStr = SecretStr("dev-encryption-key-change-in-production")
26
+ ENCRYPTION_SECONDARY_KEYS: List[SecretStr] = []
27
+ FERNET_KEYS: List[SecretStr] = []
28
+
29
+ # Database settings
30
+ DEFAULT_DATABASE_URI: str = "sqlite:///./authtuna_dev.db"
31
+ DATABASE_POOL_SIZE: int = 20
32
+ DATABASE_MAX_OVERFLOW: int = 40
33
+ DATABASE_POOL_TIMEOUT: int = 30
34
+ DATABASE_POOL_RECYCLE: int = 1800
35
+ DATABASE_POOL_PRE_PING: bool = True
36
+
37
+ # Session settings
38
+ FINGERPRINT_HEADERS: List[str] = ["User-Agent", "Accept-Language"]
39
+ SESSION_DB_VERIFICATION_INTERVAL: int = 10 # Time before rechecking if the token is still active in db
40
+ SESSION_LIFETIME_SECONDS: int = 604800
41
+ SESSION_ABSOLUTE_LIFETIME_SECONDS: int = 31536000
42
+ SESSION_LIFETIME_FROM: str = "last_activity" # "last_activity" or "creation"
43
+ SESSION_SAME_SITE: str = "LAX"
44
+ SESSION_SECURE: bool = True # obvio its gon be httponly coz it auth bruh
45
+ SESSION_TOKEN_NAME: str = "session_token"
46
+
47
+ # Email settings (disabled by default)
48
+ EMAIL_ENABLED: bool = False
49
+ SMTP_HOST: Optional[str] = None
50
+ SMTP_PORT: int = 587
51
+ SMTP_USERNAME: Optional[str] = None
52
+ SMTP_PASSWORD: Optional[SecretStr] = None
53
+ DKIM_PRIVATE_KEY_PATH: Optional[str] = None
54
+ DKIM_DOMAIN: Optional[str] = None
55
+ DKIM_SELECTOR: Optional[str] = None
56
+ DEFAULT_SENDER_EMAIL: str = "noreply@example.com"
57
+ EMAIL_DOMAINS: List[str] = ["gmail.com"]
58
+ TOKENS_EXPIRY_SECONDS: int = 3600
59
+ TOKENS_MAX_COUNT_PER_DAY_PER_USER_PER_ACTION: int = 5 # max 5 for email verification, max 5 password reset tokens etc etc...
60
+
61
+ MAIL_STARTTLS: bool = True
62
+ MAIL_SSL_TLS: bool = False
63
+ USE_CREDENTIALS: bool = True
64
+ VALIDATE_CERTS: bool = True
65
+
66
+ EMAIL_TEMPLATE_DIR: str = os.path.join(module_path, "templates/email")
67
+ HTML_TEMPLATE_DIR: str = os.path.join(module_path, "templates/pages")
68
+ # OAuth settings
69
+ GOOGLE_CLIENT_ID: Optional[str] = None
70
+ GOOGLE_CLIENT_SECRET: Optional[SecretStr] = None
71
+ GOOGLE_REDIRECT_URI: Optional[str] = None
72
+
73
+ GITHUB_CLIENT_ID: Optional[str] = None
74
+ GITHUB_CLIENT_SECRET: Optional[SecretStr] = None
75
+ GITHUB_REDIRECT_URI: Optional[str] = None
76
+
77
+ model_config = SettingsConfigDict(env_file=os.getenv("ENV_FILE_NAME", ".env"), env_file_encoding='utf-8',
78
+ extra='ignore')
79
+
80
+ def __init__(self, *args, **kwargs):
81
+ super().__init__(*args, **kwargs)
82
+ if self.EMAIL_ENABLED:
83
+ assert self.SMTP_HOST, "SMTP_HOST must be set if email is enabled"
84
+ assert self.SMTP_PORT, "SMTP_PORT must be set if email is enabled"
85
+ assert self.DEFAULT_SENDER_EMAIL != "noreply@example.com", "DEFAULT_SENDER_EMAIL must be set if email is enabled"
86
+ if self.DKIM_PRIVATE_KEY_PATH:
87
+ assert self.DKIM_DOMAIN, "DKIM_DOMAIN must be set if DKIM private key path is set"
88
+ assert self.DKIM_SELECTOR, "DKIM_SELECTOR must be set if DKIM private key path is set"
89
+ if self.DEFAULT_DATABASE_URI == "sqlite:///./authtuna_dev.db":
90
+ logger.warning("DEFAULT_DATABASE_URI is set to default value. Change it in production.")
91
+ if self.JWT_SECRET_KEY.get_secret_value() == "dev-secret-key-change-in-production":
92
+ logger.warning("JWT_SECRET_KEY is set to default value. Change it in production.")
93
+ if self.ENCRYPTION_PRIMARY_KEY.get_secret_value() == "dev-encryption-key-change-in-production":
94
+ logger.warning("ENCRYPTION_PRIMARY_KEY is set to default value. Change it in production.")
95
+
96
+
97
+ # Instantiate settings to be imported by other modules
98
+ settings = Settings()