-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Backend For Group Permissions #15
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,6 @@ | ||||||||||||||
from django.contrib.auth.hashers import make_password | ||||||||||||||
from common.serializers import LocationSerializer | ||||||||||||||
from .models import GroupPermission | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
def set_encrypted_password(password): | ||||||||||||||
|
@@ -98,3 +99,60 @@ def save_location_data(location_data, obj=None): | |||||||||||||
ValidationError: If the location data is invalid. | ||||||||||||||
""" | ||||||||||||||
return save_data(LocationSerializer, location_data, obj) | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
def manage_group_permissions(group, permissions): | ||||||||||||||
""" | ||||||||||||||
Manages group permissions for a given group by processing a dictionary of permissions. | ||||||||||||||
|
||||||||||||||
Args: | ||||||||||||||
group (Group): The group for which permissions are managed. | ||||||||||||||
permissions (dict): A dictionary containing permission data. | ||||||||||||||
|
||||||||||||||
The `permissions` dictionary should have the following structure: | ||||||||||||||
{ | ||||||||||||||
"permission_id_1": { | ||||||||||||||
"status": True or False, # Indicates whether the permission | ||||||||||||||
is granted or revoked. | ||||||||||||||
"context": context_id or None # context associated with the permission. | ||||||||||||||
}, | ||||||||||||||
# Additional permission entries... | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
For each permission in the dictionary, this function either grants or revokes | ||||||||||||||
the permission for the group. | ||||||||||||||
If "status" is True, the permission is granted. If "status" is False, | ||||||||||||||
the permission is revoked. | ||||||||||||||
The "context" field can specify additional context for the permission. | ||||||||||||||
|
||||||||||||||
Returns: | ||||||||||||||
None | ||||||||||||||
|
||||||||||||||
This function performs the necessary operations on the `GroupPermission` model | ||||||||||||||
based on the provided data. | ||||||||||||||
It creates new permissions or deletes existing ones according to the specified status. | ||||||||||||||
Any exceptions that occur during this process are printed to the console | ||||||||||||||
for debugging purposes. | ||||||||||||||
""" | ||||||||||||||
for permission, permission_data in permissions.items(): | ||||||||||||||
permission_id = int(permission) | ||||||||||||||
context_id = permission_data.get('context', None) | ||||||||||||||
status = permission_data.get('status', False) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will each field have a separate checkbox for changing status? If yes, this is okay. don't give a default value for mandatory parameters. |
||||||||||||||
try: | ||||||||||||||
if status: | ||||||||||||||
gp = GroupPermission.objects.filter(role=group, permission_id=permission_id | ||||||||||||||
).first() | ||||||||||||||
Comment on lines
+142
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
do filter and get separately |
||||||||||||||
if gp: | ||||||||||||||
gp.context_id = context_id | ||||||||||||||
gp.save() | ||||||||||||||
else: | ||||||||||||||
GroupPermission.objects.create(role=group, | ||||||||||||||
permission_id=permission_id, | ||||||||||||||
context_id=context_id) | ||||||||||||||
else: | ||||||||||||||
gp = GroupPermission.objects.get(role=group, | ||||||||||||||
permission_id=permission_id, | ||||||||||||||
context_id=context_id) | ||||||||||||||
Comment on lines
+153
to
+155
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
use gps again. |
||||||||||||||
gp.delete() | ||||||||||||||
except Exception as e: | ||||||||||||||
print(f"\033[93mException ** {e}\033[0m") | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Don't use ANSI colours. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# Generated by Django 4.2.5 on 2023-10-09 08:02 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
("auth", "0012_alter_user_first_name_max_length"), | ||
("accounts", "0004_alter_location_pincode_alter_user_email_and_more"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Context", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("name", models.CharField(max_length=100, unique=True)), | ||
("description", models.TextField()), | ||
("order", models.IntegerField()), | ||
], | ||
options={ | ||
"ordering": ["order"], | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name="Permission", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("name", models.CharField(max_length=100, unique=True)), | ||
( | ||
"resource", | ||
models.CharField( | ||
choices=[ | ||
("role", "role"), | ||
("training", "training"), | ||
("test", "test"), | ||
("gallery", "gallery"), | ||
("about", "about"), | ||
("contact", "contact"), | ||
("home", "home"), | ||
], | ||
max_length=100, | ||
), | ||
), | ||
("description", models.TextField()), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name="GroupPermission", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
("created", models.DateTimeField(auto_now_add=True)), | ||
("updated", models.DateTimeField(auto_now=True)), | ||
( | ||
"context", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
to="accounts.context", | ||
), | ||
), | ||
( | ||
"permission", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
to="accounts.permission", | ||
), | ||
), | ||
( | ||
"role", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, to="auth.group" | ||
), | ||
), | ||
], | ||
options={ | ||
"ordering": ["role__name", "context__order"], | ||
"unique_together": {("role", "permission")}, | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name="ContextAllowAssign", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
( | ||
"assignedLevel", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="assignedContext", | ||
to="accounts.context", | ||
), | ||
), | ||
( | ||
"assigningLevel", | ||
models.ForeignKey( | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name="assigningContext", | ||
to="accounts.context", | ||
), | ||
), | ||
], | ||
options={ | ||
"db_table": "accounts_context_allow_assign", | ||
"ordering": ["assigningLevel__order", "assignedLevel__order"], | ||
"unique_together": {("assigningLevel", "assignedLevel")}, | ||
}, | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,9 @@ | |
from django.core.validators import RegexValidator | ||
from django.utils.translation import gettext_lazy as _ | ||
from django.contrib.auth.models import Group | ||
|
||
from common.models import State, District, City, Language | ||
from config import RESOURCES | ||
|
||
|
||
MAX_CLASS = 12 | ||
CLASS_CHOICES = [(i, f"Class {i}") for i in range(1, MAX_CLASS+1)] | ||
|
@@ -254,3 +255,136 @@ class Message(models.Model): | |
MessageType, on_delete=models.CASCADE, | ||
related_name='message_type' | ||
) | ||
|
||
|
||
# Revised models | ||
class Context(models.Model): | ||
""" | ||
A context defines the level at which a role operates or an action is performed | ||
within the system. Example: Organization, city, class levels. It includes a name, | ||
description, and an order to define its level of hierarchy. | ||
|
||
Attributes: | ||
name (str): The name of the context. | ||
description (str): A detailed description of the context. | ||
order (int): The level of hierarchy for the context. | ||
|
||
Meta: | ||
ordering (list): The instances are ordered by their 'order' attribute in | ||
ascending order. | ||
|
||
Methods: | ||
__str__(): Returns a string representation of the context in the format | ||
'order - name'. | ||
""" | ||
name = models.CharField(max_length=100, null=False, unique=True) | ||
description = models.TextField(null=False) | ||
order = models.IntegerField(null=False) # Level of hierarchy | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should have a parent context also. for example, we will have state context with parent of all India context and various districts with parent context of their state context, We will discuss this further. |
||
def __str__(self): | ||
return f"{self.order} - {self.name}" | ||
|
||
class Meta: | ||
ordering = ['order'] | ||
|
||
|
||
class ContextAllowAssign(models.Model): | ||
""" | ||
This model maintains a relationship between two Context instances, | ||
indicating that one context can assign roles in another context. | ||
|
||
Attributes: | ||
assigningLevel (ForeignKey): A ForeignKey relationship to the Context | ||
instance that has the permission to assign roles. | ||
assignedLevel (ForeignKey): A ForeignKey relationship to the Context | ||
instance where roles can be assigned. | ||
|
||
Meta: | ||
unique_together (list of str): The combination of 'assigningLevel' and | ||
'assignedLevel' must be unique. | ||
ordering (list of str): The default ordering of ContextAllowAssign | ||
instances is by the | ||
'assigningLevel__order' and 'assignedLevel__order' attributes. | ||
db_table (str): The name of the database table for this model. | ||
|
||
Methods: | ||
__str__(): Returns a string representation of the permission. | ||
""" | ||
assigningLevel = models.ForeignKey(Context, on_delete=models.CASCADE, | ||
related_name='assigningContext') | ||
assignedLevel = models.ForeignKey(Context, on_delete=models.CASCADE, | ||
related_name='assignedContext') | ||
|
||
class Meta: | ||
unique_together = ['assigningLevel', 'assignedLevel'] | ||
ordering = ['assigningLevel__order', 'assignedLevel__order'] | ||
db_table = 'accounts_context_allow_assign' | ||
|
||
def __str__(self): | ||
return f"{self.assigningLevel} can assign roles at level: {self.assignedLevel} " | ||
|
||
|
||
class Permission(models.Model): | ||
""" | ||
This model represents a permission within the system, which is used to | ||
control access to various resources. | ||
|
||
Attributes: | ||
name (str): The name of the permission. Example: 'Can add testimonials'. | ||
resource (str): The resource to which the permission is associated. | ||
Example: 'testimonials', 'trainings'. | ||
description (str): A detailed description of the permission. | ||
|
||
Methods: | ||
__str__(): Returns a string representation of the permission. | ||
|
||
""" | ||
RESOURCES = RESOURCES | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is not required. |
||
name = models.CharField(max_length=100, null=False, unique=True) | ||
resource = models.CharField(max_length=100, null=False, choices=RESOURCES) | ||
description = models.TextField(null=False) | ||
|
||
def __str__(self): | ||
return f"{self.name}" | ||
|
||
|
||
class GroupPermission(models.Model): | ||
""" | ||
This model defines that a specific role has a particular permission | ||
within a specific context. | ||
|
||
Attributes: | ||
role (ForeignKey to Group): The role (group) to which the permission | ||
is assigned. | ||
permission (ForeignKey to Permission): The permission being assigned to | ||
the role. | ||
context (ForeignKey to Context): The context in which the permission is | ||
granted. | ||
created (DateTimeField): The timestamp when this role-permission | ||
association was created. | ||
updated (DateTimeField): The timestamp when this role-permission | ||
association was last updated. | ||
|
||
Meta: | ||
unique_together (list of str): The combination of 'role' and | ||
'permission' must be unique. | ||
ordering (list of str): The default ordering of GroupPermission instances is | ||
by the 'role__name' and 'context__order' attributes. | ||
|
||
Methods: | ||
__str__(): Returns a string representation of the role-permission relationship. | ||
""" | ||
role = models.ForeignKey(Group, on_delete=models.CASCADE) | ||
permission = models.ForeignKey(Permission, on_delete=models.CASCADE) | ||
context = models.ForeignKey(Context, on_delete=models.CASCADE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will need to discuss this context more. |
||
# ToDo For more granular control? | ||
# attribute = models.CharField(max_length=100, null=False) | ||
created = models.DateTimeField(auto_now_add=True) | ||
updated = models.DateTimeField(auto_now=True) | ||
|
||
class Meta: | ||
unique_together = ['role', 'permission'] | ||
ordering = ['role__name', 'context__order'] | ||
|
||
def __str__(self): | ||
return f"{self.role} has permission {self.permission} at level: {self.context}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't give a default value for mandatory parameters.