diff --git a/Rasaddam_Backend/settings.py b/Rasaddam_Backend/settings.py index be92d4a..a69aa13 100644 --- a/Rasaddam_Backend/settings.py +++ b/Rasaddam_Backend/settings.py @@ -37,8 +37,10 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - "rest_framework", + 'rest_framework', "corsheaders", + 'apps.authentication.apps.AuthenticationConfig', + 'apps.authorization.apps.AuthorizationConfig', ] MIDDLEWARE = [ @@ -85,6 +87,18 @@ DATABASES = { } } +AUTH_USER_MODEL = 'authentication.User' + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.BasicAuthentication', + ), +} # Password validation # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators diff --git a/apps/authentication/__init__.py b/apps/authentication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authentication/admin.py b/apps/authentication/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/authentication/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/authentication/apps.py b/apps/authentication/apps.py new file mode 100644 index 0000000..bc4ed96 --- /dev/null +++ b/apps/authentication/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthenticationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.authentication' diff --git a/apps/authentication/migrations/0001_initial.py b/apps/authentication/migrations/0001_initial.py new file mode 100644 index 0000000..38c07a3 --- /dev/null +++ b/apps/authentication/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# Generated by Django 5.0 on 2025-04-30 11:11 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('id', models.PositiveBigIntegerField(primary_key=True, serialize=False)), + ('mobile', models.CharField(max_length=18)), + ('national_code', models.CharField(max_length=16)), + ('photo', models.CharField(max_length=50)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Province', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('name', models.CharField(max_length=50)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Organization', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('name', models.CharField(max_length=50)), + ('type', models.CharField(max_length=50)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('parent_organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parents', to='authentication.organization')), + ('province', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='province_organization', to='authentication.province')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/authentication/migrations/0002_alter_user_id.py b/apps/authentication/migrations/0002_alter_user_id.py new file mode 100644 index 0000000..ad1f7ed --- /dev/null +++ b/apps/authentication/migrations/0002_alter_user_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0 on 2025-04-30 11:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/apps/authentication/migrations/__init__.py b/apps/authentication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authentication/models.py b/apps/authentication/models.py new file mode 100644 index 0000000..502673d --- /dev/null +++ b/apps/authentication/models.py @@ -0,0 +1,50 @@ +from django.db import models +from django.contrib.auth.models import ( + AbstractUser +) +from apps.core.models import BaseModel + + +class User(AbstractUser, BaseModel): + mobile = models.CharField(max_length=18) + national_code = models.CharField(max_length=16) + photo = models.CharField(max_length=50) + + def __str__(self): + return f'{self.username} {self.last_name}-{self.last_login}' + + def save(self, *args, **kwargs): + super(User, self).save(*args, **kwargs) + + +class Province(BaseModel): + name = models.CharField(max_length=50) + + def __str__(self): + return f'{self.name}' + + def save(self, *args, **kwargs): + super(Province, self).save(*args, **kwargs) + + +class Organization(BaseModel): + name = models.CharField(max_length=50) + type = models.CharField(max_length=50) + province = models.ForeignKey( + Province, + on_delete=models.CASCADE, + related_name="province_organization", + null=True + ) + parent_organization = models.ForeignKey( + 'Organization', + on_delete=models.CASCADE, + related_name='parents', + null=True + ) + + def __str__(self): + return f'{self.name}-{self.type}' + + def save(self, *args, **kwargs): + super(Organization, self).save(*args, **kwargs) diff --git a/apps/authentication/tests.py b/apps/authentication/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/authentication/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/authentication/views.py b/apps/authentication/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/authentication/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/apps/authorization/__init__.py b/apps/authorization/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authorization/admin.py b/apps/authorization/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/apps/authorization/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/authorization/apps.py b/apps/authorization/apps.py new file mode 100644 index 0000000..07d1ec0 --- /dev/null +++ b/apps/authorization/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthorizationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.authorization' diff --git a/apps/authorization/migrations/0001_initial.py b/apps/authorization/migrations/0001_initial.py new file mode 100644 index 0000000..0040a0b --- /dev/null +++ b/apps/authorization/migrations/0001_initial.py @@ -0,0 +1,68 @@ +# Generated by Django 5.0 on 2025-04-30 11:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('authentication', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Permissions', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('name', models.CharField(max_length=50)), + ('description', models.TextField(max_length=500)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Role', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('role_name', models.CharField(max_length=50)), + ('description', models.TextField(max_length=500)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('permissions', models.ManyToManyField(to='authorization.permissions')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OrganizationRole', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_date', models.DateTimeField(auto_now_add=True)), + ('modify_date', models.DateTimeField(auto_now=True)), + ('trash', models.BooleanField(default=False)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)), + ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)), + ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organization', to='authentication.organization')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_user', to=settings.AUTH_USER_MODEL)), + ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organization_role', to='authorization.role')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/authorization/migrations/__init__.py b/apps/authorization/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authorization/models.py b/apps/authorization/models.py new file mode 100644 index 0000000..14d0fb4 --- /dev/null +++ b/apps/authorization/models.py @@ -0,0 +1,53 @@ +from django.db import models +from apps.authentication import models as auth_models +from apps.core.models import BaseModel + + +# Create your models here. + +class Permissions(BaseModel): + name = models.CharField(max_length=50) + description = models.TextField(max_length=500) + + def __str__(self): + return f'{self.name}-{self.description}' + + def save(self, *args, **kwargs): + super(Permissions, self).save(*args, **kwargs) + + +class Role(BaseModel): + role_name = models.CharField(max_length=50) + description = models.TextField(max_length=500) + permissions = models.ManyToManyField(Permissions) + + def __str__(self): + return f'{self.role_name}-{self.description}' + + def save(self, *args, **kwargs): + super(Role, self).save(*args, **kwargs) + + +class OrganizationRole(BaseModel): + user = models.ForeignKey( + auth_models.User, + on_delete=models.CASCADE, + related_name='organization_user', + null=True + ) + organization = models.ForeignKey( + auth_models.Organization, + on_delete=models.CASCADE, + related_name='organization' + ) + role = models.ForeignKey( + Role, + on_delete=models.CASCADE, + related_name='organization_role' + ) + + def __str__(self): + return f'{self.organization.name}-{self.user.username}' + + def save(self, *args, **kwargs): + super(OrganizationRole, self).save(*args, **kwargs) diff --git a/apps/authorization/tests.py b/apps/authorization/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/authorization/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/authorization/views.py b/apps/authorization/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/apps/authorization/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/apps/core/models.py b/apps/core/models.py index e69de29..7933092 100644 --- a/apps/core/models.py +++ b/apps/core/models.py @@ -0,0 +1,25 @@ +from django.db import models +from django.conf import settings + + +class BaseModel(models.Model): + create_date = models.DateTimeField(auto_now_add=True) + modify_date = models.DateTimeField(auto_now=True) + created_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + related_name="%(class)s_createdby", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + modified_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="%(class)s_modifiedby", + null=True, + blank=True, + ) + trash = models.BooleanField(default=False) + + class Meta: + abstract = True diff --git a/requirements.txt b/requirements.txt index 6b190d6..8682d48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,8 @@ django-redis django-split-settings django-url-filter djangorestframework +djangorestframework-jwt +djangorestframework_simplejwt djangorestframework-recursive docopt gunicorn