defaults.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. # Copyright 2013-2016 The Distro Tracker Developers
  2. # See the COPYRIGHT file at the top-level directory of this distribution and
  3. # at http://deb.li/DTAuthors
  4. #
  5. # This file is part of Distro Tracker. It is subject to the license terms
  6. # in the LICENSE file found in the top-level directory of this
  7. # distribution and at http://deb.li/DTLicense. No part of Distro Tracker,
  8. # including this file, may be copied, modified, propagated, or distributed
  9. # except according to the terms contained in the LICENSE file.
  10. """Default Django settings for the Distro Tracker project.
  11. Most settings are documented in this file and they are initialized to some
  12. reasonable default values when possible. They will be extended (and
  13. possibly overriden) by settings from the other modules in this package
  14. depending on the setup selected by the administrator. You likely won't
  15. have to modify that file.
  16. You should instead create local.py to put your site-specific settings (use
  17. local.py.sample as template).
  18. Here are the most important settings:
  19. :py:data:`DISTRO_TRACKER_FQDN`
  20. The fully qualified domain name of the distro-tracker installation.
  21. It should be a service-specific DNS entry like "tracker.example.com".
  22. Defaults to the FQDN of the machine which might not be adequate.
  23. :py:data:`DISTRO_TRACKER_DATA_PATH`
  24. The directory where distro-tracker will hold its data. The directory is
  25. further sub-divided in multiple directories for specific use
  26. cases (e.g. cache, keyring, static, media, logs, templates, etc.).
  27. Defaults to the "data" sub-directory in the distro-tracker
  28. base directory (where the code lives).
  29. :py:data:`MEDIA_URL`
  30. URL that handles the media served from MEDIA_ROOT. Make sure to use a
  31. trailing slash.
  32. Examples: "http://example.com/media/", "http://media.example.com/"
  33. Defaults to "/media/".
  34. :py:data:`STATIC_URL`
  35. URL prefix for static files.
  36. Example: "http://example.com/static/", "http://static.example.com/"
  37. Defaults to "/static/"
  38. Some settings have default values which are computed dynamically from
  39. other settings. Those settings can also be overriden. Here's the list
  40. of those settings.
  41. :py:data:`DISTRO_TRACKER_VENDOR_NAME`
  42. The name of the vendor. Equivalent to the Vendor field of an
  43. /etc/dpkg/origins file. Default value computed from the domain
  44. name of :py:data:`DISTRO_TRACKER_FQDN`.
  45. :py:data:`DISTRO_TRACKER_VENDOR_URL`
  46. The URL of the vendor. Equivalent to the Vendor-URL field of an
  47. /etc/dpkg/origins file. Default value computed as "www." + the domain
  48. name of :py:data:`DISTRO_TRACKER_FQDN`.
  49. :py:data:`STATIC_ROOT`
  50. Absolute path to the directory static files should be collected to.
  51. Don't put anything in this directory yourself; store your static files
  52. in apps' "static/" subdirectories and in STATICFILES_DIRS. Defaults
  53. to the "static" sub-directory of :py:data:`DISTRO_TRACKER_DATA_PATH`.
  54. :py:data:`MEDIA_ROOT`
  55. Absolute filesystem path to the directory that will hold user-uploaded
  56. files. Defaults to the "media" sub-directory of
  57. :py:data:`DISTRO_TRACKER_DATA_PATH`.
  58. :py:data:`DISTRO_TRACKER_CACHE_DIRECTORY`
  59. This directory is used to store the locally cached resources.
  60. Any Distro Tracker app should be able to use this directory to store
  61. its caches. For example, it is used to store the APT cache of repository
  62. information and the cache of retrieved Web resources.
  63. Defaults to the "cache" sub-directory of
  64. :py:data:`DISTRO_TRACKER_DATA_PATH`.
  65. :py:data:`DISTRO_TRACKER_KEYRING_DIRECTORY`
  66. This directory should contain a gpg.conf listing the GPG keyrings of known
  67. public keys. It's used to identify authors of package uploads. Defaults
  68. to the "keyring" sub-directory of py:data:`DISTRO_TRACKER_DATA_PATH`.
  69. :py:data:`DISTRO_TRACKER_LOG_DIRECTORY`
  70. This directory will hold log files generated by distro-tracker.
  71. Defaults to the "logs" sub-directory of py:data:`DISTRO_TRACKER_DATA_PATH`.
  72. :py:data:`DISTRO_TRACKER_MAILDIR_DIRECTORY`
  73. This directory is used as a mailbox in the Maildir format. All incoming
  74. mails are stored here. Defaults to the "maildir" sub-directory of
  75. py:data:`DISTRO_TRACKER_DATA_PATH`.
  76. :py:data:`DISTRO_TRACKER_TEMPLATE_DIRECTORY`
  77. This directory can hold custom templates that will override the
  78. templates supplied by distro-tracker. Defaults to the "templates"
  79. sub-directory of py:data:`DISTRO_TRACKER_DATA_PATH`.
  80. :py:data:`DISTRO_TRACKER_CONTROL_EMAIL`
  81. The email address which is to receive control emails.
  82. It does not necessarily have to be in the same domain as specified in
  83. :py:data:`DISTRO_TRACKER_FQDN`. Defaults to "control@" +
  84. :py:data:`DISTRO_TRACKER_FQDN`.
  85. :py:data:`DISTRO_TRACKER_CONTACT_EMAIL`
  86. The email address which is to receive contact emails.
  87. It does not necessarily have to be in the same domain as specified in
  88. :py:data:`DISTRO_TRACKER_FQDN`. Defaults to "owner@" +
  89. :py:data:`DISTRO_TRACKER_FQDN`.
  90. :py:data:`DISTRO_TRACKER_BOUNCES_EMAIL`
  91. The email address which is to be used as the sender address when no bounce
  92. processing should happen. It does not necessarily have to be in the same
  93. domain as specified in :py:data:`DISTRO_TRACKER_FQDN`. Defaults
  94. to "bounces@" + :py:data:`DISTRO_TRACKER_FQDN`.
  95. :py:data:`DISTRO_TRACKER_BOUNCES_LIKELY_SPAM_EMAIL`
  96. The email address which should receive bounces that are likely the
  97. result of incoming spam.
  98. More settings:
  99. """
  100. from __future__ import unicode_literals
  101. import django
  102. from django.core.exceptions import ImproperlyConfigured
  103. from django.utils import six
  104. from os.path import dirname
  105. import socket
  106. import os.path
  107. if django.VERSION < (1, 8):
  108. raise ImproperlyConfigured("Distro Tracker needs Django >= 1.8")
  109. six.add_move(six.MovedModule('mock', 'mock', 'unittest.mock'))
  110. # Django's debug mode, never enable this in production
  111. DEBUG = False
  112. BASE_DIR = dirname(dirname(dirname(dirname(__file__))))
  113. DISTRO_TRACKER_DATA_PATH = os.path.join(BASE_DIR, 'data')
  114. # Local time zone for this installation. Choices can be found here:
  115. # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
  116. # although not all choices may be available on all operating systems.
  117. # In a Windows environment this must be set to your system time zone.
  118. TIME_ZONE = 'UTC'
  119. # Language code for this installation. All choices can be found here:
  120. # http://www.i18nguy.com/unicode/language-identifiers.html
  121. LANGUAGE_CODE = 'en-us'
  122. # If you set this to False, Django will make some optimizations so as not
  123. # to load the internationalization machinery.
  124. USE_I18N = True
  125. # If you set this to False, Django will not format dates, numbers and
  126. # calendars according to the current locale.
  127. USE_L10N = True
  128. # If you set this to False, Django will not use timezone-aware datetimes.
  129. USE_TZ = True
  130. # URL that handles the media served from MEDIA_ROOT. Make sure to use a
  131. # trailing slash.
  132. # Examples: "http://example.com/media/", "http://media.example.com/"
  133. MEDIA_URL = '/media/'
  134. # URL prefix for static files.
  135. # Example: "http://example.com/static/", "http://static.example.com/"
  136. STATIC_URL = '/static/'
  137. # Additional locations of static files
  138. STATICFILES_DIRS = (
  139. # Put strings here, like "/home/html/static" or "C:/www/django/static".
  140. # Always use forward slashes, even on Windows.
  141. # Don't forget to use absolute paths, not relative paths.
  142. )
  143. # List of finder classes that know how to find static files in
  144. # various locations.
  145. STATICFILES_FINDERS = (
  146. 'django.contrib.staticfiles.finders.FileSystemFinder',
  147. 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
  148. # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
  149. )
  150. # Make this unique, and don't share it with anybody.
  151. try:
  152. with open('/var/lib/distro-tracker/key', 'r') as f:
  153. SECRET_KEY = f.read().strip()
  154. except IOError:
  155. SECRET_KEY = 'etu2#5lv=!0(g9l31mw=cpwhioy!egg60lb5o3_67d83#(wu-u'
  156. # Templating rules
  157. TEMPLATES = [
  158. {
  159. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  160. 'DIRS': [],
  161. 'OPTIONS': {
  162. 'context_processors': [
  163. 'django.contrib.auth.context_processors.auth',
  164. 'django.template.context_processors.debug',
  165. 'django.template.context_processors.i18n',
  166. 'django.template.context_processors.media',
  167. 'django.template.context_processors.static',
  168. 'django.template.context_processors.tz',
  169. 'django.contrib.messages.context_processors.messages',
  170. 'django.template.context_processors.request',
  171. 'distro_tracker.core.context_processors.extras',
  172. ],
  173. 'loaders': [
  174. ('django.template.loaders.cached.Loader', [
  175. 'django.template.loaders.filesystem.Loader',
  176. 'django.template.loaders.app_directories.Loader',
  177. ]),
  178. ],
  179. }
  180. },
  181. ]
  182. MIDDLEWARE_CLASSES = (
  183. 'django.middleware.common.CommonMiddleware',
  184. 'django.contrib.sessions.middleware.SessionMiddleware',
  185. 'django.middleware.csrf.CsrfViewMiddleware',
  186. 'django.contrib.auth.middleware.AuthenticationMiddleware',
  187. 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
  188. 'django.contrib.messages.middleware.MessageMiddleware',
  189. # Disabled to allow rendering in iframes
  190. # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
  191. )
  192. AUTHENTICATION_BACKENDS = (
  193. 'django_email_accounts.auth.UserEmailBackend',
  194. )
  195. AUTH_USER_MODEL = 'accounts.User'
  196. ROOT_URLCONF = 'distro_tracker.project.urls'
  197. # Python dotted path to the WSGI application used by Django's runserver.
  198. WSGI_APPLICATION = 'distro_tracker.project.wsgi.application'
  199. INSTALLED_APPS = (
  200. 'django.contrib.auth',
  201. 'django.contrib.contenttypes',
  202. 'django.contrib.sessions',
  203. 'django.contrib.messages',
  204. 'django.contrib.staticfiles',
  205. 'django.contrib.admin',
  206. 'django_email_accounts',
  207. 'distro_tracker.html',
  208. 'distro_tracker.core',
  209. 'distro_tracker.accounts',
  210. 'distro_tracker.mail',
  211. )
  212. # See http://docs.djangoproject.com/en/dev/topics/logging for
  213. # more details on how to customize your logging configuration.
  214. LOGGING = {
  215. 'version': 1,
  216. 'disable_existing_loggers': True,
  217. 'formatters': {
  218. 'verbose': {
  219. 'format': '%(asctime)s [%(module)s/%(process)d/%(thread)d] ' +
  220. '%(levelname)s: %(message)s'
  221. },
  222. 'standard': {
  223. 'format': '%(asctime)s %(process)d %(levelname)s: %(message)s'
  224. },
  225. 'simple': {
  226. 'format': '%(asctime)s %(levelname)s: %(message)s'
  227. },
  228. },
  229. 'filters': {
  230. 'require_debug_false': {
  231. '()': 'django.utils.log.RequireDebugFalse'
  232. },
  233. 'require_debug_true': {
  234. '()': 'django.utils.log.RequireDebugTrue',
  235. },
  236. },
  237. 'handlers': {
  238. 'console': {
  239. 'level': 'INFO',
  240. 'class': 'logging.StreamHandler',
  241. 'formatter': 'simple',
  242. 'filters': ['require_debug_true'],
  243. },
  244. 'mail_admins': {
  245. 'level': 'ERROR',
  246. 'filters': ['require_debug_false'],
  247. 'class': 'django.utils.log.AdminEmailHandler',
  248. },
  249. 'null': {
  250. 'level': 'DEBUG',
  251. 'class': 'logging.NullHandler',
  252. },
  253. 'mail.log': {
  254. 'level': 'INFO',
  255. 'class': 'logging.handlers.TimedRotatingFileHandler',
  256. 'filename': 'mail.log',
  257. 'formatter': 'standard',
  258. 'when': 'W0',
  259. 'delay': True,
  260. 'backupCount': 52,
  261. },
  262. 'tasks.log': {
  263. 'level': 'INFO',
  264. 'class': 'logging.handlers.TimedRotatingFileHandler',
  265. 'filename': 'tasks.log',
  266. 'formatter': 'standard',
  267. 'when': 'W0',
  268. 'delay': True,
  269. 'backupCount': 52,
  270. },
  271. 'errors.log': {
  272. 'level': 'ERROR',
  273. 'class': 'logging.handlers.TimedRotatingFileHandler',
  274. 'filename': 'errors.log',
  275. 'formatter': 'verbose',
  276. 'when': 'W0',
  277. 'delay': True,
  278. 'backupCount': 52,
  279. },
  280. },
  281. 'loggers': {
  282. 'django.request': {
  283. 'handlers': ['errors.log', 'mail_admins'],
  284. 'level': 'ERROR',
  285. 'propagate': False,
  286. },
  287. 'django.security': {
  288. 'handlers': ['errors.log', 'mail_admins'],
  289. 'level': 'ERROR',
  290. 'propagate': False,
  291. },
  292. 'django.security.DisallowedHost': {
  293. 'handlers': ['null'],
  294. 'level': 'ERROR',
  295. 'propagate': False,
  296. },
  297. 'py.warnings': {
  298. 'handlers': ['console'],
  299. },
  300. 'distro_tracker': {
  301. 'handlers': ['errors.log', 'console'],
  302. 'level': 'DEBUG',
  303. },
  304. 'distro_tracker.mail': {
  305. 'handlers': ['mail.log', 'mail_admins'],
  306. 'level': 'DEBUG',
  307. 'propagate': True,
  308. },
  309. 'distro_tracker.tasks': {
  310. 'handlers': ['tasks.log', 'mail_admins'],
  311. 'level': 'DEBUG',
  312. 'propagate': True,
  313. },
  314. }
  315. }
  316. # === Distro Tracker specific settings ===
  317. # The fully qualified domain name for the Distro Tracker deployment
  318. DISTRO_TRACKER_FQDN = socket.getfqdn()
  319. #: This file is the trusted.gpg main file to hand out to APT
  320. DISTRO_TRACKER_TRUSTED_GPG_MAIN_FILE = '/etc/apt/trusted.gpg'
  321. #: This directory is trusted.gpg.d directory to hand out to APT
  322. DISTRO_TRACKER_TRUSTED_GPG_PARTS_DIR = '/etc/apt/trusted.gpg.d/'
  323. #: The number of days to tolerate bounced messages for subscribers.
  324. DISTRO_TRACKER_MAX_DAYS_TOLERATE_BOUNCE = 4
  325. #: The number of errors after which the processing of a command email stops.
  326. DISTRO_TRACKER_MAX_ALLOWED_ERRORS_CONTROL_COMMANDS = 5
  327. #: The number of days a command confirmation key should be valid.
  328. DISTRO_TRACKER_CONFIRMATION_EXPIRATION_DAYS = 3
  329. #: The maximum number of news to include in the news panel of a package page
  330. DISTRO_TRACKER_NEWS_PANEL_LIMIT = 30
  331. #: The maximum number of RSS news items to include in the news feed
  332. DISTRO_TRACKER_RSS_ITEM_LIMIT = 30
  333. #: A list of extra headers to include when rendering an email news item.
  334. #: See: :class:`distro_tracker.core.models.EmailNewsRenderer`
  335. DISTRO_TRACKER_EMAIL_NEWS_HEADERS = (
  336. 'Date',
  337. )
  338. #: The maximum size that the
  339. #: :class:`distro_tracker.core.utils.packages.AptCache` should
  340. #: consume for all of its cached source files, given in bytes.
  341. DISTRO_TRACKER_APT_CACHE_MAX_SIZE = 5 * 1024 ** 3 # 5 GiB
  342. #: Whether we accept foo@domain.com as valid emails to dispatch to the foo
  343. #: package
  344. DISTRO_TRACKER_ACCEPT_UNQUALIFIED_EMAILS = False
  345. DJANGO_EMAIL_ACCOUNTS_POST_MERGE_HOOK = \
  346. 'distro_tracker.accounts.hooks.post_merge'
  347. #: Whether we include a captcha check on the new user registration form
  348. DJANGO_EMAIL_ACCOUNTS_USE_CAPTCHA = False
  349. # The lambda functions are evaluated at the end of the settings import
  350. # logic. They provide default values to settings which have not yet been
  351. # set (neither above nor in local.py).
  352. _COMPUTE_DEFAULT_SETTINGS = (
  353. ('DISTRO_TRACKER_VENDOR_NAME',
  354. lambda t: ".".join(t['DISTRO_TRACKER_FQDN'].split(".")[1:2]).capitalize()),
  355. ('DISTRO_TRACKER_VENDOR_URL',
  356. lambda t: "http://www." + ".".join(
  357. t['DISTRO_TRACKER_FQDN'].split(".", 1)[1:2])),
  358. ('DISTRO_TRACKER_CONTROL_EMAIL',
  359. lambda t: 'control@' + t['DISTRO_TRACKER_FQDN']),
  360. ('DISTRO_TRACKER_CONTACT_EMAIL',
  361. lambda t: 'owner@' + t['DISTRO_TRACKER_FQDN']),
  362. ('DISTRO_TRACKER_BOUNCES_EMAIL',
  363. lambda t: 'bounces@' + t['DISTRO_TRACKER_FQDN']),
  364. ('DISTRO_TRACKER_BOUNCES_LIKELY_SPAM_EMAIL',
  365. lambda t: t['DISTRO_TRACKER_BOUNCES_EMAIL']),
  366. ('ALLOWED_HOSTS', lambda t: [t['DISTRO_TRACKER_FQDN']]),
  367. ('ADMINS', lambda t: (
  368. (t['DISTRO_TRACKER_VENDOR_NAME'] + ' Tracker Admins',
  369. t['DISTRO_TRACKER_CONTACT_EMAIL']),
  370. )),
  371. ('SERVER_EMAIL', lambda t: t['DISTRO_TRACKER_CONTACT_EMAIL']),
  372. ('DEFAULT_FROM_EMAIL', lambda t: t['DISTRO_TRACKER_CONTACT_EMAIL']),
  373. ('STATIC_ROOT',
  374. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'static')),
  375. ('MEDIA_ROOT',
  376. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'media')),
  377. ('DISTRO_TRACKER_CACHE_DIRECTORY',
  378. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'cache')),
  379. ('DISTRO_TRACKER_KEYRING_DIRECTORY',
  380. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'keyring')),
  381. ('DISTRO_TRACKER_TEMPLATE_DIRECTORY',
  382. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'templates')),
  383. ('DISTRO_TRACKER_LOG_DIRECTORY',
  384. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'logs')),
  385. ('DISTRO_TRACKER_MAILDIR_DIRECTORY',
  386. lambda t: os.path.join(t['DISTRO_TRACKER_DATA_PATH'], 'maildir')),
  387. )
  388. def compute_default_settings(target):
  389. for setting, value in _COMPUTE_DEFAULT_SETTINGS:
  390. if setting in target:
  391. continue # Settings is already defined
  392. target[setting] = value(target)
  393. # Extend TEMPLATE_DIRS with our directory
  394. target['TEMPLATES'][0]['DIRS'].append(
  395. target['DISTRO_TRACKER_TEMPLATE_DIRECTORY'])
  396. # Update LOGGING with full paths
  397. for handler in target['LOGGING']['handlers'].values():
  398. if 'filename' not in handler or "/" in handler['filename']:
  399. continue
  400. handler['filename'] = os.path.join(
  401. target['DISTRO_TRACKER_LOG_DIRECTORY'], handler['filename'])
  402. # Update DATABASES with full paths
  403. dbconf = target['DATABASES']['default']
  404. if dbconf['ENGINE'] == 'django.db.backends.sqlite3':
  405. if '/' not in dbconf['NAME']:
  406. dbconf['NAME'] = os.path.join(target['DISTRO_TRACKER_DATA_PATH'],
  407. dbconf['NAME'])
  408. if ('TEST' in dbconf and 'NAME' in dbconf['TEST'] and
  409. '/' not in dbconf['TEST']['NAME']):
  410. dbconf['TEST']['NAME'] = os.path.join(
  411. target['DISTRO_TRACKER_DATA_PATH'], dbconf['TEST']['NAME'])
  412. def GET_INSTANCE_NAME():
  413. from django.conf import settings
  414. return "{vendor} Package Tracker".format(
  415. vendor=settings.DISTRO_TRACKER_VENDOR_NAME)