Skip to content

Commit f2aafaf

Browse files
committed
refactor: replace AdminAPI with AdminSitePermission
1 parent b5db027 commit f2aafaf

File tree

15 files changed

+259
-241
lines changed

15 files changed

+259
-241
lines changed

easy/controller/admin_auto_api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from django.db import models
55
from ninja_extra import ControllerBase, api_controller
66

7-
from easy.controller.base import BaseAdminAPIController
8-
from easy.permissions import BaseApiPermission
7+
from easy.controller.base import CrudAPIController
8+
from easy.permissions import AdminSitePermission
99

1010
logger = logging.getLogger(__name__)
1111

@@ -17,7 +17,7 @@ class AdminClass(object):
1717
def create_admin_controller(
1818
model: models.Model, app_name: str
1919
) -> Union[Type[ControllerBase], Type]:
20-
"""Create AdminAPI class dynamically"""
20+
"""Create AdminAPI class dynamically, permission class set to AdminSitePermission"""
2121
model_name = model.__name__ # type:ignore
2222
Meta = type(
2323
"Meta",
@@ -31,7 +31,7 @@ def create_admin_controller(
3131
type,
3232
class_name,
3333
(
34-
BaseAdminAPIController,
34+
CrudAPIController,
3535
AdminClass,
3636
),
3737
{
@@ -43,5 +43,5 @@ def create_admin_controller(
4343
return api_controller(
4444
f"/{app_name}/{model_name.lower()}",
4545
tags=[f"{model_name} AdminAPI"],
46-
permissions=[BaseApiPermission],
46+
permissions=[AdminSitePermission],
4747
)(auto_cls)

easy/controller/base.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,12 @@
33

44
from ninja_extra import ControllerBase
55

6-
from easy.controller.meta import AdminApiMetaclass, CrudAPI, CrudApiMetaclass
6+
from easy.controller.meta import CrudAPI, CrudApiMetaclass
77
from easy.services import BaseService
88

99
logger = logging.getLogger(__name__)
1010

1111

12-
class BaseAdminAPIController(ControllerBase, CrudAPI, metaclass=AdminApiMetaclass):
13-
"""For AdminAPI"""
14-
15-
def __init__(self, service: Union["BaseService", None] = None):
16-
super().__init__(service=service) # pragma: no cover
17-
18-
1912
class CrudAPIController(ControllerBase, CrudAPI, metaclass=CrudApiMetaclass):
2013
"""For Client facing APIs"""
2114

easy/controller/meta.py

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,8 @@ async def filter_exclude_objs(self, filters: Union[str, bytes]) -> Any:
8585
# return await self.service.recover_obj()
8686

8787

88-
class AdminCrudAPI(CrudAPI):
89-
...
90-
91-
9288
class CrudApiMetaclass(ABCMeta):
9389
def __new__(mcs, name: str, bases: Tuple[Type[Any], ...], attrs: dict) -> Any:
94-
return mcs.generate_cls(mcs, name, (ControllerBase, CrudAPI), attrs)
95-
96-
@staticmethod
97-
def generate_cls(
98-
mcs: Type[Any], name: str, bases: Tuple[Type[Any], ...], attrs: dict
99-
) -> Type[Any]:
10090
# Get configs from Meta
10191
temp_base: Type = type.__new__(type, "object", (), {})
10292
temp_cls: Type = super(CrudApiMetaclass, mcs).__new__(
@@ -177,7 +167,9 @@ async def patch_obj( # type: ignore
177167
}
178168
)
179169

180-
new_base: Type = type.__new__(type, name, bases, base_cls_attrs)
170+
new_base: Type = type.__new__(
171+
type, name, (ControllerBase, CrudAPI), base_cls_attrs
172+
)
181173
new_cls: Type = super(CrudApiMetaclass, mcs).__new__(
182174
mcs, name, (new_base,), attrs
183175
)
@@ -190,11 +182,6 @@ async def patch_obj( # type: ignore
190182
return new_cls
191183

192184

193-
class AdminApiMetaclass(CrudApiMetaclass):
194-
def __new__(mcs, name: str, bases: Union[Type], attrs: dict) -> Any:
195-
return mcs.generate_cls(mcs, name, (ControllerBase, AdminCrudAPI), attrs)
196-
197-
198185
class ModelOptions:
199186
def __init__(self, options: object = None):
200187
self.model: Optional[Type[models.Model]] = getattr(options, "model", None)

easy/main.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -152,41 +152,3 @@ def create_response(
152152
status=status,
153153
temporal_response=temporal_response,
154154
) # pragma: no cover
155-
156-
157-
class EasyAdminAPI(EasyAPI):
158-
def __init__(
159-
self,
160-
*,
161-
title: str = "Easy AdminAPI",
162-
version: str = "1.0.0",
163-
description: str = "",
164-
openapi_url: Optional[str] = "/openapi.json",
165-
docs_url: Optional[str] = "/docs",
166-
urls_namespace: Optional[str] = None,
167-
csrf: bool = False,
168-
auth: Union[
169-
Sequence[Callable[..., Any]], Callable[..., Any], NOT_SET_TYPE, None
170-
] = NOT_SET,
171-
renderer: Optional[BaseRenderer] = EasyJSONRenderer(),
172-
parser: Optional[Parser] = None,
173-
app_name: str = "ninja",
174-
easy_extra: bool = True,
175-
easy_output: bool = True,
176-
) -> None:
177-
super(EasyAdminAPI, self).__init__(
178-
title=title,
179-
version=version,
180-
description=description,
181-
openapi_url=openapi_url,
182-
docs_url=docs_url,
183-
urls_namespace=urls_namespace,
184-
csrf=csrf,
185-
auth=auth,
186-
renderer=renderer,
187-
parser=parser,
188-
app_name=app_name,
189-
easy_output=easy_output,
190-
)
191-
self.easy_extra = easy_extra
192-
self.easy_output = easy_output

easy/permissions/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
IsAuthenticatedOrReadOnly,
66
)
77

8+
from .adminsite import AdminSitePermission
89
from .base import BaseApiPermission
910
from .superuser import IsSuperUser
1011

1112
__all__ = [
12-
"BaseApiPermission",
13-
"IsSuperUser",
1413
"AllowAny",
14+
"AdminSitePermission",
15+
"BaseApiPermission",
1516
"IsAuthenticated",
16-
"IsAdminUser",
1717
"IsAuthenticatedOrReadOnly",
18+
"IsAdminUser",
19+
"IsSuperUser",
1820
]

easy/permissions/adminsite.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from typing import TYPE_CHECKING
2+
3+
from django.http import HttpRequest
4+
from ninja_extra.permissions import IsAdminUser
5+
6+
if TYPE_CHECKING:
7+
from ninja_extra.controllers.base import ControllerBase # pragma: no cover
8+
9+
10+
class AdminSitePermission(IsAdminUser):
11+
"""
12+
Allows access only to super user.
13+
"""
14+
15+
def has_permission(
16+
self, request: HttpRequest, controller: "ControllerBase"
17+
) -> bool:
18+
"""
19+
Return `True` if permission is granted, `False` otherwise.
20+
"""
21+
user = request.user or request.auth # type: ignore
22+
has_perm: bool = True
23+
if request.method == "DELETE":
24+
has_perm = bool(user.is_superuser) # type: ignore
25+
if request.method in ("PUT", "PATCH", "POST"):
26+
has_perm = bool(user.is_staff or user.is_superuser) # type: ignore
27+
return has_perm and super().has_permission(request, controller)
28+
29+
30+
#
31+
# class AdminSitePermissionV2(AdminSitePermission):
32+
# def has_permission(
33+
# self, request: HttpRequest, controller: "ControllerBase"
34+
# ) -> bool:
35+
# """
36+
# Return `True` if permission is granted, `False` otherwise.
37+
# """
38+
# user = request.user
39+
# has_perm = True
40+
# if hasattr(controller, "model"):
41+
# model = controller.model
42+
# app = model._meta.app_label
43+
# has_perm = user.has_perm(f"{app}.view_{model.__name__}")
44+
# if request.method in ("PUT",):
45+
# has_perm = user.has_perm(f"{app}.add_{model.__name__}")
46+
# if request.method in ("PATCH", "POST"):
47+
# has_perm = user.has_perm(f"{app}.change_{model.__name__}")
48+
# if request.method in ("DELETE",):
49+
# has_perm = user.has_perm(f"{app}.delete_{model.__name__}")
50+
# if user.is_superuser:
51+
# has_perm = True
52+
# return bool(user and user.is_authenticated and user.is_active and has_perm)
53+
#
54+
# async def has_permission(
55+
# self, request: HttpRequest, controller: "ControllerBase"
56+
# ) -> bool:
57+
# """
58+
# Return `True` if permission is granted, `False` otherwise.
59+
# """
60+
# user = request.user
61+
# has_perm = False
62+
# if hasattr(controller, "model"):
63+
# model = controller.model
64+
# app = model._meta.app_label
65+
# has_perm = await sync_to_async(user.has_perm)(
66+
# f"{app}.view_{model.__name__}"
67+
# )
68+
# if request.method in ("PUT",):
69+
# has_perm = await sync_to_async(user.has_perm)(
70+
# f"{app}.add_{model.__name__}"
71+
# )
72+
# if request.method in ("PATCH", "POST"):
73+
# has_perm = await sync_to_async(user.has_perm)(
74+
# f"{app}.change_{model.__name__}"
75+
# )
76+
# if request.method in ("DELETE",):
77+
# has_perm = await sync_to_async(user.has_perm)(
78+
# f"{app}.delete_{model.__name__}"
79+
# )
80+
# if user.is_superuser:
81+
# has_perm = True
82+
# return bool(user and user.is_authenticated and user.is_active and has_perm)

easy/services/permission.py

Lines changed: 4 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
import logging
2-
from typing import TYPE_CHECKING, Any, Union
2+
from typing import TYPE_CHECKING, Any
33

44
from django.http import HttpRequest
55

66
if TYPE_CHECKING:
7-
from django.contrib.auth.models import AbstractUser # pragma: no cover
87
from ninja_extra.controllers.base import ControllerBase # pragma: no cover
98

109

1110
logger = logging.getLogger(__name__)
1211

1312

1413
class PermissionService(object):
14+
"""Base permission service for extra customization needs"""
15+
1516
def check_permission(
1617
self, request: HttpRequest, controller: "ControllerBase"
1718
) -> bool:
1819
"""
1920
Return `True` if permission is granted, `False` otherwise.
2021
"""
21-
user: Union[AbstractUser] = request.user # type: ignore
22-
has_perm: bool = True
23-
if request.method == "DELETE":
24-
has_perm = bool(user.is_superuser)
25-
if request.method in ("PUT", "PATCH", "POST"):
26-
has_perm = bool(user.is_staff or user.is_superuser)
27-
return bool(user and user.is_authenticated and user.is_active and has_perm)
22+
return True
2823

2924
def check_object_permission(
3025
self, request: HttpRequest, controller: "ControllerBase", obj: Any
@@ -33,56 +28,3 @@ def check_object_permission(
3328
Return `True` if permission is granted, `False` otherwise.
3429
"""
3530
return True
36-
37-
#
38-
# def check_permission_v2(
39-
# self, request: HttpRequest, controller: "ControllerBase"
40-
# ) -> bool:
41-
# """
42-
# Return `True` if permission is granted, `False` otherwise.
43-
# """
44-
# user = request.user
45-
# has_perm = True
46-
# if hasattr(controller, "model"):
47-
# model = controller.model
48-
# app = model._meta.app_label
49-
# has_perm = user.has_perm(f"{app}.view_{model.__name__}")
50-
# if request.method in ("PUT",):
51-
# has_perm = user.has_perm(f"{app}.add_{model.__name__}")
52-
# if request.method in ("PATCH", "POST"):
53-
# has_perm = user.has_perm(f"{app}.change_{model.__name__}")
54-
# if request.method in ("DELETE",):
55-
# has_perm = user.has_perm(f"{app}.delete_{model.__name__}")
56-
# if user.is_superuser:
57-
# has_perm = True
58-
# return bool(user and user.is_authenticated and user.is_active and has_perm)
59-
#
60-
# async def async_check_permission(
61-
# self, request: HttpRequest, controller: "ControllerBase"
62-
# ) -> bool:
63-
# """
64-
# Return `True` if permission is granted, `False` otherwise.
65-
# """
66-
# user = request.user
67-
# has_perm = False
68-
# if hasattr(controller, "model"):
69-
# model = controller.model
70-
# app = model._meta.app_label
71-
# has_perm = await sync_to_async(user.has_perm)(
72-
# f"{app}.view_{model.__name__}"
73-
# )
74-
# if request.method in ("PUT",):
75-
# has_perm = await sync_to_async(user.has_perm)(
76-
# f"{app}.add_{model.__name__}"
77-
# )
78-
# if request.method in ("PATCH", "POST"):
79-
# has_perm = await sync_to_async(user.has_perm)(
80-
# f"{app}.change_{model.__name__}"
81-
# )
82-
# if request.method in ("DELETE",):
83-
# has_perm = await sync_to_async(user.has_perm)(
84-
# f"{app}.delete_{model.__name__}"
85-
# )
86-
# if user.is_superuser:
87-
# has_perm = True
88-
# return bool(user and user.is_authenticated and user.is_active and has_perm)

tests/demo_app/apis.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from easy.main import EasyAPI
22
from tests.demo_app.auth import jwt_auth_async
33
from tests.demo_app.controllers import (
4-
EasyAdminAPIController,
4+
AutoGenCrudAPIController,
55
EasyCrudAPIController,
6-
EasyCrudBasePermissionAPIController,
6+
PermissionAPIController,
77
)
88

99
api_unittest = EasyAPI(auth=jwt_auth_async)
1010
api_unittest.register_controllers(
1111
EasyCrudAPIController,
12-
EasyCrudBasePermissionAPIController,
13-
EasyAdminAPIController,
12+
PermissionAPIController,
13+
AutoGenCrudAPIController,
1414
)

tests/demo_app/conftest.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import pytest
44
from django.contrib.auth import get_user_model
55

6-
from easy.controller.base import BaseAdminAPIController, CrudAPIController
7-
from easy.main import EasyAdminAPI
6+
from easy.controller.base import CrudAPIController
87
from easy.testing import EasyTestClient
98
from tests.demo_app.auth import JWTAuthAsync, jwt_auth_async
109
from tests.demo_app.factories import UserFactory
@@ -44,30 +43,3 @@ def create_client(
4443

4544
yield create_client
4645
setattr(JWTAuthAsync, "__call__", orig_func)
47-
48-
49-
@pytest.fixture
50-
def easy_admin_api_client(user) -> EasyTestClient:
51-
"""For EasyAdminAPIAuthASync testings"""
52-
53-
orig_func = copy.deepcopy(JWTAuthAsync.__call__)
54-
55-
async def mock_func(self, request):
56-
setattr(request, "user", user)
57-
return True
58-
59-
setattr(JWTAuthAsync, "__call__", mock_func)
60-
61-
def create_client(
62-
api: BaseAdminAPIController,
63-
is_staff: bool = True,
64-
is_superuser: bool = False,
65-
):
66-
setattr(user, "is_staff", is_staff)
67-
setattr(user, "is_superuser", is_superuser)
68-
69-
client = EasyTestClient(api, auth=jwt_auth_async, api_cls=EasyAdminAPI)
70-
return client
71-
72-
yield create_client
73-
setattr(JWTAuthAsync, "__call__", orig_func)

0 commit comments

Comments
 (0)