auth-drf 0.1.1__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.
auth_drf/__init__.py ADDED
File without changes
auth_drf/admin.py ADDED
@@ -0,0 +1,3 @@
1
+ from django.contrib import admin
2
+
3
+ # Register your models here.
auth_drf/apps.py ADDED
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AuthDrfConfig(AppConfig):
5
+ name = 'auth_drf'
File without changes
@@ -0,0 +1,8 @@
1
+ from .token_model import TokenRefresh
2
+ from .role_model import Role
3
+
4
+ __all__ = [
5
+ "TokenRefresh",
6
+ "Role",
7
+
8
+ ]
@@ -0,0 +1,50 @@
1
+ from django.db import models
2
+ from django.contrib.auth.models import Permission
3
+
4
+ class RoleMixin(models.Model):
5
+
6
+ roles = models.ForeignKey(
7
+ "Role",
8
+ verbose_name=("roles"),
9
+ on_delete=models.SET_NULL,
10
+ blank=True, null=True,
11
+ help_text=(
12
+ "The role this user belongs to. A user will get all permissions granted to each of their groups."
13
+ ),
14
+ related_name="user_set",
15
+ related_query_name="user",
16
+ )
17
+
18
+ user_permissions = models.ManyToManyField(
19
+ Permission,
20
+ verbose_name=("user permissions"),
21
+ blank=True,
22
+ help_text="Specific permissions for this user.",
23
+ related_name="user_set",
24
+ related_query_name="user",
25
+ )
26
+
27
+ class Meta:
28
+ abstract = True
29
+
30
+ def get_user_permissions(self, obj=None):
31
+ return _user_get_permissions(self, "user")
32
+
33
+ def get_role_permissions(self, obj=None):
34
+ return _user_get_permissions(self, "role")
35
+
36
+ def get_all_permissions(self, obj=None):
37
+ return _user_get_permissions(self, "all")
38
+
39
+
40
+ def _user_get_permissions(user, from_name):
41
+ permissions = set()
42
+ if from_name == "user":
43
+ permissions.update(user.user_permissions.all())
44
+ elif from_name == "role":
45
+ permissions.update(user.roles.permissions.all())
46
+ elif from_name == "all":
47
+ permissions.update(user.user_permissions.all())
48
+ permissions.update(user.roles.permissions.all())
49
+ return permissions
50
+
@@ -0,0 +1,24 @@
1
+ from django.db import models
2
+ from django.contrib.auth.models import Permission
3
+
4
+ from django.conf import settings
5
+
6
+ class Role(models.Model):
7
+
8
+ name = models.CharField(max_length=150)
9
+
10
+ permissions = models.ManyToManyField(
11
+ Permission,
12
+ verbose_name=("role permissions"),
13
+ help_text="Role Based Permissions",
14
+ )
15
+
16
+ admin = models.ForeignKey(
17
+ settings.AUTH_USER_MODEL,
18
+ on_delete=models.CASCADE,
19
+ related_name="users",
20
+ )
21
+
22
+ class Meta:
23
+ unique_together = ("name", "admin")
24
+
@@ -0,0 +1,67 @@
1
+ import uuid
2
+
3
+ from django.conf import settings
4
+ from django.db import models
5
+
6
+
7
+ class TokenRefresh(models.Model):
8
+ id = models.UUIDField(
9
+ primary_key=True,
10
+ default=uuid.uuid4,
11
+ editable=False,
12
+ )
13
+
14
+ user = models.ForeignKey(
15
+ settings.AUTH_USER_MODEL,
16
+ on_delete=models.CASCADE,
17
+ related_name="refresh_tokens",
18
+ )
19
+
20
+ jti = models.UUIDField(
21
+ unique=True,
22
+ db_index=True,
23
+ )
24
+
25
+ family_id = models.UUIDField(
26
+ db_index=True,
27
+ )
28
+
29
+ parent_jti = models.UUIDField(
30
+ null=True,
31
+ blank=True,
32
+ )
33
+
34
+ is_revoked = models.BooleanField(
35
+ default=False,
36
+ )
37
+
38
+ revoked_at = models.DateTimeField(
39
+ null=True,
40
+ blank=True,
41
+ )
42
+
43
+ expires_at = models.DateTimeField()
44
+
45
+ created_at = models.DateTimeField(
46
+ auto_now_add=True,
47
+ )
48
+
49
+ is_used = models.BooleanField(
50
+ default=False
51
+ )
52
+
53
+ used_at = models.DateTimeField(
54
+ null=True,
55
+ blank=True,
56
+ )
57
+
58
+ compromised = models.BooleanField(
59
+ default=False,
60
+ )
61
+
62
+ class Meta:
63
+ indexes = [
64
+ models.Index(fields=["family_id"]),
65
+ models.Index(fields=["user"]),
66
+ models.Index(fields=["expires_at"]),
67
+ ]
@@ -0,0 +1,67 @@
1
+ from django.db import models
2
+ from django.utils import timezone
3
+ from django.core.mail import send_mail
4
+ from django.contrib.auth.models import AbstractBaseUser, UserManager
5
+ from django.contrib.auth.base_user import BaseUserManager
6
+
7
+ from ..models.role_mixin_model import RoleMixin
8
+
9
+ from django.conf import settings
10
+
11
+ class BaseUser(AbstractBaseUser, RoleMixin):
12
+ first_name = models.CharField(max_length=150, blank=True)
13
+ last_name = models.CharField(max_length=150, blank=True)
14
+ email = models.EmailField(blank=True)
15
+ username = models.CharField(max_length=150, unique=True, blank=True)
16
+ is_staff = models.BooleanField(
17
+ default=False,
18
+ help_text=("Designates whether the user can log into this admin site."),
19
+ )
20
+ is_active = models.BooleanField(
21
+ default=True,
22
+ help_text=(
23
+ "Designates whether this user should be treated as active. "
24
+ "Unselect this instead of deleting accounts."
25
+ ),
26
+ )
27
+ date_joined = models.DateTimeField(default=timezone.now)
28
+
29
+ admin = models.ForeignKey(
30
+ "self",
31
+ on_delete=models.SET_NULL,
32
+ null=True,
33
+ blank=True,
34
+ related_name="managed_users",
35
+ )
36
+
37
+ objects = BaseUserManager()
38
+
39
+ EMAIL_FIELD = "email"
40
+ USERNAME_FIELD = "username"
41
+ REQUIRED_FIELDS = ["email"]
42
+
43
+ class Meta:
44
+ verbose_name = "user"
45
+ verbose_name_plural = "users"
46
+ abstract = True
47
+
48
+ def clean(self):
49
+ super().clean()
50
+ self.email = self.__class__.objects.normalize_email(self.email)
51
+
52
+ def get_full_name(self):
53
+ """
54
+ Return the first_name plus the last_name, with a space in between.
55
+ """
56
+ full_name = f"{self.first_name} {self.last_name}"
57
+ return full_name.strip()
58
+
59
+ def get_short_name(self):
60
+ """Return the short name for the user."""
61
+ return self.first_name
62
+
63
+ def email_user(self, subject, message, from_email=None, **kwargs):
64
+ """Send an email to this user."""
65
+ send_mail(subject, message, from_email, [self.email], **kwargs)
66
+
67
+
@@ -0,0 +1,2 @@
1
+ from .permission_serializer import PermissionSerializer
2
+ from .role_serializer import RoleMiniSerializer, RoleCreateSerializer, RoleUpdateSerializer, RoleListSerializer, RoleRetrieveSerializer
@@ -0,0 +1,67 @@
1
+ from rest_framework import serializers
2
+
3
+ from ..serializers.permission_serializer import PermissionSerializer
4
+ from ..serializers.role_serializer import RoleMiniSerializer
5
+
6
+ from django.contrib.auth import get_user_model
7
+ User = get_user_model()
8
+
9
+ class LoginSerializer(serializers.Serializer):
10
+ id = serializers.IntegerField(read_only=True)
11
+ email = serializers.EmailField()
12
+ username = serializers.CharField(max_length=150)
13
+ password = serializers.CharField(write_only=True)
14
+ roles = RoleMiniSerializer(read_only=True)
15
+ permissions = serializers.SerializerMethodField(read_only=True)
16
+
17
+ access = serializers.CharField(read_only=True)
18
+ refresh = serializers.CharField(read_only=True)
19
+
20
+ def get_permissions(self, obj):
21
+ user = self.context.get("user")
22
+ permissions = user.get_all_permissions()
23
+
24
+ grouped_data = {}
25
+
26
+ for perm in permissions:
27
+ if perm.content_type.name not in grouped_data:
28
+ grouped_data[perm.content_type.name] = []
29
+ grouped_data[perm.content_type.name].append(perm.codename)
30
+
31
+ formatted_list = [
32
+ {"module": module, "actions": actions}
33
+ for module, actions in grouped_data.items()
34
+ ]
35
+
36
+ return PermissionSerializer(
37
+ formatted_list,
38
+ many=True
39
+ ).data
40
+
41
+
42
+ class RegisterSerializer(serializers.Serializer):
43
+ first_name = serializers.CharField(max_length=150)
44
+ last_name = serializers.CharField(max_length=150)
45
+ email = serializers.EmailField()
46
+ username = serializers.CharField(max_length=150)
47
+ password = serializers.CharField(max_length=128)
48
+
49
+ def validate_email(self, attr):
50
+ if User.objects.filter(
51
+ email=attr,
52
+ ).exists():
53
+ raise serializers.ValidationError(
54
+ "User with this email already exists."
55
+ )
56
+
57
+ return attr
58
+
59
+ def validate_username(self, attr):
60
+ if User.objects.filter(
61
+ username=attr,
62
+ ).exists():
63
+ raise serializers.ValidationError(
64
+ "User with this username already exists."
65
+ )
66
+
67
+ return attr
@@ -0,0 +1,10 @@
1
+ from rest_framework import serializers
2
+ from django.contrib.auth.models import Permission
3
+
4
+ class PermissionSerializer(serializers.Serializer):
5
+ module = serializers.CharField(max_length=255)
6
+ actions = serializers.ListField(
7
+ child=serializers.CharField(max_length=100),
8
+ allow_empty=True
9
+ )
10
+
@@ -0,0 +1,109 @@
1
+ from rest_framework import serializers
2
+ from ..models.role_model import Role
3
+ from ..serializers.permission_serializer import *
4
+
5
+ class RoleCreateSerializer(serializers.ModelSerializer):
6
+ permissions = PermissionSerializer(many=True)
7
+
8
+ class Meta:
9
+ model = Role
10
+ exclude = [
11
+ "id",
12
+ "admin"
13
+ ]
14
+
15
+ def validate_name(self, attr):
16
+ request = self.context.get("request")
17
+
18
+ if Role.objects.filter(
19
+ name=attr,
20
+ admin=request.user.admin,
21
+ ).exists():
22
+ raise serializers.ValidationError(
23
+ "Role with this name already exist."
24
+ )
25
+
26
+ return attr
27
+
28
+ class RoleUpdateSerializer(serializers.ModelSerializer):
29
+ permissions = PermissionSerializer(many=True)
30
+
31
+ class Meta:
32
+ model = Role
33
+ exclude = [
34
+ "id",
35
+ "admin"
36
+ ]
37
+
38
+ class RoleListSerializer(serializers.ModelSerializer):
39
+ permissions = serializers.SerializerMethodField()
40
+
41
+ class Meta:
42
+ model = Role
43
+ fields = [
44
+ "id",
45
+ "name",
46
+ "permissions"
47
+ ]
48
+
49
+ def get_permissions(self, obj):
50
+ permissions = obj.permissions.all()
51
+
52
+ grouped_data = {}
53
+
54
+ for perm in permissions:
55
+ if perm.content_type.name not in grouped_data:
56
+ grouped_data[perm.content_type.name] = []
57
+ grouped_data[perm.content_type.name].append(perm.codename)
58
+
59
+ formatted_list = [
60
+ {"module": module, "actions": actions}
61
+ for module, actions in grouped_data.items()
62
+ ]
63
+
64
+ return PermissionSerializer(
65
+ formatted_list,
66
+ many=True
67
+ ).data
68
+
69
+
70
+
71
+ class RoleRetrieveSerializer(serializers.ModelSerializer):
72
+ permissions = serializers.SerializerMethodField()
73
+
74
+ class Meta:
75
+ model = Role
76
+ fields = [
77
+ "id",
78
+ "name",
79
+ "permissions"
80
+ ]
81
+
82
+ def get_permissions(self, obj):
83
+ permissions = obj.permissions.all()
84
+
85
+ grouped_data = {}
86
+
87
+ for perm in permissions:
88
+ if perm.content_type.name not in grouped_data:
89
+ grouped_data[perm.content_type.name] = []
90
+ grouped_data[perm.content_type.name].append(perm.codename)
91
+
92
+ formatted_list = [
93
+ {"module": module, "actions": actions}
94
+ for module, actions in grouped_data.items()
95
+ ]
96
+
97
+ return PermissionSerializer(
98
+ formatted_list,
99
+ many=True
100
+ ).data
101
+
102
+ class RoleMiniSerializer(serializers.ModelSerializer):
103
+
104
+ class Meta:
105
+ model = Role
106
+ exclude = [
107
+ "permissions",
108
+ "admin",
109
+ ]
@@ -0,0 +1,14 @@
1
+ from .role_service import RoleService
2
+ from .auth_service import AuthService
3
+ from .token_service import TokenService
4
+ from .jwt_service import JWTService
5
+ from .permissions_service import PermissionService
6
+
7
+ __all__ = [
8
+ "RoleService",
9
+ "AuthService",
10
+ "TokenService",
11
+ "JWTService",
12
+ "PermissionService",
13
+
14
+ ]
@@ -0,0 +1,69 @@
1
+ from django.contrib.auth import authenticate
2
+ from django.contrib.auth import get_user_model
3
+ from django.db import transaction
4
+
5
+ from rest_framework.exceptions import ValidationError
6
+
7
+ from ..services.token_service import TokenService
8
+ from ..services.role_service import RoleService
9
+ from ..services.permissions_service import PermissionService
10
+ from ..models.role_model import Role
11
+
12
+ User = get_user_model()
13
+
14
+ class AuthService:
15
+
16
+ @classmethod
17
+ @transaction.atomic
18
+ def register(cls, *, data):
19
+ password = data.pop("password")
20
+
21
+ user = User.objects.create(
22
+ **data
23
+ )
24
+ user.set_password(password)
25
+ user.admin=user
26
+ user.save()
27
+
28
+ permissions = PermissionService.list_all()
29
+ permissions_instances = PermissionService.list_all_instances()
30
+
31
+ data = {
32
+ "name": "Administrator",
33
+ "permissions": permissions
34
+ }
35
+
36
+ role = RoleService.create(
37
+ data=data,
38
+ user=user
39
+ )
40
+ user.roles=role
41
+ user.user_permissions.set(permissions_instances)
42
+
43
+ user.save()
44
+
45
+ return user
46
+
47
+ @classmethod
48
+ def login(cls, *, data):
49
+ user = authenticate(
50
+ username=data["username"],
51
+ password=data["password"]
52
+ )
53
+
54
+ if not user:
55
+ raise ValidationError("Invalid credentials")
56
+
57
+ user_data = {
58
+ "id": user.id,
59
+ "email": user.email,
60
+ "username": user.username,
61
+ "role": user.roles,
62
+ "user": user,
63
+ }
64
+
65
+ token = TokenService.issue_token_pair(user)
66
+
67
+ user_data.update(token)
68
+
69
+ return user_data
@@ -0,0 +1,37 @@
1
+ import uuid
2
+
3
+ from rest_framework_simplejwt.tokens import AccessToken, RefreshToken
4
+ from rest_framework_simplejwt.exceptions import TokenError
5
+ from rest_framework.exceptions import AuthenticationFailed
6
+
7
+ class JWTService:
8
+
9
+ @staticmethod
10
+ def create_access_token(user):
11
+ token = AccessToken.for_user(user)
12
+
13
+ return str(token)
14
+
15
+ @staticmethod
16
+ def create_refresh_token(*, user, jti, family_id, parent_jti=None):
17
+ token = RefreshToken.for_user(user)
18
+
19
+ token["jti"] = str(jti)
20
+ token["family"] = str(family_id)
21
+ token["sub"] = str(user.id)
22
+
23
+ if parent_jti:
24
+ token["parent"] = str(parent_jti)
25
+
26
+ return str(token)
27
+
28
+ @staticmethod
29
+ def decode_refresh_token(token: str) -> dict:
30
+ try:
31
+ refresh = RefreshToken(token)
32
+
33
+ return refresh
34
+ except TokenError as exc:
35
+ raise AuthenticationFailed(
36
+ f"Invalid refresh token: {exc}"
37
+ )
@@ -0,0 +1,33 @@
1
+ from django.contrib.auth.models import Permission
2
+
3
+ from ..serializers.permission_serializer import PermissionSerializer
4
+
5
+ class PermissionService:
6
+
7
+ @staticmethod
8
+ def list_all():
9
+ permissions = Permission.objects.all()
10
+
11
+ grouped_data = {}
12
+
13
+ for perm in permissions:
14
+ if perm.content_type.app_label not in grouped_data:
15
+ grouped_data[perm.content_type.app_label] = []
16
+ grouped_data[perm.content_type.app_label].append(perm.codename)
17
+
18
+ formatted_list = [
19
+ {"module": module, "actions": actions}
20
+ for module, actions in grouped_data.items()
21
+ ]
22
+
23
+ return PermissionSerializer(
24
+ formatted_list,
25
+ many=True
26
+ ).data
27
+
28
+ @staticmethod
29
+ def list_all_instances():
30
+ permissions = list(Permission.objects.all())
31
+
32
+ return permissions
33
+
@@ -0,0 +1,79 @@
1
+ from ..models.role_model import Role
2
+
3
+ from rest_framework.exceptions import ValidationError
4
+ from django.contrib.auth.models import Permission
5
+
6
+ class RoleService:
7
+
8
+ @staticmethod
9
+ def create(*, data, user):
10
+ permissions = data.pop("permissions")
11
+
12
+ code_names = [
13
+ action
14
+ for perm in permissions if isinstance(perm, dict)
15
+ for action in perm.get("actions", [])
16
+ ]
17
+
18
+ if code_names:
19
+ permission_instance = list(Permission.objects.filter(codename__in=code_names))
20
+
21
+
22
+ role = Role.objects.create(
23
+ **data,
24
+ admin=user.admin
25
+ )
26
+
27
+ role.permissions.set(permission_instance)
28
+ role.save()
29
+
30
+ return role
31
+
32
+ @staticmethod
33
+ def update(*, data, role, user):
34
+
35
+ has_permission_key = "permissions" in data
36
+ permission_list = data.pop("permissions", [])
37
+
38
+ for key, value in data.items():
39
+ setattr(role, key, value)
40
+
41
+ role.save()
42
+
43
+ if has_permission_key:
44
+ code_names = [
45
+ action
46
+ for perm in permission_list if isinstance(perm, dict)
47
+ for action in perm.get("actions", [])
48
+ ]
49
+
50
+ if code_names:
51
+ permission_instances = list(Permission.objects.filter(codename__in=code_names))
52
+
53
+ role.permissions.set(permission_instances)
54
+ else:
55
+ role.permissions.clear()
56
+
57
+ return role
58
+
59
+ @staticmethod
60
+ def list(user):
61
+
62
+ roles = Role.objects.filter(
63
+ admin=user.admin if user.admin else user
64
+ ).prefetch_related("permissions")
65
+
66
+ return roles
67
+
68
+ @staticmethod
69
+ def retrieve(*, id):
70
+ try:
71
+ role = Role.objects.prefetch_related("permissions").get(
72
+ id=id
73
+ )
74
+ except Role.DoesNotExist:
75
+ raise ValidationError({
76
+ "detail": "Role with given query does not exist."
77
+ })
78
+ else:
79
+ return role