From f5b1a215f426d980b3fb222ff3a17c006a9a02a1 Mon Sep 17 00:00:00 2001 From: Bartosz Stebel Date: Sat, 4 Feb 2023 23:47:44 +0100 Subject: [PATCH] app/mailman-web: create There's a lot of ugly hacks here, but this has been the state of prod for months now, so we should reflect that. Also, this bumps a bunch of workspace deps. Change-Id: I744e0d3aff27036cfed73416cf442c7d62444a8b Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1473 Reviewed-by: q3k --- WORKSPACE | 23 +- app/mailman-web/BUILD | 150 +++++++ app/mailman-web/LICENSE | 3 + app/mailman-web/README.md | 7 + app/mailman-web/container_main.py | 13 + app/mailman-web/kube/mailman.libsonnet | 215 ++++++++++ app/mailman-web/kube/prod.jsonnet | 20 + app/mailman-web/manage.py | 13 + app/mailman-web/serve.py | 38 ++ app/mailman-web/settings.py | 102 +++++ app/mailman-web/upstream_settings/README | 1 + app/mailman-web/upstream_settings/__init__.py | 0 app/mailman-web/upstream_settings/base.py | 300 +++++++++++++ app/mailman-web/upstream_settings/mailman.py | 124 ++++++ app/mailman-web/urls.py | 35 ++ third_party/py/requirements.in | 10 +- third_party/py/requirements.txt | 399 ++++++++++++++++-- 17 files changed, 1413 insertions(+), 40 deletions(-) create mode 100644 app/mailman-web/BUILD create mode 100644 app/mailman-web/LICENSE create mode 100644 app/mailman-web/README.md create mode 100644 app/mailman-web/container_main.py create mode 100644 app/mailman-web/kube/mailman.libsonnet create mode 100644 app/mailman-web/kube/prod.jsonnet create mode 100644 app/mailman-web/manage.py create mode 100644 app/mailman-web/serve.py create mode 100644 app/mailman-web/settings.py create mode 100644 app/mailman-web/upstream_settings/README create mode 100644 app/mailman-web/upstream_settings/__init__.py create mode 100644 app/mailman-web/upstream_settings/base.py create mode 100644 app/mailman-web/upstream_settings/mailman.py create mode 100644 app/mailman-web/urls.py diff --git a/WORKSPACE b/WORKSPACE index f8fbcfba..2b8dc07c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -129,9 +129,9 @@ go_repositories() http_archive( name = "io_bazel_rules_docker", - sha256 = "4349f2b0b45c860dd2ffe18802e9f79183806af93ce5921fb12cbd6c07ab69a8", - strip_prefix = "rules_docker-0.21.0", - urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.21.0/rules_docker-v0.21.0.tar.gz"], + sha256 = "27d53c1d646fc9537a70427ad7b034734d08a9c38924cc6357cc973fed300820", + strip_prefix = "rules_docker-0.24.0", + urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.24.0/rules_docker-v0.24.0.tar.gz"], ) load("@io_bazel_rules_docker//toolchains/docker:toolchain.bzl", docker_toolchain_configure = "toolchain_configure") @@ -139,7 +139,6 @@ load("@io_bazel_rules_docker//toolchains/docker:toolchain.bzl", docker_toolchain # This forces the use of Docker $HOME/.docker configuration. docker_toolchain_configure( name = "docker_config", - client_config = "", docker_path = "/usr/bin/docker", ) @@ -150,10 +149,26 @@ load( container_repositories() +load( + "@io_bazel_rules_docker//python3:image.bzl", + _py_image_repos = "repositories", +) + +_py_image_repos() + # Docker base images load("@io_bazel_rules_docker//container:container.bzl", "container_pull") +container_pull( + name = "python-debian", + digest = "sha256:cfa3b79333c4e56fc675b6800445b6dcbb3e6cd4d52f2a9ade944ab73dadc6a1", + registry = "index.docker.io", + repository = "python", + tag = "3.10-bullseye", # use the same version as in python_register_toolchains +) + + container_pull( name = "prodimage-bionic", digest = "sha256:1cd1f84169b8e1414a5d511b42909f2d540831c67b6799ae9af8cd6a80d75b5f", diff --git a/app/mailman-web/BUILD b/app/mailman-web/BUILD new file mode 100644 index 00000000..f79e0e3c --- /dev/null +++ b/app/mailman-web/BUILD @@ -0,0 +1,150 @@ +load("@pydeps//:requirements.bzl", "requirement") +load("@rules_python//python:defs.bzl", "py_binary") +load("@io_bazel_rules_docker//python:image.bzl", "py_layer") +load("@io_bazel_rules_docker//python3:image.bzl", "py3_image") +load("@io_bazel_rules_docker//container:container.bzl", "container_layer", "container_image") +load("@io_bazel_rules_docker//docker/util:run.bzl", "container_run_and_extract", "container_run_and_commit_layer") +load("@io_bazel_rules_docker//docker/package_managers:download_pkgs.bzl", "download_pkgs") +load("@io_bazel_rules_docker//docker/package_managers:install_pkgs.bzl", "install_pkgs") + +# - - base docker stuff - - + +download_pkgs( + name = "apt_py_is_py3", + image_tar = "@python-debian//image", + packages = [ + # rules_docker python wants /usr/bin/python + "python-is-python3", + ], +) + +install_pkgs( + name = "base_image", + output_image_name = "base_image", + image_tar = "@python-debian//image", + installables_tar = ":apt_py_is_py3.tar", + installation_cleanup_commands = "rm -rf /var/lib/apt/lists/* /usr/share/doc && apt remove -y libbluetooth3 mariadb-common tk && apt autoremove -y", +) + +BASE_IMAGE = ":base_image" + +# overkill rube goldberg setup to build static files begins +# - - - - + +container_run_and_extract( + name = "static_pack", + commands = [ + "tar cpJvf /out.tar.xz -C /opt/mailman/web/static ./", + ], + extract_file = "/out.tar.xz", + image = ":static_build_image.tar", +) + +container_image( + name = "static_build_image", + layers = [":static_build_layer"], + base = BASE_IMAGE, +) + +# this will also contain .pyc files, but the python binary will be the same +# on prod, so it's fine +container_run_and_commit_layer( + name = "static_build_layer", + commands = [ + "./app/mailman-web/manage collectstatic", + "./app/mailman-web/manage compress", + # gettext is cursed, TODO make this work + #"./app/mailman-web/manage compilemessages", + ], + image = ":build_container.tar", + docker_run_flags = ["--entrypoint="], +) + +py3_image( + name = "build_container", + srcs = [":manage"], + main = "manage.py", + base = ":build_tools_container", + layers = [":deps_layer"], + # this doesn't work for some reason - this is always rebuilt, unless + # you pass --nostamp globally + stamp = 0, +) + +download_pkgs( + name = "build_tools", + image_tar = "@python-debian//image", + packages = [ + "sassc", + "gettext", + ], +) + +install_pkgs( + name = "build_tools_container", + output_image_name = "build_tools_container", + image_tar = BASE_IMAGE + '.tar', + installables_tar = ":build_tools.tar", + installation_cleanup_commands = "rm -rf /var/lib/apt/lists/* /usr/share/doc", +) + +# - - - - +# overkill rube goldberg setup to build static files ends + + +# - - python stuff - - + +# this is purely a build optimization - put the pip deps into a separate layer +py_layer( + name = "deps_layer", + deps = [ + requirement("Django"), + requirement("postorius"), + requirement("hyperkitty"), + requirement("gunicorn"), + requirement("psycopg2-binary"), + ], +) + +py_library( + name = "django_base", + srcs = ["settings.py", "urls.py"] + + glob(["upstream_settings/*.py"]), + deps = [ + requirement("Django"), + requirement("postorius"), + requirement("hyperkitty"), + requirement("gunicorn"), + requirement("psycopg2-binary"), + ], +) + +py_binary( + name = "manage", + srcs = ["manage.py"], + deps = [":django_base"], +) + +py_binary( + name = "serve", + srcs = ["serve.py"], + deps = [":django_base"], +) + +# prod docker image + +py3_image( + name = "mailman-web", + srcs = ["container_main.py"], + deps = [ + ":django_base", + ":manage", + ":serve", + ], + layers = [ + ":deps_layer", + ], + main = "container_main.py", + #base = ":base_container" + base = ":static_build_image", +) diff --git a/app/mailman-web/LICENSE b/app/mailman-web/LICENSE new file mode 100644 index 00000000..4e2fa46c --- /dev/null +++ b/app/mailman-web/LICENSE @@ -0,0 +1,3 @@ +Mailman and its components (postorius, hyperkitty) are licensed under GPLv3 and we link/import that code here directly. +Also, a good portion of this wsgi launcher is copied from https://gitlab.com/mailman/mailman-web, GPLv3 as well. +Therefore, this entire directory likely falls under GPLv3. diff --git a/app/mailman-web/README.md b/app/mailman-web/README.md new file mode 100644 index 00000000..36da2a6d --- /dev/null +++ b/app/mailman-web/README.md @@ -0,0 +1,7 @@ +Web parts of mailman3 - postorius and hyperkitty. +Postgres only, TODO attempt cockroachization. + +This currently serves static files via an extremely cursed hack: +lists.hackerspace.pl points to boston-packets, which serves /static/* from +a local directory there, made by extracting :static_pack there; and proxy_passes +every other path to the k8s Service defined here. diff --git a/app/mailman-web/container_main.py b/app/mailman-web/container_main.py new file mode 100644 index 00000000..b6797a72 --- /dev/null +++ b/app/mailman-web/container_main.py @@ -0,0 +1,13 @@ +from sys import argv, exit + +# simple wrapper so we don't need two container entrypoints +assert len(argv) > 1, "specify a command" +if argv[1] == "serve": + import serve + serve.main() +elif argv[1] == "manage": + import manage + manage.main(argv[1:]) +else: + print("unknown command", argv[1]) + exit(1) diff --git a/app/mailman-web/kube/mailman.libsonnet b/app/mailman-web/kube/mailman.libsonnet new file mode 100644 index 00000000..c71de4ed --- /dev/null +++ b/app/mailman-web/kube/mailman.libsonnet @@ -0,0 +1,215 @@ +local kube = import "../../../kube/kube.libsonnet"; + +{ + local app = self, + local cfg = app.cfg, + + cfg:: { + namespace: error "cfg.namespace must be set", + webDomain: error "cfg.webDomain must be set", + images: { + web: "registry.k0.hswaw.net/implr/mailman-web:0.6", + # https://github.com/octeep/wireproxy + wireproxy: "registry.k0.hswaw.net/implr/wireproxy:1.0.5" + }, + passwords: { + postgres: error "cfg.secrets.postgres must be set", + mailmanRest: error "cfg.secrets.mailmanRest must be set", + mailmanArchiver: error "cfg.secrets.mailmanArchiver must be set", + }, + smtp: { + user: "postorius", + # from mail server + password: error "cfg.smtp.password must be set", + }, + secrets: { + djangoSecretKey: error "cfg.secrets.djangoSecretKey must be set", + }, + wg: { + peerPubkey: error "cfg.wg.peerPubkey must be set", + privkey: error "cfg.wg.privkey must be set", + endpoint: error "cfg.wg.endpoint must be set", + }, + }, + + env:: { + WEB_DOMAIN: cfg.webDomain, + BIND_ADDR: "0.0.0.0:8080", + + //DB_HOST: app.postgres.svc.host, + DB_HOST: "boston-packets.hackerspace.pl", + DB_USER: "mailman", + DB_NAME: "mailman-web", + DB_PASS: kube.SecretKeyRef(app.config, "postgres-pass"), + DB_PORT: "5432", + + + SMTP_HOST: "mail.hackerspace.pl", + SMTP_PORT: "587", + SMTP_USER: "postorius", + SMTP_PASSWORD: kube.SecretKeyRef(app.config, "smtp-password"), + + SECRET_KEY: kube.SecretKeyRef(app.config, "django-secret-key"), + MAILMAN_REST_API_PASS: kube.SecretKeyRef(app.config, 'mailman-api-password'), + MAILMAN_ARCHIVER_KEY: kube.SecretKeyRef(app.config, 'mailman-archiver-key'), + + }, + + namespace: kube.Namespace(cfg.namespace), + local ns = self.namespace, + + + web: ns.Contain(kube.Deployment("web")) { + spec+: { + minReadySeconds: 10, + replicas: 1, + template+: { + spec+: { + initContainers_: { + migrate: kube.Container("migrate") { + image: cfg.images.web, + env_: app.env, + args: [ + "manage", "migrate", + ], + }, + }, + volumes_: { + config: kube.SecretVolume(app.wireproxyConfig), + }, + containers_: { + default: kube.Container("default") { + image: cfg.images.web, + env_: app.env, + args: ["serve"], + ports_: { + web: { containerPort: 8080 }, + }, + # readinessProbe: { + # httpGet: { + # path: "/", + # port: "web", + # }, + # failureThreshold: 10, + # periodSeconds: 5, + # }, + resources: { + requests: { + cpu: "250m", + memory: "1024M", + }, + limits: { + cpu: "1", + memory: "1024M", + }, + }, + }, + wireproxy: kube.Container("wireproxy") { + image: cfg.images.wireproxy, + resources: { + requests: { + cpu: "100m", + memory: "64M", + }, + limits: { + cpu: "200m", + memory: "128M", + }, + }, + volumeMounts_: { + config: { mountPath: "/etc/wireproxy/config", subPath: "config" } + }, + }, + }, + }, + }, + }, + }, + + local manifestIniMultisection(sname, values) = std.join('\n', + [std.manifestIni({ + sections: { + [sname]: i, + }}) for i in values]), + wireproxyConfig: ns.Contain(kube.Secret("wireproxy-config")) { + data: { + config: std.base64(std.manifestIni({ + sections: { + Interface: { + Address: cfg.wg.address, + PrivateKey: cfg.wg.privkey, + }, + Peer: { + PublicKey: cfg.wg.peerPubkey, + Endpoint: cfg.wg.endpoint, + }, + + }, + }) + manifestIniMultisection("TCPClientTunnel", [ + # { + # # postgres + # ListenPort: 5432, + # Target: "localhost:5432", + # }, + { + # mailman core api + BindAddress: "127.0.0.1:8001", + Target: "172.17.1.1:8001", + }, + ])), + }, + }, + + + svcWeb: ns.Contain(kube.Service("web")) { + target_pod: app.web.spec.template, + spec+: { + # hax + type: "LoadBalancer", + externalTrafficPolicy: "Local", + }, + }, + + + #ingress: ns.Contain(kube.Ingress("mailman")) { + # metadata+: { + # annotations+: { + # "kubernetes.io/tls-acme": "true", + # "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod", + # "nginx.ingress.kubernetes.io/proxy-body-size": "0", + # }, + # }, + # spec+: { + # tls: [ + # { + # hosts: [cfg.webDomain], + # secretName: "mailman-ingress-tls", + # }, + # ], + # rules: [ + # { + # host: cfg.webDomain, + # http: { + # paths: [ + # { path: "/", backend: app.svcWeb.name_port }, + # //{ path: "/static/", backend: app.svcStatic.name_port }, + # ], + # }, + # }, + # ], + # }, + #}, + + config: ns.Contain(kube.Secret("config")) { + data_: { + "postgres-pass": cfg.passwords.postgres, + "django-secret-key": cfg.secrets.djangoSecretKey, + + "smtp-password": cfg.smtp.password, + + "mailman-api-password": cfg.mailmanCore.mailmanApiPass, + "mailman-archiver-key": cfg.mailmanCore.mailmanArchiverKey, + + }, + }, +} diff --git a/app/mailman-web/kube/prod.jsonnet b/app/mailman-web/kube/prod.jsonnet new file mode 100644 index 00000000..3fdd75ac --- /dev/null +++ b/app/mailman-web/kube/prod.jsonnet @@ -0,0 +1,20 @@ +local mailman = import "mailman.libsonnet"; +local secrets = import "secrets/plain/prod.libsonnet"; + +mailman { + cfg+: secrets { + namespace: "mailman-hackerspace-prod", + webDomain: "lists2.hackerspace.pl", + + wg+: { + address: "172.17.1.2/32", + peerPubkey: "sKobxe3U6Gz72MWXEETTr8fSFIPSuX/WOGGFwd3oXy8=", + endpoint: "boston-packets.hackerspace.pl:51820" + }, + + //objectStorage+: { + //bucket: "mailman-prod", + //}, + + }, +} diff --git a/app/mailman-web/manage.py b/app/mailman-web/manage.py new file mode 100644 index 00000000..1dc0f864 --- /dev/null +++ b/app/mailman-web/manage.py @@ -0,0 +1,13 @@ +import os +import sys + +def main(argv): + os.environ['DJANGO_SETTINGS_MODULE'] = "settings" + + os.environ['DJANGO_IS_MANAGEMENT_COMMAND'] = '1' + from django.core.management import execute_from_command_line + execute_from_command_line(argv) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/app/mailman-web/serve.py b/app/mailman-web/serve.py new file mode 100644 index 00000000..e56bd043 --- /dev/null +++ b/app/mailman-web/serve.py @@ -0,0 +1,38 @@ +import os +import gunicorn.app.base +from django.core.wsgi import get_wsgi_application + + +class StandaloneApplication(gunicorn.app.base.BaseApplication): + + def __init__(self, app, options=None): + self.options = options or {} + self.application = app + super().__init__() + + def load_config(self): + config = {key: value for key, value in self.options.items() + if key in self.cfg.settings and value is not None} + for key, value in config.items(): + self.cfg.set(key.lower(), value) + + def load(self): + return self.application + + +def main(): + options = { + 'bind': os.environ.get('BIND_ADDR', '127.0.0.1:8080'), + 'workers': int(os.environ.get("GUNICORN_WORKERS", "4")), + 'capture_output': True, + 'disable_redirect_access_to_syslog': True, + 'accesslog': '-', + 'errorlog': '-', + } + os.environ['DJANGO_SETTINGS_MODULE'] = "settings" + application = get_wsgi_application() + StandaloneApplication(application, options).run() + + +if __name__ == '__main__': + main() diff --git a/app/mailman-web/settings.py b/app/mailman-web/settings.py new file mode 100644 index 00000000..8cbfb2b8 --- /dev/null +++ b/app/mailman-web/settings.py @@ -0,0 +1,102 @@ +import sys +import os + +from upstream_settings.base import * +from upstream_settings.mailman import * + +# we're in a container, stdout only +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + }, + }, + 'handlers': { + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'stream': sys.stdout, + 'formatter': 'verbose' + }, + }, + 'loggers': { + '': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': True, + }, + }, + } + +SECRET_KEY = os.environ.get("SECRET_KEY", "hackme") +# assert len(SECRET_KEY) > 16 +ROOT_URLCONF = "urls" + +ALLOWED_HOSTS = [ + "localhost", # Archiving API from Mailman, keep it. + os.environ.get('WEB_DOMAIN', "lists.hackerspace.pl"), +] + +ALLOWED_HOSTS = ["*"] # TODO deleteme + +MAILMAN_REST_API_URL = 'http://localhost:8001' +MAILMAN_REST_API_USER = 'restadmin' +MAILMAN_REST_API_PASS = os.environ.get('MAILMAN_REST_API_PASS') +MAILMAN_ARCHIVER_KEY = os.environ.get('MAILMAN_ARCHIVER_KEY') +MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1', '185.236.240.38', "2a0d:eb00:2137:2::10") + +DATABASES = { + 'default': { + # Use 'sqlite3', 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + # DB name or path to database file if using sqlite3. + 'NAME': os.environ.get('DB_NAME', 'mailman-web'), + # The following settings are not used with sqlite3: + 'USER': os.environ.get('DB_USER', 'mailman'), + 'PASSWORD': os.environ.get('DB_PASS'), + # HOST: empty for localhost through domain sockets or '127.0.0.1' for + # localhost through TCP. + 'HOST': os.environ.get('DB_HOST', '127.0.0.1'), + # PORT: set to empty string for default. + 'PORT': os.environ.get('DB_PORT', ''), + # OPTIONS: for mysql engine only, do not use with other engines. + # 'OPTIONS': {'charset': 'utf8mb4'} # Enable utf8 4-byte encodings. + } +} + +# TODO check this +USE_X_FORWARDED_HOST = True # behind an Ingress + +# And if your proxy does your SSL encoding for you, set SECURE_PROXY_SSL_HEADER +# https://docs.djangoproject.com/en/1.8/ref/settings/#secure-proxy-ssl-header +# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_SCHEME', 'https') + +DEFAULT_FROM_EMAIL = 'postorius@hackerspace.pl' +SERVER_EMAIL = 'bofh@hackerspace.pl' + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = os.environ.get('SMTP_HOST', '127.0.0.1') +EMAIL_PORT = int(os.environ.get('SMTP_PORT', '465')) +EMAIL_HOST_USER = os.environ.get('SMTP_USER', 'postorius') +EMAIL_HOST_PASSWORD = os.environ.get('SMTP_PASSWORD') +EMAIL_TIMEOUT=3 +EMAIL_USE_TLS=True + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': os.environ.get('FULLTEXT_INDEX_PATH', "fulltext_index"), + # You can also use the Xapian engine, it's faster and more accurate, + # but requires another library. + # http://django-haystack.readthedocs.io/en/v2.4.1/installing_search_engines.html#xapian + # Example configuration for Xapian: + # 'ENGINE': 'xapian_backend.XapianEngine' + }, +} + +# Only display mailing-lists from the same virtual host as the webserver +FILTER_VHOST = False +POSTORIUS_TEMPLATE_BASE_URL = 'https://lists.hackerspace.pl' diff --git a/app/mailman-web/upstream_settings/README b/app/mailman-web/upstream_settings/README new file mode 100644 index 00000000..9700643e --- /dev/null +++ b/app/mailman-web/upstream_settings/README @@ -0,0 +1 @@ +Unmodified copy of default settings from mailman-web. diff --git a/app/mailman-web/upstream_settings/__init__.py b/app/mailman-web/upstream_settings/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/mailman-web/upstream_settings/base.py b/app/mailman-web/upstream_settings/base.py new file mode 100644 index 00000000..73335884 --- /dev/null +++ b/app/mailman-web/upstream_settings/base.py @@ -0,0 +1,300 @@ +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +from django.contrib.messages import constants as messages +from pathlib import Path + +#: The base directory for logs and database. +BASE_DIR = Path('/opt/mailman/web') + +#: Default list of admins who receive the emails from error logging. +ADMINS = ( + ('Mailman Suite Admin', 'root@localhost'), +) + +#: Hosts/domain names that are valid for this site; required if DEBUG is False. +#: See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [ + "localhost", # Archiving API from Mailman, keep it. + # "lists.your-domain.org", + # Add here all production URLs you may have. +] + +#: Enable Development Mode. +DEBUG = False + + +#: URL Configuration for Django +ROOT_URLCONF = 'mailman_web.urls' + + +#: Default list of django applications. +#: Each social account provider is an application and by default no social auth +#: providers are enabled. To enable a social auth provider, you can add them +#: to list of INSTALLED_APPS. For example:: +#: +#: DJANGO_SOCIAL_AUTH_PROVIDERS = [ +#: 'allauth.socialaccount.providers.openid', +#: 'django_mailman3.lib.auth.fedora', +#: 'allauth.socialaccount.providers.github', +#: 'allauth.socialaccount.providers.gitlab', +#: 'allauth.socialaccount.providers.google', +#: 'allauth.socialaccount.providers.facebook', +#: 'allauth.socialaccount.providers.twitter', +#: 'allauth.socialaccount.providers.stackexchange', +#: ] +#: INSTALLED_APPS += DJANGO_SOCIAL_AUTH_PROVIDERS +#: +#: A full list of providers can be found at +#: https://django-allauth.readthedocs.io/en/latest/providers.html +#: Please also note that extra configuration is required after +#: a provider is enabled. Django-allauth's documentation mentioned +#: above provides more details about how to configure one. +INSTALLED_APPS = [ + 'hyperkitty', + 'postorius', + 'django_mailman3', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'django_gravatar', + 'compressor', + 'haystack', + 'django_extensions', + 'django_q', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', +] + + +#: Default Django Middlewares. +MIDDLEWARE = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django_mailman3.middleware.TimezoneMiddleware', + 'postorius.middleware.PostoriusMiddleware', +) + +#: Default Template finders. +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.i18n', + 'django.template.context_processors.media', + 'django.template.context_processors.static', + 'django.template.context_processors.tz', + 'django.template.context_processors.csrf', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + 'django_mailman3.context_processors.common', + 'hyperkitty.context_processors.common', + 'postorius.context_processors.postorius', + ], + }, + }, +] + +#: Wsgi application import path. This will be used by the WSGI server which +#: will be used to deploy this application. +WSGI_APPLICATION = 'mailman_web.wsgi.application' + +#: Default Database to be used. +#: Example for PostgreSQL (**recommanded for production**):: +#: +#: 'default': { +#: 'ENGINE': 'django.db.backends.postgresql_psycopg2', +#: 'NAME': 'database_name', +#: 'USER': 'database_user', +#: 'PASSWORD': 'database_password', +#: 'HOST': 'localhost', +#: } +#: +#: For MySQL/MariaDB also add the following to the the configuration:: +#: +#: 'OPTIONS': {'charset': 'utf8mb4'} # Enable utf8 4-byte encodings. +#: +#: Check out +#: `Django documentation +#: `_ for +#: more details. +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'mailman-web.db'), + 'HOST': '', + 'PORT': '', + } +} + +# Maintain type of autogenerated keys going forward +# https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + + +#: Default password validators. +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': + 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # noqa: E501 + }, + { + 'NAME': + 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': + 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': + 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +#: Default Language code. +LANGUAGE_CODE = 'en-us' + +#: Default timezone. +TIME_ZONE = 'UTC' + +#: Enable internationalization. +USE_I18N = True + +#: Enable localization. +USE_L10N = True + +#: Use the timezone information. +USE_TZ = True + + +#: Default path where static files will be placed. +STATIC_ROOT = os.path.join(BASE_DIR, 'static') + +#: URL prefix for static files. +#: Example: "http://example.com/static/", "http://static.example.com/" +STATIC_URL = '/static/' + +#: Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + # BASE_DIR + '/static/', +) + +#: List of finder classes that know how to find static files in +#: various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + # 'django.contrib.staticfiles.finders.DefaultStorageFinder', + 'compressor.finders.CompressorFinder', +) + + +#: Default Django URL to redirect to for Login. +LOGIN_URL = 'account_login' +#: Default Django URL to redirect to after a successful login. +LOGIN_REDIRECT_URL = 'list_index' +#: Default Django URL to Logout the user. +LOGOUT_URL = 'account_logout' + +#: If you enable email reporting for error messages, this is where those emails +#: will appear to be coming from. Make sure you set a valid domain name, +#: otherwise the emails may get rejected. +#: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SERVER_EMAIL +SERVER_EMAIL = 'root@localhost.local' + +#: The default implementation to send out emails. This can be customized to +#: something else for testing purposes. +#: https://docs.djangoproject.com/en/dev/topics/email/#email-backends +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' + +MESSAGE_TAGS = { + messages.ERROR: 'danger' +} + + +#: Default Logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + }, + 'file': { + 'level': 'INFO', + 'class': 'logging.handlers.WatchedFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs', 'mailmanweb.log'), + 'formatter': 'verbose', + }, + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins', 'file'], + 'level': 'ERROR', + 'propagate': True, + }, + 'django': { + 'handlers': ['file'], + 'level': 'ERROR', + 'propagate': True, + }, + 'hyperkitty': { + 'handlers': ['file'], + 'level': 'DEBUG', + 'propagate': True, + }, + 'postorius': { + 'handlers': ['console', 'file'], + 'level': 'INFO', + }, + 'q': { + 'level': 'WARNING', + 'propagate': False, + 'handlers': ['console', 'file'], + }, + }, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s' # noqa: E501 + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, +} + +#: Current Django Site being served. This is used to customize the web host +#: being used to serve the current website. For more details about Django +#: site, see: https://docs.djangoproject.com/en/dev/ref/contrib/sites/ +SITE_ID = 1 diff --git a/app/mailman-web/upstream_settings/mailman.py b/app/mailman-web/upstream_settings/mailman.py new file mode 100644 index 00000000..f69a706f --- /dev/null +++ b/app/mailman-web/upstream_settings/mailman.py @@ -0,0 +1,124 @@ +#: Mailman Core default API Path +MAILMAN_REST_API_URL = 'http://localhost:8001' +#: Mailman Core API user +MAILMAN_REST_API_USER = 'restadmin' +#: Mailman Core API user's password. +MAILMAN_REST_API_PASS = 'restpass' +#: Mailman Core Shared archiving key. This value is set in the : +#: mailman-hyperkitty's configuration file. +MAILMAN_ARCHIVER_KEY = 'SecretArchiverAPIKey' +#: Host for Mailman Core, from where Hyperkitty will accept connections +#: for archiving. +MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1') + +#: Base URL where Django/Mailman-web would be listening for requests. Used by +#: Mailman Core for fetching templates. +POSTORIUS_TEMPLATE_BASE_URL = 'http://localhost:8000' + +#: Use gravatar in HyperKitty and Postorius. +#: If disabled django_gravatar can be removed from INSTALLED_APPS: +#: INSTALLED_APPS.remove('django_gravatar') +HYPERKITTY_ENABLE_GRAVATAR = True + +#: Filter visible Mailing Lists based on the current host being used to serve. +FILTER_VHOST = False + +#: Sender in Emails sent out by Postorius. +DEFAULT_FROM_EMAIL = 'postorius@localhost' + + +#: Django Allauth +ACCOUNT_AUTHENTICATION_METHOD = "username_email" +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_EMAIL_VERIFICATION = "mandatory" +ACCOUNT_UNIQUE_EMAIL = True + +#: Protocol for URLs generated for authentication, like email +#: confirmation. +ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https" + + +#: Extra configuration for Social auth. For these configuration to be used. +#: each of the social account providers must be first added in INSTALLED_APPS. +#: See :py:data:`mailman_web.settings.base.INSTALLED_APPS` for more +#: configuration. +SOCIALACCOUNT_PROVIDERS = { + 'openid': { + 'SERVERS': [ + dict(id='yahoo', + name='Yahoo', + openid_url='http://me.yahoo.com'), + ], + }, + 'google': { + 'SCOPE': ['profile', 'email'], + 'AUTH_PARAMS': {'access_type': 'online'}, + }, + 'facebook': { + 'METHOD': 'oauth2', + 'SCOPE': ['email'], + 'FIELDS': [ + 'email', + 'name', + 'first_name', + 'last_name', + 'locale', + 'timezone', + ], + 'VERSION': 'v2.4', + }, +} + + +#: django-compressor +#: https://pypi.python.org/pypi/django_compressor +COMPRESS_PRECOMPILERS = ( + ('text/x-scss', 'sassc -t compressed {infile} {outfile}'), + ('text/x-sass', 'sassc -t compressed {infile} {outfile}'), +) + + +# Social auth +# +#: Authentication backends for Django to be used. +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +) + +# +# Full-text search engine +# +#: Django-Haystack connection parameters. +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', + 'PATH': "fulltext_index", + # You can also use the Xapian engine, it's faster and more accurate, + # but requires another library. + # http://django-haystack.readthedocs.io/en/v2.4.1/installing_search_engines.html#xapian + # Example configuration for Xapian: + # 'ENGINE': 'xapian_backend.XapianEngine' + }, +} + + +# Asynchronous tasks +# +#: Django Q connection parameters. +Q_CLUSTER = { + 'retry': 360, + 'timeout': 300, + 'save_limit': 100, + 'orm': 'default', +} + +#: On a production setup, setting COMPRESS_OFFLINE to True will bring a +#: significant performance improvement, as CSS files will not need to be +#: recompiled on each requests. It means running an additional "compress" +#: management command after each code upgrade. +#: http://django-compressor.readthedocs.io/en/latest/usage/#offline-compression +COMPRESS_OFFLINE = True + +# Needed for debug mode +# INTERNAL_IPS = ('127.0.0.1',) diff --git a/app/mailman-web/urls.py b/app/mailman-web/urls.py new file mode 100644 index 00000000..e9359c70 --- /dev/null +++ b/app/mailman-web/urls.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 1998-2016 by the Free Software Foundation, Inc. +# +# This file is part of Postorius. +# +# Postorius is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# Postorius is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# Postorius. If not, see . + + +from django.conf.urls import include +from django.contrib import admin +from django.urls import path, reverse_lazy +from django.views.generic import RedirectView + +urlpatterns = [ + path( + '', + RedirectView.as_view(url=reverse_lazy('list_index'), permanent=True), + ), + path('mailman3/', include('postorius.urls')), + path('archives/', include('hyperkitty.urls')), + path('', include('django_mailman3.urls')), + path('accounts/', include('allauth.urls')), + path('admin/', admin.site.urls), +] diff --git a/third_party/py/requirements.in b/third_party/py/requirements.in index 93da93e7..2b8b40cb 100644 --- a/third_party/py/requirements.in +++ b/third_party/py/requirements.in @@ -1,12 +1,12 @@ # grpcio and protobuf are installed directly via WORKSPACE # do NOT add them there # depending on a py_grpc_library output will pull in the required deps -arrow==0.14.5 +arrow==1.1.1 blinker==1.4 Click==7.0 cockroachdb==0.3.3 -cryptography==2.9.2 -Django==2.2.28 +cryptography==3.4.8 +Django==3.2.16 fabric==2.4.0 Flask==1.1.1 Flask-Login==0.4.1 @@ -15,12 +15,16 @@ Flask-WTF==0.14.2 future==0.17.1 gevent==22.10.2 gunicorn==20.1.0 +hyperkitty==1.3.7 # sync with postorius itsdangerous==1.1.0 Jinja2==2.10.1 MarkupSafe==1.1.1 oauthlib==3.1.1 paramiko==2.7.2 +postorius==1.3.7 # sync with hyperkitty psycopg2==2.9.4 +# disgusten, but needed for weird container linking problems +psycopg2-binary==2.9.4 pyelftools==0.26 PyNaCl==1.3.0 python-dateutil==2.8.0 diff --git a/third_party/py/requirements.txt b/third_party/py/requirements.txt index 939be479..e36bef81 100644 --- a/third_party/py/requirements.txt +++ b/third_party/py/requirements.txt @@ -4,10 +4,20 @@ # # bazel run //third_party/py:requirements.update # -arrow==0.14.5 \ - --hash=sha256:0186026cfd94ca4fb773f30cc5398289a3027480d335e0e5c0d2772643763137 \ - --hash=sha256:a12de0124d812d15061ed36c7eb4a421fa1b95026a502a0b2062e9ea00fc4446 - # via -r third_party/py/requirements.in +arrow==1.1.1 \ + --hash=sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510 \ + --hash=sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a + # via + # -r third_party/py/requirements.in + # django-q +asgiref==3.6.0 \ + --hash=sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac \ + --hash=sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506 + # via django +atpublic==3.1.1 \ + --hash=sha256:3098ee12d0107cc5009d61f4e80e5edcfac4cda2bdaa04644af75827cb121b18 \ + --hash=sha256:37f714748e77b8a7b34d59b7b485fd452a0d5906be52cb1bd28d29a2bd84f295 + # via flufl-lock bcrypt==3.2.2 \ --hash=sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521 \ --hash=sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb \ @@ -21,6 +31,14 @@ bcrypt==3.2.2 \ --hash=sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40 \ --hash=sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa # via paramiko +bleach==6.0.0 \ + --hash=sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414 \ + --hash=sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4 + # via readme-renderer +blessed==1.19.1 \ + --hash=sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b \ + --hash=sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc + # via django-q blinker==1.4 \ --hash=sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6 # via -r third_party/py/requirements.in @@ -81,6 +99,7 @@ cffi==1.15.0 \ --hash=sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796 # via # bcrypt + # cmarkgfm # cryptography # pynacl chardet==3.0.4 \ @@ -93,37 +112,159 @@ click==7.0 \ # via # -r third_party/py/requirements.in # flask +cmarkgfm==2022.10.27 \ + --hash=sha256:0023de4b19bb557b143bed274f76cb36551f7f1d1cdffd29b6cde646b85d9ffb \ + --hash=sha256:0176d51fb57162c642b1d2c70048950a5ae119af81e77565a0383b992b1f86d6 \ + --hash=sha256:071f5f0dac9475bab6a065878f248a69be52a7736b6c661e06ca7199f25fe097 \ + --hash=sha256:0756ea0f6b55eff2617ea0518d6730e37d6077c10baaabbe8b46210ff5a250ef \ + --hash=sha256:07a06d424ccef98528cba1158946f92117e07579f1dc9942ed4fd70f81693b9f \ + --hash=sha256:1013ce61db1dd3febcaca1ee42cad9eb823852bb76cbae61c1488734ce51f2b7 \ + --hash=sha256:123ad8d50fbedacd036760ba46e36170bad9dd2c1e83655d8622b7803169bb49 \ + --hash=sha256:1790164f84e6b037d0b39df11f757e021a9f9c313681297a051d50bc7b5249fc \ + --hash=sha256:20e897160be161161a565df94ce502714a1aa63af3ad682e6d1f1c7e6656fdbb \ + --hash=sha256:210c0f0dbc1aadab30bc75c48b14b645414733a668df52b43058028e43a046e8 \ + --hash=sha256:21557c06a411b1d754eed7f6fc9a8ff41f8a4a004b32c8bd2cec2ab3f3cb4d3c \ + --hash=sha256:216a540e85258839cffa7274731a87d91b3e17c9079b3b02467c312e784b5281 \ + --hash=sha256:27149c63b1190ee6e7dd4b32d0a2c313bc1856bcdde7a42a0a5b6ae42d97ed94 \ + --hash=sha256:325c03644da5ab81a7071aae6fbafa3beb22413f7fd7440baf6d510cfcf7be21 \ + --hash=sha256:3f510fafa9d904336eecc3aa41536fd287c2d32baa21b14d48950ced802ca531 \ + --hash=sha256:4325b75a3b5b802d5edcc2378aa6405a1e5df0aeeec583d1b05d73b0562fa7d0 \ + --hash=sha256:47e267ce890b579585a32f77d347d61de2390b517cfc52bb4ca67c5c4b4c055a \ + --hash=sha256:483e48613f5c7b3350cdabfd0f69aaa086513542d0de533f39e5669bf4df5de4 \ + --hash=sha256:5342c6d12e343cc66b4b8dcd09fc0c1977cb32fd1d57c15bd756876606591ee9 \ + --hash=sha256:5a39333e1fdcd0116c24adc33423999913865bd3cc83fc44b2218aac7fbe5637 \ + --hash=sha256:5bad39b832f734f588aea00868e53ba1aaf058d569e40e5c9016702edebf88e8 \ + --hash=sha256:5fc7178a6afd69a5dfc197558791cecedead9fc77e95ec63c201e8219ce33000 \ + --hash=sha256:6672784820981d315b695bb7ce08d40886502368e133b453d675ff6f2fffae49 \ + --hash=sha256:670b414274edf3ecc0a950a80580e1de553c599a30658827a5d7f7bccbde5843 \ + --hash=sha256:69a769feb1b2d16982fe952afd44e124a4d306a44cdfd6857e74b8eb5d47d765 \ + --hash=sha256:76beb5b50b32d7bafec2154608a037601a2186d15df95cec6ab4cc937afca365 \ + --hash=sha256:799cf03a82a7849d975a3b955798d5e439a08fb678b657c5078115dc61314674 \ + --hash=sha256:7a91279ab8e2869c19120595e41ebd81a6f5034c1e6b1cfc5e81cd80d40bf3eb \ + --hash=sha256:80cf50b52bc0a47c032706de27b9526b6035c73b57ce06662021144cba4b6c5e \ + --hash=sha256:8744be702511464d04c34000005009607471f1afe65d6037777747d6b4607e5f \ + --hash=sha256:8830dfb61251f2b677dea7ffc531c3f6037f7e9a66a14ad24bdaf3cefe2dc8c4 \ + --hash=sha256:89dcd4fea4ae44f1a0697cf805b6931a126b2b3ea23ed1ccdad7e020425224a9 \ + --hash=sha256:8e9f038a4f0e54c135e468994f1ea97141b086d1f1bd8f498c12f3d559017e8e \ + --hash=sha256:90ae1b4b2c6b92f8f5b1e5416a2f5b1bba7a5f9aea29b0de79767ed80655527a \ + --hash=sha256:93d9ac7716ea901ca0bfd18ae3b68f1f6bf51de0830c3f233ef734fcd52a0799 \ + --hash=sha256:98c0527153daf16589ef095aa72f06a4bdb9213433ff47811fbc4172c91d865b \ + --hash=sha256:a6a3970cf1c8ba4465d5046dd6a6d7f6024e67d6eec812a4701a21c5161a2fbd \ + --hash=sha256:b0b13eac6194d59f9d3ab44af7076221510e788572f34e25104ad47b33d960e1 \ + --hash=sha256:b8daf62cddc81b31a8f3c9093936c4cb75b25a8024c09f276cb027f1647e3326 \ + --hash=sha256:bd6315e1036d31884bff25719636e3499a7f4593b0f7b47dc742678328f2f26f \ + --hash=sha256:c04921575e412a6459d645a45ca987061b17d89310c92aedf108f97f2b8b7b91 \ + --hash=sha256:c3a6e597bdf595f81dc214e821b579b8d665116c55ed5288b599ae941e446098 \ + --hash=sha256:c66077349e7f7d954aa37d770310de5a8214ac9dca9756440f99e008a0e693de \ + --hash=sha256:c804446b941dc08dcc3d2def3913cfc4bae954b80babfaa2a502e8ebdea29185 \ + --hash=sha256:c82af8cdb76a71459662e447f9b1545ae6146cb9287df978705a298f87a76a90 \ + --hash=sha256:ca0e03a590c6f62738d208f8689da08eae9d3bcc2f4dd97e38df45d8dbc333ab \ + --hash=sha256:cc70b89309404dd84a524d439aa2b2e54872e0f623f9523bd77e66526251954f \ + --hash=sha256:ccfc25b5abfe1398426f099d840b5fa7dec118b44f06833e2ba8b67c6ffc12d9 \ + --hash=sha256:cfe84b8912b355b8036c093ecdd6abbe6df075176879a49867dd72b9e53449f3 \ + --hash=sha256:d3fd62dd65c3a64ced175a1447ea41b01a7ac1c0df1c8358323267c9326b7745 \ + --hash=sha256:db3449fdb87752be5ad0698d6f2ca030af320cdf71ebc9a1ebae1b9c1d3661c8 \ + --hash=sha256:ddc2bbb5572722758787066f5f841745c58452e28c59ce7c13b7228be1cb48f3 \ + --hash=sha256:e65e492407d7cb3b695f3f715a1cbe6f97db69eb14011b8f156fc10c758b55c7 \ + --hash=sha256:ea7d6cb95e2d74049cf08fde4ca6cbf030b9bf9ef75009847bbefb35094bb4c2 \ + --hash=sha256:ea8a84d3702ccc32f8dfd0917dfb95f3d1843a0b6f85131c5cbfd1480d1d31ee \ + --hash=sha256:f17677e66f95f25999c959c3f5361c05e739ad4f6b70ab9fdd24b1734c3ab029 \ + --hash=sha256:f2d3bdb7e525abd03366a57eabd03e0c3f3f36bbf8af2267200605b7b712763b \ + --hash=sha256:f938c503fce528d9cb715314134f8900cf09ddbd7e2bea88cf54a4bad58d0d5b \ + --hash=sha256:fbec94c3e91b5e03d90a2cc2e865179e5bc58673e92b03ba64b520a97a0e9219 + # via readme-renderer cockroachdb==0.3.3 \ --hash=sha256:ef7aa1baf47f1ec8b187d7850f7996566a4131457fa69cb9490536e698f4540b # via -r third_party/py/requirements.in -cryptography==2.9.2 \ - --hash=sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6 \ - --hash=sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b \ - --hash=sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5 \ - --hash=sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf \ - --hash=sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e \ - --hash=sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b \ - --hash=sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae \ - --hash=sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b \ - --hash=sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0 \ - --hash=sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b \ - --hash=sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d \ - --hash=sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229 \ - --hash=sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3 \ - --hash=sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365 \ - --hash=sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55 \ - --hash=sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270 \ - --hash=sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e \ - --hash=sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785 \ - --hash=sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0 +cryptography==3.4.8 \ + --hash=sha256:0a7dcbcd3f1913f664aca35d47c1331fce738d44ec34b7be8b9d332151b0b01e \ + --hash=sha256:1eb7bb0df6f6f583dd8e054689def236255161ebbcf62b226454ab9ec663746b \ + --hash=sha256:21ca464b3a4b8d8e86ba0ee5045e103a1fcfac3b39319727bc0fc58c09c6aff7 \ + --hash=sha256:34dae04a0dce5730d8eb7894eab617d8a70d0c97da76b905de9efb7128ad7085 \ + --hash=sha256:3520667fda779eb788ea00080124875be18f2d8f0848ec00733c0ec3bb8219fc \ + --hash=sha256:3c4129fc3fdc0fa8e40861b5ac0c673315b3c902bbdc05fc176764815b43dd1d \ + --hash=sha256:3fa3a7ccf96e826affdf1a0a9432be74dc73423125c8f96a909e3835a5ef194a \ + --hash=sha256:5b0fbfae7ff7febdb74b574055c7466da334a5371f253732d7e2e7525d570498 \ + --hash=sha256:695104a9223a7239d155d7627ad912953b540929ef97ae0c34c7b8bf30857e89 \ + --hash=sha256:8695456444f277af73a4877db9fc979849cd3ee74c198d04fc0776ebc3db52b9 \ + --hash=sha256:94cc5ed4ceaefcbe5bf38c8fba6a21fc1d365bb8fb826ea1688e3370b2e24a1c \ + --hash=sha256:94fff993ee9bc1b2440d3b7243d488c6a3d9724cc2b09cdb297f6a886d040ef7 \ + --hash=sha256:9965c46c674ba8cc572bc09a03f4c649292ee73e1b683adb1ce81e82e9a6a0fb \ + --hash=sha256:a00cf305f07b26c351d8d4e1af84ad7501eca8a342dedf24a7acb0e7b7406e14 \ + --hash=sha256:a305600e7a6b7b855cd798e00278161b681ad6e9b7eca94c721d5f588ab212af \ + --hash=sha256:cd65b60cfe004790c795cc35f272e41a3df4631e2fb6b35aa7ac6ef2859d554e \ + --hash=sha256:d2a6e5ef66503da51d2110edf6c403dc6b494cc0082f85db12f54e9c5d4c3ec5 \ + --hash=sha256:d9ec0e67a14f9d1d48dd87a2531009a9b251c02ea42851c060b25c782516ff06 \ + --hash=sha256:f44d141b8c4ea5eb4dbc9b3ad992d45580c1d22bf5e24363f2fbf50c2d7ae8a7 # via # -r third_party/py/requirements.in # fabric # paramiko -django==2.2.28 \ - --hash=sha256:0200b657afbf1bc08003845ddda053c7641b9b24951e52acd51f6abda33a7413 \ - --hash=sha256:365429d07c1336eb42ba15aa79f45e1c13a0b04d5c21569e7d596696418a6a45 - # via -r third_party/py/requirements.in + # pyjwt +defusedxml==0.7.1 \ + --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \ + --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 + # via python3-openid +django==3.2.16 \ + --hash=sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121 \ + --hash=sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d + # via + # -r third_party/py/requirements.in + # django-allauth + # django-appconf + # django-extensions + # django-haystack + # django-mailman3 + # django-picklefield + # django-q + # djangorestframework + # hyperkitty + # postorius +django-allauth==0.52.0 \ + --hash=sha256:e380661ceafe55734c40102819ae720403027036f28e9f9827f0faeddc24ed5f + # via django-mailman3 +django-appconf==1.0.5 \ + --hash=sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d \ + --hash=sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4 + # via django-compressor +django-compressor==4.3.1 \ + --hash=sha256:2c451174acb6f083054af7c8089376599b22d6380bd60311f78ec3fed79acc8e \ + --hash=sha256:68858c0da6cc099cc29a022d86c3ba8aed114da9d709eeceb0d7b8181b5f8942 + # via hyperkitty +django-extensions==3.2.1 \ + --hash=sha256:2a4f4d757be2563cd1ff7cfdf2e57468f5f931cc88b23cf82ca75717aae504a4 \ + --hash=sha256:421464be390289513f86cb5e18eb43e5dc1de8b4c27ba9faa3b91261b0d67e09 + # via hyperkitty +django-gravatar2==1.4.4 \ + --hash=sha256:545a6c2c5c624c7635dec29c7bc0be1a2cb89c9b8821af8616ae9838827cc35b \ + --hash=sha256:c813280967511ced93eea0359f60e5369c35b3311efe565c3e5d4ab35c10c9ee + # via + # django-mailman3 + # hyperkitty +django-haystack==3.2.1 \ + --hash=sha256:97e3197aefc225fe405b6f17600a2534bf827cb4d6743130c20bc1a06f7293a4 + # via hyperkitty +django-mailman3==1.3.9 \ + --hash=sha256:1a92355b43bd689a4b17f99c4b6de4b490d9b0febd4b6cd0cbb76b3a25819d33 + # via + # hyperkitty + # postorius +django-picklefield==3.1 \ + --hash=sha256:c786cbeda78d6def2b43bff4840d19787809c8909f7ad683961703060398d356 \ + --hash=sha256:d77c504df7311e8ec14e8b779f10ca6fec74de6c7f8e2c136e1ef60cf955125d + # via django-q +django-q==1.3.9 \ + --hash=sha256:1b74ce3a8931990b136903e3a7bc9b07243282a2b5355117246f05ed5d076e68 \ + --hash=sha256:5c6b4d530aa3aabf9c6aa57376da1ca2abf89a1562b77038b7a04e52a4a0a91b + # via hyperkitty +djangorestframework==3.14.0 \ + --hash=sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8 \ + --hash=sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08 + # via hyperkitty +docutils==0.19 \ + --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ + --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc + # via readme-renderer fabric==2.4.0 \ --hash=sha256:93684ceaac92e0b78faae551297e29c48370cede12ff0f853cdebf67d4b87068 \ --hash=sha256:98538f2f3f63cf52497a8d0b24d18424ae83fe67ac7611225c72afb9e67f2cf6 @@ -147,6 +288,10 @@ flask-wtf==0.14.2 \ --hash=sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36 \ --hash=sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac # via -r third_party/py/requirements.in +flufl-lock==7.1.1 \ + --hash=sha256:96d2c0448ba9fd8fc65d5d681ed7217c8e1625149c1c880bba50559bb680a615 \ + --hash=sha256:af14172b35bbc58687bd06b70d1693fd8d48cbf0ffde7e51a618c148ae24042d + # via hyperkitty future==0.17.1 \ --hash=sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8 # via -r third_party/py/requirements.in @@ -270,6 +415,9 @@ gunicorn==20.1.0 \ --hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \ --hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8 # via -r third_party/py/requirements.in +hyperkitty==1.3.7 \ + --hash=sha256:4d74aca3ec30546741ca62339ece72392e298f511d0a89e69bcedbfcdc8102cb + # via -r third_party/py/requirements.in idna==2.8 \ --hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \ --hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c @@ -290,6 +438,12 @@ jinja2==2.10.1 \ # via # -r third_party/py/requirements.in # flask +mailmanclient==3.3.5 \ + --hash=sha256:63581c604ca7eac021489c15aacca06a4958eb76f66574c6fab05eac654dd857 + # via + # django-mailman3 + # hyperkitty + # postorius markupsafe==1.1.1 \ --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \ @@ -346,6 +500,14 @@ markupsafe==1.1.1 \ # via # -r third_party/py/requirements.in # jinja2 +mistune==2.0.4 \ + --hash=sha256:182cc5ee6f8ed1b807de6b7bb50155df7b66495412836b9a74c8fbdfc75fe36d \ + --hash=sha256:9ee0a66053e2267aba772c71e06891fa8f1af6d4b01d5e84e267b4570d4d9808 + # via hyperkitty +networkx==3.0 \ + --hash=sha256:58058d66b1818043527244fab9d41a51fcd7dcc271748015f3c181b8a90c8e2e \ + --hash=sha256:9a9992345353618ae98339c2b63d8201c381c2944f38a2ab49cb45a4c667e412 + # via hyperkitty oauthlib==3.1.1 \ --hash=sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc \ --hash=sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3 @@ -358,6 +520,25 @@ paramiko==2.7.2 \ # via # -r third_party/py/requirements.in # fabric +postorius==1.3.7 \ + --hash=sha256:3d40a9d025bfdec0fdeb5d320ed2db3fbbffc77bd0dfeb619f39cf345c03a2f0 + # via -r third_party/py/requirements.in +psutil==5.9.4 \ + --hash=sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff \ + --hash=sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1 \ + --hash=sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62 \ + --hash=sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549 \ + --hash=sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08 \ + --hash=sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7 \ + --hash=sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e \ + --hash=sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe \ + --hash=sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24 \ + --hash=sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad \ + --hash=sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94 \ + --hash=sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8 \ + --hash=sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7 \ + --hash=sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4 + # via flufl-lock psycopg2==2.9.4 \ --hash=sha256:07b90a24d5056687781ddaef0ea172fd951f2f7293f6ffdd03d4f5077801f426 \ --hash=sha256:1da77c061bdaab450581458932ae5e469cc6e36e0d62f988376e9f513f11cb5c \ @@ -371,6 +552,67 @@ psycopg2==2.9.4 \ --hash=sha256:c7fa041b4acb913f6968fce10169105af5200f296028251d817ab37847c30184 \ --hash=sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f # via -r third_party/py/requirements.in +psycopg2-binary==2.9.4 \ + --hash=sha256:02cde837df012fa5d579b9cf4bc8e1feb460f38d61f7a4ab4a919d55a9f6eeef \ + --hash=sha256:044b6ab68613de7ea1e63856627deea091bfea09dea5ab4f050b13250fd18cab \ + --hash=sha256:0a9465f0aa36480c8e7614991cbe8ca8aa16b0517c5398a49648ce345e446c19 \ + --hash=sha256:0d8e0c9eec79fe1ae66691e06e3cc714da6fbd77981209bf32fa823c03dbaff8 \ + --hash=sha256:0eae72190be519bf2629062eab7ac8d4ceec5bd132953cefa1596584d86964fe \ + --hash=sha256:15e0ac0ed8a85f6049e836e95ddee627766561c85be8d23f4b3edb6ddbaa7310 \ + --hash=sha256:161dc52a617f0bb610a87d391cb2e77fe65b89ebfbd752f4f3217dde701ea196 \ + --hash=sha256:181ac372a5a5308b4076933601a9b5f0cd139b389b0aa5e164786a2abbcdb978 \ + --hash=sha256:1c22c59ab7d9dc110d409445f111f58556bf699b0548f3fc5176684a29c629c4 \ + --hash=sha256:226f11be577b70a57f4910c0ee28591d4d9fcb3d455e966267179156ae2e0c41 \ + --hash=sha256:24d627ed69e754c48dd142a914124858c600b4108c92546eb0ba822e63c0c6e2 \ + --hash=sha256:2535f44b00f26f6af0e949c825e6aecb9adcb56c965c17af5b97137fb69f00c0 \ + --hash=sha256:25e0517ad7ee3c5c3c69dbe3c1d95504c811e42f452b39a3505d0763b1f6caa0 \ + --hash=sha256:2903bf90b1e6bfc9bbfc94a1db0b50ffa9830a0ca4c042fbc38d93890c02ce08 \ + --hash=sha256:2f1ded23d17af0d738e7e78087f0b88a53228887845b1989b03af4dfd3fef703 \ + --hash=sha256:30200b07779446760813eef06098ec6d084131e4365b4e023eb43100de758b11 \ + --hash=sha256:33ac8b4754e6b6b21f3ee180da169d8526d91aee9408ec1fc573c16ab32b0207 \ + --hash=sha256:34fd249275faa782c3a2016e86ac2330636ac58d731a1580e7d686e3976b9536 \ + --hash=sha256:44f5dc9b4384bafca8429759ce76c8960ffc2b583fcad9e5dfb3e5f4894269e4 \ + --hash=sha256:451550e0bb5889bbabbf92575a6d6eafced941cc28c86be6ae4667f81bf32d67 \ + --hash=sha256:52383e932e6de5595963f9178cf2af7b9e1f3daacf5135b9c0e21aabbc5bf7c4 \ + --hash=sha256:55137faec669c4277c5687c6ce7c1fbc4dece0e2f14256ee808f4a652f0a2170 \ + --hash=sha256:576b9dfbcd154a0e8b5d9dae6316d037450e64a3b31df87dec71d88e2a2d5e5f \ + --hash=sha256:59a3010d566a48b919490a982f6807f68842686941dc12d568e129d9cd7703d6 \ + --hash=sha256:61c6a258469c66412ae8358a0501df6ccb3bb48aa9c43b56624571ff9767f91d \ + --hash=sha256:63edc507f8cbfbb5903adb75bad8a99f9798981c854df9119dbebab2ec3ee0e1 \ + --hash=sha256:65d5f4e70a2d3fbaa1349236968792611088f3f2dccead36c1626e1d183cc327 \ + --hash=sha256:6a1618260a112a9c93504511f0b6254b4402a8c41b7130dc6d4c9e39aff3aa0c \ + --hash=sha256:704f1fcdc5b606b70563ea696c69bda90caee3a2f45ffc9cee60a901b394a79f \ + --hash=sha256:7751b11cd7f6b952b4b5ec5b93b5be9ce20faba786c18c25c354f5d8717a173c \ + --hash=sha256:7ad9d032dc1a31a86ca7b059f43554a049a2bfda8fe32d1492ad25f6686aff03 \ + --hash=sha256:7b01d07006a0ac2216921b69a220b9f0974345d0b1b36efaeabdc7550b1cc4f8 \ + --hash=sha256:7b47643c45e7619788c081d42e1d9d98c7c8a4933010a9967d097cc3c4c29f41 \ + --hash=sha256:80ed219ce6cb21a5b53ead0edf5b56b6d23de4cb95389ac606f47670474f4816 \ + --hash=sha256:82df4a8600999c4c0cb7d6614df1bbdb3c74732f63e79f78487893ffbed3d083 \ + --hash=sha256:8660112e9127a019969a23c878e1b4a419e8a6427f9a9050c19830f152628c8a \ + --hash=sha256:89a86c2b35460700d04b4d6461153ab39ee85af5a5385acac9563a8310e6320a \ + --hash=sha256:8d7bc25729bb6d96b44f49ad78fde0e27a1a867cb205322b7e5f5b49e04d6f1f \ + --hash=sha256:97e4f3d9b17d12e7c00cb1c29c0040044135cd5146838da4274615dbe0baae78 \ + --hash=sha256:a431deb6ffdfa551f7400b3a94fa4b964837e67f49e3c37aa26d90dc75970816 \ + --hash=sha256:a6a2d3d75d8698dee492f4af7ad07606d0734e581edf9e2ce2f74b6fce90f42e \ + --hash=sha256:ae5b41dbf7731b838021923edfbe3b5ccdec84d92d5795f5229c0d08d32509d9 \ + --hash=sha256:aff258af03dda9a990960a53759d10c3a9b936837c71fe2f3b581acd356b9121 \ + --hash=sha256:b216a15e13f6e763db40ac3beb74b588650bc030d10a78fde182b88d273b82b5 \ + --hash=sha256:b23b25b1243576b952689966205ef7d4285688068b966a1ca0e620bcb390d483 \ + --hash=sha256:b896637091cde69d170a89253dde9aee814b25ca204b7e213fd0a6462e666638 \ + --hash=sha256:d5f27b1d1b56470385faa2b2636fcb823e7ac5b5b734e0aa76b14637c66eb3b7 \ + --hash=sha256:d6ba33f39436191ece7ea2b3d0b4dff00af71acd5c6e6f1d6b7563aa7286e9f2 \ + --hash=sha256:d6c5e1df6f427d7a82606cf8f07cf3ba9fb3f366804b01e65f1f00f8df6b54f1 \ + --hash=sha256:e02f77b620ad6b36564fe41980865436912e21a3b1138cdde175cf24afde1bc5 \ + --hash=sha256:e72491d72870c3cb2f0d6f4174485533caec0e9ed7e717e2859b7cc7ff2ae1c4 \ + --hash=sha256:ea8d5cd689fa7225d81ae0a049ba03e0165f4ed9ca083b19a405be9ad0b36845 \ + --hash=sha256:eb5341fc7c53fdd95ac2415be77b1de854ab266488cff71174ebb007baf0e675 \ + --hash=sha256:edf0a66ce9517365c7dcfed597894d8dd1f27b59e550b77a089054101435213b \ + --hash=sha256:f225784812b2b57d340f2eb0d2cebef989dcc82c288f5553e28ee9767c7c8344 \ + --hash=sha256:f5fbb3b325c65010e04af206a9243e2df8606736c510c7f268aca6a93e5294a9 \ + --hash=sha256:f78cafa25731e0b5aa16fe20bea1abf643d4e853f6bfb8a64421b06b878e2b88 \ + --hash=sha256:fb639a0e65dce4a9cccbcbdd8ddd0c8c6ab10bca317b827a5c52ac3c3a4ad60a \ + --hash=sha256:ffb2f288f577a748cc23c65a818290755a4c2da1f87a40d7055b61a096d31e20 + # via -r third_party/py/requirements.in pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 @@ -379,6 +621,14 @@ pyelftools==0.26 \ --hash=sha256:86ac6cee19f6c945e8dedf78c6ee74f1112bd14da5a658d8c9d4103aed5756a2 \ --hash=sha256:cc0ea0de82b240a73ef4056fce44acbb4727dca7d66759371aff2bad457ed711 # via -r third_party/py/requirements.in +pygments==2.14.0 \ + --hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \ + --hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717 + # via readme-renderer +pyjwt[crypto]==2.6.0 \ + --hash=sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd \ + --hash=sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14 + # via django-allauth pynacl==1.3.0 \ --hash=sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255 \ --hash=sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c \ @@ -410,29 +660,104 @@ python-dateutil==2.8.0 \ # via # -r third_party/py/requirements.in # arrow + # hyperkitty +python3-openid==3.2.0 \ + --hash=sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf \ + --hash=sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b + # via django-allauth pytz==2022.7.1 \ --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a # via # -r third_party/py/requirements.in # django + # django-mailman3 + # djangorestframework + # hyperkitty +rcssmin==1.1.1 \ + --hash=sha256:271e3d2f8614a6d4637ed8fff3d90007f03e2a654cd9444f37d888797662ba72 \ + --hash=sha256:35da6a6999e9e2c5b0e691b42ed56cc479373e0ecab33ef5277dfecce625e44a \ + --hash=sha256:42576d95dfad53d77df2e68dfdec95b89b10fad320f241f1af3ca1438578254a \ + --hash=sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64 \ + --hash=sha256:705c9112d0ed54ea40aecf97e7fd29bdf0f1c46d278a32d8f957f31dde90778a \ + --hash=sha256:79421230dd67c37ec61ed9892813d2b839b68f2f48ef55c75f976e81701d60b4 \ + --hash=sha256:868215e1fd0e92a6122e0ed5973dfc7bb8330fe1e92274d05b2585253b38c0ca \ + --hash=sha256:8a26fec3c1e6b7a3765ccbaccc20fbb5c0ed3422cc381e01a2607f08d7621c44 \ + --hash=sha256:8fcfd10ae2a1c4ce231a33013f2539e07c3836bf17cc945cc25cc30bf8e68e45 \ + --hash=sha256:908fe072efd2432fb0975a61124609a8e05021367f6a3463d45f5e3e74c4fdda \ + --hash=sha256:914e589f40573035006913861ed2adc28fbe70082a8b6bff5be7ee430b7b5c2e \ + --hash=sha256:a04d58a2a21e9a089306d3f99c4b12bf5b656a79c198ef2321e80f8fd9afab06 \ + --hash=sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019 \ + --hash=sha256:c30f8bc839747b6da59274e0c6e4361915d66532e26448d589cb2b1846d7bf11 \ + --hash=sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877 \ + --hash=sha256:c7728e3b546b1b6ea08cab721e8e21409dbcc11b881d0b87d10b0be8930af2a2 \ + --hash=sha256:cf74d7ea5e191f0f344b354eed8b7c83eeafbd9a97bec3a579c3d26edf11b005 \ + --hash=sha256:d0afc6e7b64ef30d6dcde88830ec1a237b9f16a39f920a8fd159928684ccf8db \ + --hash=sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1 \ + --hash=sha256:e923c105100ab70abde1c01d3196ddd6b07255e32073685542be4e3a60870c8e \ + --hash=sha256:ee386bec6d62f8c814d65c011d604a7c82d24aa3f718facd66e850eea8d6a5a1 \ + --hash=sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f \ + --hash=sha256:f7a1fcdbafaacac0530da04edca4a44303baab430ea42e7d59aece4b3f3e9a51 + # via django-compressor +readme-renderer[md]==37.3 \ + --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ + --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 + # via postorius +redis==3.5.3 \ + --hash=sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2 \ + --hash=sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24 + # via django-q requests==2.22.0 \ --hash=sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4 \ --hash=sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31 # via # -r third_party/py/requirements.in + # django-allauth + # mailmanclient # requests-oauthlib requests-oauthlib==1.3.0 \ --hash=sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d \ --hash=sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a - # via -r third_party/py/requirements.in + # via + # -r third_party/py/requirements.in + # django-allauth +rjsmin==1.2.1 \ + --hash=sha256:113132a40ce7d03b2ced4fac215f0297338ed1c207394b739266efab7831988b \ + --hash=sha256:122aa52bcf7ad9f12728d309012d1308c6ecfe4d6b09ea867a110dcad7b7728c \ + --hash=sha256:145c6af8df42d8af102d0d39a6de2e5fa66aef9e38947cfb9d65377d1b9940b2 \ + --hash=sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411 \ + --hash=sha256:3453ee6d5e7a2723ec45c2909e2382371783400e8d51952b692884c6d850a3d0 \ + --hash=sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1 \ + --hash=sha256:35f21046504544e2941e04190ce24161255479133751550e36ddb3f4af0ecdca \ + --hash=sha256:5d67ec09da46a492186e35cabca02a0d092eda5ef5b408a419b99ee4acf28d5c \ + --hash=sha256:747bc9d3bc8a220f40858e6aad50b2ae2eb7f69c924d4fa3803b81be1c1ddd02 \ + --hash=sha256:7dd58b5ed88233bc61dc80b0ed87b93a1786031d9977c70d335221ef1ac5581a \ + --hash=sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064 \ + --hash=sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39 \ + --hash=sha256:8c340e251619c97571a5ade20f147f1f7e8664f66a2d6d7319e05e3ef6a4423c \ + --hash=sha256:99c074cd6a8302ff47118a9c3d086f89328dc8e5c4b105aa1f348fb85c765a30 \ + --hash=sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346 \ + --hash=sha256:bbd7a0abaa394afd951f5d4e05249d306fec1c9674bfee179787674dddd0bdb7 \ + --hash=sha256:bc5bc2f94e59bc81562c572b7f1bdd6bcec4f61168dc68a2993bad2d355b6e19 \ + --hash=sha256:bd1faedc425006d9e86b23837d164f01d105b7a8b66b767a9766d0014773db2a \ + --hash=sha256:ca90630b84fe94bb07739c3e3793e87d30c6ee450dde08653121f0d9153c8d0d \ + --hash=sha256:d332e44a1b21ad63401cc7eebc81157e3d982d5fb503bb4faaea5028068d71e9 \ + --hash=sha256:eb770aaf637919b0011c4eb87b9ac6317079fb9800eb17c90dda05fc9de4ebc3 \ + --hash=sha256:f0895b360dccf7e2d6af8762a52985e3fbaa56778de1bf6b20dbc96134253807 \ + --hash=sha256:f7cd33602ec0f393a0058e883284496bb4dbbdd34e0bbe23b594c8933ddf9b65 + # via django-compressor +robot-detection==0.4 \ + --hash=sha256:3d8fb72ca47164b8ce55e33bdda93742f62c348def7d3cc3b42b0ceb4795a2f5 + # via hyperkitty six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via - # cryptography + # bleach + # blessed # pynacl # python-dateutil + # robot-detection sqlalchemy==1.3.8 \ --hash=sha256:2f8ff566a4d3a92246d367f2e9cd6ed3edeef670dcd6dda6dfdc9efed88bcd80 # via @@ -450,6 +775,14 @@ urllib3==1.25.3 \ # via # -r third_party/py/requirements.in # requests +wcwidth==0.2.6 \ + --hash=sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e \ + --hash=sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0 + # via blessed +webencodings==0.5.1 \ + --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ + --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 + # via bleach werkzeug==0.15.5 \ --hash=sha256:87ae4e5b5366da2347eb3116c0e6c681a0e939a33b2805e2c0cbd282664932c4 \ --hash=sha256:a13b74dd3c45f758d4ebdb224be8f1ab8ef58b3c0ffc1783a8c7d9f4f50227e6 @@ -521,9 +854,9 @@ zope-interface==5.4.0 \ # via gevent # The following packages are considered to be unsafe in a requirements file: -setuptools==67.1.0 \ - --hash=sha256:a7687c12b444eaac951ea87a9627c4f904ac757e7abdc5aac32833234af90378 \ - --hash=sha256:e261cdf010c11a41cb5cb5f1bf3338a7433832029f559a6a7614bd42a967c300 +setuptools==68.2.0 \ + --hash=sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48 \ + --hash=sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8 # via # gevent # gunicorn