oxutils 0.1.5__py3-none-any.whl → 0.1.12__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.
- oxutils/__init__.py +2 -2
- oxutils/audit/migrations/0001_initial.py +2 -2
- oxutils/audit/models.py +2 -2
- oxutils/constants.py +6 -0
- oxutils/jwt/auth.py +150 -1
- oxutils/jwt/models.py +81 -0
- oxutils/jwt/tokens.py +69 -0
- oxutils/jwt/utils.py +45 -0
- oxutils/logger/__init__.py +10 -0
- oxutils/logger/receivers.py +10 -6
- oxutils/logger/settings.py +2 -2
- oxutils/models/base.py +102 -0
- oxutils/models/fields.py +79 -0
- oxutils/oxiliere/apps.py +9 -1
- oxutils/oxiliere/authorization.py +45 -0
- oxutils/oxiliere/caches.py +13 -11
- oxutils/oxiliere/checks.py +31 -0
- oxutils/oxiliere/constants.py +3 -0
- oxutils/oxiliere/context.py +16 -0
- oxutils/oxiliere/exceptions.py +16 -0
- oxutils/oxiliere/management/commands/grant_tenant_owners.py +19 -0
- oxutils/oxiliere/management/commands/init_oxiliere_system.py +30 -11
- oxutils/oxiliere/middleware.py +65 -11
- oxutils/oxiliere/models.py +146 -9
- oxutils/oxiliere/permissions.py +28 -35
- oxutils/oxiliere/schemas.py +16 -6
- oxutils/oxiliere/signals.py +5 -0
- oxutils/oxiliere/utils.py +36 -1
- oxutils/pagination/cursor.py +367 -0
- oxutils/permissions/__init__.py +0 -0
- oxutils/permissions/actions.py +57 -0
- oxutils/permissions/admin.py +3 -0
- oxutils/permissions/apps.py +10 -0
- oxutils/permissions/caches.py +19 -0
- oxutils/permissions/checks.py +188 -0
- oxutils/permissions/constants.py +0 -0
- oxutils/permissions/controllers.py +344 -0
- oxutils/permissions/exceptions.py +60 -0
- oxutils/permissions/management/__init__.py +0 -0
- oxutils/permissions/management/commands/__init__.py +0 -0
- oxutils/permissions/management/commands/load_permission_preset.py +112 -0
- oxutils/permissions/migrations/0001_initial.py +112 -0
- oxutils/permissions/migrations/0002_alter_grant_role.py +19 -0
- oxutils/permissions/migrations/0003_alter_grant_options_alter_group_options_and_more.py +33 -0
- oxutils/permissions/migrations/__init__.py +0 -0
- oxutils/permissions/models.py +171 -0
- oxutils/permissions/perms.py +95 -0
- oxutils/permissions/queryset.py +92 -0
- oxutils/permissions/schemas.py +276 -0
- oxutils/permissions/services.py +663 -0
- oxutils/permissions/tests.py +3 -0
- oxutils/permissions/utils.py +628 -0
- oxutils/settings.py +14 -194
- oxutils/users/apps.py +1 -1
- oxutils/users/migrations/0001_initial.py +47 -0
- oxutils/users/migrations/0002_alter_user_first_name_alter_user_last_name.py +23 -0
- oxutils/users/models.py +2 -0
- oxutils/utils.py +25 -0
- {oxutils-0.1.5.dist-info → oxutils-0.1.12.dist-info}/METADATA +21 -11
- oxutils-0.1.12.dist-info/RECORD +122 -0
- oxutils/jwt/client.py +0 -123
- oxutils/jwt/constants.py +0 -1
- oxutils/s3/settings.py +0 -34
- oxutils/s3/storages.py +0 -130
- oxutils-0.1.5.dist-info/RECORD +0 -88
- /oxutils/{s3 → pagination}/__init__.py +0 -0
- {oxutils-0.1.5.dist-info → oxutils-0.1.12.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
CACHE_CHECK_PERMISSION = getattr(settings, 'CACHE_CHECK_PERMISSION', False)
|
|
6
|
+
|
|
7
|
+
if CACHE_CHECK_PERMISSION:
|
|
8
|
+
from cacheops import cached_as
|
|
9
|
+
from .models import Grant
|
|
10
|
+
from .utils import check
|
|
11
|
+
|
|
12
|
+
@cached_as(Grant, timeout=60*5)
|
|
13
|
+
def cache_check(user, scope, actions, group = None, **context):
|
|
14
|
+
return check(user, scope, actions, group, **context)
|
|
15
|
+
else:
|
|
16
|
+
from .utils import check
|
|
17
|
+
|
|
18
|
+
def cache_check(user, scope, actions, group = None, **context):
|
|
19
|
+
return check(user, scope, actions, group, **context)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django system checks for permissions configuration.
|
|
3
|
+
|
|
4
|
+
Example configuration in settings.py:
|
|
5
|
+
|
|
6
|
+
ACCESS_MANAGER_SCOPE = "access"
|
|
7
|
+
ACCESS_MANAGER_GROUP = "manager" # or None
|
|
8
|
+
ACCESS_MANAGER_CONTEXT = {}
|
|
9
|
+
|
|
10
|
+
CACHE_CHECK_PERMISSION = False
|
|
11
|
+
|
|
12
|
+
ACCESS_SCOPES = [
|
|
13
|
+
"users",
|
|
14
|
+
"articles",
|
|
15
|
+
"comments"
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
PERMISSION_PRESET = {
|
|
19
|
+
"roles": [...],
|
|
20
|
+
"group": [...],
|
|
21
|
+
"role_grants": [...]
|
|
22
|
+
}
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from django.conf import settings
|
|
26
|
+
from django.core.checks import Error, Warning, register, Tags
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@register(Tags.security)
|
|
30
|
+
def check_permission_settings(app_configs, **kwargs):
|
|
31
|
+
"""
|
|
32
|
+
Validate permission-related settings.
|
|
33
|
+
|
|
34
|
+
Checks:
|
|
35
|
+
- ACCESS_MANAGER_SCOPE is defined
|
|
36
|
+
- ACCESS_MANAGER_GROUP is defined (can be None)
|
|
37
|
+
- ACCESS_MANAGER_CONTEXT is defined
|
|
38
|
+
- ACCESS_SCOPES is defined
|
|
39
|
+
- PERMISSION_PRESET is defined
|
|
40
|
+
- ACCESS_MANAGER_SCOPE exists in ACCESS_SCOPES
|
|
41
|
+
- ACCESS_MANAGER_GROUP exists in PERMISSION_PRESET groups (if not None)
|
|
42
|
+
"""
|
|
43
|
+
errors = []
|
|
44
|
+
|
|
45
|
+
# Check ACCESS_MANAGER_SCOPE
|
|
46
|
+
if not hasattr(settings, 'ACCESS_MANAGER_SCOPE'):
|
|
47
|
+
errors.append(
|
|
48
|
+
Error(
|
|
49
|
+
'ACCESS_MANAGER_SCOPE is not defined',
|
|
50
|
+
hint='Add ACCESS_MANAGER_SCOPE = "access" to your settings',
|
|
51
|
+
id='permissions.E001',
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Check ACCESS_MANAGER_GROUP
|
|
56
|
+
if not hasattr(settings, 'ACCESS_MANAGER_GROUP'):
|
|
57
|
+
errors.append(
|
|
58
|
+
Error(
|
|
59
|
+
'ACCESS_MANAGER_GROUP is not defined',
|
|
60
|
+
hint='Add ACCESS_MANAGER_GROUP = "manager" or None to your settings',
|
|
61
|
+
id='permissions.E002',
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Check ACCESS_MANAGER_CONTEXT
|
|
66
|
+
if not hasattr(settings, 'ACCESS_MANAGER_CONTEXT'):
|
|
67
|
+
errors.append(
|
|
68
|
+
Error(
|
|
69
|
+
'ACCESS_MANAGER_CONTEXT is not defined',
|
|
70
|
+
hint='Add ACCESS_MANAGER_CONTEXT = {} to your settings',
|
|
71
|
+
id='permissions.E003',
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Check ACCESS_SCOPES
|
|
76
|
+
if not hasattr(settings, 'ACCESS_SCOPES'):
|
|
77
|
+
errors.append(
|
|
78
|
+
Error(
|
|
79
|
+
'ACCESS_SCOPES is not defined',
|
|
80
|
+
hint='Add ACCESS_SCOPES = ["users", "articles", ...] to your settings',
|
|
81
|
+
id='permissions.E004',
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
else:
|
|
85
|
+
# Validate ACCESS_SCOPES is a list
|
|
86
|
+
if not isinstance(settings.ACCESS_SCOPES, list):
|
|
87
|
+
errors.append(
|
|
88
|
+
Error(
|
|
89
|
+
'ACCESS_SCOPES must be a list',
|
|
90
|
+
hint='Set ACCESS_SCOPES = ["users", "articles", ...]',
|
|
91
|
+
id='permissions.E005',
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Check PERMISSION_PRESET
|
|
96
|
+
if not hasattr(settings, 'PERMISSION_PRESET'):
|
|
97
|
+
errors.append(
|
|
98
|
+
Warning(
|
|
99
|
+
'PERMISSION_PRESET is not defined',
|
|
100
|
+
hint='Add PERMISSION_PRESET dict to your settings or use load_permission_preset',
|
|
101
|
+
id='permissions.W001',
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
# Validate PERMISSION_PRESET structure
|
|
106
|
+
preset = settings.PERMISSION_PRESET
|
|
107
|
+
if not isinstance(preset, dict):
|
|
108
|
+
errors.append(
|
|
109
|
+
Error(
|
|
110
|
+
'PERMISSION_PRESET must be a dictionary',
|
|
111
|
+
id='permissions.E006',
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
# Check required keys
|
|
116
|
+
required_keys = ['roles', 'group', 'role_grants']
|
|
117
|
+
for key in required_keys:
|
|
118
|
+
if key not in preset:
|
|
119
|
+
errors.append(
|
|
120
|
+
Error(
|
|
121
|
+
f'PERMISSION_PRESET is missing required key: {key}',
|
|
122
|
+
hint=f'Add "{key}" key to PERMISSION_PRESET',
|
|
123
|
+
id=f'permissions.E007',
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Cross-validation: ACCESS_MANAGER_SCOPE in ACCESS_SCOPES
|
|
128
|
+
if (hasattr(settings, 'ACCESS_MANAGER_SCOPE') and
|
|
129
|
+
hasattr(settings, 'ACCESS_SCOPES') and
|
|
130
|
+
isinstance(settings.ACCESS_SCOPES, list)):
|
|
131
|
+
|
|
132
|
+
if settings.ACCESS_MANAGER_SCOPE not in settings.ACCESS_SCOPES:
|
|
133
|
+
errors.append(
|
|
134
|
+
Error(
|
|
135
|
+
f'ACCESS_MANAGER_SCOPE "{settings.ACCESS_MANAGER_SCOPE}" is not in ACCESS_SCOPES',
|
|
136
|
+
hint=f'Add "{settings.ACCESS_MANAGER_SCOPE}" to ACCESS_SCOPES list',
|
|
137
|
+
id='permissions.E008',
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Cross-validation: ACCESS_MANAGER_GROUP in PERMISSION_PRESET groups
|
|
142
|
+
if (hasattr(settings, 'ACCESS_MANAGER_GROUP') and
|
|
143
|
+
settings.ACCESS_MANAGER_GROUP is not None and
|
|
144
|
+
hasattr(settings, 'PERMISSION_PRESET') and
|
|
145
|
+
isinstance(settings.PERMISSION_PRESET, dict) and
|
|
146
|
+
'group' in settings.PERMISSION_PRESET):
|
|
147
|
+
|
|
148
|
+
group_slugs = [g.get('slug') for g in settings.PERMISSION_PRESET.get('group', [])]
|
|
149
|
+
|
|
150
|
+
if settings.ACCESS_MANAGER_GROUP not in group_slugs:
|
|
151
|
+
errors.append(
|
|
152
|
+
Error(
|
|
153
|
+
f'ACCESS_MANAGER_GROUP "{settings.ACCESS_MANAGER_GROUP}" is not in PERMISSION_PRESET groups',
|
|
154
|
+
hint=f'Add a group with slug "{settings.ACCESS_MANAGER_GROUP}" to PERMISSION_PRESET["group"]',
|
|
155
|
+
id='permissions.E009',
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Validate ACCESS_MANAGER_CONTEXT is a dict
|
|
160
|
+
if hasattr(settings, 'ACCESS_MANAGER_CONTEXT'):
|
|
161
|
+
if not isinstance(settings.ACCESS_MANAGER_CONTEXT, dict):
|
|
162
|
+
errors.append(
|
|
163
|
+
Error(
|
|
164
|
+
'ACCESS_MANAGER_CONTEXT must be a dictionary',
|
|
165
|
+
hint='Set ACCESS_MANAGER_CONTEXT = {}',
|
|
166
|
+
id='permissions.E010',
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Check CACHE_CHECK_PERMISSION and cacheops dependency
|
|
171
|
+
if hasattr(settings, 'CACHE_CHECK_PERMISSION') and settings.CACHE_CHECK_PERMISSION:
|
|
172
|
+
if not hasattr(settings, 'INSTALLED_APPS'):
|
|
173
|
+
errors.append(
|
|
174
|
+
Error(
|
|
175
|
+
'INSTALLED_APPS is not defined',
|
|
176
|
+
id='permissions.E011',
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
elif 'cacheops' not in settings.INSTALLED_APPS:
|
|
180
|
+
errors.append(
|
|
181
|
+
Error(
|
|
182
|
+
'CACHE_CHECK_PERMISSION is True but cacheops is not in INSTALLED_APPS',
|
|
183
|
+
hint='Add "cacheops" to INSTALLED_APPS or set CACHE_CHECK_PERMISSION = False',
|
|
184
|
+
id='permissions.E012',
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return errors
|
|
File without changes
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
from django.http import HttpRequest
|
|
4
|
+
from ninja_extra import (
|
|
5
|
+
api_controller,
|
|
6
|
+
ControllerBase,
|
|
7
|
+
http_get,
|
|
8
|
+
http_post,
|
|
9
|
+
http_put,
|
|
10
|
+
http_delete,
|
|
11
|
+
paginate,
|
|
12
|
+
)
|
|
13
|
+
from ninja_extra.permissions import IsAuthenticated
|
|
14
|
+
from ninja_extra.pagination import PageNumberPaginationExtra, PaginatedResponseSchema
|
|
15
|
+
from . import schemas
|
|
16
|
+
from .models import Role, Group, RoleGrant, Grant
|
|
17
|
+
from .services import PermissionService
|
|
18
|
+
from .perms import access_manager
|
|
19
|
+
from oxutils.exceptions import NotFoundException, ValidationException
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@api_controller(
|
|
25
|
+
"/access",
|
|
26
|
+
permissions=[
|
|
27
|
+
IsAuthenticated & access_manager('r')
|
|
28
|
+
]
|
|
29
|
+
)
|
|
30
|
+
class PermissionController(ControllerBase):
|
|
31
|
+
"""
|
|
32
|
+
Contrôleur pour la gestion des permissions, rôles et groupes.
|
|
33
|
+
"""
|
|
34
|
+
service = PermissionService()
|
|
35
|
+
|
|
36
|
+
@http_get('/scopes', response=List[str])
|
|
37
|
+
def list_scopes(self):
|
|
38
|
+
return getattr(settings, 'ACCESS_SCOPES', [])
|
|
39
|
+
|
|
40
|
+
@http_get("/roles", response=PaginatedResponseSchema[schemas.RoleSchema])
|
|
41
|
+
@paginate(PageNumberPaginationExtra, page_size=20)
|
|
42
|
+
def list_roles(self):
|
|
43
|
+
"""
|
|
44
|
+
Liste tous les rôles.
|
|
45
|
+
"""
|
|
46
|
+
return self.service.get_roles()
|
|
47
|
+
|
|
48
|
+
@http_get("/roles/{role_slug}", response=schemas.RoleSchema)
|
|
49
|
+
def get_role(self, role_slug: str):
|
|
50
|
+
"""
|
|
51
|
+
Récupère un rôle par son slug.
|
|
52
|
+
"""
|
|
53
|
+
return self.service.get_role(role_slug)
|
|
54
|
+
|
|
55
|
+
# Groupes
|
|
56
|
+
@http_post(
|
|
57
|
+
"/groups",
|
|
58
|
+
response=schemas.GroupSchema,
|
|
59
|
+
permissions=[
|
|
60
|
+
IsAuthenticated & access_manager('w')
|
|
61
|
+
]
|
|
62
|
+
)
|
|
63
|
+
def create_group(self, group_data: schemas.GroupCreateSchema):
|
|
64
|
+
"""
|
|
65
|
+
Crée un nouveau groupe de rôles.
|
|
66
|
+
"""
|
|
67
|
+
return self.service.create_group(group_data)
|
|
68
|
+
|
|
69
|
+
@http_get(
|
|
70
|
+
"/groups",
|
|
71
|
+
response=PaginatedResponseSchema[schemas.GroupSchema],
|
|
72
|
+
)
|
|
73
|
+
@paginate(PageNumberPaginationExtra, page_size=20)
|
|
74
|
+
def list_groups(self):
|
|
75
|
+
"""
|
|
76
|
+
Liste tous les groupes de rôles.
|
|
77
|
+
"""
|
|
78
|
+
return Group.objects.all()
|
|
79
|
+
|
|
80
|
+
@http_get(
|
|
81
|
+
"/groups/{group_slug}",
|
|
82
|
+
response=schemas.GroupSchema,
|
|
83
|
+
)
|
|
84
|
+
def get_group(self, group_slug: str):
|
|
85
|
+
"""
|
|
86
|
+
Récupère un groupe par son slug.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
return Group.objects.get(slug=group_slug)
|
|
90
|
+
except Group.DoesNotExist:
|
|
91
|
+
raise NotFoundException("Groupe non trouvé")
|
|
92
|
+
|
|
93
|
+
@http_put(
|
|
94
|
+
"/groups/{group_slug}",
|
|
95
|
+
response=schemas.GroupSchema,
|
|
96
|
+
permissions=[
|
|
97
|
+
IsAuthenticated & access_manager('ru')
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
def update_group(self, group_slug: str, group_data: schemas.GroupUpdateSchema):
|
|
101
|
+
"""
|
|
102
|
+
Met à jour un groupe existant.
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
group = Group.objects.get(slug=group_slug)
|
|
106
|
+
|
|
107
|
+
# Mise à jour des champs simples
|
|
108
|
+
for field, value in group_data.dict(exclude_unset=True, exclude={"roles"}).items():
|
|
109
|
+
setattr(group, field, value)
|
|
110
|
+
|
|
111
|
+
# Mise à jour des rôles si fournis
|
|
112
|
+
if group_data.roles is not None:
|
|
113
|
+
roles = Role.objects.filter(slug__in=group_data.roles)
|
|
114
|
+
group.roles.set(roles)
|
|
115
|
+
|
|
116
|
+
group.save()
|
|
117
|
+
return group
|
|
118
|
+
except Group.DoesNotExist:
|
|
119
|
+
raise NotFoundException("Groupe non trouvé")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
raise ValidationException(str(e))
|
|
122
|
+
|
|
123
|
+
@http_delete(
|
|
124
|
+
"/groups/{group_slug}",
|
|
125
|
+
response={
|
|
126
|
+
"204": None
|
|
127
|
+
},
|
|
128
|
+
permissions=[
|
|
129
|
+
IsAuthenticated & access_manager('d')
|
|
130
|
+
]
|
|
131
|
+
)
|
|
132
|
+
def delete_group(self, group_slug: str):
|
|
133
|
+
"""
|
|
134
|
+
Supprime un groupe.
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
group = Group.objects.get(slug=group_slug)
|
|
138
|
+
group.delete()
|
|
139
|
+
return None
|
|
140
|
+
except Group.DoesNotExist:
|
|
141
|
+
raise NotFoundException("Groupe non trouvé")
|
|
142
|
+
|
|
143
|
+
# Rôles des utilisateurs
|
|
144
|
+
@http_post(
|
|
145
|
+
"/users/assign-role",
|
|
146
|
+
response=schemas.RoleSchema,
|
|
147
|
+
permissions=[
|
|
148
|
+
IsAuthenticated & access_manager('rw')
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
def assign_role_to_user(self, data: schemas.AssignRoleSchema, request: HttpRequest):
|
|
152
|
+
"""
|
|
153
|
+
Assigne un rôle à un utilisateur.
|
|
154
|
+
"""
|
|
155
|
+
return self.service.assign_role_to_user(
|
|
156
|
+
user_id=data.user_id,
|
|
157
|
+
role_slug=data.role,
|
|
158
|
+
by_user=request.user if request.user.is_authenticated else None
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
@http_post(
|
|
162
|
+
"/users/revoke-role",
|
|
163
|
+
response={
|
|
164
|
+
"204": None
|
|
165
|
+
},
|
|
166
|
+
permissions=[
|
|
167
|
+
IsAuthenticated & access_manager('rw')
|
|
168
|
+
]
|
|
169
|
+
)
|
|
170
|
+
def revoke_role_from_user(self, data: schemas.RevokeRoleSchema):
|
|
171
|
+
"""
|
|
172
|
+
Révoque un rôle d'un utilisateur.
|
|
173
|
+
"""
|
|
174
|
+
self.service.revoke_role_from_user(
|
|
175
|
+
user_id=data.user_id,
|
|
176
|
+
role_slug=data.role
|
|
177
|
+
)
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
@http_post(
|
|
181
|
+
"/users/assign-group",
|
|
182
|
+
response=List[schemas.RoleSchema],
|
|
183
|
+
permissions=[
|
|
184
|
+
IsAuthenticated & access_manager('rw')
|
|
185
|
+
]
|
|
186
|
+
)
|
|
187
|
+
def assign_group_to_user(self, data: schemas.AssignGroupSchema, request: HttpRequest):
|
|
188
|
+
"""
|
|
189
|
+
Assigne un groupe de rôles à un utilisateur.
|
|
190
|
+
"""
|
|
191
|
+
return self.service.assign_group_to_user(
|
|
192
|
+
user_id=data.user_id,
|
|
193
|
+
group_slug=data.group,
|
|
194
|
+
by_user=request.user if request.user.is_authenticated else None
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
@http_post(
|
|
198
|
+
"/users/revoke-group",
|
|
199
|
+
response={
|
|
200
|
+
"204": None
|
|
201
|
+
},
|
|
202
|
+
permissions=[
|
|
203
|
+
IsAuthenticated & access_manager('rw')
|
|
204
|
+
]
|
|
205
|
+
)
|
|
206
|
+
def revoke_group_from_user(self, data: schemas.RevokeGroupSchema):
|
|
207
|
+
"""
|
|
208
|
+
Révoque un groupe de rôles d'un utilisateur.
|
|
209
|
+
"""
|
|
210
|
+
self.service.revoke_group_from_user(
|
|
211
|
+
user_id=data.user_id,
|
|
212
|
+
group_slug=data.group
|
|
213
|
+
)
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
@http_post(
|
|
217
|
+
"/groups/{group_slug}/sync",
|
|
218
|
+
response=schemas.GroupSyncResponseSchema,
|
|
219
|
+
permissions=[
|
|
220
|
+
IsAuthenticated & access_manager('rw')
|
|
221
|
+
]
|
|
222
|
+
)
|
|
223
|
+
def sync_group(self, group_slug: str):
|
|
224
|
+
"""
|
|
225
|
+
Synchronise les grants de tous les utilisateurs d'un groupe.
|
|
226
|
+
À appeler après modification des RoleGrants ou des rôles du groupe.
|
|
227
|
+
"""
|
|
228
|
+
return self.service.sync_group(group_slug)
|
|
229
|
+
|
|
230
|
+
# Grants
|
|
231
|
+
@http_post(
|
|
232
|
+
"/grants",
|
|
233
|
+
response=schemas.GrantSchema,
|
|
234
|
+
permissions=[
|
|
235
|
+
IsAuthenticated & access_manager('rw')
|
|
236
|
+
]
|
|
237
|
+
)
|
|
238
|
+
def create_grant(self, grant_data: schemas.GrantCreateSchema):
|
|
239
|
+
"""
|
|
240
|
+
Crée une nouvelle permission personnalisée pour un utilisateur.
|
|
241
|
+
"""
|
|
242
|
+
return self.service.create_grant(grant_data)
|
|
243
|
+
|
|
244
|
+
@http_get(
|
|
245
|
+
"/grants",
|
|
246
|
+
response=PaginatedResponseSchema[schemas.GrantSchema],
|
|
247
|
+
)
|
|
248
|
+
@paginate(PageNumberPaginationExtra, page_size=20)
|
|
249
|
+
def list_grants(self, user_id: Optional[int] = None, role: Optional[str] = None):
|
|
250
|
+
"""
|
|
251
|
+
Liste les grants, avec filtrage optionnel par utilisateur et/ou rôle.
|
|
252
|
+
"""
|
|
253
|
+
queryset = Grant.objects.all()
|
|
254
|
+
if user_id:
|
|
255
|
+
queryset = queryset.filter(user_id=user_id)
|
|
256
|
+
if role:
|
|
257
|
+
queryset = queryset.filter(role__slug=role)
|
|
258
|
+
return queryset
|
|
259
|
+
|
|
260
|
+
@http_put(
|
|
261
|
+
"/grants/{grant_id}",
|
|
262
|
+
response=schemas.GrantSchema,
|
|
263
|
+
permissions=[
|
|
264
|
+
IsAuthenticated & access_manager('ru')
|
|
265
|
+
]
|
|
266
|
+
)
|
|
267
|
+
def update_grant(self, grant_id: int, grant_data: schemas.GrantUpdateSchema):
|
|
268
|
+
"""
|
|
269
|
+
Met à jour une permission personnalisée.
|
|
270
|
+
"""
|
|
271
|
+
return self.service.update_grant(grant_id, grant_data)
|
|
272
|
+
|
|
273
|
+
@http_delete(
|
|
274
|
+
"/grants/{grant_id}",
|
|
275
|
+
response={
|
|
276
|
+
"204": None
|
|
277
|
+
},
|
|
278
|
+
permissions=[
|
|
279
|
+
IsAuthenticated & access_manager('d')
|
|
280
|
+
]
|
|
281
|
+
)
|
|
282
|
+
def delete_grant(self, grant_id: int):
|
|
283
|
+
"""
|
|
284
|
+
Supprime une permission personnalisée.
|
|
285
|
+
"""
|
|
286
|
+
self.service.delete_grant(grant_id)
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
# Role Grants
|
|
290
|
+
@http_post(
|
|
291
|
+
"/role-grants",
|
|
292
|
+
response=schemas.RoleGrantSchema,
|
|
293
|
+
permissions=[
|
|
294
|
+
IsAuthenticated & access_manager('rw')
|
|
295
|
+
]
|
|
296
|
+
)
|
|
297
|
+
def create_role_grant(self, grant_data: schemas.RoleGrantCreateSchema):
|
|
298
|
+
"""
|
|
299
|
+
Crée une nouvelle permission pour un rôle.
|
|
300
|
+
"""
|
|
301
|
+
return self.service.create_role_grant(grant_data)
|
|
302
|
+
|
|
303
|
+
@http_get(
|
|
304
|
+
"/role-grants",
|
|
305
|
+
response=PaginatedResponseSchema[schemas.RoleGrantSchema],
|
|
306
|
+
)
|
|
307
|
+
@paginate(PageNumberPaginationExtra, page_size=20)
|
|
308
|
+
def list_role_grants(self, role: Optional[str] = None):
|
|
309
|
+
"""
|
|
310
|
+
Liste les permissions de rôles, avec filtrage optionnel par rôle.
|
|
311
|
+
"""
|
|
312
|
+
queryset = RoleGrant.objects.select_related('role').all()
|
|
313
|
+
if role:
|
|
314
|
+
queryset = queryset.filter(role__slug=role)
|
|
315
|
+
return queryset
|
|
316
|
+
|
|
317
|
+
@http_put(
|
|
318
|
+
"/role-grants/{grant_id}",
|
|
319
|
+
response=schemas.RoleGrantSchema,
|
|
320
|
+
permissions=[
|
|
321
|
+
IsAuthenticated & access_manager('ru')
|
|
322
|
+
]
|
|
323
|
+
)
|
|
324
|
+
def update_role_grant(self, grant_id: int, grant_data: schemas.RoleGrantUpdateSchema):
|
|
325
|
+
"""
|
|
326
|
+
Met à jour une permission de rôle.
|
|
327
|
+
"""
|
|
328
|
+
return self.service.update_role_grant(grant_id, grant_data)
|
|
329
|
+
|
|
330
|
+
@http_delete(
|
|
331
|
+
"/role-grants/{grant_id}/",
|
|
332
|
+
response={
|
|
333
|
+
"204": None
|
|
334
|
+
},
|
|
335
|
+
permissions=[
|
|
336
|
+
IsAuthenticated & access_manager('d')
|
|
337
|
+
]
|
|
338
|
+
)
|
|
339
|
+
def delete_role_grant(self, grant_id: int):
|
|
340
|
+
"""
|
|
341
|
+
Supprime une permission de rôle.
|
|
342
|
+
"""
|
|
343
|
+
self.service.delete_role_grant(grant_id)
|
|
344
|
+
return None
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from django.utils.translation import gettext_lazy as _
|
|
2
|
+
from oxutils.exceptions import (
|
|
3
|
+
APIException,
|
|
4
|
+
NotFoundException,
|
|
5
|
+
ValidationException,
|
|
6
|
+
DuplicateEntryException,
|
|
7
|
+
PermissionDeniedException,
|
|
8
|
+
ExceptionCode
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RoleNotFoundException(NotFoundException):
|
|
13
|
+
"""Exception levée quand un rôle n'est pas trouvé."""
|
|
14
|
+
default_detail = _('Le rôle demandé n\'existe pas')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GroupNotFoundException(NotFoundException):
|
|
18
|
+
"""Exception levée quand un groupe n'est pas trouvé."""
|
|
19
|
+
default_detail = _('Le groupe demandé n\'existe pas')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class GrantNotFoundException(NotFoundException):
|
|
23
|
+
"""Exception levée quand un grant n'est pas trouvé."""
|
|
24
|
+
default_detail = _('Le grant demandé n\'existe pas')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RoleGrantNotFoundException(NotFoundException):
|
|
28
|
+
"""Exception levée quand un role grant n'est pas trouvé."""
|
|
29
|
+
default_detail = _('Le role grant demandé n\'existe pas')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RoleAlreadyAssignedException(DuplicateEntryException):
|
|
33
|
+
"""Exception levée quand un rôle est déjà assigné à un utilisateur."""
|
|
34
|
+
default_detail = _('Ce rôle est déjà assigné à l\'utilisateur')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class GroupAlreadyAssignedException(DuplicateEntryException):
|
|
38
|
+
"""Exception levée quand un groupe est déjà assigné à un utilisateur."""
|
|
39
|
+
default_detail = _('Ce groupe est déjà assigné à l\'utilisateur')
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class InvalidActionsException(ValidationException):
|
|
43
|
+
"""Exception levée quand des actions invalides sont fournies."""
|
|
44
|
+
default_detail = _('Les actions fournies sont invalides')
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InsufficientPermissionsException(PermissionDeniedException):
|
|
48
|
+
"""Exception levée quand l'utilisateur n'a pas les permissions suffisantes."""
|
|
49
|
+
default_code = ExceptionCode.INSUFFICIENT_PERMISSIONS
|
|
50
|
+
default_detail = _('Permissions insuffisantes pour effectuer cette action')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class RoleGrantConflictException(DuplicateEntryException):
|
|
54
|
+
"""Exception levée quand un role grant existe déjà pour ce rôle et scope."""
|
|
55
|
+
default_detail = _('Un role grant existe déjà pour ce rôle et ce scope')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class GrantConflictException(DuplicateEntryException):
|
|
59
|
+
"""Exception levée quand un grant existe déjà pour cet utilisateur et scope."""
|
|
60
|
+
default_detail = _('Un grant existe déjà pour cet utilisateur et ce scope')
|
|
File without changes
|
|
File without changes
|