diff --git a/.gitignore b/.gitignore index 4d43f387e85330c273f8f85b730634253f3f74e7..0eeb06ba57fd256d5cd7eab1ba083b5be4f0cc83 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ __pycache__ *webpack-stats.json .python-version +*secrets.json /uploads* /media* diff --git a/README.md b/README.md index fc63276cd5eea6b4756d47419c148c7f7f4a47ad..4964856aaf986dfcde25c82e5ea719ff786481bc 100644 --- a/README.md +++ b/README.md @@ -35,40 +35,32 @@ Now install dependencies: (scipostenv) $ npm install ``` -### Host-specific settings -In this project, host-specific settings are defined in the `scipost-host-settings.json` file in the directory *above* the project root. The structure is as follows: +### Settings +In this project, most settings are tracked using Git. Some settings however, are still secret are and should stay that way. These settings may be saved into the `secrets.json` file in the root of the project. The minimum required structure is as follows, please mind the non-empty, but still invalid `SECRET_KEY`: ```json { - "SECRET_KEY": "<change_me>", - "CERTFILE": "none", - "DEBUG": true, - "ADMINS": "", - "MANAGERS": "", - "ALLOWED_HOSTS": ["localhost", "127.0.0.1"], - "SESSION_COOKIE_SECURE": false, - "CSRF_COOKIE_SECURE": false, - "DB_NAME": "scipost", - "DB_USER": "scipost", - "DB_PWD": "", - "MEDIA_ROOT": "<media_dir>", - "MEDIA_URL": "/media/", - "STATIC_URL": "/static/", - "STATIC_ROOT": "<static_dir>", - "EMAIL_BACKEND": "django.core.mail.backends.filebased.EmailBackend", - "EMAIL_FILE_PATH": "<email_dir>", - "EMAIL_HOST": "", - "EMAIL_HOST_USER": "", - "EMAIL_HOST_PASSWORD": "", - "DEFAULT_FROM_EMAIL": "", - "SERVER_EMAIL": "", - "JOURNALS_DIR": "<journals_dir>", - "CROSSREF_LOGIN_ID": "", - "CROSSREF_LOGIN_PASSWORD": "", - "HAYSTACK_PATH": "<haystack_dir>" + "SECRET_KEY": "<key>", + "DB_NAME": "", + "DB_USER": "", + "DB_PWD": "" } ``` +The settings files itself are saved into `SciPost_v1/settings/local_<name>.py`. Be sure to *wildcard import* the `base.py` file in the top of your settings file. To run the server, one can do it two ways. Either: + +```shell +(scipostenv) $ ./manage.py runserver --settings=SciPost_v1.settings.local_<name> +``` + +...or for convenience export the same settingsfile path to the `DJANGO_SETTINGS_MODULE` variable, so that one can run the django commands are default: + +```shell +(scipostenv) $ export DJANGO_SETTINGS_MODULE="SciPost_v1.settings.local_<name>" +``` + +One can of course also add the variable to the `~/.bash_profile` for convenience. + ### Check, double check To make sure everything is setup and configured well, run: diff --git a/SciPost_v1/settings.py b/SciPost_v1/__settings.py similarity index 100% rename from SciPost_v1/settings.py rename to SciPost_v1/__settings.py diff --git a/SciPost_v1/settings/__init__.py b/SciPost_v1/settings/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/SciPost_v1/settings/base.py b/SciPost_v1/settings/base.py new file mode 100644 index 0000000000000000000000000000000000000000..d454b4c5416d1a6d186483c141e1d21ebbf6aed8 --- /dev/null +++ b/SciPost_v1/settings/base.py @@ -0,0 +1,238 @@ + +""" +Django settings for SciPost_v1 project. + +Generated by 'django-admin startproject' using Django 1.8.5. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +import json + +from django.utils.translation import ugettext_lazy as _ + +from django.core.exceptions import ImproperlyConfigured + +# Build paths inside the project +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +PROJECT_ROOT = os.path.dirname(BASE_DIR) + +# JSON-based secrets +secrets = json.load(open(os.path.join(BASE_DIR, "secrets.json"))) + + +def get_secret(setting, secrets=secrets): + """Get the secret variable or return explicit exception.""" + try: + return secrets[setting] + except KeyError: + error_msg = "Set the {0} environment variable".format(setting) + raise ImproperlyConfigured(error_msg) + + +SECRET_KEY = get_secret("SECRET_KEY") +CERTFILE = '' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['localhost', '127.0.0.1'] + +# Secure proxy SSL header and secure cookies +SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +SESSION_COOKIE_SECURE = False +CSRF_COOKIE_SECURE = False + +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'guardian.backends.ObjectPermissionBackend' + ) + +LOGIN_URL = '/login/' + +GUARDIAN_RENDER_403 = True + +# Session expire at browser close +SESSION_EXPIRE_AT_BROWSER_CLOSE = True + +# Wsgi scheme +os.environ['wsgi.url_scheme'] = 'https' + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.admindocs', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django_countries', + 'django_extensions', + 'django_mathjax', + 'captcha', + 'crispy_forms', + 'guardian', + 'haystack', + 'rest_framework', + 'sphinxdoc', + 'commentaries', + 'comments', + 'journals', + 'scipost', + 'submissions', + 'theses', + 'webpack_loader' +) + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': 'local_files/haystack/', + }, +} + +SPHINXDOC_BASE_TEMPLATE = 'scipost/base.html' +SPHINXDOC_PROTECTED_PROJECTS = { + 'scipost': ['scipost.can_view_docs_scipost'], +} + +CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge' +CAPTCHA_LETTER_ROTATION = (-15, 15) +CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',) + +SHELL_PLUS_POST_IMPORTS = ( + ('theses.factories', ('ThesisLinkFactory')), + ('comments.factories', ('CommentFactory')), + ('submissions.factories', ('SubmissionFactory', 'EICassignedSubmissionFactory')), + ('commentaries.factories', + ('EmptyCommentaryFactory', + 'VettedCommentaryFactory', + 'UnvettedCommentaryFactory', + 'UnpublishedVettedCommentaryFactory',)), +) + +MATHJAX_ENABLED = True +MATHJAX_CONFIG_DATA = { + "tex2jax": { + "inlineMath": [['$', '$'], ['\\(', '\\)']], + "processEscapes": True + } + } + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'SciPost_v1.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + 'scipost.context_processors.searchform', + ], + }, + }, +] + +WSGI_APPLICATION = 'SciPost_v1.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': get_secret("DB_NAME"), + 'USER': get_secret("DB_USER"), + 'PASSWORD': get_secret("DB_PWD"), + 'HOST': '127.0.0.1', + 'PORT': '5432', + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' +LANGUAGES = ( + ('en', _('English')), +) +LOCALE_PATHS = ( + os.path.join(BASE_DIR, 'locale'), +) +TIME_ZONE = 'CET' + +USE_I18N = True + +USE_L10N = False +DATE_FORMAT = 'Y-m-d' +DATETIME_FORMAT = 'Y-m-d H:i' + +USE_TZ = True + +# MEDIA +MEDIA_URL = '/media/' +MEDIA_ROOT = 'local_files/media/' +MAX_UPLOAD_SIZE = "1310720" # Default max attachment size in Bytes + +# Static files (CSS, JavaScript, Images) +STATIC_URL = '/static/' +STATIC_ROOT = 'local_files/static/' +STATICFILES_DIRS = ( + os.path.join(BASE_DIR, 'static_bundles'), +) + +# Webpack handling the static bundles +WEBPACK_LOADER = { + 'DEFAULT': { + 'CACHE': not DEBUG, + 'BUNDLE_DIR_NAME': 'local_files/static/bundles/', + 'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'), + 'POLL_INTERVAL': 0.1, + 'TIMEOUT': None, + 'IGNORE': ['.+\.hot-update.js', '.+\.map'] + } +} + +# Email +EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' +EMAIL_FILE_PATH = 'local_files/email/' + +# Own settings +JOURNALS_DIR = 'journals' + +CROSSREF_LOGIN_ID = '' +CROSSREF_LOGIN_PASSWORD = '' + +# Google reCaptcha +RECAPTCHA_PUBLIC_KEY = get_secret("GOOGLE_RECAPTCHA_PUBLIC_KEY") +RECAPTCHA_PRIVATE_KEY = get_secret("GOOGLE_RECAPTCHA_PRIVATE_KEY") +NOCAPTCHA = True diff --git a/SciPost_v1/settings/local_jorran.py b/SciPost_v1/settings/local_jorran.py new file mode 100644 index 0000000000000000000000000000000000000000..1eef068e824bdaaa5e51f1b16a15cf80cee2b724 --- /dev/null +++ b/SciPost_v1/settings/local_jorran.py @@ -0,0 +1,10 @@ +from .base import * + +# THE MAIN THING HERE +DEBUG = True + +# Static and media +STATIC_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/' +MEDIA_ROOT = '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/media/' +WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME'] =\ + '/Users/jorranwit/Develop/SciPost/scipost_v1/local_files/static/bundles/' diff --git a/SciPost_v1/settings/production.py b/SciPost_v1/settings/production.py new file mode 100644 index 0000000000000000000000000000000000000000..8f7644123f9e5aef19caa640b73dd60bd8934a06 --- /dev/null +++ b/SciPost_v1/settings/production.py @@ -0,0 +1,39 @@ +from .base import * + +# THE MAIN THING HERE +DEBUG = False +CERTFILE = get_secret("CERTFILE") + +# Static and media +STATIC_URL = 'https://scipost.org/static/' +STATIC_ROOT = '/home/jscaux/webapps/scipost_static/' +MEDIA_URL = 'https://scipost.org/media/' +MEDIA_ROOT = '/home/jscaux/webapps/scipost_media/' +WEBPACK_LOADER = { + 'DEFAULT': { + 'CACHE': True, + 'BUNDLE_DIR_NAME': '/home/jscaux/webapps/scipost_static/bundles/', + } +} + +# Error reporting +ADMINS = MANAGERS = (('J.S.Caux', 'J.S.Caux@uva.nl'), ) + +# Cookies +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SECURE = True + +# Email +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' + +EMAIL_HOST = get_secret("EMAIL_HOST_USER") +EMAIL_HOST_USER = get_secret("EMAIL_HOST_USER") +EMAIL_HOST_PASSWORD = get_secret("EMAIL_HOST_PASSWORD") + +DEFAULT_FROM_EMAIL = 'admin@scipost.org' +SERVER_EMAIL = get_secret("SERVER_EMAIL") + +# Other +CROSSREF_LOGIN_ID = get_secret("CROSSREF_LOGIN_ID") +CROSSREF_LOGIN_PASSWORD = get_secret("CROSSREF_LOGIN_PASSWORD") +HAYSTACK_CONNECTIONS['default']['PATH'] = '/home/jscaux/webapps/scipost/SciPost_v1/whoosh_index' diff --git a/SciPost_v1/urls.py b/SciPost_v1/urls.py index 8bb1cbd455cf3f734dcc129e7f8c02ba31d6cf1f..9b85d34e8e3967fdfc82a646d9d8c76443eb3c9a 100644 --- a/SciPost_v1/urls.py +++ b/SciPost_v1/urls.py @@ -31,6 +31,5 @@ urlpatterns = [ url(r'^submission/', include('submissions.urls', namespace="submissions")), url(r'^theses/', include('theses.urls', namespace="theses")), url(r'^thesis/', include('theses.urls', namespace="theses")), - url(r'^captcha/', include('captcha.urls')), + # url(r'^captcha/', include('captcha.urls')), ] - diff --git a/requirements.txt b/requirements.txt index f486993b4c640c6c32030fda6ece23aaafe4e8d3..4e1cfb752935824e29985016edb1fd1bbd4c30e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,8 +9,8 @@ django-guardian==1.4.6 django-haystack==2.5.1 django-mathjax==0.0.5 django-mptt==0.8.6 -django-simple-captcha==0.5.3 django-sphinxdoc==1.5.1 +django-recaptcha==1.2.1 django-webpack-loader==0.4.1 djangorestframework==3.5.3 docutils==0.12 diff --git a/scipost/forms.py b/scipost/forms.py index b4d7422a13dda2bc52cc11d3d2c9a4e228c00234..7c628d2f224eff4db20149f95e7765ffc3137e63 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -6,7 +6,7 @@ from django.db.models import Q from django_countries import countries from django_countries.widgets import CountrySelectWidget from django_countries.fields import LazyTypedChoiceField -from captcha.fields import CaptchaField +from captcha.fields import ReCaptchaField from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Div, Field, HTML, Submit @@ -60,7 +60,9 @@ class RegistrationForm(forms.Form): username = forms.CharField(label='* Username', max_length=100) password = forms.CharField(label='* Password', widget=forms.PasswordInput()) password_verif = forms.CharField(label='* Verify pwd', widget=forms.PasswordInput()) - captcha = CaptchaField(label='* Answer this simple maths question:') + captcha = ReCaptchaField(attrs={ + 'theme' : 'clean', +}, label='* Answer this simple maths question:') class DraftInvitationForm(forms.ModelForm):