Commit 6aa87096 authored by Oliver Falk's avatar Oliver Falk
Browse files

Merge branch 'devel' into 'master'

v1.5 massive (code) update

See merge request !191
parents 12d69576 355af235
Pipeline #2285 passed with stages
in 7 minutes and 13 seconds
...@@ -7,8 +7,12 @@ omit = ...@@ -7,8 +7,12 @@ omit =
import_libravatar.py import_libravatar.py
requirements.txt requirements.txt
static/admin/* static/admin/*
static/humans.txt **/static/humans.txt
static/img/robots.txt **/static/img/robots.txt
ivatar/ivataraccount/read_libravatar_export.py
templates/maintenance.html
encryption_test.py
libravatarproxy.py
[html] [html]
......
[flake8]
ignore = E501, W503, E402
max-line-length = 79
max-complexity = 18
select = B,C,E,F,W,T4,B9
exclude = .git,__pycache__,.virtualenv
fail_fast: true
repos:
- repo: meta
hooks:
- id: check-useless-excludes
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.4.0
hooks:
- id: prettier
files: \.(css|js|md|markdown|json)
- repo: https://github.com/python/black
rev: 21.9b0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: check-added-large-files
- id: check-ast
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-vcs-permalinks
- id: check-xml
- id: check-yaml
args:
- --unsafe
- id: end-of-file-fixer
- id: fix-encoding-pragma
- id: forbid-new-submodules
- id: no-commit-to-branch
args:
- --branch
- gh-pages
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: trailing-whitespace
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
- repo: local
hooks:
- id: shfmt
name: shfmt
minimum_pre_commit_version: 2.4.0
language: golang
additional_dependencies:
- mvdan.cc/sh/v3/cmd/shfmt@v3.1.1
entry: shfmt
args:
- -w
- -i
- '4'
types:
- shell
- repo: https://github.com/asottile/blacken-docs
rev: v1.11.0
hooks:
- id: blacken-docs
- repo: https://github.com/hcodes/yaspeller.git
rev: v7.0.0
hooks:
- id: yaspeller
types:
- markdown
- repo: https://github.com/kadrach/pre-commit-gitlabci-lint
rev: 22d0495c9894e8b27cc37c2ed5d9a6b46385a44c
hooks:
- id: gitlabci-lint
args: ["https://git.linux-kernel.at"]
''' yes # -*- coding: utf-8 -*-
"""
Configuration overrides for settings.py Configuration overrides for settings.py
''' """
import os import os
import sys import sys
...@@ -14,50 +15,61 @@ from ivatar.settings import INSTALLED_APPS ...@@ -14,50 +15,61 @@ from ivatar.settings import INSTALLED_APPS
from ivatar.settings import TEMPLATES from ivatar.settings import TEMPLATES
ADMIN_USERS = [] ADMIN_USERS = []
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
INSTALLED_APPS.extend([ INSTALLED_APPS.extend(
'django_extensions', [
'django_openid_auth', "django_extensions",
'bootstrap4', "django_openid_auth",
'anymail', "bootstrap4",
'ivatar', "anymail",
'ivatar.ivataraccount', "ivatar",
'ivatar.tools', "ivatar.ivataraccount",
]) "ivatar.tools",
]
MIDDLEWARE.extend([ )
'django.middleware.locale.LocaleMiddleware',
]) MIDDLEWARE.extend(
[
"django.middleware.locale.LocaleMiddleware",
]
)
MIDDLEWARE.insert( MIDDLEWARE.insert(
0, 'ivatar.middleware.MultipleProxyMiddleware', 0,
"ivatar.middleware.MultipleProxyMiddleware",
) )
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
# Enable this to allow LDAP authentication. # Enable this to allow LDAP authentication.
# See INSTALL for more information. # See INSTALL for more information.
# 'django_auth_ldap.backend.LDAPBackend', # 'django_auth_ldap.backend.LDAPBackend',
'django_openid_auth.auth.OpenIDBackend', "django_openid_auth.auth.OpenIDBackend",
'django.contrib.auth.backends.ModelBackend', "django.contrib.auth.backends.ModelBackend",
) )
TEMPLATES[0]['DIRS'].extend([ TEMPLATES[0]["DIRS"].extend(
os.path.join(BASE_DIR, 'templates'), [
]) os.path.join(BASE_DIR, "templates"),
TEMPLATES[0]['OPTIONS']['context_processors'].append( ]
'ivatar.context_processors.basepage', )
TEMPLATES[0]["OPTIONS"]["context_processors"].append(
"ivatar.context_processors.basepage",
) )
OPENID_CREATE_USERS = True OPENID_CREATE_USERS = True
OPENID_UPDATE_DETAILS_FROM_SREG = True OPENID_UPDATE_DETAILS_FROM_SREG = True
SITE_NAME = os.environ.get('SITE_NAME', 'libravatar') SITE_NAME = os.environ.get("SITE_NAME", "libravatar")
IVATAR_VERSION = '1.4' IVATAR_VERSION = "1.5"
SECURE_BASE_URL = os.environ.get('SECURE_BASE_URL', 'https://avatars.linux-kernel.at/avatar/') SCHEMAROOT = "https://www.libravatar.org/schemas/export/0.2"
BASE_URL = os.environ.get('BASE_URL', 'http://avatars.linux-kernel.at/avatar/')
LOGIN_REDIRECT_URL = reverse_lazy('profile') SECURE_BASE_URL = os.environ.get(
"SECURE_BASE_URL", "https://avatars.linux-kernel.at/avatar/"
)
BASE_URL = os.environ.get("BASE_URL", "http://avatars.linux-kernel.at/avatar/")
LOGIN_REDIRECT_URL = reverse_lazy("profile")
MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294
MAX_NUM_PHOTOS = 5 MAX_NUM_PHOTOS = 5
...@@ -75,119 +87,120 @@ MIN_LENGTH_EMAIL = 6 # eg. x@x.xx ...@@ -75,119 +87,120 @@ MIN_LENGTH_EMAIL = 6 # eg. x@x.xx
MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294
BOOTSTRAP4 = { BOOTSTRAP4 = {
'include_jquery': False, "include_jquery": False,
'javascript_in_head': False, "javascript_in_head": False,
'css_url': { "css_url": {
'href': '/static/css/bootstrap.min.css', "href": "/static/css/bootstrap.min.css",
'integrity': "integrity": "sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB",
'sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB', "crossorigin": "anonymous",
'crossorigin': 'anonymous',
}, },
'javascript_url': { "javascript_url": {
'url': '/static/js/bootstrap.min.js', "url": "/static/js/bootstrap.min.js",
'integrity': '', "integrity": "",
'crossorigin': 'anonymous', "crossorigin": "anonymous",
}, },
'popper_url': { "popper_url": {
'url': '/static/js/popper.min.js', "url": "/static/js/popper.min.js",
'integrity': "integrity": "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49",
'sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49', "crossorigin": "anonymous",
'crossorigin': 'anonymous',
}, },
} }
if 'EMAIL_BACKEND' in os.environ: if "EMAIL_BACKEND" in os.environ:
EMAIL_BACKEND = os.environ['EMAIL_BACKEND'] # pragma: no cover EMAIL_BACKEND = os.environ["EMAIL_BACKEND"] # pragma: no cover
else: else:
if 'test' in sys.argv or 'collectstatic' in sys.argv: if "test" in sys.argv or "collectstatic" in sys.argv:
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
else: else:
try: try:
ANYMAIL = { # pragma: no cover ANYMAIL = { # pragma: no cover
'MAILGUN_API_KEY': os.environ['IVATAR_MAILGUN_API_KEY'], "MAILGUN_API_KEY": os.environ["IVATAR_MAILGUN_API_KEY"],
'MAILGUN_SENDER_DOMAIN': os.environ['IVATAR_MAILGUN_SENDER_DOMAIN'], "MAILGUN_SENDER_DOMAIN": os.environ["IVATAR_MAILGUN_SENDER_DOMAIN"],
} }
EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' # pragma: no cover EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" # pragma: no cover
except Exception as exc: # pragma: nocover except Exception: # pragma: nocover # pylint: disable=broad-except
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
SERVER_EMAIL = os.environ.get('SERVER_EMAIL', 'ivatar@mg.linux-kernel.at') SERVER_EMAIL = os.environ.get("SERVER_EMAIL", "ivatar@mg.linux-kernel.at")
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'ivatar@mg.linux-kernel.at') DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL", "ivatar@mg.linux-kernel.at")
try: try:
from ivatar.settings import DATABASES from ivatar.settings import DATABASES
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
DATABASES = [] # pragma: no cover DATABASES = [] # pragma: no cover
if 'default' not in DATABASES: if "default" not in DATABASES:
DATABASES['default'] = { # pragma: no cover DATABASES["default"] = { # pragma: no cover
'ENGINE': 'django.db.backends.sqlite3', "ENGINE": "django.db.backends.sqlite3",
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
} }
if 'MYSQL_DATABASE' in os.environ: if "MYSQL_DATABASE" in os.environ:
DATABASES['default'] = { # pragma: no cover DATABASES["default"] = { # pragma: no cover
'ENGINE': 'django.db.backends.mysql', "ENGINE": "django.db.backends.mysql",
'NAME': os.environ['MYSQL_DATABASE'], "NAME": os.environ["MYSQL_DATABASE"],
'USER': os.environ['MYSQL_USER'], "USER": os.environ["MYSQL_USER"],
'PASSWORD': os.environ['MYSQL_PASSWORD'], "PASSWORD": os.environ["MYSQL_PASSWORD"],
'HOST': 'mysql', "HOST": "mysql",
} }
if 'POSTGRESQL_DATABASE' in os.environ: if "POSTGRESQL_DATABASE" in os.environ:
DATABASES['default'] = { # pragma: no cover DATABASES["default"] = { # pragma: no cover
'ENGINE': 'django.db.backends.postgresql', "ENGINE": "django.db.backends.postgresql",
'NAME': os.environ['POSTGRESQL_DATABASE'], "NAME": os.environ["POSTGRESQL_DATABASE"],
'USER': os.environ['POSTGRESQL_USER'], "USER": os.environ["POSTGRESQL_USER"],
'PASSWORD': os.environ['POSTGRESQL_PASSWORD'], "PASSWORD": os.environ["POSTGRESQL_PASSWORD"],
'HOST': 'postgresql', "HOST": "postgresql",
} }
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer"
USE_X_FORWARDED_HOST = True USE_X_FORWARDED_HOST = True
ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = ['avatars.linux-kernel.at', 'localhost',] ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = [
"avatars.linux-kernel.at",
"localhost",
]
DEFAULT_AVATAR_SIZE = 80 DEFAULT_AVATAR_SIZE = 80
LANGUAGES = ( LANGUAGES = (
('de', _('Deutsch')), ("de", _("Deutsch")),
('en', _('English')), ("en", _("English")),
('ca', _('Català')), ("ca", _("Català")),
('cs', _('Česky')), ("cs", _("Česky")),
('es', _('Español')), ("es", _("Español")),
('eu', _('Basque')), ("eu", _("Basque")),
('fr', _('Français')), ("fr", _("Français")),
('it', _('Italiano')), ("it", _("Italiano")),
('ja', _('日本語')), ("ja", _("日本語")),
('nl', _('Nederlands')), ("nl", _("Nederlands")),
('pt', _('Português')), ("pt", _("Português")),
('ru', _('Русский')), ("ru", _("Русский")),
('sq', _('Shqip')), ("sq", _("Shqip")),
('tr', _('Türkçe')), ("tr", _("Türkçe")),
('uk', _('Українська')), ("uk", _("Українська")),
) )
MESSAGE_TAGS = { MESSAGE_TAGS = {
message_constants.DEBUG: 'debug', message_constants.DEBUG: "debug",
message_constants.INFO: 'info', message_constants.INFO: "info",
message_constants.SUCCESS: 'success', message_constants.SUCCESS: "success",
message_constants.WARNING: 'warning', message_constants.WARNING: "warning",
message_constants.ERROR: 'danger', message_constants.ERROR: "danger",
} }
CACHES = { CACHES = {
'default': { "default": {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', "BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
'LOCATION': [ "LOCATION": [
'127.0.0.1:11211', "127.0.0.1:11211",
], ],
}, },
'filesystem': { "filesystem": {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
'LOCATION': '/var/tmp/ivatar_cache', "LOCATION": "/var/tmp/ivatar_cache",
'TIMEOUT': 900, # 15 minutes "TIMEOUT": 900, # 15 minutes
} },
} }
# This is 5 minutes caching for generated/resized images, # This is 5 minutes caching for generated/resized images,
...@@ -197,5 +210,5 @@ CACHE_IMAGES_MAX_AGE = 5 * 60 ...@@ -197,5 +210,5 @@ CACHE_IMAGES_MAX_AGE = 5 * 60
CACHE_RESPONSE = True CACHE_RESPONSE = True
# This MUST BE THE LAST! # This MUST BE THE LAST!
if os.path.isfile(os.path.join(BASE_DIR, 'config_local.py')): if os.path.isfile(os.path.join(BASE_DIR, "config_local.py")):
from config_local import * # noqa # flake8: noqa # NOQA # pragma: no cover from config_local import * # noqa # flake8: noqa # NOQA # pragma: no cover
''' # -*- coding: utf-8 -*-
"""
Module init Module init
''' """
app_label = __name__ # pylint: disable=invalid-name app_label = __name__ # pylint: disable=invalid-name
''' # -*- coding: utf-8 -*-
"""
Default: useful variables for the base page templates. Default: useful variables for the base page templates.
''' """
from ipware import get_client_ip from ipware import get_client_ip
from ivatar.settings import IVATAR_VERSION, SITE_NAME, MAX_PHOTO_SIZE from ivatar.settings import IVATAR_VERSION, SITE_NAME, MAX_PHOTO_SIZE
...@@ -9,27 +10,28 @@ from ivatar.settings import MAX_NUM_UNCONFIRMED_EMAILS ...@@ -9,27 +10,28 @@ from ivatar.settings import MAX_NUM_UNCONFIRMED_EMAILS
def basepage(request): def basepage(request):
''' """
Our contextprocessor adds additional context variables Our contextprocessor adds additional context variables
in order to be used in the templates in order to be used in the templates
''' """
context = {} context = {}
if 'openid_identifier' in request.GET: if "openid_identifier" in request.GET:
context['openid_identifier'] = \ context["openid_identifier"] = request.GET[
request.GET['openid_identifier'] # pragma: no cover "openid_identifier"
] # pragma: no cover
client_ip = get_client_ip(request)[0] client_ip = get_client_ip(request)[0]
context['client_ip'] = client_ip context["client_ip"] = client_ip
context['ivatar_version'] = IVATAR_VERSION context["ivatar_version"] = IVATAR_VERSION
context['site_name'] = SITE_NAME context["site_name"] = SITE_NAME
context['site_url'] = request.build_absolute_uri('/')[:-1] context["site_url"] = request.build_absolute_uri("/")[:-1]
context['max_file_size'] = MAX_PHOTO_SIZE context["max_file_size"] = MAX_PHOTO_SIZE
context['BASE_URL'] = BASE_URL context["BASE_URL"] = BASE_URL
context['SECURE_BASE_URL'] = SECURE_BASE_URL context["SECURE_BASE_URL"] = SECURE_BASE_URL
context['max_emails'] = False context["max_emails"] = False
if request.user: if request.user:
if not request.user.is_anonymous: if not request.user.is_anonymous:
unconfirmed = request.user.unconfirmedemail_set.count() unconfirmed = request.user.unconfirmedemail_set.count()
if unconfirmed >= MAX_NUM_UNCONFIRMED_EMAILS: if unconfirmed >= MAX_NUM_UNCONFIRMED_EMAILS:
context['max_emails'] = True context["max_emails"] = True
return context return context
This diff is collapsed.
''' # -*- coding: utf-8 -*-
"""
Reading libravatar export Reading libravatar export
''' """
import binascii import binascii
import os
from io import BytesIO from io import BytesIO
import gzip import gzip
import xml.etree.ElementTree import xml.etree.ElementTree
import base64 import base64
from PIL import Image from PIL import Image
import django
import sys
sys.path.append(
os.path.join(
os.path.dirname(__file__),
"..",
"..",
)
)
SCHEMAROOT = 'https://www.libravatar.org/schemas/export/0.2' os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
django.setup()
# pylint: disable=wrong-import-position
from ivatar.settings import SCHEMAROOT
def read_gzdata(gzdata=None): def read_gzdata(gzdata=None):
''' """
Read gzipped data file Read gzipped data file
''' """
emails = [] # pylint: disable=invalid-name emails = [] # pylint: disable=invalid-name
openids = [] # pylint: disable=invalid-name openids = [] # pylint: disable=invalid-name
photos = [] # pylint: disable=invalid-name photos = [] # pylint: disable=invalid-name
username = None # pylint: disable=invalid-name username = None # pylint: disable=invalid-name
password = None # pylint: disable=invalid-name password = None # pylint: disable=invalid-name
if not gzdata: if not gzdata:
return False return False
fh = gzip.open(BytesIO(gzdata), 'rb') # pylint: disable=invalid-name fh = gzip.open(BytesIO(gzdata), "rb") # pylint: disable=invalid-name
content = fh.read() content = fh.read()
fh.close() fh.close()
root = xml.etree.ElementTree.fromstring(content) root = xml.etree.ElementTree.fromstring(content)
if not root.tag == '{%s}user' % SCHEMAROOT: if not root.tag == "{%s}user" % SCHEMAROOT:
print('Unknown export format: %s' % root.tag) print("Unknown export format: %s" % root.tag)
exit(-1) exit(-1)
# Username # Username
for item in root.findall('{%s}account' % SCHEMAROOT)[0].items(): for item in root.findall("{%s}account" % SCHEMAROOT)[0].items():
if item[0] == 'username': if item[0] == "username":
username = item[1] username = item[1]
if item[0] == 'password': if item[0] == "password":
password = item[1] password = item[1]