drf-iam 0.0.1.post0__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.
- drf_iam/__init__.py +0 -0
- drf_iam/admin.py +29 -0
- drf_iam/apps.py +19 -0
- drf_iam/migrations/0001_initial.py +97 -0
- drf_iam/migrations/__init__.py +0 -0
- drf_iam/models.py +40 -0
- drf_iam/permissions.py +24 -0
- drf_iam/tests.py +3 -0
- drf_iam-0.0.1.post0.dist-info/METADATA +35 -0
- drf_iam-0.0.1.post0.dist-info/RECORD +12 -0
- drf_iam-0.0.1.post0.dist-info/WHEEL +5 -0
- drf_iam-0.0.1.post0.dist-info/top_level.txt +1 -0
drf_iam/__init__.py
ADDED
File without changes
|
drf_iam/admin.py
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
from django.contrib import admin
|
2
|
+
|
3
|
+
from drf_iam.models import Policy, Role, RolePolicy
|
4
|
+
|
5
|
+
|
6
|
+
@admin.register(Policy)
|
7
|
+
class PolicyAdmin(admin.ModelAdmin):
|
8
|
+
list_display = ('action', 'resource_type', 'description')
|
9
|
+
search_fields = ('action', 'resource_type')
|
10
|
+
list_filter = ('action', 'resource_type')
|
11
|
+
|
12
|
+
class PoliciesInline(admin.TabularInline):
|
13
|
+
model = Role.policies.through
|
14
|
+
|
15
|
+
@admin.register(Role)
|
16
|
+
class RoleAdmin(admin.ModelAdmin):
|
17
|
+
list_display = ('name', 'description')
|
18
|
+
search_fields = ('name', 'description')
|
19
|
+
list_filter = ('name', 'description')
|
20
|
+
inlines = [PoliciesInline]
|
21
|
+
|
22
|
+
|
23
|
+
@admin.register(RolePolicy)
|
24
|
+
class RolePolicyAdmin(admin.ModelAdmin):
|
25
|
+
list_display = ('role', 'policy')
|
26
|
+
search_fields = ('role', 'policy')
|
27
|
+
list_filter = ('role', 'policy')
|
28
|
+
|
29
|
+
|
drf_iam/apps.py
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from django.apps import AppConfig
|
4
|
+
from django.db.models.signals import post_migrate
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
class DrfIamConfig(AppConfig):
|
9
|
+
default_auto_field = "django.db.models.BigAutoField"
|
10
|
+
name = "drf_iam"
|
11
|
+
|
12
|
+
def ready(self):
|
13
|
+
if not hasattr(self, 'already_loaded'):
|
14
|
+
from drf_iam.utils.load_viewset_permissions import load_permissions_from_urls
|
15
|
+
post_migrate.connect(
|
16
|
+
load_permissions_from_urls,
|
17
|
+
dispatch_uid="drf_iam.utils.load_permissions_from_urls",
|
18
|
+
)
|
19
|
+
self.already_loaded = True
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Generated by Django 5.2 on 2025-04-19 11:30
|
2
|
+
|
3
|
+
import django.db.models.deletion
|
4
|
+
from django.db import migrations, models
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
initial = True
|
10
|
+
|
11
|
+
dependencies = []
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.CreateModel(
|
15
|
+
name="Role",
|
16
|
+
fields=[
|
17
|
+
(
|
18
|
+
"id",
|
19
|
+
models.BigAutoField(
|
20
|
+
auto_created=True,
|
21
|
+
primary_key=True,
|
22
|
+
serialize=False,
|
23
|
+
verbose_name="ID",
|
24
|
+
),
|
25
|
+
),
|
26
|
+
("name", models.CharField(max_length=100, unique=True)),
|
27
|
+
("description", models.TextField(blank=True)),
|
28
|
+
],
|
29
|
+
),
|
30
|
+
migrations.CreateModel(
|
31
|
+
name="Policy",
|
32
|
+
fields=[
|
33
|
+
(
|
34
|
+
"id",
|
35
|
+
models.BigAutoField(
|
36
|
+
auto_created=True,
|
37
|
+
primary_key=True,
|
38
|
+
serialize=False,
|
39
|
+
verbose_name="ID",
|
40
|
+
),
|
41
|
+
),
|
42
|
+
("action", models.CharField(max_length=100)),
|
43
|
+
("resource_type", models.CharField(max_length=100)),
|
44
|
+
("description", models.TextField(blank=True)),
|
45
|
+
],
|
46
|
+
options={
|
47
|
+
"verbose_name": "policy",
|
48
|
+
"verbose_name_plural": "policies",
|
49
|
+
"ordering": ["action", "resource_type"],
|
50
|
+
"unique_together": {("action", "resource_type")},
|
51
|
+
},
|
52
|
+
),
|
53
|
+
migrations.CreateModel(
|
54
|
+
name="RolePolicy",
|
55
|
+
fields=[
|
56
|
+
(
|
57
|
+
"id",
|
58
|
+
models.BigAutoField(
|
59
|
+
auto_created=True,
|
60
|
+
primary_key=True,
|
61
|
+
serialize=False,
|
62
|
+
verbose_name="ID",
|
63
|
+
),
|
64
|
+
),
|
65
|
+
(
|
66
|
+
"policy",
|
67
|
+
models.ForeignKey(
|
68
|
+
on_delete=django.db.models.deletion.CASCADE, to="drf_iam.policy"
|
69
|
+
),
|
70
|
+
),
|
71
|
+
(
|
72
|
+
"role",
|
73
|
+
models.ForeignKey(
|
74
|
+
on_delete=django.db.models.deletion.CASCADE, to="drf_iam.role"
|
75
|
+
),
|
76
|
+
),
|
77
|
+
],
|
78
|
+
options={
|
79
|
+
"verbose_name": "role policy",
|
80
|
+
"verbose_name_plural": "role policies",
|
81
|
+
"ordering": ["role", "policy"],
|
82
|
+
"unique_together": {("role", "policy")},
|
83
|
+
},
|
84
|
+
),
|
85
|
+
migrations.AddField(
|
86
|
+
model_name="role",
|
87
|
+
name="policies",
|
88
|
+
field=models.ManyToManyField(
|
89
|
+
blank=True,
|
90
|
+
db_index=True,
|
91
|
+
related_name="policies",
|
92
|
+
related_query_name="policies",
|
93
|
+
through="drf_iam.RolePolicy",
|
94
|
+
to="drf_iam.policy",
|
95
|
+
),
|
96
|
+
),
|
97
|
+
]
|
File without changes
|
drf_iam/models.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
from django.db import models
|
2
|
+
|
3
|
+
|
4
|
+
class Role(models.Model):
|
5
|
+
name = models.CharField(max_length=100, unique=True)
|
6
|
+
description = models.TextField(blank=True)
|
7
|
+
policies = models.ManyToManyField('Policy', through='RolePolicy', blank=True, related_name='policies',
|
8
|
+
related_query_name='policies', db_index=True)
|
9
|
+
|
10
|
+
def __str__(self):
|
11
|
+
return self.name
|
12
|
+
|
13
|
+
|
14
|
+
class Policy(models.Model):
|
15
|
+
action = models.CharField(max_length=100)
|
16
|
+
resource_type = models.CharField(max_length=100)
|
17
|
+
description = models.TextField(blank=True)
|
18
|
+
|
19
|
+
def __str__(self):
|
20
|
+
return f"{self.action} on {self.resource_type}"
|
21
|
+
|
22
|
+
class Meta:
|
23
|
+
unique_together = ('action', 'resource_type')
|
24
|
+
ordering = ['action', 'resource_type']
|
25
|
+
verbose_name_plural = 'policies'
|
26
|
+
verbose_name = 'policy'
|
27
|
+
|
28
|
+
|
29
|
+
class RolePolicy(models.Model):
|
30
|
+
role = models.ForeignKey(Role, on_delete=models.CASCADE, db_index=True)
|
31
|
+
policy = models.ForeignKey(Policy, on_delete=models.CASCADE, db_index=True)
|
32
|
+
|
33
|
+
class Meta:
|
34
|
+
unique_together = ('role', 'policy')
|
35
|
+
ordering = ['role', 'policy']
|
36
|
+
verbose_name_plural = 'role policies'
|
37
|
+
verbose_name = 'role policy'
|
38
|
+
|
39
|
+
def __str__(self):
|
40
|
+
return f"{self.role.name} - {self.policy}"
|
drf_iam/permissions.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
from rest_framework import permissions
|
2
|
+
|
3
|
+
class DRFIamPermission(permissions.IsAuthenticated):
|
4
|
+
def has_permission(self, request, view):
|
5
|
+
user = request.user
|
6
|
+
role = getattr(user, 'role', None)
|
7
|
+
if not role:
|
8
|
+
return False
|
9
|
+
if not hasattr(user, '_cached_policy_actions'):
|
10
|
+
user._cached_policy_actions = set(
|
11
|
+
role.policies.values_list('action', flat=True)
|
12
|
+
)
|
13
|
+
policy_actions = user._cached_policy_actions
|
14
|
+
# Resolve view name
|
15
|
+
view_name = getattr(view, 'iam_policy_name', None)
|
16
|
+
if not view_name:
|
17
|
+
view_class_name = view.__class__.__name__.lower()
|
18
|
+
view_name = view_class_name.replace('viewset', '')
|
19
|
+
|
20
|
+
# Resolve action (DRF view.action or fallback to HTTP method)
|
21
|
+
action = getattr(view, 'action', request.method.lower())
|
22
|
+
policy_action = f"{view_name}:{action}"
|
23
|
+
|
24
|
+
return policy_action in policy_actions
|
drf_iam/tests.py
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: drf-iam
|
3
|
+
Version: 0.0.1.post0
|
4
|
+
Summary: IAM-style roles and permissions for Django Rest Framework
|
5
|
+
Home-page: https://github.com/tushar1328/drf-iam.git
|
6
|
+
Author: Tushar Patel
|
7
|
+
Author-email: tushar.patel@gmail.com
|
8
|
+
Classifier: Framework :: Django
|
9
|
+
Classifier: Framework :: Django :: 3.2
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Operating System :: OS Independent
|
13
|
+
Requires-Python: >=3.6
|
14
|
+
Description-Content-Type: text/markdown
|
15
|
+
Requires-Dist: Django>=3.2
|
16
|
+
Requires-Dist: djangorestframework>=3.12
|
17
|
+
Dynamic: author
|
18
|
+
Dynamic: author-email
|
19
|
+
Dynamic: classifier
|
20
|
+
Dynamic: description
|
21
|
+
Dynamic: description-content-type
|
22
|
+
Dynamic: home-page
|
23
|
+
Dynamic: requires-dist
|
24
|
+
Dynamic: requires-python
|
25
|
+
Dynamic: summary
|
26
|
+
|
27
|
+
## Rest Framework IAM
|
28
|
+
|
29
|
+
A simple Django application for Role Based Access Control (RBAC) using Django Rest Framework
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
```bash
|
34
|
+
pip install drf-iam
|
35
|
+
```
|
@@ -0,0 +1,12 @@
|
|
1
|
+
drf_iam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
drf_iam/admin.py,sha256=xH4T6xGKuylRCTwqLPJAx3Eb07Mb0Qb43iYAWX_cSN8,788
|
3
|
+
drf_iam/apps.py,sha256=HUJatT0Wf-6ft19lWNkSqI7fnsnAjoLKQrjPUnVAcn8,611
|
4
|
+
drf_iam/models.py,sha256=3K0f0SKWiiMFKbiLQ_SKrwV-B5jzzm-HH2gO8nOGs54,1296
|
5
|
+
drf_iam/permissions.py,sha256=kUhB7V48sb4v3uZI2lWv40hvI85nE61rL-amh6OY8mg,958
|
6
|
+
drf_iam/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
|
7
|
+
drf_iam/migrations/0001_initial.py,sha256=y_4jXnr7gjU4UXxVrgVrStTSFu3h1ZrmjEZDL4FtZa4,3086
|
8
|
+
drf_iam/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
drf_iam-0.0.1.post0.dist-info/METADATA,sha256=LXFRG58OZ96UenArozVYTCdrxlBqA34vNLGVCiWEB8k,947
|
10
|
+
drf_iam-0.0.1.post0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
11
|
+
drf_iam-0.0.1.post0.dist-info/top_level.txt,sha256=daz6AaQ9e_cfCjLk2aRoLb_PCOoFofYUX4DU85VwHSM,8
|
12
|
+
drf_iam-0.0.1.post0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
drf_iam
|