• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

chiefonboarding / ChiefOnboarding / 16131770315

08 Jul 2025 01:33AM UTC coverage: 90.64% (-0.009%) from 90.649%
16131770315

Pull #560

github

web-flow
Merge 528f891fb into 652164dab
Pull Request #560: Add ipware for correctly blocking users when behind proxy

8270 of 9124 relevant lines covered (90.64%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.46
back/back/settings.py
1
"""
2
Django settings for back project.
3

4
Generated by 'django-admin startproject' using Django 3.0.1.
5

6
For more information on this file, see
7
https://docs.djangoproject.com/en/3.0/topics/settings/
8

9
For the full list of settings and their values, see
10
https://docs.djangoproject.com/en/3.0/ref/settings/
11
"""
12

13
import os
1✔
14
import sys
1✔
15

16
import environ
1✔
17
from django.utils.translation import gettext_lazy as _
1✔
18

19
env = environ.Env()
1✔
20
environ.Env.read_env(env.str("ENV_PATH", "back/.env"))
1✔
21

22
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
23
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
1✔
24

25
# Quick-start development settings - unsuitable for production
26
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
27

28
# SECURITY WARNING: keep the secret key used in production secret!
29
SECRET_KEY = env("SECRET_KEY")
1✔
30

31
# SECURITY WARNING: don't run with debug turned on in production!
32
DEBUG = env.bool("DEBUG", default=False)
1✔
33

34
if env("ALLOWED_HOSTS", default="") != "":
1✔
35
    ALLOWED_HOSTS = [host for host in env.list("ALLOWED_HOSTS")]
×
36
else:
37
    # Fallback for old environment variable to avoid breaking change
38
    ALLOWED_HOSTS = [
1✔
39
        env("ALLOWED_HOST", default="0.0.0.0"),
40
    ]
41

42
if DEBUG:
1✔
43
    ALLOWED_HOSTS = ["*"]
1✔
44

45

46
if env.str("BASE_URL", "") == "":
1✔
47
    BASE_URL = "https://" + ALLOWED_HOSTS[0]
×
48
else:
49
    BASE_URL = env("BASE_URL")
1✔
50

51
CSRF_TRUSTED_ORIGINS = env.list(
1✔
52
    "CSRF_TRUSTED_ORIGINS",
53
    default=[
54
        BASE_URL,
55
    ],
56
)
57

58
INSTALLED_APPS = [
1✔
59
    "django.contrib.auth",
60
    "django.contrib.contenttypes",
61
    "django.contrib.postgres",
62
    "django.contrib.sessions",
63
    "django.contrib.messages",
64
    "django.contrib.staticfiles",
65
    "django.contrib.humanize",
66
    "users",
67
    "organization",
68
    "user_auth",
69
    "misc",
70
    "back",
71
    # admin
72
    "admin.templates",
73
    "admin.notes",
74
    "admin.to_do",
75
    "admin.resources",
76
    "admin.introductions",
77
    "admin.admin_tasks",
78
    "admin.badges",
79
    "admin.integrations",
80
    "admin.preboarding",
81
    "admin.appointments",
82
    "admin.sequences",
83
    "admin.people",
84
    "admin.settings",
85
    "admin.hardware",
86
    # new hires
87
    "new_hire",
88
    # slack
89
    "slack_bot",
90
    # external
91
    "rest_framework",
92
    "axes",
93
    "anymail",
94
    "django_q",
95
    "crispy_forms",
96
]
97

98
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
1✔
99

100
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
1✔
101
CRISPY_TEMPLATE_PACK = "bootstrap5"
1✔
102

103
# Login Defaults
104
LOGIN_REDIRECT_URL = "logged_in_user_redirect"
1✔
105
LOGOUT_REDIRECT_URL = "login"
1✔
106
LOGIN_URL = "login"
1✔
107

108
RUNNING_TESTS = "pytest" in sys.modules
1✔
109
FAKE_SLACK_API = False
1✔
110
SLACK_USE_SOCKET = env.bool("SLACK_USE_SOCKET", default=False)
1✔
111
SLACK_APP_TOKEN = env("SLACK_APP_TOKEN", default="")
1✔
112
SLACK_BOT_TOKEN = env("SLACK_BOT_TOKEN", default="")
1✔
113
SLACK_DISABLE_AUTO_UPDATE_CHANNELS = env.bool(
1✔
114
    "SLACK_DISABLE_AUTO_UPDATE_CHANNELS", default=False
115
)
116

117
MIDDLEWARE = [
1✔
118
    "django.middleware.security.SecurityMiddleware",
119
    "whitenoise.middleware.WhiteNoiseMiddleware",
120
    "django.contrib.sessions.middleware.SessionMiddleware",
121
    "django.middleware.locale.LocaleMiddleware",
122
    "organization.middleware.HealthCheckMiddleware",
123
    "django.middleware.common.CommonMiddleware",
124
    "django.middleware.csrf.CsrfViewMiddleware",
125
    "django.contrib.auth.middleware.AuthenticationMiddleware",
126
    "django.contrib.messages.middleware.MessageMiddleware",
127
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
128
    "users.middleware.language_middleware",
129
    "axes.middleware.AxesMiddleware",
130
]
131

132
# Django Debug Bar
133
if DEBUG:
1✔
134
    import socket
1✔
135

136
    INSTALLED_APPS += [
1✔
137
        "debug_toolbar",
138
    ]
139
    MIDDLEWARE += [
1✔
140
        "debug_toolbar.middleware.DebugToolbarMiddleware",
141
    ]
142
    hostname, _dummy, ips = socket.gethostbyname_ex(socket.gethostname())
1✔
143
    INTERNAL_IPS = [ip[:-1] + "1" for ip in ips] + ["127.0.0.1", "10.0.2.2"]
1✔
144

145

146
ROOT_URLCONF = "back.urls"
1✔
147

148
TEMPLATES = [
1✔
149
    {
150
        "BACKEND": "django.template.backends.django.DjangoTemplates",
151
        "DIRS": [],
152
        "APP_DIRS": True,
153
        "OPTIONS": {
154
            "context_processors": [
155
                "django.template.context_processors.debug",
156
                "django.template.context_processors.request",
157
                "django.contrib.auth.context_processors.auth",
158
                "django.contrib.messages.context_processors.messages",
159
                "organization.context_processor.org_include",
160
            ],
161
        },
162
    },
163
]
164
TEMPLATE_DIRS = (os.path.join(BASE_DIR, "templates"),)
1✔
165

166
WSGI_APPLICATION = "back.wsgi.application"
1✔
167

168
# Database
169
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
170

171
DATABASES = {"default": env.db()}
1✔
172

173
# Password validation
174
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
175

176
AUTH_PASSWORD_VALIDATORS = [
1✔
177
    {
178
        "NAME": (
179
            "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
180
        ),
181
    },
182
    {
183
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
184
    },
185
    {
186
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
187
    },
188
    {
189
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
190
    },
191
]
192

193
# Internationalization
194
# https://docs.djangoproject.com/en/3.0/topics/i18n/
195

196
LANGUAGE_CODE = "en-us"
1✔
197

198
TIME_ZONE = "UTC"
1✔
199

200
USE_I18N = True
1✔
201

202
USE_TZ = True
1✔
203

204
# Static files (CSS, JavaScript, Images)
205
# https://docs.djangoproject.com/en/3.0/howto/static-files/
206

207
STATIC_URL = "/static/"
1✔
208
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
1✔
209

210
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
1✔
211

212
AUTH_USER_MODEL = "users.User"
1✔
213

214
REST_FRAMEWORK = {
1✔
215
    "DEFAULT_AUTHENTICATION_CLASSES": [
216
        "rest_framework.authentication.TokenAuthentication",
217
    ],
218
    "DEFAULT_RENDERER_CLASSES": [
219
        "rest_framework.renderers.JSONRenderer",
220
    ],
221
    "DEFAULT_PERMISSION_CLASSES": [
222
        "api.permissions.AdminPermission",
223
    ],
224
}
225

226

227
# API
228
API_ACCESS = env.bool("API_ACCESS", default=DEBUG or RUNNING_TESTS)
1✔
229
if API_ACCESS:
1✔
230
    INSTALLED_APPS += ["rest_framework.authtoken", "api"]
1✔
231

232
# Email
233
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
1✔
234

235
if env("MAILGUN_API_KEY", default="") != "":
1✔
236
    ANYMAIL = {
×
237
        "MAILGUN_API_KEY": env("MAILGUN_KEY", default=""),
238
        "MAILGUN_SENDER_DOMAIN": env("MAILGUN_DOMAIN", default=""),
239
    }
240
    EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
×
241

242
if env("MAILJET_API_KEY", default="") != "":
1✔
243
    ANYMAIL = {
×
244
        "MAILJET_API_KEY": env("MAILJET_API_KEY", default=""),
245
        "MAILJET_SECRET_KEY": env("MAILJET_SECRET_KEY", default=""),
246
    }
247
    EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend"
×
248

249
if env("MANDRILL_KEY", default="") != "":
1✔
250
    ANYMAIL = {"MANDRILL_API_KEY": env("MANDRILL_KEY", default="")}
×
251
    EMAIL_BACKEND = "anymail.backends.mandrill.EmailBackend"
×
252

253
if env("POSTMARK_KEY", default="") != "":
1✔
254
    ANYMAIL = {"POSTMARK_SERVER_TOKEN": env("POSTMARK_KEY", default="")}
×
255
    EMAIL_BACKEND = "anymail.backends.postmark.EmailBackend"
×
256

257
if env("SENDGRID_KEY", default="") != "":
1✔
258
    ANYMAIL = {"SENDGRID_API_KEY": env("SENDGRID_KEY", default="")}
×
259
    EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend"
×
260

261
if env("SENDINBLUE_KEY", default="") != "":
1✔
262
    ANYMAIL = {"SENDINBLUE_API_KEY": env("SENDINBLUE_KEY", default="")}
×
263
    EMAIL_BACKEND = "anymail.backends.sendinblue.EmailBackend"
×
264

265
if env("EMAIL_HOST", default="") != "":
1✔
266
    EMAIL_HOST = env("EMAIL_HOST", default="localhost")
×
267
    EMAIL_PORT = env.int("EMAIL_PORT", default=25)
×
268
    EMAIL_HOST_USER = env("EMAIL_HOST_USER", default="")
×
269
    EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", default="")
×
270
    EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", default=False)
×
271
    EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", default=False)
×
272
    EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
×
273

274

275
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL", default="example@example.com")
1✔
276

277
OLD_PASSWORD_FIELD_ENABLED = True
1✔
278

279
# Caching
280
CACHES = {
1✔
281
    "default": {
282
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
283
        "LOCATION": "cached_items",
284
    }
285
}
286

287
Q_CLUSTER = {
1✔
288
    "name": "DjangORM",
289
    "workers": 1,
290
    "timeout": 90,
291
    "retry": 1800,
292
    "queue_limit": 50,
293
    "bulk": 10,
294
    "orm": "default",
295
    "catch_up": False,
296
    "max_attempts": 2,
297
}
298

299
if DEBUG and RUNNING_TESTS:
1✔
300
    Q_CLUSTER["sync"] = True
1✔
301

302
# AWS
303
AWS_S3_ENDPOINT_URL = env(
1✔
304
    "AWS_S3_ENDPOINT_URL", default="https://s3.eu-west-1.amazonaws.com"
305
)
306
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID", default="")
1✔
307
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY", default="")
1✔
308
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME", default="")
1✔
309
# fallback for old environment variable, AWS_DEFAULT_REGION should be prefered
310
AWS_REGION = env("AWS_REGION", default="eu-west-1")
1✔
311
AWS_DEFAULT_REGION = env("AWS_DEFAULT_REGION", default=AWS_REGION)
1✔
312

313
# Twilio
314
TWILIO_FROM_NUMBER = env("TWILIO_FROM_NUMBER", default="")
1✔
315
TWILIO_ACCOUNT_SID = env("TWILIO_ACCOUNT_SID", default="")
1✔
316
TWILIO_AUTH_TOKEN = env("TWILIO_AUTH_TOKEN", default="")
1✔
317

318
# Django-Axes
319
AUTHENTICATION_BACKENDS = [
1✔
320
    # AxesBackend should be the first backend in the AUTHENTICATION_BACKENDS list.
321
    "axes.backends.AxesBackend",
322
    # Django ModelBackend is the default authentication backend.
323
    "django.contrib.auth.backends.ModelBackend",
324
]
325
AXES_ENABLED = env.bool("AXES_ENABLED", default=True)
1✔
326
AXES_FAILURE_LIMIT = env.int("AXES_FAILURE_LIMIT", default=10)
1✔
327
AXES_COOLOFF_TIME = env.int("AXES_COOLOFF_TIME", default=24)
1✔
328
AXES_IPWARE_PROXY_COUNT = env.int("AXES_IPWARE_PROXY_COUNT", default=None)
1✔
329
AXES_IPWARE_META_PRECEDENCE_ORDER = env.tuple(
1✔
330
    "AXES_IPWARE_META_PRECEDENCE_ORDER", default=("REMOTE_ADDR",)
331
)
332

333
if env.bool("AXES_USE_FORWARDED_FOR", True):
1✔
334
    AXES_IPWARE_META_PRECEDENCE_ORDER = [
1✔
335
        "HTTP_X_FORWARDED_FOR",
336
        "REMOTE_ADDR",
337
    ]
338

339
# Error tracking
340
if env("SENTRY_URL", default="") != "":
1✔
341
    import logging
×
342

343
    import sentry_sdk
×
344
    from sentry_sdk.integrations.django import DjangoIntegration
×
345
    from sentry_sdk.integrations.logging import LoggingIntegration
×
346

347
    sentry_logging = LoggingIntegration(
×
348
        level=logging.INFO,  # Capture info and above as breadcrumbs
349
        event_level=logging.ERROR,  # Send errors as events
350
    )
351

352
    sentry_sdk.init(
×
353
        dsn=env("SENTRY_URL", default=""),
354
        integrations=[DjangoIntegration(), sentry_logging],
355
        # If you wish to associate users to errors (assuming you are using
356
        # django.contrib.auth) you may enable sending PII data.
357
        send_default_pii=False,
358
    )
359

360
if not env.bool("DEBUG", default=False) and not env.bool(
1✔
361
    "HTTP_INSECURE", default=False
362
):
363
    SESSION_COOKIE_SECURE = True
×
364
    CSRF_COOKIE_SECURE = True
×
365

366
FIXTURE_DIRS = ["fixtures"]
1✔
367

368
# Forcing SSL from Django - preferably done a few levels before,
369
# but this is a last resort in the case of Heroku
370
if env.bool("SSL_REDIRECT", default=False):
1✔
371
    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
×
372
    SECURE_SSL_REDIRECT = True
×
373

374
# Storing static files compressed
375
STORAGES = {
1✔
376
    "staticfiles": {"BACKEND": "whitenoise.storage.CompressedStaticFilesStorage"},
377
}
378

379
# Languages
380
LANGUAGES = [
1✔
381
    ("en", _("English")),
382
    ("nl", _("Dutch")),
383
    ("fr", _("French")),
384
    ("de", _("German")),
385
    ("tr", _("Turkish")),
386
    ("pt", _("Portuguese")),
387
    ("jp", _("Japanese")),
388
    ("es", _("Spanish")),
389
    ("cs", _("Czech")),
390
]
391
LANGUAGE_SESSION_KEY = "chief-language"
1✔
392
SITE_ROOT = os.path.dirname(os.path.realpath(__name__))
1✔
393
LOCALE_PATHS = (os.path.join(SITE_ROOT, "locale"),)
1✔
394

395
if env.bool("DEBUG_LOGGING", default=False):
1✔
396
    LOGGING = {
×
397
        "version": 1,
398
        "disable_existing_loggers": False,
399
        "formatters": {
400
            "simple": {"format": "%(levelname)s %(message)s"},
401
        },
402
        "handlers": {
403
            "console": {
404
                "level": "DEBUG",
405
                "class": "logging.StreamHandler",
406
                "formatter": "simple",
407
            },
408
        },
409
        "loggers": {
410
            "django.request": {
411
                "handlers": ["console"],
412
                "propagate": False,
413
                "level": "DEBUG",
414
            },
415
            "root": {"level": "DEBUG", "handlers": ["console"]},
416
        },
417
    }
418

419

420
# OIDC
421
OIDC_LOGIN_DISPLAY = env("OIDC_LOGIN_DISPLAY", default="Custom-OIDC")
1✔
422
OIDC_CLIENT_ID = env("OIDC_CLIENT_ID", default="")
1✔
423
OIDC_CLIENT_SECRET = env("OIDC_CLIENT_SECRET", default="")
1✔
424
OIDC_AUTHORIZATION_URL = env("OIDC_AUTHORIZATION_URL", default="")
1✔
425
OIDC_TOKEN_URL = env("OIDC_TOKEN_URL", default="")
1✔
426
OIDC_USERINFO_URL = env("OIDC_USERINFO_URL", default="")
1✔
427
OIDC_SCOPES = env("OIDC_SCOPES", default="openid email name profile")
1✔
428
OIDC_LOGOUT_URL = env("OIDC_LOGOUT_URL", default="")
1✔
429
OIDC_FORCE_AUTHN = env.bool("OIDC_FORCE_AUTHN", default=False)
1✔
430
OIDC_ROLE_ADMIN_PATTERN = env("OIDC_ROLE_ADMIN_PATTERN", default="^cn=Administrators.*")
1✔
431
OIDC_ROLE_MANAGER_PATTERN = env("OIDC_ROLE_MANAGER_PATTERN", default="^cn=Managers.*")
1✔
432
OIDC_ROLE_NEW_HIRE_PATTERN = env("OIDC_ROLE_NEW_HIRE_PATTERN", default="^cn=Newhires.*")
1✔
433
OIDC_ROLE_DEFAULT = env.int("OIDC_DEFAULT_ROLE", "3")
1✔
434
OIDC_ROLE_UPDATING = env.bool("OIDC_ROLE_UPDATING", True)
1✔
435
OIDC_ROLE_PATH_IN_RETURN = env("OIDC_ROLE_PATH_IN_RETURN", default="zoneinfo").split(
1✔
436
    "."
437
)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc