diff --git a/horilla/__init__.py b/horilla/__init__.py index 8e7897ee7..3a1669149 100755 --- a/horilla/__init__.py +++ b/horilla/__init__.py @@ -1,11 +1,3 @@ """ init.py """ - -from horilla import ( - horilla_apps, - horilla_context_processors, - horilla_middlewares, - horilla_settings, - rest_conf, -) diff --git a/horilla/config.py b/horilla/config.py index a5409497c..66f1eebb1 100644 --- a/horilla/config.py +++ b/horilla/config.py @@ -11,13 +11,11 @@ from django.apps import apps from django.conf import settings from django.contrib.auth.context_processors import PermWrapper -from horilla.horilla_apps import SIDEBARS - logger = logging.getLogger(__name__) def get_apps_in_base_dir(): - return SIDEBARS + return settings.SIDEBARS def import_method(accessibility): @@ -89,3 +87,32 @@ def get_MENUS(request): ALL_MENUS[request.session.session_key] = [] sidebar(request) return {"sidebar": ALL_MENUS.get(request.session.session_key)} + + +def load_ldap_settings(): + """ + Fetch LDAP settings dynamically from the database after Django is ready. + """ + try: + from django.db import connection + + from horilla_ldap.models import LDAPSettings + + # Ensure DB is ready before querying + if not connection.introspection.table_names(): + print("⚠️ Database is empty. Using default LDAP settings.") + return settings.DEFAULT_LDAP_CONFIG + + ldap_config = LDAPSettings.objects.first() + if ldap_config: + return { + "LDAP_SERVER": ldap_config.ldap_server, + "BIND_DN": ldap_config.bind_dn, + "BIND_PASSWORD": ldap_config.bind_password, + "BASE_DN": ldap_config.base_dn, + } + except Exception as e: + print(f"⚠️ Warning: Could not load LDAP settings ({e})") + return settings.DEFAULT_LDAP_CONFIG # Return default on error + + return settings.DEFAULT_LDAP_CONFIG # Fallback in case of an issue diff --git a/horilla/horilla_apps.py b/horilla/horilla_apps.py deleted file mode 100644 index ef9c9b48c..000000000 --- a/horilla/horilla_apps.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -horilla_apps - -This module is used to register horilla addons -""" - -import os - -from horilla import settings -from horilla.settings import INSTALLED_APPS - -INSTALLED_APPS.append("accessibility") -INSTALLED_APPS.append("horilla_audit") -INSTALLED_APPS.append("horilla_widgets") -INSTALLED_APPS.append("horilla_crumbs") -INSTALLED_APPS.append("horilla_documents") -INSTALLED_APPS.append("horilla_views") -INSTALLED_APPS.append("horilla_automations") -INSTALLED_APPS.append("auditlog") -INSTALLED_APPS.append("biometric") -INSTALLED_APPS.append("helpdesk") -INSTALLED_APPS.append("offboarding") -INSTALLED_APPS.append("horilla_backup") -INSTALLED_APPS.append("project") -INSTALLED_APPS.append("horilla_meet") -INSTALLED_APPS.append("report") -INSTALLED_APPS.append("whatsapp") - -if settings.env("AWS_ACCESS_KEY_ID", default=None) and "storages" not in INSTALLED_APPS: - INSTALLED_APPS.append("storages") - - -AUDITLOG_INCLUDE_ALL_MODELS = True - -AUDITLOG_EXCLUDE_TRACKING_MODELS = ( - # "", - # "." -) - -setattr(settings, "AUDITLOG_INCLUDE_ALL_MODELS", AUDITLOG_INCLUDE_ALL_MODELS) -setattr(settings, "AUDITLOG_EXCLUDE_TRACKING_MODELS", AUDITLOG_EXCLUDE_TRACKING_MODELS) - -settings.MIDDLEWARE.append( - "auditlog.middleware.AuditlogMiddleware", -) - -SETTINGS_EMAIL_BACKEND = getattr(settings, "EMAIL_BACKEND", False) -setattr(settings, "EMAIL_BACKEND", "base.backends.ConfiguredEmailBackend") -if SETTINGS_EMAIL_BACKEND: - setattr(settings, "EMAIL_BACKEND", SETTINGS_EMAIL_BACKEND) - -# os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' - -SIDEBARS = [ - "recruitment", - "onboarding", - "employee", - "attendance", - "leave", - "payroll", - "pms", - "offboarding", - "asset", - "helpdesk", - "project", - "report", -] - -WHITE_LABELLING = False -NESTED_SUBORDINATE_VISIBILITY = False -TWO_FACTORS_AUTHENTICATION = False diff --git a/horilla/horilla_context_processors.py b/horilla/horilla_context_processors.py deleted file mode 100644 index 8cbe4b77c..000000000 --- a/horilla/horilla_context_processors.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -horilla_context_process.py - -This module is used to register context processors without effecting the horilla/settings.py module -""" - -from horilla.settings import TEMPLATES - -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "horilla.config.get_MENUS", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.get_companies", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.white_labelling_company", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.resignation_request_enabled", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.timerunner_enabled", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.intial_notice_period", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.check_candidate_self_tracking", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.check_candidate_self_tracking_rating", -) - -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.get_initial_prefix", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.biometric_app_exists", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.enable_late_come_early_out_tracking", -) -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "base.context_processors.enable_profile_edit", -) diff --git a/horilla/horilla_middlewares.py b/horilla/horilla_middlewares.py index a20a09907..d7a1e8db8 100644 --- a/horilla/horilla_middlewares.py +++ b/horilla/horilla_middlewares.py @@ -9,16 +9,6 @@ import threading from django.http import HttpResponseNotAllowed from django.shortcuts import render -from horilla.settings import MIDDLEWARE - -MIDDLEWARE.append("base.middleware.CompanyMiddleware") -MIDDLEWARE.append("horilla.horilla_middlewares.MethodNotAllowedMiddleware") -MIDDLEWARE.append("horilla.horilla_middlewares.ThreadLocalMiddleware") -MIDDLEWARE.append("horilla.horilla_middlewares.SVGSecurityMiddleware") -MIDDLEWARE.append("accessibility.middlewares.AccessibilityMiddleware") -MIDDLEWARE.append("accessibility.middlewares.AccessibilityMiddleware") -MIDDLEWARE.append("base.middleware.ForcePasswordChangeMiddleware") -MIDDLEWARE.append("base.middleware.TwoFactorAuthMiddleware") _thread_locals = threading.local() diff --git a/horilla/horilla_settings.py b/horilla/horilla_settings.py deleted file mode 100644 index 4c01c5006..000000000 --- a/horilla/horilla_settings.py +++ /dev/null @@ -1,168 +0,0 @@ -from django.core.files.storage import FileSystemStorage - -from horilla import settings -from horilla.horilla_apps import INSTALLED_APPS - -""" -DB_INIT_PASSWORD: str - -The password used for database setup and initialization. This password is a -48-character alphanumeric string generated using a UUID to ensure high entropy and security. -""" -DB_INIT_PASSWORD = settings.env( - "DB_INIT_PASSWORD", default="d3f6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d" -) - - -HORILLA_DATE_FORMATS = { - "DD/MM/YY": "%d/%m/%y", - "DD-MM-YYYY": "%d-%m-%Y", - "DD.MM.YYYY": "%d.%m.%Y", - "DD/MM/YYYY": "%d/%m/%Y", - "MM/DD/YYYY": "%m/%d/%Y", - "YYYY-MM-DD": "%Y-%m-%d", - "YYYY/MM/DD": "%Y/%m/%d", - "MMMM D, YYYY": "%B %d, %Y", - "DD MMMM, YYYY": "%d %B, %Y", - "MMM. D, YYYY": "%b. %d, %Y", - "D MMM. YYYY": "%d %b. %Y", - "dddd, MMMM D, YYYY": "%A, %B %d, %Y", -} - -HORILLA_TIME_FORMATS = { - "hh:mm A": "%I:%M %p", # 12-hour format - "HH:mm": "%H:%M", # 24-hour format - "HH:mm:ss.SSSSSS": "%H:%M:%S.%f", # 24-hour format with seconds and microseconds -} - - -BIO_DEVICE_THREADS = {} - -DYNAMIC_URL_PATTERNS = [] - -FILE_STORAGE = FileSystemStorage(location="csv_tmp/") - -APP_URLS = [ - "base.urls", - "employee.urls", -] - -APPS = [ - "base", - "employee", - "horilla_documents", - "horilla_automations", -] - -NO_PERMISSION_MODALS = [ - "historicalbonuspoint", - "assetreport", - "assetdocuments", - "returnimages", - "holiday", - "companyleave", - "historicalavailableleave", - "historicalleaverequest", - "historicalleaveallocationrequest", - "leaverequestconditionapproval", - "historicalcompensatoryleaverequest", - "employeepastleaverestrict", - "overrideleaverequests", - "historicalrotatingworktypeassign", - "employeeshiftday", - "historicalrotatingshiftassign", - "historicalworktyperequest", - "historicalshiftrequest", - "multipleapprovalmanagers", - "attachment", - "announcementview", - "emaillog", - "driverviewed", - "dashboardemployeecharts", - "attendanceallowedip", - "tracklatecomeearlyout", - "historicalcontract", - "overrideattendance", - "overrideleaverequest", - "overrideworkinfo", - "multiplecondition", - "historicalpayslip", - "reimbursementmultipleattachment", - "historicalcontract", - "overrideattendance", - "overrideleaverequest", - "workrecord", - "historicalticket", - "skill", - "historicalcandidate", - "rejectreason", - "historicalrejectedcandidate", - "rejectedcandidate", - "stagefiles", - "stagenote", - "questionordering", - "recruitmentsurveyordering", - "recruitmentsurveyanswer", - "recruitmentgeneralsetting", - "resume", - "recruitmentmailtemplate", - "profileeditfeature", -] - -if settings.env("AWS_ACCESS_KEY_ID", default=None): - AWS_ACCESS_KEY_ID = settings.env("AWS_ACCESS_KEY_ID") - AWS_SECRET_ACCESS_KEY = settings.env("AWS_SECRET_ACCESS_KEY") - AWS_STORAGE_BUCKET_NAME = settings.env("AWS_STORAGE_BUCKET_NAME") - AWS_S3_REGION_NAME = settings.env("AWS_S3_REGION_NAME") - DEFAULT_FILE_STORAGE = settings.env("DEFAULT_FILE_STORAGE") - AWS_S3_ADDRESSING_STYLE = settings.env("AWS_S3_ADDRESSING_STYLE") - - settings.AWS_ACCESS_KEY_ID = AWS_ACCESS_KEY_ID - settings.AWS_SECRET_ACCESS_KEY = AWS_SECRET_ACCESS_KEY - settings.AWS_STORAGE_BUCKET_NAME = AWS_STORAGE_BUCKET_NAME - settings.AWS_S3_REGION_NAME = AWS_S3_REGION_NAME - settings.DEFAULT_FILE_STORAGE = DEFAULT_FILE_STORAGE - settings.AWS_S3_ADDRESSING_STYLE = AWS_S3_ADDRESSING_STYLE - - -if settings.env("AWS_ACCESS_KEY_ID", default=None) and "storages" in INSTALLED_APPS: - settings.MEDIA_URL = f"{settings.env('MEDIA_URL')}/{settings.env('NAMESPACE')}/" - settings.MEDIA_ROOT = f"{settings.env('MEDIA_ROOT')}/{settings.env('NAMESPACE')}/" - - -# Default LDAP settings -DEFAULT_LDAP_CONFIG = { - "LDAP_SERVER": settings.env("LDAP_SERVER", default="ldap://127.0.0.1:389"), - "BIND_DN": settings.env("BIND_DN", default="cn=admin,dc=horilla,dc=com"), - "BIND_PASSWORD": settings.env("BIND_PASSWORD", default="horilla"), - "BASE_DN": settings.env("BASE_DN", default="ou=users,dc=horilla,dc=com"), -} - - -def load_ldap_settings(): - """ - Fetch LDAP settings dynamically from the database after Django is ready. - """ - try: - from django.db import connection - - from horilla_ldap.models import LDAPSettings - - # Ensure DB is ready before querying - if not connection.introspection.table_names(): - print("⚠️ Database is empty. Using default LDAP settings.") - return DEFAULT_LDAP_CONFIG - - ldap_config = LDAPSettings.objects.first() - if ldap_config: - return { - "LDAP_SERVER": ldap_config.ldap_server, - "BIND_DN": ldap_config.bind_dn, - "BIND_PASSWORD": ldap_config.bind_password, - "BASE_DN": ldap_config.base_dn, - } - except Exception as e: - print(f"⚠️ Warning: Could not load LDAP settings ({e})") - return DEFAULT_LDAP_CONFIG # Return default on error - - return DEFAULT_LDAP_CONFIG # Fallback in case of an issue diff --git a/horilla/methods.py b/horilla/methods.py index 12f2cf0bb..104116516 100644 --- a/horilla/methods.py +++ b/horilla/methods.py @@ -1,10 +1,10 @@ import contextlib import importlib -from django.contrib.auth.models import User +from django.conf import settings from django.contrib.contenttypes.models import ContentType -from horilla.horilla_settings import APP_URLS, DYNAMIC_URL_PATTERNS +from horilla_auth.models import HorillaUser def get_horilla_model_class(app_label, model): @@ -63,12 +63,12 @@ def horilla_users_with_perms(permissions): permissions = [permissions] # Start with a queryset that includes all superusers - users_with_permissions = User.objects.filter(is_superuser=True) + users_with_permissions = HorillaUser.objects.filter(is_superuser=True) # Filter users based on the permissions list for perm in permissions: app_label, codename = perm.split(".") - users_with_permissions |= User.objects.filter( + users_with_permissions |= HorillaUser.objects.filter( user_permissions__codename=codename, user_permissions__content_type__app_label=app_label, ) @@ -87,7 +87,7 @@ def remove_dynamic_url(path_info): """Function to remove a dynamically added URL from any app's urlpatterns.""" # Iterate over all app URL patterns - for app_urls in APP_URLS: + for app_urls in settings.APP_URLS: try: # Dynamically import the app's urls.py module urls_module = importlib.import_module(app_urls) @@ -105,5 +105,5 @@ def remove_dynamic_url(path_info): print(f"Module {app_urls} not found. Skipping...") # Also remove it from the tracked dynamic paths - if path_info in DYNAMIC_URL_PATTERNS: - DYNAMIC_URL_PATTERNS.remove(path_info) + if path_info in settings.DYNAMIC_URL_PATTERNS: + settings.DYNAMIC_URL_PATTERNS.remove(path_info) diff --git a/horilla/models.py b/horilla/models.py index 7a95f2779..9ba5b7680 100644 --- a/horilla/models.py +++ b/horilla/models.py @@ -13,7 +13,6 @@ from uuid import uuid4 from auditlog.models import AuditlogHistoryField from auditlog.registry import auditlog -from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.db import models from django.db.models.fields.files import FieldFile @@ -22,6 +21,7 @@ from django.utils.text import slugify from django.utils.translation import gettext as _ from horilla.horilla_middlewares import _thread_locals +from horilla_auth.models import HorillaUser @property @@ -96,7 +96,7 @@ class HorillaModel(models.Model): verbose_name=_("Created At"), ) created_by = models.ForeignKey( - User, + HorillaUser, on_delete=models.SET_NULL, null=True, blank=True, @@ -105,7 +105,7 @@ class HorillaModel(models.Model): ) modified_by = models.ForeignKey( - User, + HorillaUser, on_delete=models.SET_NULL, null=True, blank=True, @@ -146,7 +146,7 @@ class HorillaModel(models.Model): if ( hasattr(self, "created_by") and hasattr(self._meta.get_field("created_by"), "related_model") - and self._meta.get_field("created_by").related_model == User + and self._meta.get_field("created_by").related_model == HorillaUser ): if request and not self.pk: if user.is_authenticated: diff --git a/horilla/rest_conf.py b/horilla/rest_conf.py deleted file mode 100644 index 158aba7b5..000000000 --- a/horilla/rest_conf.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -rest_conf.py -""" - -from datetime import timedelta - -from horilla import settings -from horilla.settings import INSTALLED_APPS - -# Injecting installed apps to settings - -REST_APPS = ["rest_framework", "rest_framework_simplejwt", "drf_yasg", "horilla_api"] - -INSTALLED_APPS.extend(REST_APPS) - -REST_FRAMEWORK_SETTINGS = { - "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], - "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", - "DEFAULT_AUTHENTICATION_CLASSES": ( - "rest_framework_simplejwt.authentication.JWTAuthentication", - ), - "PAGE_SIZE": 20, -} - -SIMPLE_JWT = { - "ACCESS_TOKEN_LIFETIME": timedelta(days=30), -} -SWAGGER_SETTINGS = { - "SECURITY_DEFINITIONS": { - "Bearer": { - "type": "apiKey", - "name": "Authorization", - "in": "header", - "description": "Enter your Bearer token here", - }, - "Basic": { - "type": "basic", - "description": "Basic authentication. Enter your username and password.", - }, - }, - "SECURITY": [{"Bearer": []}, {"Basic": []}], -} -# Inject the REST framework settings into the Django project settings -setattr(settings, "REST_FRAMEWORK", REST_FRAMEWORK_SETTINGS) -setattr(settings, "SIMPLE_JWT", SIMPLE_JWT) -setattr(settings, "SWAGGER_SETTINGS", SWAGGER_SETTINGS) diff --git a/horilla/settings.py b/horilla/settings.py deleted file mode 100755 index f2142631f..000000000 --- a/horilla/settings.py +++ /dev/null @@ -1,272 +0,0 @@ -""" -Django settings for horilla project. - -Generated by 'django-admin startproject' using Django 4.1.4. - -For more information on this file, see -https://docs.djangoproject.com/en/4.1/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.1/ref/settings/ -""" - -import os -from os.path import join -from pathlib import Path - -import environ -from django.contrib.messages import constants as messages - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ - -env = environ.Env( - DEBUG=(bool, True), - SECRET_KEY=( - str, - "django-insecure-j8op9)1q8$1&0^s&p*_0%d#pr@w9qj@1o=3#@d=a(^@9@zd@%j", - ), - ALLOWED_HOSTS=(list, ["*"]), - CSRF_TRUSTED_ORIGINS=(list, ["http://localhost:8000"]), -) - -env.read_env(os.path.join(BASE_DIR, ".env"), overwrite=True) - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env("SECRET_KEY") - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = env("DEBUG") - -ALLOWED_HOSTS = env("ALLOWED_HOSTS") - -# Application definition - -THEME_APP = "horilla_theme" - - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", - "notifications", - "mathfilters", - "corsheaders", - "simple_history", - "django_filters", - THEME_APP, - "base", - "employee", - "recruitment", - "leave", - "pms", - "onboarding", - "asset", - "attendance", - "payroll", - "widget_tweaks", - "django_apscheduler", -] -APSCHEDULER_DATETIME_FORMAT = "N j, Y, f:s a" - -APSCHEDULER_RUN_NOW_TIMEOUT = 25 # Seconds - - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "whitenoise.middleware.WhiteNoiseMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "corsheaders.middleware.CorsMiddleware", - "simple_history.middleware.HistoryRequestMiddleware", - "django.middleware.locale.LocaleMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "horilla.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [ - BASE_DIR / "templates", - ], - "APP_DIRS": False, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - "loaders": [ - ( - "django.template.loaders.filesystem.Loader", - [BASE_DIR / THEME_APP / "templates"], - ), - "django.template.loaders.app_directories.Loader", - ("django.template.loaders.filesystem.Loader", [BASE_DIR / "templates"]), - ], - }, - }, -] - -WSGI_APPLICATION = "horilla.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/4.1/ref/settings/#databases - -if env("DATABASE_URL", default=None): - DATABASES = { - "default": env.db(), - } -else: - DATABASES = { - "default": { - "ENGINE": env("DB_ENGINE", default="django.db.backends.sqlite3"), - "NAME": env( - "DB_NAME", - default=os.path.join( - BASE_DIR, - "TestDB.sqlite3", - ), - ), - "USER": env("DB_USER", default=""), - "PASSWORD": env("DB_PASSWORD", default=""), - "HOST": env("DB_HOST", default=""), - "PORT": env("DB_PORT", default=""), - } - } - -# Password validation -# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.1/howto/static-files/ - -STATIC_URL = "static/" -STATIC_ROOT = BASE_DIR / "staticfiles" - -STATICFILES_DIRS = [ - BASE_DIR / "static", -] - -STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage" - -MEDIA_URL = "/media/" -MEDIA_ROOT = os.path.join(BASE_DIR, "media/") -# Default primary key field type -# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" - - -MESSAGE_TAGS = { - messages.DEBUG: "oh-alert--warning", - messages.INFO: "oh-alert--info", - messages.SUCCESS: "oh-alert--success", - messages.WARNING: "oh-alert--warning", - messages.ERROR: "oh-alert--danger", -} - - -CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS") - -LOGIN_URL = "/login" - - -SIMPLE_HISTORY_REVERT_DISABLED = True - - -DJANGO_NOTIFICATIONS_CONFIG = { - "USE_JSONFIELD": True, - "SOFT_DELETE": True, - "USE_WATCHED": True, - "NOTIFICATIONS_STORAGE": "notifications.storage.DatabaseStorage", - "TEMPLATE": "notifications.html", # Add this line -} - -X_FRAME_OPTIONS = "SAMEORIGIN" - -LANGUAGES = ( - ("en", "English (US)"), - ("de", "Deutsche"), - ("es", "Español"), - ("fr", "Français"), - ("ar", "عربى"), - ("pt-br", "Português (Brasil)"), - ("zh-hans", "Simplified Chinese"), - ("zh-hant", "Traditional Chinese"), - ("it", "Italian"), -) - -LOCALE_PATHS = [ - join(BASE_DIR, "horilla", "locale"), -] - - -# Internationalization -# https://docs.djangoproject.com/en/4.1/topics/i18n/ - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = env("TIME_ZONE", default="Asia/Kolkata") - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# LDAP Configuration -# import ldap -# from django_auth_ldap.config import LDAPSearch, GroupOfNamesType - -AUTH_LDAP_SERVER_URI = "ldap://127.0.0.1:389" # Replace with your LDAP server address - -# Bind credentials, if required -AUTH_LDAP_BIND_DN = "cn=admin,dc=horilla,dc=com" # Replace with your bind DN -AUTH_LDAP_BIND_PASSWORD = "your_password" # Replace with your LDAP bind password - -# Define the search base and user lookup -# AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=horilla,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") - -# Map LDAP fields to Django user fields -AUTH_LDAP_USER_ATTR_MAP = { - "first_name": "givenName", - "last_name": "sn", - "email": "mail", -} - -AUTHENTICATION_BACKENDS = [ - # "django_auth_ldap.backend.LDAPBackend", - "django.contrib.auth.backends.ModelBackend", -] -AUTH_LDAP_ALWAYS_UPDATE_USER = True diff --git a/horilla/settings/__init__.py b/horilla/settings/__init__.py new file mode 100644 index 000000000..649b29675 --- /dev/null +++ b/horilla/settings/__init__.py @@ -0,0 +1,13 @@ +""" +horilla/settings/__init__.py +Combines official base settings + client overrides. +""" + +from .base import * + +# Import client overrides (if file exists) +try: + from .local_settings import * + from .addons import * +except ImportError: + pass diff --git a/horilla/settings/addons.py b/horilla/settings/addons.py new file mode 100644 index 000000000..0e2c13254 --- /dev/null +++ b/horilla/settings/addons.py @@ -0,0 +1,16 @@ +from .local_settings import * + +if env("AWS_ACCESS_KEY_ID", default=None): + AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID") + AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY") + AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME") + AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME") + DEFAULT_FILE_STORAGE = env("DEFAULT_FILE_STORAGE") + AWS_S3_ADDRESSING_STYLE = env("AWS_S3_ADDRESSING_STYLE") + +if env("AWS_ACCESS_KEY_ID", default=None) and "storages" not in INSTALLED_APPS: + INSTALLED_APPS.append("storages") + +if env("AWS_ACCESS_KEY_ID", default=None) and "storages" in INSTALLED_APPS: + MEDIA_URL = f"{env('MEDIA_URL')}/{env('NAMESPACE')}/" + MEDIA_ROOT = f"{env('MEDIA_ROOT')}/{env('NAMESPACE')}/" diff --git a/horilla/settings/base.py b/horilla/settings/base.py new file mode 100644 index 000000000..70cdb3cb8 --- /dev/null +++ b/horilla/settings/base.py @@ -0,0 +1,442 @@ +""" +base.py — Main Django settings for Horilla +""" + +import os +from os.path import join +from pathlib import Path +from datetime import timedelta +import environ +from django.contrib.messages import constants as messages +from django.core.files.storage import FileSystemStorage + +# ======================================== +# BASE PATH & ENVIRONMENT CONFIGURATION +# ======================================== +BASE_DIR = Path(__file__).resolve().parent.parent.parent + +env = environ.Env( + DEBUG=(bool, True), + SECRET_KEY=(str, "django-insecure-default-key"), + ALLOWED_HOSTS=(list, ["*"]), + CSRF_TRUSTED_ORIGINS=(list, ["http://localhost:8000"]), +) + +env.read_env(os.path.join(BASE_DIR, ".env"), overwrite=True) + +# ======================================== +# CORE DJANGO SETTINGS +# ======================================== +SECRET_KEY = env("SECRET_KEY") +DEBUG = env("DEBUG") +ALLOWED_HOSTS = env("ALLOWED_HOSTS") +CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS") + +THEME_APP = "horilla_theme" + +INSTALLED_APPS = [ + # Default Django apps + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + # Third-party apps + "notifications", + "mathfilters", + "corsheaders", + "simple_history", + "django_filters", + "widget_tweaks", + "auditlog", + "django_apscheduler", + "rest_framework", + "rest_framework_simplejwt", + "drf_yasg", + # Core Horilla apps + "horilla_auth", + THEME_APP, + "base", + "employee", + "recruitment", + "leave", + "pms", + "onboarding", + "asset", + "attendance", + "payroll", + "accessibility", + "horilla_audit", + "horilla_widgets", + "horilla_crumbs", + "horilla_documents", + "horilla_views", + "horilla_automations", + "horilla_api", + "biometric", + "helpdesk", + "offboarding", + "horilla_backup", + "project", + "horilla_meet", + "report", + "whatsapp", +] + +# ======================================== +# REST FRAMEWORK CONFIGURATION +# ======================================== + +REST_FRAMEWORK = { + "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", + ), + "PAGE_SIZE": 20, +} + +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(days=30), +} + +SWAGGER_SETTINGS = { + "SECURITY_DEFINITIONS": { + "Bearer": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "description": "Enter your Bearer token here", + }, + "Basic": {"type": "basic", "description": "Basic authentication."}, + }, + "SECURITY": [{"Bearer": []}, {"Basic": []}], +} + +APSCHEDULER_DATETIME_FORMAT = "N j, Y, f:s a" + +APSCHEDULER_RUN_NOW_TIMEOUT = 25 # Seconds + +# ======================================== +# MIDDLEWARE +# ======================================== +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "corsheaders.middleware.CorsMiddleware", + "simple_history.middleware.HistoryRequestMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + # Horilla-specific middlewares + "base.middleware.CompanyMiddleware", + "base.middleware.ForcePasswordChangeMiddleware", + "base.middleware.TwoFactorAuthMiddleware", + "accessibility.middlewares.AccessibilityMiddleware", + "horilla.horilla_middlewares.MethodNotAllowedMiddleware", + "horilla.horilla_middlewares.ThreadLocalMiddleware", + "horilla.horilla_middlewares.SVGSecurityMiddleware", + "auditlog.middleware.AuditlogMiddleware", +] + +ROOT_URLCONF = "horilla.urls" + +# ======================================== +# DATABASE CONFIGURATION +# ======================================== +if env("DATABASE_URL", default=None): + DATABASES = {"default": env.db()} +else: + DATABASES = { + "default": { + "ENGINE": env("DB_ENGINE", default="django.db.backends.sqlite3"), + "NAME": env("DB_NAME", default=os.path.join(BASE_DIR, "db.sqlite3")), + "USER": env("DB_USER", default=""), + "PASSWORD": env("DB_PASSWORD", default=""), + "HOST": env("DB_HOST", default=""), + "PORT": env("DB_PORT", default=""), + } + } + +# ======================================== +# STATIC & MEDIA FILES +# ======================================== +STATIC_URL = "static/" +STATIC_ROOT = BASE_DIR / "staticfiles" +STATICFILES_DIRS = [BASE_DIR / "static"] +STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage" + +MEDIA_URL = "/media/" +MEDIA_ROOT = os.path.join(BASE_DIR, "media/") + +# ======================================== +# AUTHENTICATION & SECURITY +# ======================================== +AUTH_PASSWORD_VALIDATORS = [ + {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, +] + +AUTH_USER_MODEL = 'horilla_auth.HorillaUser' + +X_FRAME_OPTIONS = "SAMEORIGIN" + +# ======================================== +# TEMPLATES +# ======================================== +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": False, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + # Horilla dynamic context processors + "horilla.config.get_MENUS", + "base.context_processors.get_companies", + "base.context_processors.white_labelling_company", + "base.context_processors.resignation_request_enabled", + "base.context_processors.timerunner_enabled", + "base.context_processors.intial_notice_period", + "base.context_processors.check_candidate_self_tracking", + "base.context_processors.check_candidate_self_tracking_rating", + "base.context_processors.get_initial_prefix", + "base.context_processors.biometric_app_exists", + "base.context_processors.enable_late_come_early_out_tracking", + "base.context_processors.enable_profile_edit", + ], + "loaders": [ + ( + "django.template.loaders.filesystem.Loader", + [BASE_DIR / THEME_APP / "templates"] + ), + "django.template.loaders.app_directories.Loader", + ( + "django.template.loaders.filesystem.Loader", + [BASE_DIR / "templates"] + ), + ], + }, + }, +] + +WSGI_APPLICATION = "horilla.wsgi.application" + +# ======================================== +# INTERNATIONALIZATION +# ======================================== +LANGUAGE_CODE = "en-us" +TIME_ZONE = env("TIME_ZONE", default="Asia/Kolkata") +USE_I18N = True +USE_TZ = True + +LANGUAGES = ( + ("en", "English (US)"), + ("de", "Deutsche"), + ("es", "Español"), + ("fr", "Français"), + ("ar", "عربى"), + ("pt-br", "Português (Brasil)"), + ("zh-hans", "Simplified Chinese"), + ("zh-hant", "Traditional Chinese"), + ("it", "Italian"), +) + +LOCALE_PATHS = [join(BASE_DIR, "horilla", "locale")] + +# ======================================== +# LOGGING, MESSAGES, OTHER GLOBALS +# ======================================== +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +MESSAGE_TAGS = { + messages.DEBUG: "oh-alert--warning", + messages.INFO: "oh-alert--info", + messages.SUCCESS: "oh-alert--success", + messages.WARNING: "oh-alert--warning", + messages.ERROR: "oh-alert--danger", +} + +LOGIN_URL = "/login" +SIMPLE_HISTORY_REVERT_DISABLED = True + +DJANGO_NOTIFICATIONS_CONFIG = { + "USE_JSONFIELD": True, + "SOFT_DELETE": True, + "USE_WATCHED": True, + "NOTIFICATIONS_STORAGE": "notifications.storage.DatabaseStorage", + "TEMPLATE": "notifications.html", +} + +# ======================================== +# HORILLA-SPECIFIC SETTINGS +# ======================================== +WHITE_LABELLING = False +NESTED_SUBORDINATE_VISIBILITY = False +TWO_FACTORS_AUTHENTICATION = False + +SIDEBARS = [ + "recruitment", + "onboarding", + "employee", + "attendance", + "leave", + "payroll", + "pms", + "offboarding", + "asset", + "helpdesk", + "project", + "report", +] + +AUDITLOG_INCLUDE_ALL_MODELS = True +AUDITLOG_EXCLUDE_TRACKING_MODELS = ( + # "", + # "." +) + +EMAIL_BACKEND = "base.backends.ConfiguredEmailBackend" + +""" +DB_INIT_PASSWORD: str + +The password used for database setup and initialization. This password is a +48-character alphanumeric string generated using a UUID to ensure high entropy and security. +""" +DB_INIT_PASSWORD = env( + "DB_INIT_PASSWORD", default="d3f6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d" +) + +# ======================================== +# PERMISSIONS / CUSTOM LOGIC +# ======================================== +NO_PERMISSION_MODALS = [ + "historicalbonuspoint", + "assetreport", + "assetdocuments", + "returnimages", + "holiday", + "companyleave", + "historicalavailableleave", + "historicalleaverequest", + "historicalleaveallocationrequest", + "leaverequestconditionapproval", + "historicalcompensatoryleaverequest", + "employeepastleaverestrict", + "overrideleaverequests", + "historicalrotatingworktypeassign", + "employeeshiftday", + "historicalrotatingshiftassign", + "historicalworktyperequest", + "historicalshiftrequest", + "multipleapprovalmanagers", + "attachment", + "announcementview", + "emaillog", + "driverviewed", + "dashboardemployeecharts", + "attendanceallowedip", + "tracklatecomeearlyout", + "historicalcontract", + "overrideattendance", + "overrideleaverequest", + "overrideworkinfo", + "multiplecondition", + "historicalpayslip", + "reimbursementmultipleattachment", + "workrecord", + "historicalticket", + "skill", + "historicalcandidate", + "rejectreason", + "historicalrejectedcandidate", + "rejectedcandidate", + "stagefiles", + "stagenote", + "questionordering", + "recruitmentsurveyordering", + "recruitmentsurveyanswer", + "recruitmentgeneralsetting", + "resume", + "recruitmentmailtemplate", + "profileeditfeature", +] + +FILE_STORAGE = FileSystemStorage(location="csv_tmp/") + +HORILLA_DATE_FORMATS = { + "DD/MM/YY": "%d/%m/%y", + "DD-MM-YYYY": "%d-%m-%Y", + "DD.MM.YYYY": "%d.%m.%Y", + "DD/MM/YYYY": "%d/%m/%Y", + "MM/DD/YYYY": "%m/%d/%Y", + "YYYY-MM-DD": "%Y-%m-%d", + "YYYY/MM/DD": "%Y/%m/%d", + "MMMM D, YYYY": "%B %d, %Y", + "DD MMMM, YYYY": "%d %B, %Y", + "MMM. D, YYYY": "%b. %d, %Y", + "D MMM. YYYY": "%d %b. %Y", + "dddd, MMMM D, YYYY": "%A, %B %d, %Y", +} + +HORILLA_TIME_FORMATS = { + "hh:mm A": "%I:%M %p", # 12-hour format + "HH:mm": "%H:%M", # 24-hour format + "HH:mm:ss.SSSSSS": "%H:%M:%S.%f", # 24-hour format with seconds and microseconds +} + +BIO_DEVICE_THREADS = {} + +DYNAMIC_URL_PATTERNS = [] + +APP_URLS = [ + "base.urls", + "employee.urls", +] + +APPS = [ + "base", + "employee", + "horilla_documents", + "horilla_automations", +] + +# ======================================== +# LDAP CONFIGURATION (Default) +# ======================================== +AUTH_LDAP_SERVER_URI = "ldap://127.0.0.1:389" +AUTH_LDAP_BIND_DN = "cn=admin,dc=horilla,dc=com" +AUTH_LDAP_BIND_PASSWORD = "your_password" + +AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail", +} + +# Default LDAP settings +DEFAULT_LDAP_CONFIG = { + "LDAP_SERVER": env("LDAP_SERVER", default="ldap://127.0.0.1:389"), + "BIND_DN": env("BIND_DN", default="cn=admin,dc=horilla,dc=com"), + "BIND_PASSWORD": env("BIND_PASSWORD", default="horilla"), + "BASE_DN": env("BASE_DN", default="ou=users,dc=horilla,dc=com"), +} + +AUTHENTICATION_BACKENDS = [ + "django.contrib.auth.backends.ModelBackend", + # "django_auth_ldap.backend.LDAPBackend", +] + +AUTH_LDAP_ALWAYS_UPDATE_USER = True \ No newline at end of file