Skip to content
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

App user model changes #149

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 4.2.8 on 2024-03-12 05:15

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("appauth", "0006_appusermodel_app_objects"),
]

operations = [
migrations.RemoveField(
model_name="appusermodel",
name="roles",
),
migrations.CreateModel(
name="AppUserRoleMappingModel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"created_by",
models.CharField(blank=True, editable=False, max_length=255),
),
("modified_at", models.DateTimeField(auto_now=True)),
(
"modified_by",
models.CharField(blank=True, editable=False, max_length=255),
),
("is_active", models.BooleanField(default=True)),
(
"role",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="app_user_role",
to="appauth.userrolemodel",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="app_user",
to="appauth.appusermodel",
),
),
],
options={
"abstract": False,
},
),
]
175 changes: 166 additions & 9 deletions backend/src/zelthy/apps/appauth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ def delete(self, *args, **kwargs):


class AppUserModel(AbstractZelthyUserModel, PermissionMixin):
roles = models.ManyToManyField(UserRoleModel, related_name="users")
policies = models.ManyToManyField(PolicyModel, related_name="user_policies")
policy_groups = models.ManyToManyField(
PolicyGroupModel, related_name="user_policy_groups"
Expand All @@ -67,6 +66,32 @@ class AppUserModel(AbstractZelthyUserModel, PermissionMixin):
def __str__(self):
return self.name

def is_user_active(self, role_name=None):
"""
Check if the user is active.

Parameters:
- role_name (str): The name of the role to check for. If provided, the method will check if the user has the specified role and if both the role and the user are active.

Returns:
- bool: True if the user is active and, if role_name is provided, the role is active as well. False otherwise.

"""
if role_name:
user_role_mapping = self._get_role_mapping(role_name)
if user_role_mapping:
return user_role_mapping.is_active and self.is_active
return False
return self.is_active

@property
def roles(self):
roles = UserRoleModel.objects.none()
for user_role in AppUserRoleMappingModel.objects.filter(
user=self, is_active=True
).only("role"):
roles |= UserRoleModel.objects.filter(pk=user_role.role.pk)
return roles

def get_app_object(self, role_id):
if self.app_objects:
Expand All @@ -75,7 +100,6 @@ def get_app_object(self, role_id):
return ObjectStore.get_object(object_uuid)
return None


def has_perm(self, request, perm_type, view=None, dataModel=None):
if perm_type == "userAccess":
pass
Expand All @@ -102,9 +126,54 @@ def validate_password(cls, password):
return False

def add_roles(self, role_ids):
self.roles.clear()
"""
Add roles to the user.

Parameters:
- role_ids (list): A list of role IDs to be added to the user.

Returns:
- None

"""
roles = UserRoleModel.objects.filter(id__in=role_ids)
assigned_roles = AppUserRoleMappingModel.objects.filter(user=self)
for role in roles:
if assigned_roles.filter(role=role).exists():
assigned_roles.filter(role=role).update(is_active=True)
else:
AppUserRoleMappingModel.objects.create(user=self, role=role)

def remove_roles(self, role_ids):
"""
Remove roles from the user.

Parameters:
- role_ids (list): A list of role IDs to be removed from the user.

Returns:
- None
"""
roles = UserRoleModel.objects.filter(id__in=role_ids)
for role in roles:
AppUserRoleMappingModel.objects.filter(user=self, role=role).delete()

def update_roles(self, role_ids):
"""
Update the roles of the user by deleting all existing roles and adding new ones.
This effectively replaces the user's roles with the new set of roles provided.

Parameters:
- role_ids (list): A list of role IDs to be assigned to the user. These IDs replace any existing roles the user has.

Returns:
- None
"""

AppUserRoleMappingModel.objects.filter(user=self).delete()
roles = UserRoleModel.objects.filter(id__in=role_ids)
self.roles.add(*roles)
for role in roles:
AppUserRoleMappingModel.objects.create(user=self, role=role)

def check_password_validity(self, password):
"""
Expand All @@ -129,7 +198,7 @@ def create_user(
role_ids=[],
force_password_reset=True,
require_verification=True,
app_objects=None
app_objects=None,
):
""" """
success = False
Expand All @@ -150,7 +219,7 @@ def create_user(
"Another user already exists matching the provided credentials"
)
else:
if not cls.validate_password(password):
if password and not cls.validate_password(password):
message = """
Invalid password. Password must follow rules
1. Must have at least 8 characters
Expand All @@ -166,7 +235,11 @@ def create_user(
mobile=mobile,
)
app_user.add_roles(role_ids)
app_user.set_password(password)
if password:
app_user.set_password(password)
else:
app_user.set_unusable_password() # Ask Rajat

if require_verification:
app_user.is_active = False
else:
Expand All @@ -185,7 +258,7 @@ def create_user(
message = "App User created successfully."
except Exception as e:
message = str(e)
return {"success": success, "message": message, 'app_user': app_user}
return {"success": success, "message": message, "app_user": app_user}

def update_user(self, data):
success = False
Expand Down Expand Up @@ -222,7 +295,7 @@ def update_user(self, data):

role_ids = data.getlist("roles", [])
if role_ids:
self.add_roles(role_ids)
self.update_roles(role_ids)

is_active = data.get("is_active", self.is_active)
if isinstance(is_active, str):
Expand Down Expand Up @@ -263,6 +336,90 @@ def has_password_reset_step(self, request, days=90):
return False
return True

def _get_role_mapping(self, role_name):
try:
return AppUserRoleMappingModel.objects.get(user=self, role__name=role_name)
except:
pass

def has_role(self, role_name):
"""
Checks if the user has a specific role.

Parameters:
- role_name (str): The name of the role to check for.

Returns:
- bool: True if the user has the specified role, False otherwise.
"""
user_role_mapping = self._get_role_mapping(role_name=role_name)
return True if user_role_mapping and user_role_mapping.is_active else False

def activate(self, role_name=None):
"""
Activate the user or a specific role.

Parameters:
- role_name (str, optional): The name of the role to activate. If provided, only the specified role will be activated. If not provided, user will be activated.

Returns:
- None

"""
if role_name:
user_role_mapping = self._get_role_mapping(role_name)
if user_role_mapping:
user_role_mapping.is_active = True
user_role_mapping.save(update_fields=["is_active"])
else:
self.is_active = True
self.save(update_fields=["is_active"])

def deactivate(self, role_name=None):
"""
Deactivates the user or a specific role.

Parameters:
- role_name (str, optional): The name of the role to deactivate. If provided, only the specified role will be deactivated. If not provided, user will be deactivated.

Returns:
- None

"""
if role_name:
user_role_mapping = self._get_role_mapping(role_name)
if user_role_mapping:
user_role_mapping.is_active = False
user_role_mapping.save(update_fields=["is_active"])
else:
self.is_active = False
self.save(update_fields=["is_active"])

@classmethod
def filter_users(cls, query):
"""
Filter users based on the provided query parameters.

Parameters:
- query (dict): A dictionary containing the query parameters to filter the users. The keys of the dictionary should correspond to the field names of the User model, and the values should be the desired values for those fields.

Returns:
- QuerySet: A QuerySet containing the filtered users.

"""
return cls.objects.filter(**query)


class OldPasswords(AbstractOldPasswords):
user = models.ForeignKey(AppUserModel, on_delete=models.PROTECT)


class AppUserRoleMappingModel(FullAuditMixin):

user = models.ForeignKey(
AppUserModel, related_name="app_user", on_delete=models.CASCADE
)
role = models.ForeignKey(
UserRoleModel, related_name="app_user_role", on_delete=models.CASCADE
)
is_active = models.BooleanField(default=True)