Skip to content

Commit c766685

Browse files
Added more permsison
1 parent 797fc7b commit c766685

File tree

8 files changed

+145
-12
lines changed

8 files changed

+145
-12
lines changed

nxtbn/core/admin_permissions.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
"""
2+
Dashboard User Permission Hierarchy:
3+
4+
- `is_superuser`:
5+
Grants unrestricted access to all dashboard functionalities without any limitations.
6+
Inherits all permissions of `is_store_admin` by default.
7+
8+
- `is_staff`:
9+
Required for all users to access the dashboard.
10+
11+
- `is_store_admin`:
12+
Has extensive permissions and authority within the dashboard.
13+
Can perform almost all operations except a few critical ones restricted to the superuser.
14+
Inherits all permissions assigned to other users by default.
15+
16+
- `is_store_staff`:
17+
Has limited access to the dashboard.
18+
Their permissions can be extended granularly by assigning specific permissions, which are managed by the store admin.
19+
20+
**Additional Notes:**
21+
- All users have read permissions by default, except for certain critical data that require explicit authorization.
22+
- The superuser automatically inherits all permissions of a store admin.
23+
- A store admin inherits all permissions granted to other users by default.
24+
"""
25+
26+
27+
128
from rest_framework.permissions import BasePermission
229
from rest_framework.permissions import SAFE_METHODS
330

@@ -7,22 +34,41 @@
734
import functools
835
from graphql import GraphQLError
936

10-
class IsStaffUser(BasePermission):
37+
38+
class IsStoreAdmin(BasePermission):
39+
def has_permission(self, request, view):
40+
if not request.user.is_staff:
41+
return False
42+
return request.user.is_store_admin
43+
44+
class IsStoreStaff(BasePermission):
1145
def has_permission(self, request, view):
12-
return request.user.is_staff
46+
if not request.user.is_staff:
47+
return False
48+
49+
if request.user.is_superuser:
50+
return True
51+
52+
if request.user.is_store_admin:
53+
return True
54+
55+
return request.user.is_store_staff
1356

1457
class GranularPermission(BasePermission):
1558
def get_permission_name(self, model_name, action):
1659

1760
return f"{model_name}.{action}"
1861

1962
def has_permission(self, request, view):
20-
if not request.user.is_authenticated:
63+
if not request.user.is_staff:
2164
return False
2265

2366
if request.user.is_superuser:
2467
return True
2568

69+
if request.user.is_store_admin:
70+
return True
71+
2672
if request.method in SAFE_METHODS and request.user.is_staff: # Every staff can view
2773
return True
2874

@@ -47,12 +93,15 @@ def has_permission(self, request, view):
4793
class CommonPermissions(BasePermission):
4894

4995
def has_permission(self, request, view):
50-
if not request.user.is_authenticated:
96+
if not request.user.is_staff:
5197
return False
5298

5399
if request.user.is_superuser:
54100
return True
55101

102+
if request.user.is_store_admin:
103+
return True
104+
56105
if request.method in SAFE_METHODS and request.user.is_staff: # Every staff can view
57106
return True
58107

@@ -88,11 +137,14 @@ def has_permission(self, request, view):
88137

89138

90139
def has_required_perm(user, code: str, model_cls=None):
91-
if not user.is_authenticated:
140+
if not user.is_staff:
92141
return False
93142

94143
if user.is_superuser:
95144
return True
145+
146+
if user.is_store_admin:
147+
return True
96148

97149
perm_code = model_cls._meta.app_label + '.' + code
98150
return user.has_perm(perm_code)

nxtbn/order/api/dashboard/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import django_filters
2222
from django_filters import rest_framework as filters
2323

24-
from nxtbn.core.admin_permissions import GranularPermission, CommonPermissions, IsStaffUser, has_required_perm
24+
from nxtbn.core.admin_permissions import GranularPermission, CommonPermissions, has_required_perm
2525
from nxtbn.core.enum_perms import PermissionsEnum
2626
from nxtbn.core.utils import to_currency_unit
2727
from nxtbn.order.proccesor.views import OrderProccessorAPIView

nxtbn/tax/api/dashboard/views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from rest_framework.permissions import AllowAny
55
from rest_framework.exceptions import APIException
66

7+
from nxtbn.core.admin_permissions import CommonPermissions
78
from nxtbn.core.paginator import NxtbnPagination
89

910
from nxtbn.tax.models import TaxClass, TaxClassTranslation, TaxRate
@@ -20,17 +21,23 @@
2021
from nxtbn.users import UserRole
2122

2223
class TaxClassView(generics.ListCreateAPIView):
24+
permission_classes = (CommonPermissions, )
25+
model = TaxClass
2326
queryset = TaxClass.objects.all()
2427
serializer_class = TaxClassSerializer
2528
pagination_class = NxtbnPagination
2629

2730

2831
class TaxClassDetailsView(generics.RetrieveUpdateDestroyAPIView):
32+
permission_classes = (CommonPermissions, )
33+
model = TaxClass
2934
queryset = TaxClass.objects.all()
3035
serializer_class = TaxClassDetailSerializer
3136
lookup_field = 'id'
3237

