abs-auth-rbac-core 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.
Potentially problematic release.
This version of abs-auth-rbac-core might be problematic. Click here for more details.
- abs_auth_rbac_core-0.1.0/PKG-INFO +232 -0
- abs_auth_rbac_core-0.1.0/README.md +211 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/__init__.py +0 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/auth/__init__.py +3 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/auth/auth_functions.py +31 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/auth/jwt_functions.py +134 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/auth/middleware.py +50 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/__init__.py +7 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/base_model.py +20 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/gov_casbin_rule.py +25 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/permissions.py +26 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/rbac_model.py +10 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/role_permission.py +12 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/roles.py +21 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/seeder/permission_seeder.py +101 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/user.py +27 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/models/user_role.py +20 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/rbac/__init__.py +2 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/rbac/database.py +52 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/rbac/decorator.py +48 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/rbac/policy.conf +14 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/rbac/service.py +688 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/util/__init__.py +0 -0
- abs_auth_rbac_core-0.1.0/abs_auth_rbac_core/util/permission_constants.py +1624 -0
- abs_auth_rbac_core-0.1.0/pyproject.toml +28 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: abs-auth-rbac-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: RBAC and Auth core utilities including JWT token management.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: AutoBridgeSystems
|
|
7
|
+
Author-email: info@autobridgesystems.com
|
|
8
|
+
Requires-Python: >=3.13,<4.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Requires-Dist: abs-exception-core (>=0.1.0,<0.2.0)
|
|
13
|
+
Requires-Dist: casbin (>=1.41.0,<2.0.0)
|
|
14
|
+
Requires-Dist: casbin-sqlalchemy-adapter (>=1.4.0,<2.0.0)
|
|
15
|
+
Requires-Dist: fastapi[standard] (>=0.115.12,<0.116.0)
|
|
16
|
+
Requires-Dist: passlib (>=1.7.4,<2.0.0)
|
|
17
|
+
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
|
18
|
+
Requires-Dist: sqlalchemy (>=2.0.40,<3.0.0)
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# ABS Auth RBAC Core
|
|
22
|
+
|
|
23
|
+
A comprehensive authentication and Role-Based Access Control (RBAC) package for FastAPI applications. This package provides robust JWT-based authentication and flexible role-based permission management using Casbin.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- JWT-based authentication with customizable token expiration
|
|
28
|
+
- Password hashing using bcrypt
|
|
29
|
+
- Role-Based Access Control (RBAC) with Casbin integration
|
|
30
|
+
- Flexible permission management
|
|
31
|
+
- User-role and role-permission associations
|
|
32
|
+
- Middleware for authentication and authorization
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install abs-auth-rbac-core
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### 1. Authentication Setup
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from abs_auth_rbac_core.auth.jwt_functions import JWTFunctions
|
|
46
|
+
import os
|
|
47
|
+
|
|
48
|
+
# Initialize JWT functions with environment variables
|
|
49
|
+
jwt_functions = JWTFunctions(
|
|
50
|
+
secret_key=os.getenv("JWT_SECRET_KEY"),
|
|
51
|
+
algorithm=os.getenv("JWT_ALGORITHM", "HS256"),
|
|
52
|
+
expire_minutes=int(os.getenv("JWT_EXPIRE_MINUTES", "60"))
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Create access token
|
|
56
|
+
token = jwt_functions.create_access_token(data={"sub": "user_id"})
|
|
57
|
+
|
|
58
|
+
# Verify password
|
|
59
|
+
is_valid = jwt_functions.verify_password(plain_password, hashed_password)
|
|
60
|
+
|
|
61
|
+
# Get password hash
|
|
62
|
+
hashed_password = jwt_functions.get_password_hash(plain_password)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. RBAC Setup
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from abs_auth_rbac_core.rbac.service import RBACService
|
|
69
|
+
|
|
70
|
+
# Initialize RBAC service
|
|
71
|
+
rbac_service = RBACService(
|
|
72
|
+
session=your_db_session
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Create a role with permissions
|
|
76
|
+
role = rbac_service.create_role(
|
|
77
|
+
name="admin",
|
|
78
|
+
description="Administrator role",
|
|
79
|
+
permission_ids=["permission_uuid1", "permission_uuid2"]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Assign roles to user
|
|
83
|
+
rbac_service.bulk_assign_roles_to_user(
|
|
84
|
+
user_uuid="user_uuid",
|
|
85
|
+
role_uuids=["role_uuid1", "role_uuid2"]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Check permission
|
|
89
|
+
has_permission = rbac_service.check_permission(
|
|
90
|
+
user_uuid="user_uuid",
|
|
91
|
+
resource="resource_name",
|
|
92
|
+
action="action_name",
|
|
93
|
+
module="module_name"
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Components
|
|
98
|
+
|
|
99
|
+
### Authentication (`auth/`)
|
|
100
|
+
- `jwt_functions.py`: JWT token management and password hashing
|
|
101
|
+
- `middleware.py`: Authentication middleware for FastAPI
|
|
102
|
+
- `auth_functions.py`: Core authentication functions
|
|
103
|
+
|
|
104
|
+
### RBAC (`rbac/`)
|
|
105
|
+
- `service.py`: Main RBAC service with role and permission management
|
|
106
|
+
- `decorator.py`: Decorators for permission checking
|
|
107
|
+
|
|
108
|
+
### Models (`models/`)
|
|
109
|
+
- `user.py`: User model
|
|
110
|
+
- `roles.py`: Role model
|
|
111
|
+
- `permissions.py`: Permission model
|
|
112
|
+
- `user_role.py`: User-Role association model
|
|
113
|
+
- `role_permission.py`: Role-Permission association model
|
|
114
|
+
- `rbac_model.py`: Base RBAC model
|
|
115
|
+
- `base_model.py`: Base model with common fields
|
|
116
|
+
|
|
117
|
+
## Usage Examples
|
|
118
|
+
|
|
119
|
+
### 1. Setting Up Authentication Middleware
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from fastapi import FastAPI, Depends
|
|
123
|
+
from dependency_injector import containers, providers
|
|
124
|
+
from abs_auth_rbac_core.auth.middleware import auth_middleware
|
|
125
|
+
from abs_auth_rbac_core.rbac import RBACService
|
|
126
|
+
|
|
127
|
+
# Create a container for dependency injection
|
|
128
|
+
class Container(containers.DeclarativeContainer):
|
|
129
|
+
# Database session provider
|
|
130
|
+
db_session = providers.Factory(your_db_session_factory)
|
|
131
|
+
|
|
132
|
+
# RBAC service provider
|
|
133
|
+
rbac_service = providers.Factory(
|
|
134
|
+
RBACService,
|
|
135
|
+
session=db_session
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Auth middleware provider
|
|
139
|
+
get_auth_middleware = providers.Factory(
|
|
140
|
+
auth_middleware,
|
|
141
|
+
db_session=db_session,
|
|
142
|
+
jwt_secret_key=os.getenv("JWT_SECRET_KEY"),
|
|
143
|
+
jwt_algorithm=os.getenv("JWT_ALGORITHM", "HS256")
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Initialize FastAPI app
|
|
147
|
+
app = FastAPI()
|
|
148
|
+
container = Container()
|
|
149
|
+
app.container = container
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2. Applying Middleware to Routers
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from fastapi import FastAPI, Depends
|
|
156
|
+
from src.core.container import Container
|
|
157
|
+
|
|
158
|
+
class CreateApp:
|
|
159
|
+
def __init__(self):
|
|
160
|
+
self.container = Container()
|
|
161
|
+
self.auth_middleware = self.container.get_auth_middleware()
|
|
162
|
+
|
|
163
|
+
self.app = FastAPI(
|
|
164
|
+
title="Your Service",
|
|
165
|
+
description="Service Description",
|
|
166
|
+
version="0.0.1"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Apply middleware to specific routers
|
|
170
|
+
self.app.include_router(
|
|
171
|
+
users_router,
|
|
172
|
+
dependencies=[Depends(self.auth_middleware)],
|
|
173
|
+
tags=["Users"]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Public routes (no middleware)
|
|
177
|
+
self.app.include_router(
|
|
178
|
+
public_router,
|
|
179
|
+
tags=["Public"]
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 3. Permission Management
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from abs_auth_rbac_core.util.permission_constants import (
|
|
187
|
+
PermissionAction,
|
|
188
|
+
PermissionModule,
|
|
189
|
+
PermissionResource
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# permissions
|
|
193
|
+
permission = PermissionData(
|
|
194
|
+
name="User Management",
|
|
195
|
+
description="Manage user accounts",
|
|
196
|
+
module=PermissionModule.USER_MANAGEMENT,
|
|
197
|
+
resource=PermissionResource.USER_MANAGEMENT,
|
|
198
|
+
action=PermissionAction.MANAGE
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Check permissions in route
|
|
202
|
+
@app.get("/users")
|
|
203
|
+
@rbac_require_permission(
|
|
204
|
+
f"{PermissionModule.USER_MANAGEMENT.value}:{PermissionResource.USER_MANAGEMENT.value}:{PermissionAction.VIEW.value}"
|
|
205
|
+
)
|
|
206
|
+
async def list_users():
|
|
207
|
+
return {"users": [...]}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
211
|
+
|
|
212
|
+
The package includes comprehensive error handling for common scenarios:
|
|
213
|
+
- `UnauthorizedError`: For invalid or expired tokens
|
|
214
|
+
- `ValidationError`: For invalid token formats
|
|
215
|
+
- `DuplicatedError`: For duplicate role names
|
|
216
|
+
- `NotFoundError`: For non-existent resources
|
|
217
|
+
- `PermissionDeniedError`: For insufficient permissions
|
|
218
|
+
|
|
219
|
+
## Best Practices
|
|
220
|
+
|
|
221
|
+
1. Always use environment variables for sensitive data (secret keys, etc.)
|
|
222
|
+
2. Implement proper error handling for authentication and authorization failures
|
|
223
|
+
3. Use the middleware for global authentication
|
|
224
|
+
4. Implement proper logging for security-related events
|
|
225
|
+
5. Regularly rotate secret keys and tokens
|
|
226
|
+
6. Use strong password policies
|
|
227
|
+
7. Implement rate limiting for authentication endpoints
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
232
|
+
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# ABS Auth RBAC Core
|
|
2
|
+
|
|
3
|
+
A comprehensive authentication and Role-Based Access Control (RBAC) package for FastAPI applications. This package provides robust JWT-based authentication and flexible role-based permission management using Casbin.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- JWT-based authentication with customizable token expiration
|
|
8
|
+
- Password hashing using bcrypt
|
|
9
|
+
- Role-Based Access Control (RBAC) with Casbin integration
|
|
10
|
+
- Flexible permission management
|
|
11
|
+
- User-role and role-permission associations
|
|
12
|
+
- Middleware for authentication and authorization
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install abs-auth-rbac-core
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Authentication Setup
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from abs_auth_rbac_core.auth.jwt_functions import JWTFunctions
|
|
26
|
+
import os
|
|
27
|
+
|
|
28
|
+
# Initialize JWT functions with environment variables
|
|
29
|
+
jwt_functions = JWTFunctions(
|
|
30
|
+
secret_key=os.getenv("JWT_SECRET_KEY"),
|
|
31
|
+
algorithm=os.getenv("JWT_ALGORITHM", "HS256"),
|
|
32
|
+
expire_minutes=int(os.getenv("JWT_EXPIRE_MINUTES", "60"))
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Create access token
|
|
36
|
+
token = jwt_functions.create_access_token(data={"sub": "user_id"})
|
|
37
|
+
|
|
38
|
+
# Verify password
|
|
39
|
+
is_valid = jwt_functions.verify_password(plain_password, hashed_password)
|
|
40
|
+
|
|
41
|
+
# Get password hash
|
|
42
|
+
hashed_password = jwt_functions.get_password_hash(plain_password)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. RBAC Setup
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from abs_auth_rbac_core.rbac.service import RBACService
|
|
49
|
+
|
|
50
|
+
# Initialize RBAC service
|
|
51
|
+
rbac_service = RBACService(
|
|
52
|
+
session=your_db_session
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Create a role with permissions
|
|
56
|
+
role = rbac_service.create_role(
|
|
57
|
+
name="admin",
|
|
58
|
+
description="Administrator role",
|
|
59
|
+
permission_ids=["permission_uuid1", "permission_uuid2"]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Assign roles to user
|
|
63
|
+
rbac_service.bulk_assign_roles_to_user(
|
|
64
|
+
user_uuid="user_uuid",
|
|
65
|
+
role_uuids=["role_uuid1", "role_uuid2"]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Check permission
|
|
69
|
+
has_permission = rbac_service.check_permission(
|
|
70
|
+
user_uuid="user_uuid",
|
|
71
|
+
resource="resource_name",
|
|
72
|
+
action="action_name",
|
|
73
|
+
module="module_name"
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core Components
|
|
78
|
+
|
|
79
|
+
### Authentication (`auth/`)
|
|
80
|
+
- `jwt_functions.py`: JWT token management and password hashing
|
|
81
|
+
- `middleware.py`: Authentication middleware for FastAPI
|
|
82
|
+
- `auth_functions.py`: Core authentication functions
|
|
83
|
+
|
|
84
|
+
### RBAC (`rbac/`)
|
|
85
|
+
- `service.py`: Main RBAC service with role and permission management
|
|
86
|
+
- `decorator.py`: Decorators for permission checking
|
|
87
|
+
|
|
88
|
+
### Models (`models/`)
|
|
89
|
+
- `user.py`: User model
|
|
90
|
+
- `roles.py`: Role model
|
|
91
|
+
- `permissions.py`: Permission model
|
|
92
|
+
- `user_role.py`: User-Role association model
|
|
93
|
+
- `role_permission.py`: Role-Permission association model
|
|
94
|
+
- `rbac_model.py`: Base RBAC model
|
|
95
|
+
- `base_model.py`: Base model with common fields
|
|
96
|
+
|
|
97
|
+
## Usage Examples
|
|
98
|
+
|
|
99
|
+
### 1. Setting Up Authentication Middleware
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from fastapi import FastAPI, Depends
|
|
103
|
+
from dependency_injector import containers, providers
|
|
104
|
+
from abs_auth_rbac_core.auth.middleware import auth_middleware
|
|
105
|
+
from abs_auth_rbac_core.rbac import RBACService
|
|
106
|
+
|
|
107
|
+
# Create a container for dependency injection
|
|
108
|
+
class Container(containers.DeclarativeContainer):
|
|
109
|
+
# Database session provider
|
|
110
|
+
db_session = providers.Factory(your_db_session_factory)
|
|
111
|
+
|
|
112
|
+
# RBAC service provider
|
|
113
|
+
rbac_service = providers.Factory(
|
|
114
|
+
RBACService,
|
|
115
|
+
session=db_session
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Auth middleware provider
|
|
119
|
+
get_auth_middleware = providers.Factory(
|
|
120
|
+
auth_middleware,
|
|
121
|
+
db_session=db_session,
|
|
122
|
+
jwt_secret_key=os.getenv("JWT_SECRET_KEY"),
|
|
123
|
+
jwt_algorithm=os.getenv("JWT_ALGORITHM", "HS256")
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Initialize FastAPI app
|
|
127
|
+
app = FastAPI()
|
|
128
|
+
container = Container()
|
|
129
|
+
app.container = container
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Applying Middleware to Routers
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from fastapi import FastAPI, Depends
|
|
136
|
+
from src.core.container import Container
|
|
137
|
+
|
|
138
|
+
class CreateApp:
|
|
139
|
+
def __init__(self):
|
|
140
|
+
self.container = Container()
|
|
141
|
+
self.auth_middleware = self.container.get_auth_middleware()
|
|
142
|
+
|
|
143
|
+
self.app = FastAPI(
|
|
144
|
+
title="Your Service",
|
|
145
|
+
description="Service Description",
|
|
146
|
+
version="0.0.1"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Apply middleware to specific routers
|
|
150
|
+
self.app.include_router(
|
|
151
|
+
users_router,
|
|
152
|
+
dependencies=[Depends(self.auth_middleware)],
|
|
153
|
+
tags=["Users"]
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Public routes (no middleware)
|
|
157
|
+
self.app.include_router(
|
|
158
|
+
public_router,
|
|
159
|
+
tags=["Public"]
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 3. Permission Management
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from abs_auth_rbac_core.util.permission_constants import (
|
|
167
|
+
PermissionAction,
|
|
168
|
+
PermissionModule,
|
|
169
|
+
PermissionResource
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# permissions
|
|
173
|
+
permission = PermissionData(
|
|
174
|
+
name="User Management",
|
|
175
|
+
description="Manage user accounts",
|
|
176
|
+
module=PermissionModule.USER_MANAGEMENT,
|
|
177
|
+
resource=PermissionResource.USER_MANAGEMENT,
|
|
178
|
+
action=PermissionAction.MANAGE
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Check permissions in route
|
|
182
|
+
@app.get("/users")
|
|
183
|
+
@rbac_require_permission(
|
|
184
|
+
f"{PermissionModule.USER_MANAGEMENT.value}:{PermissionResource.USER_MANAGEMENT.value}:{PermissionAction.VIEW.value}"
|
|
185
|
+
)
|
|
186
|
+
async def list_users():
|
|
187
|
+
return {"users": [...]}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Error Handling
|
|
191
|
+
|
|
192
|
+
The package includes comprehensive error handling for common scenarios:
|
|
193
|
+
- `UnauthorizedError`: For invalid or expired tokens
|
|
194
|
+
- `ValidationError`: For invalid token formats
|
|
195
|
+
- `DuplicatedError`: For duplicate role names
|
|
196
|
+
- `NotFoundError`: For non-existent resources
|
|
197
|
+
- `PermissionDeniedError`: For insufficient permissions
|
|
198
|
+
|
|
199
|
+
## Best Practices
|
|
200
|
+
|
|
201
|
+
1. Always use environment variables for sensitive data (secret keys, etc.)
|
|
202
|
+
2. Implement proper error handling for authentication and authorization failures
|
|
203
|
+
3. Use the middleware for global authentication
|
|
204
|
+
4. Implement proper logging for security-related events
|
|
205
|
+
5. Regularly rotate secret keys and tokens
|
|
206
|
+
6. Use strong password policies
|
|
207
|
+
7. Implement rate limiting for authentication endpoints
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Callable, Any
|
|
2
|
+
from ..models import Users
|
|
3
|
+
|
|
4
|
+
from abs_exception_core.exceptions import NotFoundError, ValidationError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_user_by_attribute(db_session: Callable[...,Any],attribute: str, value: str):
|
|
8
|
+
"""
|
|
9
|
+
Get a user by an attribute.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
attribute (str): The attribute to get the user by.
|
|
13
|
+
value (str): The value of the attribute.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
User: The user object if found, otherwise None.
|
|
17
|
+
"""
|
|
18
|
+
with db_session() as session:
|
|
19
|
+
try:
|
|
20
|
+
if not hasattr(Users, attribute):
|
|
21
|
+
raise ValidationError(detail=f"Attribute {attribute} does not exist on the User model")
|
|
22
|
+
|
|
23
|
+
user = session.query(Users).filter(getattr(Users, attribute) == value).first()
|
|
24
|
+
|
|
25
|
+
if not user:
|
|
26
|
+
raise NotFoundError(detail="User not found")
|
|
27
|
+
|
|
28
|
+
return user
|
|
29
|
+
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise e
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, UTC
|
|
2
|
+
from typing import Dict, Type, Callable,Any
|
|
3
|
+
|
|
4
|
+
import jwt
|
|
5
|
+
from fastapi import Depends
|
|
6
|
+
from fastapi.security import HTTPBearer
|
|
7
|
+
from passlib.context import CryptContext
|
|
8
|
+
from abs_exception_core.exceptions import UnauthorizedError,ValidationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# === JWT Setup ===
|
|
12
|
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
13
|
+
bearer_scheme = HTTPBearer()
|
|
14
|
+
|
|
15
|
+
# === Password Hashing ===
|
|
16
|
+
|
|
17
|
+
class JWTFunctions:
|
|
18
|
+
def __init__(self,secret_key: str,algorithm: str,expire_minutes: int=None):
|
|
19
|
+
"""
|
|
20
|
+
Args:
|
|
21
|
+
secret_key (str): The secret key for the JWT token.
|
|
22
|
+
algorithm (str): The algorithm for the JWT token.
|
|
23
|
+
expire_minutes (int): The expiration time for the JWT token in minutes.
|
|
24
|
+
"""
|
|
25
|
+
self.secret_key = secret_key
|
|
26
|
+
self.algorithm = algorithm
|
|
27
|
+
self.expire_minutes = expire_minutes
|
|
28
|
+
|
|
29
|
+
def verify_password(self,plain_password: str, hashed_password: str) -> bool:
|
|
30
|
+
"""
|
|
31
|
+
Verify a plain password against a hashed password using the password hashing context.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
plain_password (str): The plain password to verify.
|
|
35
|
+
hashed_password (str): The hashed password to verify against.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
bool: True if the password is verified, False otherwise.
|
|
39
|
+
"""
|
|
40
|
+
return pwd_context.verify(plain_password, hashed_password)
|
|
41
|
+
|
|
42
|
+
def get_password_hash(self,password: str) -> str:
|
|
43
|
+
"""
|
|
44
|
+
Generate a hashed password using the password hashing context.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
password (str): The plain password to hash.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: The hashed password.
|
|
51
|
+
"""
|
|
52
|
+
return pwd_context.hash(password)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# === Token Dependencies ===
|
|
56
|
+
|
|
57
|
+
async def get_token(self,token=Depends(bearer_scheme)) -> str:
|
|
58
|
+
"""
|
|
59
|
+
Get the token from the bearer scheme.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
token (str): The token to get.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
str: The token without the bearer prefix.
|
|
66
|
+
"""
|
|
67
|
+
return str(token.credentials)
|
|
68
|
+
|
|
69
|
+
# === JWT Token Creation & Decoding ===
|
|
70
|
+
|
|
71
|
+
def create_jwt_token(self,data: dict, expires_delta: timedelta=None) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Create a JWT token.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
data (dict): The data to encode in the token.
|
|
77
|
+
expires_delta (timedelta): The expiration time for the token.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
str: The JWT token.
|
|
81
|
+
"""
|
|
82
|
+
payload = data.copy()
|
|
83
|
+
if expires_delta:
|
|
84
|
+
expire = datetime.now(UTC) + expires_delta
|
|
85
|
+
else:
|
|
86
|
+
expire = datetime.now(UTC) + timedelta(
|
|
87
|
+
minutes=self.expire_minutes
|
|
88
|
+
)
|
|
89
|
+
payload.update({"exp": expire})
|
|
90
|
+
return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
|
|
91
|
+
|
|
92
|
+
def decode_jwt(self,token: str) -> dict:
|
|
93
|
+
"""
|
|
94
|
+
Decode a JWT token.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
token (str): The token to decode.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
dict: The decoded token.
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
return jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
|
104
|
+
except jwt.ExpiredSignatureError:
|
|
105
|
+
raise UnauthorizedError(detail="Token has expired")
|
|
106
|
+
except jwt.InvalidTokenError:
|
|
107
|
+
raise ValidationError(detail="Invalid token")
|
|
108
|
+
|
|
109
|
+
def get_data(self,token: str = Depends(get_token)) -> Dict:
|
|
110
|
+
"""
|
|
111
|
+
Get the data from the JWT token.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
token (str): The token to get the data from.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Dict: The decoded token.
|
|
118
|
+
"""
|
|
119
|
+
return self.decode_jwt(token)
|
|
120
|
+
|
|
121
|
+
# === Token Generators ===
|
|
122
|
+
|
|
123
|
+
def create_access_token(self,data: Dict, expires_delta: timedelta=None) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Create an access token.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
data (Dict): The data to encode in the token.
|
|
129
|
+
expires_delta (timedelta): The expiration time for the token.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
str: The access token.
|
|
133
|
+
"""
|
|
134
|
+
return self.create_jwt_token(data=data, expires_delta=expires_delta)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from fastapi import Depends
|
|
2
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable, Any
|
|
5
|
+
|
|
6
|
+
from .jwt_functions import JWTFunctions
|
|
7
|
+
from .auth_functions import get_user_by_attribute
|
|
8
|
+
from abs_exception_core.exceptions import UnauthorizedError
|
|
9
|
+
|
|
10
|
+
security = HTTPBearer()
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Dependency acting like per-route middleware
|
|
15
|
+
def auth_middleware(
|
|
16
|
+
db_session: Callable[...,Any],
|
|
17
|
+
jwt_secret_key:str,
|
|
18
|
+
jwt_algorithm:str
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
This middleware is used for authentication of the user.
|
|
22
|
+
Args:
|
|
23
|
+
db_session: Callable[...,Any]: Session of the SQLAlchemy database engine
|
|
24
|
+
Users: User table fo teh system
|
|
25
|
+
jwt_secret_key: Secret key of the JWT for jwt functions
|
|
26
|
+
jwt_algorithm: Algorithm used for JWT
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
"""
|
|
30
|
+
def get_auth(token: HTTPAuthorizationCredentials = Depends(security)):
|
|
31
|
+
jwt_functions = JWTFunctions(secret_key=jwt_secret_key,algorithm=jwt_algorithm)
|
|
32
|
+
try:
|
|
33
|
+
if not token or not token.credentials:
|
|
34
|
+
raise UnauthorizedError(detail="Invalid authentication credentials")
|
|
35
|
+
|
|
36
|
+
payload = jwt_functions.get_data(token=token.credentials)
|
|
37
|
+
uuid = payload.get("uuid")
|
|
38
|
+
|
|
39
|
+
user = get_user_by_attribute(db_session=db_session,attribute="uuid", value=uuid)
|
|
40
|
+
|
|
41
|
+
if not user:
|
|
42
|
+
logger.error(f"Authentication failed: User with id {uuid} not found")
|
|
43
|
+
raise UnauthorizedError(detail="Authentication failed")
|
|
44
|
+
|
|
45
|
+
return user # Attach user for use in route
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.error(f"Authentication error: {str(e)}", exc_info=True)
|
|
49
|
+
raise UnauthorizedError(detail="Authentication failed")
|
|
50
|
+
return get_auth
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import UTC, datetime
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import Column, DateTime, Integer, String
|
|
5
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
6
|
+
|
|
7
|
+
Base = declarative_base()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseModel(Base):
|
|
11
|
+
"""
|
|
12
|
+
Base model for all models
|
|
13
|
+
"""
|
|
14
|
+
__abstract__ = True
|
|
15
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
16
|
+
uuid = Column(String(36), unique=True, default=lambda: str(uuid.uuid4()))
|
|
17
|
+
created_at = Column(DateTime, default=lambda: datetime.now(UTC))
|
|
18
|
+
updated_at = Column(
|
|
19
|
+
DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC)
|
|
20
|
+
)
|