secureapi 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.
- secureapi-0.1.0/LICENSE +5 -0
- secureapi-0.1.0/PKG-INFO +21 -0
- secureapi-0.1.0/README.md +3 -0
- secureapi-0.1.0/pyproject.toml +25 -0
- secureapi-0.1.0/secureapi/__init__.py +1 -0
- secureapi-0.1.0/secureapi/config/__init__.py +0 -0
- secureapi-0.1.0/secureapi/config/loader.py +17 -0
- secureapi-0.1.0/secureapi/core/__init__.py +0 -0
- secureapi-0.1.0/secureapi/core/context.py +7 -0
- secureapi-0.1.0/secureapi/core/engine.py +36 -0
- secureapi-0.1.0/secureapi/core/exceptions.py +1 -0
- secureapi-0.1.0/secureapi/core/policy.py +9 -0
- secureapi-0.1.0/secureapi/core/validator.py +1 -0
- secureapi-0.1.0/secureapi/integrations/__init__.py +0 -0
- secureapi-0.1.0/secureapi/integrations/django/__init__.py +0 -0
- secureapi-0.1.0/secureapi/integrations/django/middleware.py +1 -0
- secureapi-0.1.0/secureapi/integrations/django/permission.py +41 -0
- secureapi-0.1.0/secureapi/integrations/fastapi/__init__.py +0 -0
- secureapi-0.1.0/secureapi/integrations/fastapi/dependency.py +23 -0
- secureapi-0.1.0/secureapi/integrations/flask/__init__.py +0 -0
- secureapi-0.1.0/secureapi/integrations/flask/decorator.py +32 -0
- secureapi-0.1.0/secureapi/utils/__init__.py +0 -0
- secureapi-0.1.0/secureapi/utils/helpers.py +1 -0
- secureapi-0.1.0/secureapi.egg-info/PKG-INFO +21 -0
- secureapi-0.1.0/secureapi.egg-info/SOURCES.txt +27 -0
- secureapi-0.1.0/secureapi.egg-info/dependency_links.txt +1 -0
- secureapi-0.1.0/secureapi.egg-info/requires.txt +11 -0
- secureapi-0.1.0/secureapi.egg-info/top_level.txt +1 -0
- secureapi-0.1.0/setup.cfg +4 -0
secureapi-0.1.0/LICENSE
ADDED
secureapi-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: secureapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package for secure API access control.
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pyyaml
|
|
10
|
+
Provides-Extra: django
|
|
11
|
+
Requires-Dist: django; extra == "django"
|
|
12
|
+
Requires-Dist: djangorestframework; extra == "django"
|
|
13
|
+
Provides-Extra: fastapi
|
|
14
|
+
Requires-Dist: fastapi; extra == "fastapi"
|
|
15
|
+
Provides-Extra: flask
|
|
16
|
+
Requires-Dist: flask; extra == "flask"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# secureapi
|
|
20
|
+
|
|
21
|
+
Secure API access control library.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "secureapi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A package for secure API access control."
|
|
9
|
+
authors = [
|
|
10
|
+
{ name="Your Name", email="your.email@example.com" }
|
|
11
|
+
]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"pyyaml",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
django = ["django", "djangorestframework"]
|
|
20
|
+
fastapi = ["fastapi"]
|
|
21
|
+
flask = ["flask"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["."]
|
|
25
|
+
include = ["secureapi*"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# secureapi package
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from secureapi.core.engine import SecureEngine
|
|
2
|
+
from secureapi.core.policy import PolicyLoader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
_engine = None
|
|
6
|
+
|
|
7
|
+
def get_engine():
|
|
8
|
+
global _engine
|
|
9
|
+
|
|
10
|
+
if _engine is None:
|
|
11
|
+
_engine = SecureEngine(
|
|
12
|
+
policy_loader=PolicyLoader(),
|
|
13
|
+
role_resolver=default_role_resolver,
|
|
14
|
+
resource_resolver=default_resource_resolver,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
return _engine
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class SecureEngine:
|
|
2
|
+
def __init__(self, policy_loader, role_resolver, resource_resolver):
|
|
3
|
+
self.policy_loader = policy_loader
|
|
4
|
+
self.role_resolver = role_resolver
|
|
5
|
+
self.resource_resolver = resource_resolver
|
|
6
|
+
|
|
7
|
+
def authorize(self, context):
|
|
8
|
+
user = context.user
|
|
9
|
+
resource = context.resource
|
|
10
|
+
action = context.action
|
|
11
|
+
resource_id = context.resource_id
|
|
12
|
+
tenant_id = context.tenant_id
|
|
13
|
+
|
|
14
|
+
# Step 1: Get role
|
|
15
|
+
role = self.role_resolver(user, tenant_id)
|
|
16
|
+
|
|
17
|
+
# Step 2: Get policy
|
|
18
|
+
allowed_roles = self.policy_loader(resource, action)
|
|
19
|
+
|
|
20
|
+
if role not in allowed_roles:
|
|
21
|
+
raise Exception(f"Role '{role}' not allowed")
|
|
22
|
+
|
|
23
|
+
# Step 3: Fetch object
|
|
24
|
+
obj = self.resource_resolver(resource, resource_id)
|
|
25
|
+
|
|
26
|
+
# Step 4: Tenant check
|
|
27
|
+
if hasattr(obj, "tenant_id") and obj.tenant_id != tenant_id:
|
|
28
|
+
raise Exception("Cross-tenant access denied")
|
|
29
|
+
|
|
30
|
+
# Step 5: Ownership check
|
|
31
|
+
if "owner" in allowed_roles:
|
|
32
|
+
if hasattr(obj, "created_by_id"):
|
|
33
|
+
if obj.created_by_id != user.id and role != "admin":
|
|
34
|
+
raise Exception("Not owner")
|
|
35
|
+
|
|
36
|
+
return True
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Custom exceptions used by the core engine."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Validation utilities for secure API rules."""
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Django middleware integration."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from rest_framework.permissions import BasePermission
|
|
2
|
+
from secureapi.core.engine import SecureEngine
|
|
3
|
+
from secureapi.core.context import RequestContext
|
|
4
|
+
from secureapi.config.loader import get_engine
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SecurePermission(BasePermission):
|
|
8
|
+
def has_permission(self, request, view):
|
|
9
|
+
return True # allow, actual check in object level
|
|
10
|
+
|
|
11
|
+
def has_object_permission(self, request, view, obj):
|
|
12
|
+
engine = get_engine()
|
|
13
|
+
|
|
14
|
+
resource = view.basename # e.g., "form"
|
|
15
|
+
action = self.map_method(request.method)
|
|
16
|
+
|
|
17
|
+
context = RequestContext(
|
|
18
|
+
user=request.user,
|
|
19
|
+
resource=resource,
|
|
20
|
+
action=action,
|
|
21
|
+
resource_id=obj.id,
|
|
22
|
+
tenant_id=self.get_tenant(request),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
engine.authorize(context)
|
|
27
|
+
return True
|
|
28
|
+
except Exception as e:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
def map_method(self, method):
|
|
32
|
+
return {
|
|
33
|
+
"GET": "read",
|
|
34
|
+
"POST": "create",
|
|
35
|
+
"PUT": "update",
|
|
36
|
+
"PATCH": "update",
|
|
37
|
+
"DELETE": "delete",
|
|
38
|
+
}.get(method, "read")
|
|
39
|
+
|
|
40
|
+
def get_tenant(self, request):
|
|
41
|
+
return request.headers.get("X-Tenant-ID")
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from fastapi import Depends, HTTPException
|
|
2
|
+
from secureapi.core.context import RequestContext
|
|
3
|
+
from secureapi.config.loader import get_engine
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def secure_dependency(resource, action):
|
|
7
|
+
def wrapper(user=Depends(get_current_user), tenant_id=None, resource_id=None):
|
|
8
|
+
engine = get_engine()
|
|
9
|
+
|
|
10
|
+
context = RequestContext(
|
|
11
|
+
user=user,
|
|
12
|
+
resource=resource,
|
|
13
|
+
action=action,
|
|
14
|
+
resource_id=resource_id,
|
|
15
|
+
tenant_id=tenant_id,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
engine.authorize(context)
|
|
20
|
+
except Exception as e:
|
|
21
|
+
raise HTTPException(status_code=403, detail=str(e))
|
|
22
|
+
|
|
23
|
+
return wrapper
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
from flask import request, jsonify
|
|
3
|
+
from secureapi.core.context import RequestContext
|
|
4
|
+
from secureapi.config.loader import get_engine
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def secure_endpoint(resource, action):
|
|
8
|
+
def decorator(func):
|
|
9
|
+
@wraps(func)
|
|
10
|
+
def wrapper(*args, **kwargs):
|
|
11
|
+
engine = get_engine()
|
|
12
|
+
|
|
13
|
+
user = request.user
|
|
14
|
+
tenant_id = request.headers.get("X-Tenant-ID")
|
|
15
|
+
resource_id = kwargs.get(f"{resource}_id")
|
|
16
|
+
|
|
17
|
+
context = RequestContext(
|
|
18
|
+
user=user,
|
|
19
|
+
resource=resource,
|
|
20
|
+
action=action,
|
|
21
|
+
resource_id=resource_id,
|
|
22
|
+
tenant_id=tenant_id,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
engine.authorize(context)
|
|
27
|
+
except Exception as e:
|
|
28
|
+
return jsonify({"error": str(e)}), 403
|
|
29
|
+
|
|
30
|
+
return func(*args, **kwargs)
|
|
31
|
+
return wrapper
|
|
32
|
+
return decorator
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""General helper utilities."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: secureapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package for secure API access control.
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pyyaml
|
|
10
|
+
Provides-Extra: django
|
|
11
|
+
Requires-Dist: django; extra == "django"
|
|
12
|
+
Requires-Dist: djangorestframework; extra == "django"
|
|
13
|
+
Provides-Extra: fastapi
|
|
14
|
+
Requires-Dist: fastapi; extra == "fastapi"
|
|
15
|
+
Provides-Extra: flask
|
|
16
|
+
Requires-Dist: flask; extra == "flask"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# secureapi
|
|
20
|
+
|
|
21
|
+
Secure API access control library.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
secureapi/__init__.py
|
|
5
|
+
secureapi.egg-info/PKG-INFO
|
|
6
|
+
secureapi.egg-info/SOURCES.txt
|
|
7
|
+
secureapi.egg-info/dependency_links.txt
|
|
8
|
+
secureapi.egg-info/requires.txt
|
|
9
|
+
secureapi.egg-info/top_level.txt
|
|
10
|
+
secureapi/config/__init__.py
|
|
11
|
+
secureapi/config/loader.py
|
|
12
|
+
secureapi/core/__init__.py
|
|
13
|
+
secureapi/core/context.py
|
|
14
|
+
secureapi/core/engine.py
|
|
15
|
+
secureapi/core/exceptions.py
|
|
16
|
+
secureapi/core/policy.py
|
|
17
|
+
secureapi/core/validator.py
|
|
18
|
+
secureapi/integrations/__init__.py
|
|
19
|
+
secureapi/integrations/django/__init__.py
|
|
20
|
+
secureapi/integrations/django/middleware.py
|
|
21
|
+
secureapi/integrations/django/permission.py
|
|
22
|
+
secureapi/integrations/fastapi/__init__.py
|
|
23
|
+
secureapi/integrations/fastapi/dependency.py
|
|
24
|
+
secureapi/integrations/flask/__init__.py
|
|
25
|
+
secureapi/integrations/flask/decorator.py
|
|
26
|
+
secureapi/utils/__init__.py
|
|
27
|
+
secureapi/utils/helpers.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
secureapi
|