fastapi-payloadshield 1.0.0__py3-none-any.whl

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,43 @@
1
+ """
2
+ FastAPI Payload Shield Package
3
+ Provides decorators for automatic encryption/decryption of request/response payloads
4
+ with pluggable encryption handlers (base64, AES, Fernet, etc.)
5
+ """
6
+
7
+ # Decorators
8
+ from .decorators import (
9
+ PayloadShieldEnc,
10
+ PayloadShieldDec,
11
+ PayloadShield,
12
+ # Backward compatibility
13
+ encrypt_response,
14
+ decrypt_request,
15
+ crypto_middleware,
16
+ )
17
+
18
+ # Encryption handlers and utilities
19
+ from .crypto import (
20
+ EncryptionHandler,
21
+ Base64EncryptionHandler,
22
+ register_handler,
23
+ get_handler,
24
+ )
25
+
26
+ __version__ = "2.0.0"
27
+ __author__ = "Ganesh Kandu"
28
+
29
+ __all__ = [
30
+ # New decorator names
31
+ "PayloadShieldEnc",
32
+ "PayloadShieldDec",
33
+ "PayloadShield",
34
+ # Backward compatibility
35
+ "encrypt_response",
36
+ "decrypt_request",
37
+ "crypto_middleware",
38
+ # Encryption handlers
39
+ "EncryptionHandler",
40
+ "Base64EncryptionHandler",
41
+ "register_handler",
42
+ "get_handler",
43
+ ]
@@ -0,0 +1,206 @@
1
+ """
2
+ Crypto utilities with pluggable encryption handlers
3
+ Supports multiple encryption types (base64, AES, etc.)
4
+ """
5
+
6
+ import base64
7
+ import json
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, Dict, Union
10
+
11
+
12
+ # ============================================================================
13
+ # Abstract Encryption Handler Interface
14
+ # ============================================================================
15
+
16
+ class EncryptionHandler(ABC):
17
+ """
18
+ Abstract base class for encryption handlers.
19
+ Implement this to add support for new encryption types.
20
+ """
21
+
22
+ @abstractmethod
23
+ def encode(self, data: Any) -> str:
24
+ """
25
+ Encode data to encrypted string.
26
+
27
+ Args:
28
+ data: Dictionary or JSON serializable object
29
+
30
+ Returns:
31
+ Encrypted string
32
+ """
33
+ pass
34
+
35
+ @abstractmethod
36
+ def decode(self, encoded_data: str) -> Any:
37
+ """
38
+ Decode encrypted string to data.
39
+
40
+ Args:
41
+ encoded_data: Encrypted string
42
+
43
+ Returns:
44
+ Decoded dictionary or object
45
+
46
+ Raises:
47
+ ValueError: If data cannot be decoded
48
+ """
49
+ pass
50
+
51
+
52
+ # ============================================================================
53
+ # Base64 Encryption Handler
54
+ # ============================================================================
55
+
56
+ class Base64EncryptionHandler(EncryptionHandler):
57
+ """
58
+ Base64 encoding/decoding handler.
59
+ Note: Base64 is encoding, not encryption. Use for obfuscation only.
60
+ """
61
+
62
+ def encode(self, data: Any) -> str:
63
+ """Encode data to base64 string."""
64
+ if isinstance(data, dict):
65
+ json_str = json.dumps(data)
66
+ else:
67
+ json_str = str(data)
68
+
69
+ return base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
70
+
71
+ def decode(self, encoded_data: str) -> Any:
72
+ """Decode base64 string to data."""
73
+ try:
74
+ decoded_bytes = base64.b64decode(encoded_data.encode('utf-8'))
75
+ decoded_str = decoded_bytes.decode('utf-8')
76
+ return json.loads(decoded_str)
77
+ except Exception as e:
78
+ raise ValueError(f"Failed to decode base64 data: {str(e)}")
79
+
80
+
81
+ # ============================================================================
82
+ # Handler Registry & Factory
83
+ # ============================================================================
84
+
85
+ _HANDLERS = {
86
+ "base64": Base64EncryptionHandler(),
87
+ }
88
+
89
+
90
+ def register_handler(name: str, handler: EncryptionHandler) -> None:
91
+ """
92
+ Register a new encryption handler.
93
+
94
+ Example:
95
+ from cryptography.fernet import Fernet
96
+
97
+ class FernetEncryptionHandler(EncryptionHandler):
98
+ def __init__(self, key):
99
+ self.cipher = Fernet(key)
100
+
101
+ def encode(self, data):
102
+ # implementation
103
+ pass
104
+
105
+ def decode(self, encoded_data):
106
+ # implementation
107
+ pass
108
+
109
+ key = Fernet.generate_key()
110
+ register_handler("fernet", FernetEncryptionHandler(key))
111
+ """
112
+ _HANDLERS[name.lower()] = handler
113
+
114
+
115
+ def get_handler(name: str) -> EncryptionHandler:
116
+ """
117
+ Get a registered encryption handler by name.
118
+
119
+ Args:
120
+ name: Name of the handler (e.g., "base64", "aes", "fernet")
121
+
122
+ Returns:
123
+ EncryptionHandler instance
124
+
125
+ Raises:
126
+ ValueError: If handler not found
127
+ """
128
+ handler_name = name.lower()
129
+ if handler_name not in _HANDLERS:
130
+ available = ", ".join(_HANDLERS.keys())
131
+ raise ValueError(
132
+ f"Encryption handler '{name}' not found. "
133
+ f"Available handlers: {available}"
134
+ )
135
+ return _HANDLERS[handler_name]
136
+
137
+
138
+ # ============================================================================
139
+ # Legacy Utility Functions (for backward compatibility)
140
+ # ============================================================================
141
+
142
+ def encode_base64(data: Any) -> str:
143
+ """
144
+ Encode data to base64 string.
145
+
146
+ Args:
147
+ data: Dictionary or JSON serializable object
148
+
149
+ Returns:
150
+ Base64 encoded string
151
+ """
152
+ handler = get_handler("base64")
153
+ return handler.encode(data)
154
+
155
+
156
+ def decode_base64(encoded_data: str) -> Union[Dict, Any]:
157
+ """
158
+ Decode base64 string to data.
159
+
160
+ Args:
161
+ encoded_data: Base64 encoded string
162
+
163
+ Returns:
164
+ Decoded dictionary or object
165
+
166
+ Raises:
167
+ ValueError: If data cannot be decoded
168
+ """
169
+ handler = get_handler("base64")
170
+ return handler.decode(encoded_data)
171
+
172
+
173
+ def encode_response(data: Any, handler: EncryptionHandler = None) -> Dict[str, str]:
174
+ """
175
+ Encode response data with encryption handler.
176
+
177
+ Args:
178
+ data: Response data to encode
179
+ handler: EncryptionHandler instance (uses base64 if None)
180
+
181
+ Returns:
182
+ Dictionary with 'encrypted' key containing encoded data
183
+ """
184
+ if handler is None:
185
+ handler = get_handler("base64")
186
+
187
+ return {
188
+ "encrypted": handler.encode(data)
189
+ }
190
+
191
+
192
+ def decode_request(encoded_data: str, handler: EncryptionHandler = None) -> Any:
193
+ """
194
+ Decode request data with encryption handler.
195
+
196
+ Args:
197
+ encoded_data: Encrypted request data
198
+ handler: EncryptionHandler instance (uses base64 if None)
199
+
200
+ Returns:
201
+ Decoded request data
202
+ """
203
+ if handler is None:
204
+ handler = get_handler("base64")
205
+
206
+ return handler.decode(encoded_data)
@@ -0,0 +1,229 @@
1
+ """
2
+ Decorators for automatic payload encryption/decryption in FastAPI
3
+ Supports multiple encryption types via pluggable EncryptionHandler interface
4
+ """
5
+
6
+ from functools import wraps
7
+ from typing import Callable, Any, Optional
8
+ from fastapi import Request
9
+ from fastapi.responses import JSONResponse
10
+ from .crypto import (
11
+ EncryptionHandler,
12
+ get_handler,
13
+ encode_response,
14
+ decode_request,
15
+ )
16
+
17
+
18
+ # ============================================================================
19
+ # PayloadShieldEnc - Response Encryption Decorator
20
+ # ============================================================================
21
+
22
+ def PayloadShieldEnc(encryption_type: str = "base64"):
23
+ """
24
+ Decorator to automatically encrypt response payload.
25
+
26
+ The decorator intercepts the response and wraps it in a JSON object with
27
+ an 'encrypted' key containing the encrypted response.
28
+
29
+ Args:
30
+ encryption_type: Type of encryption to use (default: "base64")
31
+ Options: "base64", "aes", "fernet", etc.
32
+ Register custom handlers with register_handler()
33
+
34
+ Usage:
35
+ @app.get("/api/endpoint")
36
+ @PayloadShieldEnc("base64")
37
+ async def my_route():
38
+ return {"message": "hello", "data": "world"}
39
+
40
+ # Response: {"encrypted": "base64_encoded_data"}
41
+
42
+ Returns:
43
+ Decorator function
44
+ """
45
+ def decorator(func: Callable) -> Callable:
46
+ handler = get_handler(encryption_type)
47
+
48
+ @wraps(func)
49
+ async def wrapper(*args, **kwargs):
50
+ result = await func(*args, **kwargs)
51
+
52
+ # Handle different response types
53
+ if isinstance(result, dict):
54
+ encrypted = encode_response(result, handler)
55
+ else:
56
+ encrypted = encode_response({"data": result}, handler)
57
+
58
+ return JSONResponse(content=encrypted)
59
+
60
+ return wrapper
61
+
62
+ return decorator
63
+
64
+
65
+ # ============================================================================
66
+ # PayloadShieldDec - Request Decryption Decorator
67
+ # ============================================================================
68
+
69
+ def PayloadShieldDec(encryption_type: str = "base64"):
70
+ """
71
+ Decorator to automatically decrypt encrypted request payload.
72
+
73
+ The decorator expects the request body to be a JSON object with an 'encrypted' key
74
+ containing the encrypted data. It decodes this and passes the original data
75
+ to the route function.
76
+
77
+ Args:
78
+ encryption_type: Type of encryption to use (default: "base64")
79
+ Options: "base64", "aes", "fernet", etc.
80
+ Register custom handlers with register_handler()
81
+
82
+ Usage:
83
+ @app.post("/api/endpoint")
84
+ @PayloadShieldDec("base64")
85
+ async def my_route(data: dict):
86
+ # data will be automatically decrypted
87
+ return {"message": "success"}
88
+
89
+ # Expects: {"encrypted": "encrypted_data"}
90
+
91
+ Returns:
92
+ Decorator function
93
+ """
94
+ def decorator(func: Callable) -> Callable:
95
+ handler = get_handler(encryption_type)
96
+
97
+ @wraps(func)
98
+ async def wrapper(*args, **kwargs):
99
+ # Check if first argument is a Request object
100
+ request = None
101
+ for arg in args:
102
+ if isinstance(arg, Request):
103
+ request = arg
104
+ break
105
+
106
+ if request:
107
+ try:
108
+ body = await request.json()
109
+ if isinstance(body, dict) and "encrypted" in body:
110
+ # Decrypt the encrypted data
111
+ decrypted_data = decode_request(body["encrypted"], handler)
112
+ # Replace the request body in kwargs
113
+ for key, value in kwargs.items():
114
+ if isinstance(value, dict) and value == body:
115
+ kwargs[key] = decrypted_data
116
+ break
117
+ except Exception as e:
118
+ return JSONResponse(
119
+ status_code=400,
120
+ content={"error": f"Failed to decrypt request: {str(e)}"}
121
+ )
122
+
123
+ return await func(*args, **kwargs)
124
+
125
+ return wrapper
126
+
127
+ return decorator
128
+
129
+
130
+ # ============================================================================
131
+ # PayloadShield - Combined Encryption & Decryption Decorator
132
+ # ============================================================================
133
+
134
+ def PayloadShield(encryption_type: str = "base64"):
135
+ """
136
+ Combined decorator for both request decryption and response encryption.
137
+
138
+ Automatically handles both incoming encrypted requests and outgoing encrypted responses.
139
+
140
+ Args:
141
+ encryption_type: Type of encryption to use (default: "base64")
142
+ Options: "base64", "aes", "fernet", etc.
143
+ Register custom handlers with register_handler()
144
+
145
+ Usage:
146
+ @app.post("/api/endpoint")
147
+ @PayloadShield("base64")
148
+ async def my_route(data: dict):
149
+ # Automatically decrypts incoming request and encrypts response
150
+ return {"message": "success"}
151
+
152
+ # Expects: {"encrypted": "encrypted_data"}
153
+ # Returns: {"encrypted": "encrypted_data"}
154
+
155
+ Returns:
156
+ Decorator function
157
+ """
158
+ def decorator(func: Callable) -> Callable:
159
+ handler = get_handler(encryption_type)
160
+
161
+ @wraps(func)
162
+ async def wrapper(*args, **kwargs):
163
+ # Handle request decryption
164
+ request = None
165
+ for arg in args:
166
+ if isinstance(arg, Request):
167
+ request = arg
168
+ break
169
+
170
+ if request:
171
+ try:
172
+ body = await request.json()
173
+ if isinstance(body, dict) and "encrypted" in body:
174
+ decrypted_data = decode_request(body["encrypted"], handler)
175
+ for key, value in kwargs.items():
176
+ if isinstance(value, dict) and value == body:
177
+ kwargs[key] = decrypted_data
178
+ break
179
+ except Exception as e:
180
+ return JSONResponse(
181
+ status_code=400,
182
+ content={"error": f"Failed to decrypt request: {str(e)}"}
183
+ )
184
+
185
+ # Call the original function
186
+ result = await func(*args, **kwargs)
187
+
188
+ # Handle response encryption
189
+ if isinstance(result, dict):
190
+ encrypted = encode_response(result, handler)
191
+ else:
192
+ encrypted = encode_response({"data": result}, handler)
193
+
194
+ return JSONResponse(content=encrypted)
195
+
196
+ return wrapper
197
+
198
+ return decorator
199
+
200
+
201
+ # ============================================================================
202
+ # Backward Compatibility - Old Decorator Names
203
+ # ============================================================================
204
+
205
+ def encrypt_response(func: Callable) -> Callable:
206
+ """
207
+ Deprecated: Use PayloadShieldEnc("base64") instead.
208
+
209
+ Decorator to automatically encrypt response payload with base64.
210
+ """
211
+ return PayloadShieldEnc("base64")(func)
212
+
213
+
214
+ def decrypt_request(func: Callable) -> Callable:
215
+ """
216
+ Deprecated: Use PayloadShieldDec("base64") instead.
217
+
218
+ Decorator to automatically decrypt base64 encoded request payload.
219
+ """
220
+ return PayloadShieldDec("base64")(func)
221
+
222
+
223
+ def crypto_middleware(func: Callable) -> Callable:
224
+ """
225
+ Deprecated: Use PayloadShield("base64") instead.
226
+
227
+ Combined decorator for both request decryption and response encryption.
228
+ """
229
+ return PayloadShield("base64")(func)
@@ -0,0 +1,364 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi_payloadshield
3
+ Version: 1.0.0
4
+ Summary: FastAPI decorators for automatic base64 encryption/decryption of request/response payloads
5
+ Home-page: https://github.com/PayloadShield/FastAPIPS
6
+ Author: Ganesh Kandu
7
+ Author-email: Ganesh Kandu <kanduganesh@gmail.com>
8
+ License: Apache-2.0
9
+ Project-URL: Homepage, https://github.com/PayloadShield/FastAPIPS
10
+ Project-URL: Repository, https://github.com/PayloadShield/FastAPIPS.git
11
+ Project-URL: Issues, https://github.com/PayloadShield/FastAPIPS/issues
12
+ Keywords: fastapi,base64,crypto,encryption,decorator
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: License :: OSI Approved :: Apache Software License
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Development Status :: 4 - Beta
22
+ Classifier: Intended Audience :: Developers
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Framework :: FastAPI
25
+ Requires-Python: >=3.7
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: fastapi>=0.68.0
29
+ Requires-Dist: starlette>=0.19.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=6.0; extra == "dev"
32
+ Requires-Dist: pytest-asyncio>=0.18.0; extra == "dev"
33
+ Dynamic: author
34
+ Dynamic: home-page
35
+ Dynamic: license-file
36
+ Dynamic: requires-python
37
+
38
+ # FastAPI Payload Shield
39
+
40
+ Lightweight FastAPI decorators for automatic encryption/decryption of request and response payloads. **Supports pluggable encryption handlers** - easily add new encryption types like AES, Fernet, or custom algorithms!
41
+
42
+ ## 🎯 Key Features
43
+
44
+ - 🔒 **Flexible Encryption**: Multiple encryption types (base64, AES, Fernet, custom)
45
+ - 🔓 **Automatic Decryption**: Decrypt incoming requests automatically
46
+ - 📝 **JSON-Friendly**: Works seamlessly with JSON requests and responses
47
+ - 🎯 **Route-Agnostic**: No changes needed to your existing route logic
48
+ - ⚡ **Lightweight**: Minimal dependencies and overhead
49
+ - 🚀 **Easy Integration**: Just add decorators to your routes
50
+ - 🧩 **Pluggable**: Create custom encryption handlers easily
51
+
52
+ ## Installation
53
+
54
+ ### From Local Development
55
+ ```bash
56
+ cd FastAPIPS
57
+ pip install -e .
58
+ ```
59
+
60
+ ### From PyPI (when published)
61
+ ```bash
62
+ pip install fastapi_payloadshield
63
+ ```
64
+
65
+ ## Quick Start
66
+
67
+ ### Basic Usage with Base64
68
+
69
+ ```python
70
+ from fastapi import FastAPI
71
+ from fastapi_payloadshield import PayloadShieldEnc, PayloadShieldDec, PayloadShield
72
+
73
+ app = FastAPI()
74
+
75
+ # Encrypt response only
76
+ @app.get("/api/data")
77
+ @PayloadShieldEnc("base64")
78
+ async def get_data():
79
+ return {"message": "hello", "data": "world"}
80
+
81
+ # Decrypt request only
82
+ @app.post("/api/process")
83
+ @PayloadShieldDec("base64")
84
+ async def process_data(data: dict):
85
+ return {"received": data, "status": "success"}
86
+
87
+ @app.post("/api/secure")
88
+ @PayloadShield("base64")
89
+ async def secure_endpoint(data: dict):
90
+ return {"processed": data}
91
+ ```
92
+
93
+ ## Decorators
94
+
95
+ ### `@PayloadShieldEnc(encryption_type)`
96
+
97
+ Encrypts the response payload.
98
+
99
+ ```python
100
+ @app.get("/api/users")
101
+ @PayloadShieldEnc("base64")
102
+ async def get_users():
103
+ return [{"id": 1, "name": "Alice"}]
104
+
105
+ # Response: {"encrypted": "W3siaWQiOiAxLCAibmFtZSI6ICJBbGljZSJ9XQ=="}
106
+ ```
107
+
108
+ ### `@PayloadShieldDec(encryption_type)`
109
+
110
+ Decrypts the request payload.
111
+
112
+ ```python
113
+ @app.post("/api/login")
114
+ @PayloadShieldDec("base64")
115
+ async def login(credentials: dict):
116
+ return {"status": "success"}
117
+
118
+ # Expects: {"encrypted": "base64_encoded_json"}
119
+ ```
120
+
121
+ ### `@PayloadShield(encryption_type)`
122
+
123
+ Combined encryption and decryption
124
+
125
+ ```python
126
+ @app.post("/api/secure")
127
+ @PayloadShield("base64")
128
+ async def secure_endpoint(data: dict):
129
+ return {"processed": data}
130
+
131
+ # Expects: {"encrypted": "encrypted_data"}
132
+ # Returns: {"encrypted": "encrypted_data"}
133
+ ```
134
+
135
+ ## Advanced Example: Multiple Encryption Types
136
+
137
+ ```python
138
+ from fastapi import FastAPI
139
+ from fastapi_payloadshield import PayloadShield, register_handler, EncryptionHandler
140
+ from cryptography.fernet import Fernet
141
+ import json
142
+
143
+ app = FastAPI()
144
+
145
+ # Create Fernet handler
146
+ class FernetHandler(EncryptionHandler):
147
+ def __init__(self, key):
148
+ self.cipher = Fernet(key)
149
+
150
+ def encode(self, data):
151
+ return self.cipher.encrypt(json.dumps(data).encode()).decode()
152
+
153
+ def decode(self, encoded_data):
154
+ return json.loads(self.cipher.decrypt(encoded_data.encode()))
155
+
156
+ # Register
157
+ key = Fernet.generate_key()
158
+ register_handler("fernet", FernetHandler(key))
159
+
160
+ # Use different encryption for different endpoints
161
+ @app.post("/api/public")
162
+ @PayloadShield("base64") # Light encryption
163
+ async def public_endpoint(data: dict):
164
+ return data
165
+
166
+ @app.post("/api/private")
167
+ @PayloadShield("fernet") # Strong encryption
168
+ async def private_endpoint(data: dict):
169
+ return data
170
+ ```
171
+
172
+ ## How It Works
173
+
174
+ ### Request Decryption Flow
175
+ 1. Client sends: `{"encrypted": "encrypted_data"}`
176
+ 2. `@PayloadShieldDec` decorator intercepts
177
+ 3. Decrypts using specified handler
178
+ 4. Route receives: `{"key": "value"}` (normal dict)
179
+
180
+ ### Response Encryption Flow
181
+ 1. Route returns: `{"key": "value"}`
182
+ 2. `@PayloadShieldEnc` decorator intercepts
183
+ 3. Encrypts using specified handler
184
+ 4. Client receives: `{"encrypted": "encrypted_data"}`
185
+
186
+ ## Testing
187
+
188
+ ### Run Example Application
189
+ ```bash
190
+ python examples/example_app.py
191
+ ```
192
+
193
+ ### Run Test Client
194
+ ```bash
195
+ python examples/test_client.py
196
+ ```
197
+
198
+ ### Manual Test with cURL
199
+
200
+ ```bash
201
+ # Encrypt test data
202
+ echo '{"username":"admin"}' | base64
203
+ # eyJ1c2VybmFtZSI6ImFkbWluIn0=
204
+
205
+ # Send encrypted request
206
+ curl -X POST http://localhost:8000/api/login \
207
+ -H "Content-Type: application/json" \
208
+ -d '{"encrypted":"eyJ1c2VybmFtZSI6ImFkbWluIn0="}'
209
+ ```
210
+
211
+ ### Testing with Python
212
+ ```python
213
+ import requests
214
+ import json
215
+ import base64
216
+
217
+ # Encode request
218
+ data = {"username": "admin", "password": "secret"}
219
+ json_str = json.dumps(data)
220
+ encrypted = base64.b64encode(json_str.encode()).decode()
221
+
222
+ # Send request
223
+ response = requests.post(
224
+ "http://localhost:8000/api/login",
225
+ json={"encrypted": encrypted}
226
+ )
227
+
228
+ # Decode response
229
+ encrypted_response = response.json()["encrypted"]
230
+ decrypted = json.loads(base64.b64decode(encrypted_response).decode())
231
+ print(decrypted)
232
+ # {'status': 'success', 'token': 'abc123'}
233
+ ```
234
+
235
+ ## Creating Custom Encryption Handlers
236
+
237
+ See [CUSTOM_HANDLERS.md](CUSTOM_HANDLERS.md) for detailed guide on:
238
+
239
+ - Creating custom handlers
240
+ - Fernet encryption example
241
+ - AES encryption example
242
+ - Best practices
243
+ - Performance tips
244
+ - Security considerations
245
+
246
+ ## Backward Compatibility
247
+
248
+ Old decorator names still work:
249
+
250
+ ```python
251
+ from fastapi_payloadshield import encrypt_response, decrypt_request, crypto_middleware
252
+
253
+ # These are equivalent to:
254
+ # PayloadShieldEnc("base64")
255
+ # PayloadShieldDec("base64")
256
+ # PayloadShield("base64")
257
+
258
+ @app.get("/api/data")
259
+ @encrypt_response
260
+ async def get_data():
261
+ return {"data": "value"}
262
+ ```
263
+
264
+ ## API Reference
265
+
266
+ ### Decorators
267
+
268
+ | Decorator | Purpose |
269
+ |-----------|---------|
270
+ | `PayloadShieldEnc(type)` | Encrypt response |
271
+ | `PayloadShieldDec(type)` | Decrypt request |
272
+ | `PayloadShield(type)` | Both encrypt & decrypt |
273
+
274
+ ### Functions
275
+
276
+ | Function | Purpose |
277
+ |----------|---------|
278
+ | `register_handler(name, handler)` | Register custom encryption handler |
279
+ | `get_handler(name)` | Get handler by name |
280
+ | `EncryptionHandler` | Base class for handlers |
281
+
282
+ ### Built-in Handlers
283
+
284
+ | Handler | Type | Security | Use Case |
285
+ |---------|------|----------|----------|
286
+ | `base64` | Encoding | None | Obfuscation, development |
287
+
288
+ ## Error Handling
289
+
290
+ The decorators include built-in error handling:
291
+
292
+ ```python
293
+ # Invalid encrypted data
294
+ # Response: {"error": "Failed to decrypt request: ..."}
295
+
296
+ # Missing encryption handler
297
+ # Response: ValueError: Encryption handler 'xyz' not found. Available: base64, fernet
298
+ ```
299
+
300
+ ## Performance Considerations
301
+
302
+ - **Caching**: Handler instances are cached
303
+ - **Compression**: Consider compressing before encryption for large payloads
304
+ - **Async**: All operations are async-friendly
305
+
306
+ ## Requirements
307
+
308
+ - Python 3.7+
309
+ - FastAPI 0.68+
310
+ - Starlette 0.19+
311
+
312
+ ## Files Included
313
+
314
+ - `fastapi_payloadshield/` - Main package
315
+ - `__init__.py` - Exports decorators and handlers
316
+ - `crypto.py` - Encryption handlers
317
+ - `decorators.py` - FastAPI decorators
318
+ - `examples/` - Working examples
319
+ - `example_app.py` - Full-featured demo
320
+ - `test_client.py` - Test/client script
321
+ - `README.md` - This file
322
+ - `QUICKSTART.md` - Quick start guide
323
+ - `CUSTOM_HANDLERS.md` - Creating custom handlers
324
+ - `DEVELOPMENT.md` - Development guide
325
+
326
+ ## License
327
+
328
+ Apache-2.0 - See LICENSE file for details
329
+
330
+ ## Contributing
331
+
332
+ Contributions welcome! Areas for contribution:
333
+
334
+ 1. New encryption handlers (AES, Fernet, etc.)
335
+ 2. Performance optimizations
336
+ 3. Documentation improvements
337
+ 4. Test coverage
338
+ 5. Examples
339
+
340
+ ## Support
341
+
342
+ - 📖 Full guide: [README.md](README.md)
343
+ - ⚡ Quick start: [QUICKSTART.md](QUICKSTART.md)
344
+ - 🧩 Custom handlers: [CUSTOM_HANDLERS.md](CUSTOM_HANDLERS.md)
345
+ - 🛠️ Development: [DEVELOPMENT.md](DEVELOPMENT.md)
346
+
347
+ ---
348
+
349
+ **Happy encrypting!** 🔒
350
+
351
+ ## Why Payload Shield?
352
+
353
+ This package was designed with extensibility in mind. Unlike static encryption libraries, Payload Shield lets you:
354
+
355
+ - Mix and match encryption types in the same app
356
+ - Add new encryption types without touching core code
357
+ - Keep route logic clean and simple
358
+ - Support multiple security levels
359
+
360
+ Perfect for:
361
+ - Building multi-tier security APIs
362
+ - Migrating from one encryption to another
363
+ - Testing different encryption strategies
364
+ - Production systems requiring flexible crypto
@@ -0,0 +1,8 @@
1
+ fastapi_payloadshield/__init__.py,sha256=LmGDsOSDmwkN-pSmS20ei92RT0W2AS0w7FVeok3BzHU,969
2
+ fastapi_payloadshield/crypto.py,sha256=O-AjgJUlywp4f2tK4Fg-hBOT1Ouz_Y-gsTe_R28UTkM,5811
3
+ fastapi_payloadshield/decorators.py,sha256=D8ph3Wq0E1PT58htsshJHI2W5fodXvYcOglU403lGVQ,8214
4
+ fastapi_payloadshield-1.0.0.dist-info/licenses/LICENSE,sha256=y-lTKIS0jHHMx0Q3zimWoYF-2snYD1e3QVQUuMBzoC0,6535
5
+ fastapi_payloadshield-1.0.0.dist-info/METADATA,sha256=72-RPyFq7iiXPt0VP-Wg_ATcqzsps_DF2ipsIlbS7KY,9950
6
+ fastapi_payloadshield-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
7
+ fastapi_payloadshield-1.0.0.dist-info/top_level.txt,sha256=eebO3hvNAOw8hutul4CiOt2sXRq-YqgULWFWM-Lxd94,22
8
+ fastapi_payloadshield-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,133 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+
4
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5
+
6
+ 1. Definitions.
7
+
8
+ "License" shall mean the terms and conditions for use, reproduction,
9
+ and distribution as defined in Sections 1 through 9 of this document.
10
+
11
+ "Licensor" shall mean the copyright owner or entity authorized by
12
+ the copyright owner that is granting the License.
13
+
14
+ "Legal Entity" shall mean the union of the acting entity and all
15
+ other entities that control, are controlled by, or are under common
16
+ control with that entity. For the purposes of this definition,
17
+ "control" means (i) the power, direct or indirect, to cause the
18
+ direction or management of such entity, whether by contract or
19
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+ outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications,
26
+ including but not limited to software source code, documentation
27
+ source, and configuration files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical
30
+ transformation or translation of a Source form, including but
31
+ not limited to compiled object code, generated documentation,
32
+ and conversions to other media types.
33
+
34
+ "Work" shall mean the work of authorship, whether in Source or Object
35
+ form, made available under the License, including but not limited to
36
+ the software source code, documentation source, and configuration files.
37
+
38
+ "Derivative Works" shall mean any work, whether in Source or Object
39
+ form, that is based on (or derived from) the Work and for which the
40
+ editorial revisions, annotations, elaborations, or other modifications
41
+ represent, as a whole, an original work of authorship.
42
+
43
+ "Contribution" shall mean any work of authorship, including
44
+ the original version of the Work and any modifications thereof
45
+ or derivative works thereof.
46
+
47
+ "Contributor" shall mean Licensor and any individual or Legal Entity
48
+ on behalf of whom a Contribution has been received by Licensor and
49
+ subsequently incorporated within the Work.
50
+
51
+ "Licensed Patents" shall mean the patent claims licensable by Licensor
52
+ that are necessarily infringed by the use or sale of either the Work
53
+ or Contribution alone or by combination with the Work.
54
+
55
+ 2. Grant of Copyright License. Subject to the terms and conditions of
56
+ this License, each Contributor hereby grants to You a perpetual,
57
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
58
+ copyright license to reproduce, prepare Derivative Works of,
59
+ publicly display, publicly perform, sublicense, and distribute the
60
+ Work and such Derivative Works in Source or Object form.
61
+
62
+ 3. Grant of Patent License. Subject to the terms and conditions of
63
+ this License, each Contributor hereby grants to You a perpetual,
64
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
65
+ (except as stated in the License) patent license to make, have made,
66
+ use, offer to sell, sell, import, and otherwise transfer the Work.
67
+
68
+ 4. Redistribution. You may reproduce and distribute copies of the
69
+ Work or Derivative Works thereof in any medium, with or without
70
+ modifications, and in Source or Object form, provided that You
71
+ meet the following conditions:
72
+
73
+ (a) You must give any other recipients of the Work or
74
+ Derivative Works a copy of this License; and
75
+
76
+ (b) You must cause any modified files to carry prominent notices
77
+ stating that You changed the files; and
78
+
79
+ (c) You must retain, in the Source form of any Derivative Works
80
+ that You distribute, all copyright, patent, trademark, and
81
+ attribution notices from the Source form of the Work; and
82
+
83
+ (d) If the Work includes a "NOTICE" text file, then any
84
+ Derivative Works that You distribute must include a readable
85
+ copy of the attribution notices contained within such NOTICE file.
86
+
87
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
88
+ any Contribution intentionally submitted for inclusion in the Work
89
+ by You to Licensor shall be under the terms and conditions of
90
+ this License, without any additional terms or conditions.
91
+
92
+ 6. Trademarks. This License does not grant permission to use the trade
93
+ names, trademarks, service marks, or product names of the Licensor,
94
+ except as required for reasonable and customary use in describing the
95
+ origin of the Work and reproducing the content of the NOTICE file.
96
+
97
+ 7. Disclaimer of Warranty. Unless required by applicable law or
98
+ agreed to in writing, Licensor provides the Work (and each
99
+ Contributor provides its Contributions) on an "AS IS" BASIS,
100
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
101
+ or implied, including, without limitation, any warranties or
102
+ conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
103
+ FITNESS FOR A PARTICULAR PURPOSE.
104
+
105
+ 8. Limitation of Liability. In no event and under no legal theory,
106
+ whether in tort (including negligence), contract, or otherwise,
107
+ unless required by applicable law (such as deliberate and grossly
108
+ negligent acts) or agreed to in writing, shall any Contributor be
109
+ liable to You for damages, including any direct, indirect, special,
110
+ incidental, or consequential damages of any character arising as a
111
+ result of this License or out of the use or inability to use the
112
+ Work.
113
+
114
+ 9. Accepting Warranty or Additional Liability. While redistributing
115
+ the Work or Derivative Works thereof, You may choose to offer,
116
+ and charge a fee for, acceptance of support, warranty, indemnity,
117
+ or other liability obligations and/or rights consistent with this License.
118
+
119
+ END OF TERMS AND CONDITIONS
120
+
121
+ Copyright 2024 FastAPI Payload Shield Contributors
122
+
123
+ Licensed under the Apache License, Version 2.0 (the "License");
124
+ you may not use this file except in compliance with the License.
125
+ You may obtain a copy of the License at
126
+
127
+ http://www.apache.org/licenses/LICENSE-2.0
128
+
129
+ Unless required by applicable law or agreed to in writing, software
130
+ distributed under the License is distributed on an "AS IS" BASIS,
131
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132
+ See the License for the specific language governing permissions and
133
+ limitations under the License.
@@ -0,0 +1 @@
1
+ fastapi_payloadshield