You've already forked bthlabs-jsonrpc
Initial public releases
* `bthlabs-jsonrpc-aiohttp` v1.0.0 * `bthlabs-jsonrpc-core` v1.0.0 * `bthlabs-jsonrpc-django` v1.0.0
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
from .auth_checks import ( # noqa
|
||||
has_perms,
|
||||
is_authenticated,
|
||||
is_staff,
|
||||
)
|
||||
from .views import JSONRPCView # noqa
|
||||
|
||||
__version__ = '1.0.0'
|
||||
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
import importlib
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BTHLabsJSONRPCConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'bthlabs_jsonrpc_django'
|
||||
verbose_name = 'BTHLabs JSONRPC'
|
||||
|
||||
def ready(self):
|
||||
from django.conf import settings
|
||||
|
||||
for module_path in settings.JSONRPC_METHOD_MODULES:
|
||||
_ = importlib.import_module(module_path)
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
import typing
|
||||
|
||||
from django.http import HttpRequest
|
||||
|
||||
|
||||
def is_authenticated(request: HttpRequest) -> bool:
|
||||
"""Checks if the request user is authenticated and active."""
|
||||
return all((
|
||||
request.user.is_anonymous is False,
|
||||
request.user.is_active is True,
|
||||
))
|
||||
|
||||
|
||||
def is_staff(request: HttpRequest) -> bool:
|
||||
"""Checks if the request user is a staff user."""
|
||||
return request.user.is_staff
|
||||
|
||||
|
||||
def has_perms(perms: list[str]) -> typing.Callable:
|
||||
"""Checks if the request user has the specified permissions."""
|
||||
|
||||
def internal_has_perms(request: HttpRequest) -> bool:
|
||||
return request.user.has_perms(perms)
|
||||
|
||||
return internal_has_perms
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
import typing
|
||||
|
||||
from bthlabs_jsonrpc_core import Executor, JSONRPCAccessDeniedError
|
||||
from django.http import HttpRequest
|
||||
|
||||
from bthlabs_jsonrpc_django.serializer import DjangoJSONRPCSerializer
|
||||
|
||||
|
||||
class DjangoExecutor(Executor):
|
||||
serializer = DjangoJSONRPCSerializer
|
||||
|
||||
def __init__(self,
|
||||
request: HttpRequest,
|
||||
can_call: typing.Callable,
|
||||
namespace: typing.Optional[str] = None):
|
||||
super().__init__(namespace=namespace)
|
||||
self.request: HttpRequest = request
|
||||
self.can_call: typing.Callable = can_call
|
||||
|
||||
def enrich_args(self, args):
|
||||
return [self.request, *super().enrich_args(args)]
|
||||
|
||||
def before_call(self, method, args, kwargs):
|
||||
can_call = self.can_call(self.request, method, args, kwargs)
|
||||
if can_call is False:
|
||||
raise JSONRPCAccessDeniedError(data='can_call')
|
||||
@@ -0,0 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
from bthlabs_jsonrpc_core import JSONRPCSerializer
|
||||
from django.db.models import QuerySet
|
||||
|
||||
|
||||
class DjangoJSONRPCSerializer(JSONRPCSerializer):
|
||||
SEQUENCE_TYPES = (QuerySet, *JSONRPCSerializer.SEQUENCE_TYPES)
|
||||
122
packages/bthlabs-jsonrpc-django/bthlabs_jsonrpc_django/views.py
Normal file
122
packages/bthlabs-jsonrpc-django/bthlabs_jsonrpc_django/views.py
Normal file
@@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# django-jsonrpc-django | (c) 2022-present Tomek Wójcik | MIT License
|
||||
import typing
|
||||
|
||||
from bthlabs_jsonrpc_core import Executor
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.utils.decorators import classonlymethod
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic.base import View
|
||||
|
||||
from bthlabs_jsonrpc_django.executor import DjangoExecutor
|
||||
|
||||
|
||||
class JSONRPCView(View):
|
||||
"""
|
||||
The JSONRPC View. This is the main JSONRPC entry point. Use it to register
|
||||
your JSONRPC endpoints.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from bthlabs_jsonrpc_django import JSONRPCView, is_authenticated
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
'rpc/private',
|
||||
JSONRPCView.as_view(
|
||||
auth_checks=[is_authenticated],
|
||||
namespace='admin',
|
||||
),
|
||||
)
|
||||
path('rpc', JSONRPCView.as_view()),
|
||||
]
|
||||
"""
|
||||
|
||||
# pragma mark - Private class attributes
|
||||
|
||||
# The executor class.
|
||||
executor: Executor = DjangoExecutor
|
||||
|
||||
# pragma mark - Public class attributes
|
||||
|
||||
#: List of auth check functions.
|
||||
auth_checks: list[typing.Callable] = []
|
||||
|
||||
#: Namespace of this endpoint.
|
||||
namespace: typing.Optional[str] = None
|
||||
|
||||
# pragma mark - Private interface
|
||||
|
||||
def ensure_auth(self, request: HttpRequest) -> None:
|
||||
"""
|
||||
Runs auth checks (if any) and raises
|
||||
:py:exc:`django.core.exceptions.PermissionDenied` if any of them
|
||||
returns ``False``.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
if len(self.auth_checks) == []:
|
||||
return
|
||||
|
||||
has_auth = all((
|
||||
auth_check(request)
|
||||
for auth_check
|
||||
in self.auth_checks
|
||||
))
|
||||
if has_auth is False:
|
||||
raise PermissionDenied('This RPC endpoint requires auth.')
|
||||
|
||||
def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
"""
|
||||
Dispatches the *request*.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
if request.method.lower() in self.http_method_names:
|
||||
handler = getattr(
|
||||
self, request.method.lower(), self.http_method_not_allowed,
|
||||
)
|
||||
else:
|
||||
handler = self.http_method_not_allowed
|
||||
|
||||
self.ensure_auth(request)
|
||||
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
def post(self, request: HttpRequest) -> HttpResponse:
|
||||
"""
|
||||
The POST handler.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
executor = self.executor(
|
||||
request, self.can_call, self.namespace,
|
||||
)
|
||||
|
||||
serializer = executor.execute(request.body)
|
||||
if serializer is None:
|
||||
return HttpResponse('')
|
||||
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
|
||||
# pragma mark - Public interface
|
||||
|
||||
@classonlymethod
|
||||
def as_view(cls, **initkwargs):
|
||||
result = super().as_view(**initkwargs)
|
||||
|
||||
return csrf_exempt(result)
|
||||
|
||||
def can_call(self,
|
||||
request: HttpRequest,
|
||||
method: str,
|
||||
args: list,
|
||||
kwargs: dict) -> bool:
|
||||
"""
|
||||
Hook for subclasses to perform additional per-call permissions checks
|
||||
etc. The default implementation returns ``True``.
|
||||
"""
|
||||
return True
|
||||
Reference in New Issue
Block a user