django-permission-engine 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.
- django_permission_engine-0.1.0/CHANGELOG.md +35 -0
- django_permission_engine-0.1.0/LICENSE +21 -0
- django_permission_engine-0.1.0/MANIFEST.in +7 -0
- django_permission_engine-0.1.0/PKG-INFO +170 -0
- django_permission_engine-0.1.0/README.md +126 -0
- django_permission_engine-0.1.0/django_permission_engine/__init__.py +32 -0
- django_permission_engine-0.1.0/django_permission_engine/apps.py +37 -0
- django_permission_engine-0.1.0/django_permission_engine/management/__init__.py +0 -0
- django_permission_engine-0.1.0/django_permission_engine/management/commands/__init__.py +0 -0
- django_permission_engine-0.1.0/django_permission_engine/management/commands/upr_list.py +92 -0
- django_permission_engine-0.1.0/django_permission_engine/management/commands/upr_sync.py +144 -0
- django_permission_engine-0.1.0/django_permission_engine/management/commands/upr_validate.py +88 -0
- django_permission_engine-0.1.0/django_permission_engine/migrations/0001_initial.py +73 -0
- django_permission_engine-0.1.0/django_permission_engine/migrations/__init__.py +0 -0
- django_permission_engine-0.1.0/django_permission_engine/models.py +284 -0
- django_permission_engine-0.1.0/django_permission_engine/permission_management.py +361 -0
- django_permission_engine-0.1.0/django_permission_engine/permissions.py +259 -0
- django_permission_engine-0.1.0/django_permission_engine/registry.py +417 -0
- django_permission_engine-0.1.0/django_permission_engine/urls.py +16 -0
- django_permission_engine-0.1.0/django_permission_engine/views.py +213 -0
- django_permission_engine-0.1.0/django_permission_engine.egg-info/PKG-INFO +170 -0
- django_permission_engine-0.1.0/django_permission_engine.egg-info/SOURCES.txt +36 -0
- django_permission_engine-0.1.0/django_permission_engine.egg-info/dependency_links.txt +1 -0
- django_permission_engine-0.1.0/django_permission_engine.egg-info/requires.txt +2 -0
- django_permission_engine-0.1.0/django_permission_engine.egg-info/top_level.txt +1 -0
- django_permission_engine-0.1.0/pyproject.toml +70 -0
- django_permission_engine-0.1.0/setup.cfg +23 -0
- django_permission_engine-0.1.0/setup.py +48 -0
- django_permission_engine-0.1.0/tests/test_all.py +10 -0
- django_permission_engine-0.1.0/tests/test_catalog_api.py +150 -0
- django_permission_engine-0.1.0/tests/test_integration.py +119 -0
- django_permission_engine-0.1.0/tests/test_management_commands.py +227 -0
- django_permission_engine-0.1.0/tests/test_models.py +217 -0
- django_permission_engine-0.1.0/tests/test_performance.py +85 -0
- django_permission_engine-0.1.0/tests/test_permission_definition.py +192 -0
- django_permission_engine-0.1.0/tests/test_permissions.py +233 -0
- django_permission_engine-0.1.0/tests/test_registry.py +226 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release
|
|
12
|
+
- Permission registry engine
|
|
13
|
+
- Database models (Permission, Module, UserPermission)
|
|
14
|
+
- Declarative permission definitions with decorators
|
|
15
|
+
- DRF integration with PermissionRequired class
|
|
16
|
+
- Permission catalog API
|
|
17
|
+
- Management commands (upr_sync, upr_validate, upr_list)
|
|
18
|
+
- Comprehensive test suite
|
|
19
|
+
- Documentation
|
|
20
|
+
|
|
21
|
+
## [0.1.0] - 2024-01-15
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Core permission models
|
|
25
|
+
- Registry engine for permission synchronization
|
|
26
|
+
- Module and action decorators
|
|
27
|
+
- Runtime permission resolution
|
|
28
|
+
- DRF PermissionRequired class
|
|
29
|
+
- Permission catalog API endpoints
|
|
30
|
+
- Management commands for sync, validate, and list
|
|
31
|
+
- Test infrastructure and test suite
|
|
32
|
+
- Sphinx documentation setup
|
|
33
|
+
|
|
34
|
+
[Unreleased]: https://github.com/yourusername/django-permission-engine/compare/v0.1.0...HEAD
|
|
35
|
+
[0.1.0]: https://github.com/yourusername/django-permission-engine/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-permission-engine
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified Permission Registry (UPR) for Django & DRF
|
|
5
|
+
Home-page: https://github.com/sarthaksnh5/django_permission_engine
|
|
6
|
+
Author: Sarthak
|
|
7
|
+
Author-email: sarthaksnh5@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Documentation, https://django-permission-engine.readthedocs.io/
|
|
10
|
+
Project-URL: Source, https://github.com/sarthaksnh5/django_permission_engine
|
|
11
|
+
Project-URL: Tracker, https://github.com/sarthaksnh5/django_permission_engine/issues
|
|
12
|
+
Keywords: django permissions drf rest-framework
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Framework :: Django
|
|
22
|
+
Classifier: Framework :: Django :: 3.2
|
|
23
|
+
Classifier: Framework :: Django :: 4.0
|
|
24
|
+
Classifier: Framework :: Django :: 4.1
|
|
25
|
+
Classifier: Framework :: Django :: 4.2
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: Django>=3.2
|
|
30
|
+
Requires-Dist: djangorestframework>=3.12
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: keywords
|
|
38
|
+
Dynamic: license
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: project-url
|
|
41
|
+
Dynamic: requires-dist
|
|
42
|
+
Dynamic: requires-python
|
|
43
|
+
Dynamic: summary
|
|
44
|
+
|
|
45
|
+
# Django Permission Engine
|
|
46
|
+
|
|
47
|
+
Unified Permission Registry (UPR) for Django & DRF
|
|
48
|
+
|
|
49
|
+
## Overview
|
|
50
|
+
|
|
51
|
+
Django Permission Engine provides a single-source, action-aware, declarative permission system for Django and Django REST Framework. It eliminates permission drift and provides a maintainable foundation for complex permission requirements.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- 🎯 **Single Source of Truth** - Define permissions once, everything else is derived
|
|
56
|
+
- 🔑 **Permission Keys** - Simple, immutable, string-based permission identifiers
|
|
57
|
+
- 🎬 **Action-Aware** - DRF actions automatically map to permissions
|
|
58
|
+
- 📊 **Frontend-Ready** - Permission catalog API for frontend consumption
|
|
59
|
+
- 🚫 **Drift Prevention** - Startup validation ensures code and database never drift
|
|
60
|
+
- ⚡ **Performance** - O(1) permission checks with optional caching
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install django-permission-engine
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Note:** This library uses flexible version requirements (minimum versions only) to avoid conflicts with your existing Django and DRF installations. It will work with any compatible version you already have installed.
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
### 1. Add to INSTALLED_APPS
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
INSTALLED_APPS = [
|
|
76
|
+
# ... other apps
|
|
77
|
+
'rest_framework',
|
|
78
|
+
'django_permission_engine',
|
|
79
|
+
]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 2. Configure
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
UPR_CONFIG = {
|
|
86
|
+
'validate_on_startup': True,
|
|
87
|
+
'strict_mode': True,
|
|
88
|
+
'auto_sync': False,
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Define Permissions
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# upr_config.py
|
|
96
|
+
from django_permission_engine import module, action
|
|
97
|
+
|
|
98
|
+
@module('users', label='User Management')
|
|
99
|
+
class UsersModule:
|
|
100
|
+
crud = ['view', 'create', 'update', 'delete']
|
|
101
|
+
actions = ['reset_password']
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 4. Sync to Database
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python manage.py upr_sync
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 5. Assign Permissions to Users
|
|
111
|
+
|
|
112
|
+
Use the Permission Management API (admin only):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Assign permission to user
|
|
116
|
+
POST /api/permissions/users/1/assign/
|
|
117
|
+
{
|
|
118
|
+
"permission_key": "users.view"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Bulk assign to multiple users
|
|
122
|
+
POST /api/permissions/bulk-assign/
|
|
123
|
+
{
|
|
124
|
+
"permission_keys": ["users.view", "users.create"],
|
|
125
|
+
"user_ids": [1, 2, 3]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Or programmatically:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from django_permission_engine.models import Permission, UserPermission
|
|
133
|
+
|
|
134
|
+
user = User.objects.get(username='john')
|
|
135
|
+
permission = Permission.objects.get(key='users.view')
|
|
136
|
+
UserPermission.objects.get_or_create(user=user, permission=permission)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 6. Use in ViewSets
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from rest_framework import viewsets
|
|
143
|
+
from django_permission_engine.permissions import PermissionRequired
|
|
144
|
+
|
|
145
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
146
|
+
permission_classes = [PermissionRequired]
|
|
147
|
+
module = 'users'
|
|
148
|
+
queryset = User.objects.all()
|
|
149
|
+
serializer_class = UserSerializer
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Documentation
|
|
153
|
+
|
|
154
|
+
Full documentation is available in the `docs/` folder:
|
|
155
|
+
|
|
156
|
+
- [Architecture](docs/architecture.md)
|
|
157
|
+
- [Core Concepts](docs/core-concepts.md)
|
|
158
|
+
- [Permission Definition](docs/permission-definition.md)
|
|
159
|
+
- [DRF Integration](docs/drf-integration.md)
|
|
160
|
+
- [API Reference](docs/catalog-api.md)
|
|
161
|
+
|
|
162
|
+
## Requirements
|
|
163
|
+
|
|
164
|
+
- Python 3.8+
|
|
165
|
+
- Django 3.2+
|
|
166
|
+
- Django REST Framework 3.12+
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT License
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Django Permission Engine
|
|
2
|
+
|
|
3
|
+
Unified Permission Registry (UPR) for Django & DRF
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Django Permission Engine provides a single-source, action-aware, declarative permission system for Django and Django REST Framework. It eliminates permission drift and provides a maintainable foundation for complex permission requirements.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🎯 **Single Source of Truth** - Define permissions once, everything else is derived
|
|
12
|
+
- 🔑 **Permission Keys** - Simple, immutable, string-based permission identifiers
|
|
13
|
+
- 🎬 **Action-Aware** - DRF actions automatically map to permissions
|
|
14
|
+
- 📊 **Frontend-Ready** - Permission catalog API for frontend consumption
|
|
15
|
+
- 🚫 **Drift Prevention** - Startup validation ensures code and database never drift
|
|
16
|
+
- ⚡ **Performance** - O(1) permission checks with optional caching
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install django-permission-engine
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Note:** This library uses flexible version requirements (minimum versions only) to avoid conflicts with your existing Django and DRF installations. It will work with any compatible version you already have installed.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Add to INSTALLED_APPS
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
INSTALLED_APPS = [
|
|
32
|
+
# ... other apps
|
|
33
|
+
'rest_framework',
|
|
34
|
+
'django_permission_engine',
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Configure
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
UPR_CONFIG = {
|
|
42
|
+
'validate_on_startup': True,
|
|
43
|
+
'strict_mode': True,
|
|
44
|
+
'auto_sync': False,
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Define Permissions
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
# upr_config.py
|
|
52
|
+
from django_permission_engine import module, action
|
|
53
|
+
|
|
54
|
+
@module('users', label='User Management')
|
|
55
|
+
class UsersModule:
|
|
56
|
+
crud = ['view', 'create', 'update', 'delete']
|
|
57
|
+
actions = ['reset_password']
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. Sync to Database
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python manage.py upr_sync
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 5. Assign Permissions to Users
|
|
67
|
+
|
|
68
|
+
Use the Permission Management API (admin only):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Assign permission to user
|
|
72
|
+
POST /api/permissions/users/1/assign/
|
|
73
|
+
{
|
|
74
|
+
"permission_key": "users.view"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Bulk assign to multiple users
|
|
78
|
+
POST /api/permissions/bulk-assign/
|
|
79
|
+
{
|
|
80
|
+
"permission_keys": ["users.view", "users.create"],
|
|
81
|
+
"user_ids": [1, 2, 3]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or programmatically:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from django_permission_engine.models import Permission, UserPermission
|
|
89
|
+
|
|
90
|
+
user = User.objects.get(username='john')
|
|
91
|
+
permission = Permission.objects.get(key='users.view')
|
|
92
|
+
UserPermission.objects.get_or_create(user=user, permission=permission)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 6. Use in ViewSets
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from rest_framework import viewsets
|
|
99
|
+
from django_permission_engine.permissions import PermissionRequired
|
|
100
|
+
|
|
101
|
+
class UserViewSet(viewsets.ModelViewSet):
|
|
102
|
+
permission_classes = [PermissionRequired]
|
|
103
|
+
module = 'users'
|
|
104
|
+
queryset = User.objects.all()
|
|
105
|
+
serializer_class = UserSerializer
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Documentation
|
|
109
|
+
|
|
110
|
+
Full documentation is available in the `docs/` folder:
|
|
111
|
+
|
|
112
|
+
- [Architecture](docs/architecture.md)
|
|
113
|
+
- [Core Concepts](docs/core-concepts.md)
|
|
114
|
+
- [Permission Definition](docs/permission-definition.md)
|
|
115
|
+
- [DRF Integration](docs/drf-integration.md)
|
|
116
|
+
- [API Reference](docs/catalog-api.md)
|
|
117
|
+
|
|
118
|
+
## Requirements
|
|
119
|
+
|
|
120
|
+
- Python 3.8+
|
|
121
|
+
- Django 3.2+
|
|
122
|
+
- Django REST Framework 3.12+
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT License
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django Permission Engine - Unified Permission Registry (UPR) for Django & DRF
|
|
3
|
+
"""
|
|
4
|
+
from .registry import (
|
|
5
|
+
PermissionRegistry,
|
|
6
|
+
PermissionDefinition,
|
|
7
|
+
get_registry,
|
|
8
|
+
registry,
|
|
9
|
+
module,
|
|
10
|
+
action,
|
|
11
|
+
)
|
|
12
|
+
from .permissions import (
|
|
13
|
+
PermissionResolver,
|
|
14
|
+
PermissionRequired,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__author__ = "Your Name"
|
|
19
|
+
__email__ = "your.email@example.com"
|
|
20
|
+
|
|
21
|
+
default_app_config = "django_permission_engine.apps.PermissionEngineConfig"
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PermissionRegistry",
|
|
25
|
+
"PermissionDefinition",
|
|
26
|
+
"get_registry",
|
|
27
|
+
"registry",
|
|
28
|
+
"module",
|
|
29
|
+
"action",
|
|
30
|
+
"PermissionResolver",
|
|
31
|
+
"PermissionRequired",
|
|
32
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django app configuration for Permission Engine
|
|
3
|
+
"""
|
|
4
|
+
from django.apps import AppConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PermissionEngineConfig(AppConfig):
|
|
8
|
+
"""Django app configuration for Permission Engine"""
|
|
9
|
+
|
|
10
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
11
|
+
name = "django_permission_engine"
|
|
12
|
+
verbose_name = "Permission Engine"
|
|
13
|
+
|
|
14
|
+
def ready(self):
|
|
15
|
+
"""Called when Django starts"""
|
|
16
|
+
from django.conf import settings
|
|
17
|
+
|
|
18
|
+
# Get UPR config
|
|
19
|
+
upr_config = getattr(settings, "UPR_CONFIG", {})
|
|
20
|
+
validate_on_startup = upr_config.get("validate_on_startup", False)
|
|
21
|
+
auto_sync = upr_config.get("auto_sync", False)
|
|
22
|
+
|
|
23
|
+
# Initialize registry if configured
|
|
24
|
+
if validate_on_startup or auto_sync:
|
|
25
|
+
from .registry import get_registry
|
|
26
|
+
registry = get_registry()
|
|
27
|
+
|
|
28
|
+
# Validate
|
|
29
|
+
if validate_on_startup:
|
|
30
|
+
errors = registry.validate()
|
|
31
|
+
if errors and registry.strict_mode:
|
|
32
|
+
from django.core.exceptions import ValidationError
|
|
33
|
+
raise ValidationError(f"Registry validation failed: {errors}")
|
|
34
|
+
|
|
35
|
+
# Auto-sync if configured
|
|
36
|
+
if auto_sync:
|
|
37
|
+
registry.sync()
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Management command to list permissions
|
|
3
|
+
"""
|
|
4
|
+
from django.core.management.base import BaseCommand
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from django_permission_engine.models import Permission
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Command(BaseCommand):
|
|
11
|
+
help = 'List all registered permissions'
|
|
12
|
+
|
|
13
|
+
def add_arguments(self, parser):
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
'--module',
|
|
16
|
+
type=str,
|
|
17
|
+
help='Filter by module',
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
'--type',
|
|
21
|
+
type=str,
|
|
22
|
+
choices=['crud', 'action'],
|
|
23
|
+
help='Filter by type',
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
'--format',
|
|
27
|
+
type=str,
|
|
28
|
+
choices=['table', 'json', 'simple'],
|
|
29
|
+
default='table',
|
|
30
|
+
help='Output format',
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def handle(self, *args, **options):
|
|
34
|
+
"""Execute list command"""
|
|
35
|
+
module_filter = options.get('module')
|
|
36
|
+
type_filter = options.get('type')
|
|
37
|
+
format_type = options['format']
|
|
38
|
+
|
|
39
|
+
# Get permissions
|
|
40
|
+
if module_filter:
|
|
41
|
+
permissions = Permission.objects.filter(module=module_filter)
|
|
42
|
+
else:
|
|
43
|
+
permissions = Permission.objects.all()
|
|
44
|
+
|
|
45
|
+
if type_filter:
|
|
46
|
+
if type_filter == 'crud':
|
|
47
|
+
permissions = permissions.filter(capability__in=['view', 'create', 'update', 'delete'])
|
|
48
|
+
else:
|
|
49
|
+
permissions = permissions.exclude(capability__in=['view', 'create', 'update', 'delete'])
|
|
50
|
+
|
|
51
|
+
permissions = permissions.order_by('module', 'capability')
|
|
52
|
+
|
|
53
|
+
# Display
|
|
54
|
+
if format_type == 'json':
|
|
55
|
+
self._display_json(permissions)
|
|
56
|
+
elif format_type == 'simple':
|
|
57
|
+
self._display_simple(permissions)
|
|
58
|
+
else:
|
|
59
|
+
self._display_table(permissions)
|
|
60
|
+
|
|
61
|
+
def _display_table(self, permissions):
|
|
62
|
+
"""Display permissions in table format"""
|
|
63
|
+
self.stdout.write('\nRegistered Permissions:\n')
|
|
64
|
+
self.stdout.write('-' * 80)
|
|
65
|
+
self.stdout.write('{:<40} {:<20} {:<20}'.format('Key', 'Module', 'Capability'))
|
|
66
|
+
self.stdout.write('-' * 80)
|
|
67
|
+
|
|
68
|
+
for perm in permissions:
|
|
69
|
+
self.stdout.write(
|
|
70
|
+
'{:<40} {:<20} {:<20}'.format(perm.key, perm.module, perm.capability)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.stdout.write('-' * 80)
|
|
74
|
+
self.stdout.write(f'\nTotal: {permissions.count()} permissions')
|
|
75
|
+
|
|
76
|
+
def _display_simple(self, permissions):
|
|
77
|
+
"""Display permissions in simple format"""
|
|
78
|
+
for perm in permissions:
|
|
79
|
+
self.stdout.write(perm.key)
|
|
80
|
+
|
|
81
|
+
def _display_json(self, permissions):
|
|
82
|
+
"""Display permissions in JSON format"""
|
|
83
|
+
data = [
|
|
84
|
+
{
|
|
85
|
+
'key': perm.key,
|
|
86
|
+
'module': perm.module,
|
|
87
|
+
'capability': perm.capability,
|
|
88
|
+
'label': perm.label,
|
|
89
|
+
}
|
|
90
|
+
for perm in permissions
|
|
91
|
+
]
|
|
92
|
+
self.stdout.write(json.dumps(data, indent=2))
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Management command to sync permissions with database
|
|
3
|
+
"""
|
|
4
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
5
|
+
from django.db import transaction
|
|
6
|
+
|
|
7
|
+
from django_permission_engine import get_registry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Command(BaseCommand):
|
|
11
|
+
help = 'Synchronize permission definitions with database'
|
|
12
|
+
|
|
13
|
+
def add_arguments(self, parser):
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
'--dry-run',
|
|
16
|
+
action='store_true',
|
|
17
|
+
help='Show what would change without making changes',
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
'--force',
|
|
21
|
+
action='store_true',
|
|
22
|
+
help='Force sync even with warnings',
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
'--clean-orphans',
|
|
26
|
+
action='store_true',
|
|
27
|
+
help='Delete orphaned permissions',
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
'--verbose',
|
|
31
|
+
action='store_true',
|
|
32
|
+
help='Show detailed output',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def handle(self, *args, **options):
|
|
36
|
+
"""Execute sync command"""
|
|
37
|
+
dry_run = options['dry_run']
|
|
38
|
+
force = options['force']
|
|
39
|
+
clean_orphans = options['clean_orphans']
|
|
40
|
+
verbose = options['verbose']
|
|
41
|
+
|
|
42
|
+
# Get registry
|
|
43
|
+
registry = get_registry()
|
|
44
|
+
|
|
45
|
+
# Set orphan action
|
|
46
|
+
if clean_orphans:
|
|
47
|
+
registry.orphan_action = 'delete'
|
|
48
|
+
elif not force:
|
|
49
|
+
registry.orphan_action = 'warn'
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
# Validate first
|
|
53
|
+
if verbose:
|
|
54
|
+
self.stdout.write('Validating permissions...')
|
|
55
|
+
|
|
56
|
+
errors = registry.validate()
|
|
57
|
+
if errors:
|
|
58
|
+
self.stdout.write(
|
|
59
|
+
self.style.ERROR(f'Validation errors found: {len(errors)}')
|
|
60
|
+
)
|
|
61
|
+
for error in errors:
|
|
62
|
+
self.stdout.write(self.style.ERROR(f' - {error}'))
|
|
63
|
+
|
|
64
|
+
if not force:
|
|
65
|
+
raise CommandError('Validation failed. Use --force to continue.')
|
|
66
|
+
|
|
67
|
+
# Sync
|
|
68
|
+
if dry_run:
|
|
69
|
+
self.stdout.write(self.style.WARNING('DRY RUN - No changes will be made'))
|
|
70
|
+
plan = registry.sync(dry_run=True)
|
|
71
|
+
self._display_plan(plan, verbose)
|
|
72
|
+
else:
|
|
73
|
+
self.stdout.write('Syncing permissions...')
|
|
74
|
+
result = registry.sync()
|
|
75
|
+
self._display_result(result, verbose)
|
|
76
|
+
self.stdout.write(
|
|
77
|
+
self.style.SUCCESS('Sync complete.')
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
raise CommandError(f'Sync failed: {e}')
|
|
82
|
+
|
|
83
|
+
def _display_plan(self, plan, verbose):
|
|
84
|
+
"""Display sync plan"""
|
|
85
|
+
self.stdout.write('\nSync Plan:')
|
|
86
|
+
|
|
87
|
+
if plan['create']:
|
|
88
|
+
self.stdout.write(
|
|
89
|
+
self.style.SUCCESS(f' Would create: {len(plan["create"])} permissions')
|
|
90
|
+
)
|
|
91
|
+
if verbose:
|
|
92
|
+
for perm in plan['create']:
|
|
93
|
+
self.stdout.write(f' - {perm.key}')
|
|
94
|
+
|
|
95
|
+
if plan['update']:
|
|
96
|
+
self.stdout.write(
|
|
97
|
+
self.style.WARNING(f' Would update: {len(plan["update"])} permissions')
|
|
98
|
+
)
|
|
99
|
+
if verbose:
|
|
100
|
+
for perm in plan['update']:
|
|
101
|
+
self.stdout.write(f' - {perm.key}')
|
|
102
|
+
|
|
103
|
+
if plan['orphaned']:
|
|
104
|
+
self.stdout.write(
|
|
105
|
+
self.style.ERROR(f' Orphaned: {len(plan["orphaned"])} permissions')
|
|
106
|
+
)
|
|
107
|
+
if verbose:
|
|
108
|
+
for perm in plan['orphaned']:
|
|
109
|
+
self.stdout.write(f' - {perm.key}')
|
|
110
|
+
|
|
111
|
+
unchanged = len(get_registry().get_all_permissions()) - len(plan['create']) - len(plan['update'])
|
|
112
|
+
if unchanged > 0:
|
|
113
|
+
self.stdout.write(f' Unchanged: {unchanged} permissions')
|
|
114
|
+
|
|
115
|
+
def _display_result(self, result, verbose):
|
|
116
|
+
"""Display sync result"""
|
|
117
|
+
if result['created']:
|
|
118
|
+
self.stdout.write(
|
|
119
|
+
self.style.SUCCESS(f' Created: {len(result["created"])} permissions')
|
|
120
|
+
)
|
|
121
|
+
if verbose:
|
|
122
|
+
for key in result['created']:
|
|
123
|
+
self.stdout.write(f' - {key}')
|
|
124
|
+
|
|
125
|
+
if result['updated']:
|
|
126
|
+
self.stdout.write(
|
|
127
|
+
self.style.WARNING(f' Updated: {len(result["updated"])} permissions')
|
|
128
|
+
)
|
|
129
|
+
if verbose:
|
|
130
|
+
for key in result['updated']:
|
|
131
|
+
self.stdout.write(f' - {key}')
|
|
132
|
+
|
|
133
|
+
if result['orphaned']:
|
|
134
|
+
self.stdout.write(
|
|
135
|
+
self.style.ERROR(f' Orphaned: {len(result["orphaned"])} permissions')
|
|
136
|
+
)
|
|
137
|
+
if verbose:
|
|
138
|
+
for key in result['orphaned']:
|
|
139
|
+
self.stdout.write(f' - {key}')
|
|
140
|
+
|
|
141
|
+
total = len(get_registry().get_all_permissions())
|
|
142
|
+
unchanged = total - len(result['created']) - len(result['updated'])
|
|
143
|
+
if unchanged > 0:
|
|
144
|
+
self.stdout.write(f' Unchanged: {unchanged} permissions')
|