3338
class TaxRateListCreateAPIView(generics.ListCreateAPIView):
39+
permission_classes = (CommonPermissions, )
40+
model = TaxClass
3441
serializer_class = TaxRateSerializer
3542
queryset = TaxRate.objects.all()
3643
filter_backends = [
@@ -43,6 +50,8 @@ class TaxRateListCreateAPIView(generics.ListCreateAPIView):
4350

4451

4552
class TaxRateRetrieveUpdateDelete(generics.RetrieveUpdateDestroyAPIView):
53+
permission_classes = (CommonPermissions, )
54+
model = TaxClass
4655
serializer_class = TaxRateSerializer
4756
queryset = TaxRate.objects.all()
4857
lookup_field = 'id'

nxtbn/users/admin_queries.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
import graphene
22

3-
from nxtbn.users.admin_types import PermissionType
3+
from nxtbn.users.admin_types import AdminUserType, PermissionType
44
from django.contrib.auth.models import Permission
5+
from graphene_django.filter import DjangoFilterConnectionField
56

67
from nxtbn.users.models import User
78

89

910
class UserAdminQuery(graphene.ObjectType):
11+
users = DjangoFilterConnectionField(AdminUserType)
12+
user = graphene.Field(AdminUserType, id=graphene.Int(required=True))
1013
permissions = graphene.List(PermissionType, user_id=graphene.Int(required=True))
1114

15+
def resolve_users(self, info, **kwargs):
16+
return User.objects.all()
17+
18+
def resolve_user(self, info, id):
19+
try:
20+
user = User.objects.get(id=id)
21+
except User.DoesNotExist:
22+
raise Exception("User not found")
23+
24+
return user
25+
1226
def resolve_permissions(self, info, user_id):
1327
# Get the user by the provided user_id
1428
try:
@@ -31,4 +45,4 @@ def resolve_permissions(self, info, user_id):
3145
)
3246
for permission in permissions
3347
]
34-
return permission_data
48+
return permission_data

nxtbn/users/admin_types.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,30 @@ class AdminUserType(DjangoObjectType):
88
db_id = graphene.ID(source='id')
99
class Meta:
1010
model = User
11-
fields = ('id', 'username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active', 'date_joined')
11+
fields = (
12+
'id',
13+
'username',
14+
'role',
15+
'email',
16+
'phone_number',
17+
'first_name',
18+
'last_name',
19+
'is_staff',
20+
'is_active',
21+
'is_superuser',
22+
'is_store_admin',
23+
'is_store_staff',
24+
'date_joined',
25+
)
1226
interfaces = (graphene.relay.Node, )
27+
filter_fields = {
28+
'id': ['exact'],
29+
'is_staff': ['exact'],
30+
'is_active': ['exact'],
31+
'is_superuser': ['exact'],
32+
'is_store_admin': ['exact'],
33+
'is_store_staff': ['exact'],
34+
}
1335

1436

1537
def resolve_full_name(self, info):

nxtbn/users/api/dashboard/serializers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class DashboardLoginSerializer(serializers.Serializer):
2020
class UserSerializer(serializers.ModelSerializer):
2121
class Meta:
2222
model = User
23-
fields = ['id', 'avatar', 'username', 'email', 'first_name', 'last_name', 'role', 'full_name', 'phone_number']
23+
fields = ['id', 'avatar', 'username', 'email', 'first_name', 'last_name', 'role', 'full_name', 'phone_number', 'is_store_admin', 'is_store_staff']
2424

2525

2626
class CustomerSerializer(serializers.ModelSerializer):
@@ -113,6 +113,8 @@ class Meta:
113113
'is_active',
114114
'is_staff',
115115
'is_superuser',
116+
'is_store_admin',
117+
'is_store_staff',
116118
'full_name',
117119
'password',
118120
'role'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 4.2.11 on 2025-01-31 17:30
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('users', '0004_alter_user_role'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='user',
15+
name='is_store_admin',
16+
field=models.BooleanField(default=False),
17+
),
18+
migrations.AddField(
19+
model_name='user',
20+
name='is_store_staff',
21+
field=models.BooleanField(default=False),
22+
),
23+
migrations.AlterField(
24+
model_name='user',
25+
name='role',
26+
field=models.CharField(default='Store Admin', max_length=255),
27+
),
28+
]

nxtbn/users/models.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@
1010

1111

1212
class User(AbstractUser):
13-
role = models.CharField(max_length=255, choices=UserRole.choices, default=UserRole.CUSTOMER)
13+
role = models.CharField(max_length=255, default='Store Admin')
14+
15+
# To learn more about permissions hierarchy, check this files: nxtbn/core/admin_permissions.py
16+
is_store_admin = models.BooleanField(default=False)
17+
is_store_staff = models.BooleanField(default=False)
18+
19+
1420
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
1521
phone_number = models.CharField(max_length=255, null=True, blank=True)
1622

1723
def __str__(self):
18-
parts = [self.get_full_name(), self.username, self.email, self.get_role_display()]
24+
parts = [self.get_full_name(), self.username, self.email]
1925
return " - ".join(part for part in parts if part)
2026

2127
def full_name(self):

0 commit comments

Comments
 (0)