secureapi 0.1.4__tar.gz → 0.1.5__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.
- secureapi-0.1.5/PKG-INFO +360 -0
- secureapi-0.1.5/README.md +341 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/pyproject.toml +1 -1
- secureapi-0.1.5/secureapi.egg-info/PKG-INFO +360 -0
- secureapi-0.1.4/PKG-INFO +0 -357
- secureapi-0.1.4/README.md +0 -338
- secureapi-0.1.4/secureapi.egg-info/PKG-INFO +0 -357
- {secureapi-0.1.4 → secureapi-0.1.5}/LICENSE +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/config/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/config/loader.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/context.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/engine.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/exceptions.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/policy.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/core/validator.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/django/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/django/middleware.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/django/permission.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/fastapi/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/fastapi/dependency.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/flask/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/integrations/flask/decorator.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/utils/__init__.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi/utils/helpers.py +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi.egg-info/SOURCES.txt +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi.egg-info/dependency_links.txt +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi.egg-info/requires.txt +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/secureapi.egg-info/top_level.txt +0 -0
- {secureapi-0.1.4 → secureapi-0.1.5}/setup.cfg +0 -0
secureapi-0.1.5/PKG-INFO
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: secureapi
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Framework-agnostic API security layer for Django, FastAPI, and Flask
|
|
5
|
+
Author-email: Vaibhav Patil <v7patil77@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: api security,authorization,django,fastapi,flask
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Framework :: Django
|
|
10
|
+
Classifier: Framework :: FastAPI
|
|
11
|
+
Classifier: Framework :: Flask
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: PyYAML
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# SecureAPI
|
|
21
|
+
|
|
22
|
+
SecureAPI is a Python API authorization library that works with any framework.
|
|
23
|
+
It provides a centralized and reusable way to enforce access control, prevent ID tampering, and standardize authorization logic across applications.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Overview
|
|
28
|
+
|
|
29
|
+
Modern APIs often suffer from inconsistent authorization logic. Developers typically implement access checks manually in each endpoint, which leads to:
|
|
30
|
+
|
|
31
|
+
* Repeated code
|
|
32
|
+
* Security gaps
|
|
33
|
+
* Broken object-level authorization (BOLA)
|
|
34
|
+
* ID tampering vulnerabilities
|
|
35
|
+
|
|
36
|
+
SecureAPI solves this by introducing a unified authorization engine that can be integrated into any Python-based backend.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
# Key Features
|
|
41
|
+
|
|
42
|
+
* Centralized authorization engine
|
|
43
|
+
* Role-based and ownership-based access control
|
|
44
|
+
* Protection against ID tampering
|
|
45
|
+
* Framework-agnostic design
|
|
46
|
+
* Works with Django, FastAPI, and Flask
|
|
47
|
+
* No mandatory configuration files
|
|
48
|
+
* Fully customizable via resolvers
|
|
49
|
+
* Minimal integration effort
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install secureapi
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
# Core Concepts
|
|
62
|
+
|
|
63
|
+
## Resource
|
|
64
|
+
|
|
65
|
+
A resource represents any entity in your system.
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
|
|
69
|
+
* user
|
|
70
|
+
* order
|
|
71
|
+
* document
|
|
72
|
+
* project
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Action
|
|
77
|
+
|
|
78
|
+
| HTTP Method | Action |
|
|
79
|
+
| ----------- | ------ |
|
|
80
|
+
| GET | read |
|
|
81
|
+
| POST | create |
|
|
82
|
+
| PUT/PATCH | update |
|
|
83
|
+
| DELETE | delete |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Context
|
|
88
|
+
|
|
89
|
+
SecureAPI evaluates access using a context object:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
RequestContext(
|
|
93
|
+
user=<authenticated_user>,
|
|
94
|
+
resource="resource_name",
|
|
95
|
+
action="read",
|
|
96
|
+
resource_id=<resource_id>,
|
|
97
|
+
tenant_id=None
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
# How It Works
|
|
104
|
+
|
|
105
|
+
When authorization is triggered, SecureAPI performs:
|
|
106
|
+
|
|
107
|
+
1. Role resolution
|
|
108
|
+
2. Resource fetching
|
|
109
|
+
3. Ownership validation
|
|
110
|
+
4. Access decision
|
|
111
|
+
|
|
112
|
+
If access is not allowed, an exception is raised.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
# Implementation Guide (Industry Standard Example)
|
|
117
|
+
|
|
118
|
+
This section demonstrates how to integrate SecureAPI into a typical backend system using a **Document Management API**.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Example Use Case
|
|
123
|
+
|
|
124
|
+
You have a system where:
|
|
125
|
+
|
|
126
|
+
* Users can view documents
|
|
127
|
+
* Only owners or collaborators can access a document
|
|
128
|
+
* Admins can access all documents
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Step 1: Define Your Model (Example)
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
class Document:
|
|
136
|
+
def __init__(self, id, owner_id, collaborators):
|
|
137
|
+
self.id = id
|
|
138
|
+
self.owner_id = owner_id
|
|
139
|
+
self.collaborators = collaborators
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Step 2: Configure SecureAPI (Resolvers)
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from secureapi.config.loader import get_engine
|
|
148
|
+
|
|
149
|
+
engine = get_engine()
|
|
150
|
+
|
|
151
|
+
# Role Resolver
|
|
152
|
+
def role_resolver(user, tenant_id=None):
|
|
153
|
+
if user.is_admin:
|
|
154
|
+
return "admin"
|
|
155
|
+
return "user"
|
|
156
|
+
|
|
157
|
+
# Resource Resolver
|
|
158
|
+
DOCUMENT_DB = {
|
|
159
|
+
1: Document(id=1, owner_id=10, collaborators=[20, 30]),
|
|
160
|
+
2: Document(id=2, owner_id=20, collaborators=[10]),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def resource_resolver(resource, resource_id):
|
|
164
|
+
if resource == "document":
|
|
165
|
+
return DOCUMENT_DB.get(resource_id)
|
|
166
|
+
|
|
167
|
+
# Ownership Resolver
|
|
168
|
+
def ownership_resolver(user, obj):
|
|
169
|
+
if obj.owner_id == user.id:
|
|
170
|
+
return "owner"
|
|
171
|
+
if user.id in obj.collaborators:
|
|
172
|
+
return "collaborator"
|
|
173
|
+
return "user"
|
|
174
|
+
|
|
175
|
+
# Attach resolvers
|
|
176
|
+
engine.role_resolver = role_resolver
|
|
177
|
+
engine.resource_resolver = resource_resolver
|
|
178
|
+
engine.ownership_resolver = ownership_resolver
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Step 3: Use in API Endpoint
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from secureapi.core.context import RequestContext
|
|
187
|
+
from secureapi.config.loader import get_engine
|
|
188
|
+
|
|
189
|
+
def get_document(request, document_id):
|
|
190
|
+
engine = get_engine()
|
|
191
|
+
|
|
192
|
+
context = RequestContext(
|
|
193
|
+
user=request.user,
|
|
194
|
+
resource="document",
|
|
195
|
+
action="read",
|
|
196
|
+
resource_id=document_id,
|
|
197
|
+
tenant_id=None
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
engine.authorize(context)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
return {"error": str(e)}, 403
|
|
204
|
+
|
|
205
|
+
return {"message": "Document data returned successfully"}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Step 4: Behavior
|
|
211
|
+
|
|
212
|
+
### Allowed Access
|
|
213
|
+
|
|
214
|
+
* Document owner
|
|
215
|
+
* Document collaborator
|
|
216
|
+
* Admin user
|
|
217
|
+
|
|
218
|
+
### Denied Access
|
|
219
|
+
|
|
220
|
+
* Any unrelated user
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
# FastAPI Example
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from fastapi import FastAPI, HTTPException, Depends
|
|
228
|
+
from secureapi.core.context import RequestContext
|
|
229
|
+
from secureapi.config.loader import get_engine
|
|
230
|
+
|
|
231
|
+
app = FastAPI()
|
|
232
|
+
|
|
233
|
+
def authorize(user, document_id):
|
|
234
|
+
engine = get_engine()
|
|
235
|
+
context = RequestContext(user, "document", "read", document_id, None)
|
|
236
|
+
try:
|
|
237
|
+
engine.authorize(context)
|
|
238
|
+
except Exception as e:
|
|
239
|
+
raise HTTPException(status_code=403, detail=str(e))
|
|
240
|
+
|
|
241
|
+
@app.get("/documents/{document_id}")
|
|
242
|
+
def get_document(document_id: int, user=Depends(get_current_user)):
|
|
243
|
+
authorize(user, document_id)
|
|
244
|
+
return {"message": "Access granted"}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
# Flask Example
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from flask import Flask, request, jsonify
|
|
253
|
+
from secureapi.core.context import RequestContext
|
|
254
|
+
from secureapi.config.loader import get_engine
|
|
255
|
+
|
|
256
|
+
app = Flask(__name__)
|
|
257
|
+
|
|
258
|
+
@app.route("/documents/<int:document_id>")
|
|
259
|
+
def get_document(document_id):
|
|
260
|
+
user = request.user
|
|
261
|
+
engine = get_engine()
|
|
262
|
+
|
|
263
|
+
context = RequestContext(user, "document", "read", document_id, None)
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
engine.authorize(context)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
return jsonify({"error": str(e)}), 403
|
|
269
|
+
|
|
270
|
+
return jsonify({"message": "Access granted"})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
# Multi-Tenant Example
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
context = RequestContext(
|
|
279
|
+
user=request.user,
|
|
280
|
+
resource="document",
|
|
281
|
+
action="read",
|
|
282
|
+
resource_id=1,
|
|
283
|
+
tenant_id=1001
|
|
284
|
+
)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Tenant validation logic can be added inside resolvers.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
# Error Handling
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
try:
|
|
295
|
+
engine.authorize(context)
|
|
296
|
+
except Exception as e:
|
|
297
|
+
return {"error": str(e)}, 403
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
# Best Practices
|
|
303
|
+
|
|
304
|
+
* Keep resolver functions efficient
|
|
305
|
+
* Avoid heavy database queries in resolvers
|
|
306
|
+
* Use consistent role naming across the system
|
|
307
|
+
* Always validate ownership for sensitive resources
|
|
308
|
+
* Centralize resolver definitions in one place
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
# Security Benefits
|
|
313
|
+
|
|
314
|
+
SecureAPI helps protect against:
|
|
315
|
+
|
|
316
|
+
* Broken Object Level Authorization (BOLA)
|
|
317
|
+
* ID tampering attacks
|
|
318
|
+
* Unauthorized resource access
|
|
319
|
+
* Role escalation vulnerabilities
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
# Limitations
|
|
324
|
+
|
|
325
|
+
* No built-in policy engine (planned)
|
|
326
|
+
* No admin UI (planned)
|
|
327
|
+
* Requires resolver implementation
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
# Roadmap
|
|
332
|
+
|
|
333
|
+
* Policy-based configuration (YAML / DB)
|
|
334
|
+
* Native DRF permission class
|
|
335
|
+
* Audit logging
|
|
336
|
+
* Threat detection mechanisms
|
|
337
|
+
* Admin dashboard
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
# Contributing
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
git clone https://github.com/yourusername/secureapi
|
|
345
|
+
cd secureapi
|
|
346
|
+
pip install -e .
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
# License
|
|
352
|
+
|
|
353
|
+
MIT License © 2026 Vaibhav Patil
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
# Summary
|
|
358
|
+
|
|
359
|
+
SecureAPI provides a structured and reusable approach to authorization in Python applications.
|
|
360
|
+
It eliminates repetitive security logic and ensures consistent access control across all endpoints.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# SecureAPI
|
|
2
|
+
|
|
3
|
+
SecureAPI is a Python API authorization library that works with any framework.
|
|
4
|
+
It provides a centralized and reusable way to enforce access control, prevent ID tampering, and standardize authorization logic across applications.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Overview
|
|
9
|
+
|
|
10
|
+
Modern APIs often suffer from inconsistent authorization logic. Developers typically implement access checks manually in each endpoint, which leads to:
|
|
11
|
+
|
|
12
|
+
* Repeated code
|
|
13
|
+
* Security gaps
|
|
14
|
+
* Broken object-level authorization (BOLA)
|
|
15
|
+
* ID tampering vulnerabilities
|
|
16
|
+
|
|
17
|
+
SecureAPI solves this by introducing a unified authorization engine that can be integrated into any Python-based backend.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Key Features
|
|
22
|
+
|
|
23
|
+
* Centralized authorization engine
|
|
24
|
+
* Role-based and ownership-based access control
|
|
25
|
+
* Protection against ID tampering
|
|
26
|
+
* Framework-agnostic design
|
|
27
|
+
* Works with Django, FastAPI, and Flask
|
|
28
|
+
* No mandatory configuration files
|
|
29
|
+
* Fully customizable via resolvers
|
|
30
|
+
* Minimal integration effort
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install secureapi
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# Core Concepts
|
|
43
|
+
|
|
44
|
+
## Resource
|
|
45
|
+
|
|
46
|
+
A resource represents any entity in your system.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
|
|
50
|
+
* user
|
|
51
|
+
* order
|
|
52
|
+
* document
|
|
53
|
+
* project
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Action
|
|
58
|
+
|
|
59
|
+
| HTTP Method | Action |
|
|
60
|
+
| ----------- | ------ |
|
|
61
|
+
| GET | read |
|
|
62
|
+
| POST | create |
|
|
63
|
+
| PUT/PATCH | update |
|
|
64
|
+
| DELETE | delete |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Context
|
|
69
|
+
|
|
70
|
+
SecureAPI evaluates access using a context object:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
RequestContext(
|
|
74
|
+
user=<authenticated_user>,
|
|
75
|
+
resource="resource_name",
|
|
76
|
+
action="read",
|
|
77
|
+
resource_id=<resource_id>,
|
|
78
|
+
tenant_id=None
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
# How It Works
|
|
85
|
+
|
|
86
|
+
When authorization is triggered, SecureAPI performs:
|
|
87
|
+
|
|
88
|
+
1. Role resolution
|
|
89
|
+
2. Resource fetching
|
|
90
|
+
3. Ownership validation
|
|
91
|
+
4. Access decision
|
|
92
|
+
|
|
93
|
+
If access is not allowed, an exception is raised.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# Implementation Guide (Industry Standard Example)
|
|
98
|
+
|
|
99
|
+
This section demonstrates how to integrate SecureAPI into a typical backend system using a **Document Management API**.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Example Use Case
|
|
104
|
+
|
|
105
|
+
You have a system where:
|
|
106
|
+
|
|
107
|
+
* Users can view documents
|
|
108
|
+
* Only owners or collaborators can access a document
|
|
109
|
+
* Admins can access all documents
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Step 1: Define Your Model (Example)
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
class Document:
|
|
117
|
+
def __init__(self, id, owner_id, collaborators):
|
|
118
|
+
self.id = id
|
|
119
|
+
self.owner_id = owner_id
|
|
120
|
+
self.collaborators = collaborators
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Step 2: Configure SecureAPI (Resolvers)
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from secureapi.config.loader import get_engine
|
|
129
|
+
|
|
130
|
+
engine = get_engine()
|
|
131
|
+
|
|
132
|
+
# Role Resolver
|
|
133
|
+
def role_resolver(user, tenant_id=None):
|
|
134
|
+
if user.is_admin:
|
|
135
|
+
return "admin"
|
|
136
|
+
return "user"
|
|
137
|
+
|
|
138
|
+
# Resource Resolver
|
|
139
|
+
DOCUMENT_DB = {
|
|
140
|
+
1: Document(id=1, owner_id=10, collaborators=[20, 30]),
|
|
141
|
+
2: Document(id=2, owner_id=20, collaborators=[10]),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
def resource_resolver(resource, resource_id):
|
|
145
|
+
if resource == "document":
|
|
146
|
+
return DOCUMENT_DB.get(resource_id)
|
|
147
|
+
|
|
148
|
+
# Ownership Resolver
|
|
149
|
+
def ownership_resolver(user, obj):
|
|
150
|
+
if obj.owner_id == user.id:
|
|
151
|
+
return "owner"
|
|
152
|
+
if user.id in obj.collaborators:
|
|
153
|
+
return "collaborator"
|
|
154
|
+
return "user"
|
|
155
|
+
|
|
156
|
+
# Attach resolvers
|
|
157
|
+
engine.role_resolver = role_resolver
|
|
158
|
+
engine.resource_resolver = resource_resolver
|
|
159
|
+
engine.ownership_resolver = ownership_resolver
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Step 3: Use in API Endpoint
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from secureapi.core.context import RequestContext
|
|
168
|
+
from secureapi.config.loader import get_engine
|
|
169
|
+
|
|
170
|
+
def get_document(request, document_id):
|
|
171
|
+
engine = get_engine()
|
|
172
|
+
|
|
173
|
+
context = RequestContext(
|
|
174
|
+
user=request.user,
|
|
175
|
+
resource="document",
|
|
176
|
+
action="read",
|
|
177
|
+
resource_id=document_id,
|
|
178
|
+
tenant_id=None
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
engine.authorize(context)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
return {"error": str(e)}, 403
|
|
185
|
+
|
|
186
|
+
return {"message": "Document data returned successfully"}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Step 4: Behavior
|
|
192
|
+
|
|
193
|
+
### Allowed Access
|
|
194
|
+
|
|
195
|
+
* Document owner
|
|
196
|
+
* Document collaborator
|
|
197
|
+
* Admin user
|
|
198
|
+
|
|
199
|
+
### Denied Access
|
|
200
|
+
|
|
201
|
+
* Any unrelated user
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
# FastAPI Example
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from fastapi import FastAPI, HTTPException, Depends
|
|
209
|
+
from secureapi.core.context import RequestContext
|
|
210
|
+
from secureapi.config.loader import get_engine
|
|
211
|
+
|
|
212
|
+
app = FastAPI()
|
|
213
|
+
|
|
214
|
+
def authorize(user, document_id):
|
|
215
|
+
engine = get_engine()
|
|
216
|
+
context = RequestContext(user, "document", "read", document_id, None)
|
|
217
|
+
try:
|
|
218
|
+
engine.authorize(context)
|
|
219
|
+
except Exception as e:
|
|
220
|
+
raise HTTPException(status_code=403, detail=str(e))
|
|
221
|
+
|
|
222
|
+
@app.get("/documents/{document_id}")
|
|
223
|
+
def get_document(document_id: int, user=Depends(get_current_user)):
|
|
224
|
+
authorize(user, document_id)
|
|
225
|
+
return {"message": "Access granted"}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
# Flask Example
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from flask import Flask, request, jsonify
|
|
234
|
+
from secureapi.core.context import RequestContext
|
|
235
|
+
from secureapi.config.loader import get_engine
|
|
236
|
+
|
|
237
|
+
app = Flask(__name__)
|
|
238
|
+
|
|
239
|
+
@app.route("/documents/<int:document_id>")
|
|
240
|
+
def get_document(document_id):
|
|
241
|
+
user = request.user
|
|
242
|
+
engine = get_engine()
|
|
243
|
+
|
|
244
|
+
context = RequestContext(user, "document", "read", document_id, None)
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
engine.authorize(context)
|
|
248
|
+
except Exception as e:
|
|
249
|
+
return jsonify({"error": str(e)}), 403
|
|
250
|
+
|
|
251
|
+
return jsonify({"message": "Access granted"})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
# Multi-Tenant Example
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
context = RequestContext(
|
|
260
|
+
user=request.user,
|
|
261
|
+
resource="document",
|
|
262
|
+
action="read",
|
|
263
|
+
resource_id=1,
|
|
264
|
+
tenant_id=1001
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Tenant validation logic can be added inside resolvers.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
# Error Handling
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
try:
|
|
276
|
+
engine.authorize(context)
|
|
277
|
+
except Exception as e:
|
|
278
|
+
return {"error": str(e)}, 403
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
# Best Practices
|
|
284
|
+
|
|
285
|
+
* Keep resolver functions efficient
|
|
286
|
+
* Avoid heavy database queries in resolvers
|
|
287
|
+
* Use consistent role naming across the system
|
|
288
|
+
* Always validate ownership for sensitive resources
|
|
289
|
+
* Centralize resolver definitions in one place
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
# Security Benefits
|
|
294
|
+
|
|
295
|
+
SecureAPI helps protect against:
|
|
296
|
+
|
|
297
|
+
* Broken Object Level Authorization (BOLA)
|
|
298
|
+
* ID tampering attacks
|
|
299
|
+
* Unauthorized resource access
|
|
300
|
+
* Role escalation vulnerabilities
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
# Limitations
|
|
305
|
+
|
|
306
|
+
* No built-in policy engine (planned)
|
|
307
|
+
* No admin UI (planned)
|
|
308
|
+
* Requires resolver implementation
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
# Roadmap
|
|
313
|
+
|
|
314
|
+
* Policy-based configuration (YAML / DB)
|
|
315
|
+
* Native DRF permission class
|
|
316
|
+
* Audit logging
|
|
317
|
+
* Threat detection mechanisms
|
|
318
|
+
* Admin dashboard
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
# Contributing
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
git clone https://github.com/yourusername/secureapi
|
|
326
|
+
cd secureapi
|
|
327
|
+
pip install -e .
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
# License
|
|
333
|
+
|
|
334
|
+
MIT License © 2026 Vaibhav Patil
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
# Summary
|
|
339
|
+
|
|
340
|
+
SecureAPI provides a structured and reusable approach to authorization in Python applications.
|
|
341
|
+
It eliminates repetitive security logic and ensures consistent access control across all endpoints.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "secureapi"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
8
8
|
description = "Framework-agnostic API security layer for Django, FastAPI, and Flask"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Vaibhav Patil", email="v7patil77@gmail.com" }
|