Django

⌘K
  1. Home
  2. Django
  3. Django তে কিভাবে কাজ করতে...
  4. Field level permission (ফিল্ড-লেভেল পারমিশন)

Field level permission (ফিল্ড-লেভেল পারমিশন)

Dynamic

1. Project Setup

Start by creating a new Django project if you don’t already have one.

django-admin startproject field_level_permissions
cd field_level_permissions
python manage.py startapp company_management

Make sure to add company_management to INSTALLED_APPS in settings.py:

# field_level_permissions/settings.py
INSTALLED_APPS = [
    ...
    'company_management',
    'django.contrib.auth',  # Ensure auth is included for permission management
]

Run initial migrations:

python manage.py migrate

2. Create a Model with Sensitive Fields

We’ll create a Company model where certain fields (e.g., salary and designation) are restricted based on field-level permissions.

# company_management/models.py
from django.db import models
from django.core.validators import MinLengthValidator, RegexValidator
from django.contrib.auth.models import User, Group

class Company(models.Model):
    name = models.CharField(
        max_length=255,
        unique=True,
        validators=[
            MinLengthValidator(3),
            RegexValidator(regex=r'^[^\d]*$', message="Company name must not contain numbers.")
        ]
    )
    address = models.CharField(max_length=255, blank=True, null=True)
    salary = models.DecimalField(max_digits=10, decimal_places=2)
    designation = models.CharField(max_length=255, blank=True, null=True)
    
    class Meta:
        permissions = [
            ("change_salary", "Can change salary"),
            ("change_designation", "Can change designation"),
            ("add_salary", "Can add salary"),
            ("add_designation", "Can add designation"),
        ]

    def __str__(self):
        return self.name

Here, we have custom permissions for the fields salary and designation.


3. Assigning Permissions for Fields

Now, we need to assign these permissions to specific users. Django has built-in permission models, so you can easily manage this via the Django Admin interface or programmatically.

Assigning Permissions via Django Admin

  1. Create a superuser:
python manage.py createsuperuser
  1. Register the Company model in the admin:
# company_management/admin.py
from django.contrib import admin
from .models import Company

@admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
    list_display = ('name', 'address', 'salary', 'designation')
  1. After running python manage.py makemigrations and python manage.py migrate, head to /admin, login, and assign permissions to users for salary and designation fields.

এবার ডাটাবেজ migrate করলে পারমিশন গুলো অ্যাডমিন প্যানেল এ দেখা যাবে ওখান থেকে ইউসার বা গ্রুপ ধরে পারমিশন দেব যেমন মডেল লেভেল এ দেই ওখানেই এই নতুন পারমিশন গুলো শো করবে।

4. Checking Permissions at Field Level

We’ll now implement logic to ensure that users can only change specific fields based on their permissions.

from rest_framework import serializers
from .models import Company

class CompanySerializer(serializers.ModelSerializer):
    class Meta:
        model = Company
        fields = '__all__'  # This will include all fields from the model

In the Company model, override the save method to check permissions before updating fields.

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import PermissionDenied
from .models import Company
from .serializers import CompanySerializer

class CompanyViewSet(viewsets.ModelViewSet):
    queryset = Company.objects.all()
    serializer_class = CompanySerializer
    permission_classes = [IsAuthenticated]  # Ensure user is authenticated

    # Check permissions for creating (add) data
    def create(self, request, *args, **kwargs):
        user = request.user
        
        # Check if 'salary' field is in request and if user has permission to add it
        if 'salary' in request.data and not user.has_perm('company_management.add_salary'):
            raise PermissionDenied("You don't have permission to add salary.")
        
        # Check if 'designation' field is in request and if user has permission to add it
        if 'designation' in request.data and not user.has_perm('company_management.add_designation'):
            raise PermissionDenied("You don't have permission to add designation.")
        
        # If all checks pass, proceed with the normal create operation
        return super().create(request, *args, **kwargs)

    # Check permissions for updating data
    def update(self, request, *args, **kwargs):
        company = self.get_object()
        user = request.user
        
        # Check if user is trying to update 'salary' and has the permission to do so
        if 'salary' in request.data and not user.has_perm('company_management.change_salary'):
            raise PermissionDenied("You don't have permission to change the salary.")
        
        # Check if user is trying to update 'designation' and has the permission to do so
        if 'designation' in request.data and not user.has_perm('company_management.change_designation'):
            raise PermissionDenied("You don't have permission to change the designation.")
        
        # If all checks pass, proceed with the normal update operation
        return super().update(request, *args, **kwargs)

All is Manual

ধরুন, আমরা Django REST Framework (DRF) ব্যবহার করে ফিল্ড-লেভেল পারমিশন অ্যাপ্লাই করতে চাই, যেখানে আমরা ViewSet এবং Serializer ব্যবহার করে API তৈরি করব। এখানে আমরা EmployeeProfile মডেলের নির্দিষ্ট ফিল্ডে CRUD অপারেশনগুলো গ্রুপের ভিত্তিতে নিয়ন্ত্রণ করব।

ধাপ ১: মডেল তৈরি

# models.py

from django.db import models
from django.contrib.auth.models import User, Group

class EmployeeProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    contact_number = models.CharField(max_length=15)
    address = models.CharField(max_length=255)

    # ফিল্ড-লেভেল পারমিশন চেক করার জন্য কাস্টম মেথড
    def has_field_permission(self, field_name, action, user):
        """
        গ্রুপের উপর ভিত্তি করে নির্দিষ্ট ফিল্ডের পারমিশন চেক করা
        :param field_name: ফিল্ডের নাম (যেমন contact_number, address)
        :param action: create, read, update, delete
        :param user: request.user (বর্তমান লগিনকৃত ইউজার)
        """
        # ফিল্ড পারমিশনের জন্য প্রয়োজনীয় গ্রুপ ডিফাইন করা
        FIELD_PERMISSIONS = {
            'contact_number': {
                'create': ['Admin', 'Manager'],
                'read': ['Admin', 'Manager', 'Supervisor'],
                'update': ['Admin', 'Manager'],
                'delete': ['Admin'],
            },
            'address': {
                'create': ['Admin'],
                'read': ['Admin', 'Manager', 'Supervisor', 'User'],
                'update': ['Admin', 'Manager'],
                'delete': ['Admin'],
            }
        }

        # ইউজার কোন গ্রুপে আছে কিনা তা যাচাই করা
        if field_name in FIELD_PERMISSIONS:
            allowed_groups = FIELD_PERMISSIONS[field_name].get(action, [])
            return user.groups.filter(name__in=allowed_groups).exists()
        return False

ধাপ ২: সিরিয়ালাইজার তৈরি

আমরা DRF-এর মাধ্যমে EmployeeProfileSerializer তৈরি করব, যেখানে EmployeeProfile মডেলের ফিল্ডগুলিকে ফিল্ড-লেভেল পারমিশনের মাধ্যমে নিয়ন্ত্রণ করব।

# serializers.py

from rest_framework import serializers
from .models import EmployeeProfile

class EmployeeProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = EmployeeProfile
        fields = ['user', 'contact_number', 'address']

    # ফিল্ড-লেভেল পারমিশন চেক করা
    def to_representation(self, instance):
        request = self.context.get('request')
        user = request.user
        representation = super().to_representation(instance)

        # ফিল্ড-লেভেল পারমিশনের ভিত্তিতে ফিল্ডগুলি চেক করা
        if not instance.has_field_permission('contact_number', 'read', user):
            representation.pop('contact_number')

        if not instance.has_field_permission('address', 'read', user):
            representation.pop('address')

        return representation

এখানে, to_representation মেথডটি ব্যবহার করে আমরা API-তে রেসপন্সে কোন ফিল্ডগুলো দেখানো হবে, তা নির্ধারণ করছি। যদি ইউজার সেই ফিল্ড পড়ার অনুমতি না পায়, তবে আমরা সেই ফিল্ডটি সরিয়ে দিচ্ছি।

ধাপ ৩: API ViewSet তৈরি

এখন আমরা ViewSet তৈরি করব যা EmployeeProfileSerializer এবং ফিল্ড-লেভেল পারমিশন ব্যবহার করবে।

# views.py

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from .models import EmployeeProfile
from .serializers import EmployeeProfileSerializer

class EmployeeProfileViewSet(viewsets.ModelViewSet):
    queryset = EmployeeProfile.objects.all()
    serializer_class = EmployeeProfileSerializer
    permission_classes = [IsAuthenticated]

    # ফিল্ড আপডেট করার সময় পারমিশন চেক করা
    def update(self, request, *args, **kwargs):
        profile = self.get_object()

        # ফিল্ড-লেভেল পারমিশন চেক করা
        if not profile.has_field_permission('contact_number', 'update', request.user):
            return Response({"detail": "আপনার contact_number আপডেট করার পারমিশন নেই।"}, status=status.HTTP_403_FORBIDDEN)

        if not profile.has_field_permission('address', 'update', request.user):
            return Response({"detail": "আপনার address আপডেট করার পারমিশন নেই।"}, status=status.HTTP_403_FORBIDDEN)

        return super().update(request, *args, **kwargs)

এখানে, update মেথডের মধ্যে আমরা ফিল্ড-লেভেল পারমিশন চেক করছি এবং যদি ইউজার সেই ফিল্ড আপডেট করার পারমিশন না পায়, তাহলে আমরা 403 Forbidden রেসপন্স পাঠাচ্ছি।

ধাপ ৪: URL কনফিগারেশন

ViewSet-কে URL-এ যুক্ত করতে হবে।

# urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import EmployeeProfileViewSet

router = DefaultRouter()
router.register(r'employee-profiles', EmployeeProfileViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

ধাপ ৫: গ্রুপ এবং পারমিশন সেট করা

অ্যাডমিন প্যানেল বা কাস্টম স্ক্রিপ্টের মাধ্যমে ইউজারকে গ্রুপে যুক্ত করা এবং পারমিশন সেটআপ করা।

from django.contrib.auth.models import Group, User

# গ্রুপ তৈরি করা
admin_group, created = Group.objects.get_or_create(name='Admin')
manager_group, created = Group.objects.get_or_create(name='Manager')

# ইউজারকে গ্রুপে যুক্ত করা
user = User.objects.get(username='john')
user.groups.add(admin_group)

সারসংক্ষেপ

ViewSet-এ ফিল্ড আপডেট করার সময় পারমিশন চেক করেছি এবং উপযুক্ত রেসপন্স পাঠিয়েছি।

আমরা Django-র ডিফল্ট গ্রুপ ব্যবহার করে ফিল্ড-লেভেল পারমিশন কন্ট্রোল করেছি।

Serializer-এর মধ্যে ফিল্ড-লেভেল পারমিশন চেক করেছি।

How can we help?