Update urls.py + settings.py

- modify paths to match package patches
- add SAML configuration

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
Georg Pfuetzenreuter 2022-12-18 16:41:24 +01:00
parent 86bc48f3e0
commit 7819d7980b
Signed by: Georg
GPG Key ID: 1ED2F138E7E6FF57
2 changed files with 131 additions and 28 deletions

View File

@ -12,9 +12,12 @@ import sentry_sdk
from pydantic import AnyUrl, BaseSettings, EmailStr, Field, validator from pydantic import AnyUrl, BaseSettings, EmailStr, Field, validator
from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.django import DjangoIntegration
from takahe import __version__ from takahe.takahe import __version__
BASE_DIR = Path(__file__).resolve().parent.parent import saml2
from saml2.saml import NAMEID_FORMAT_PERSISTENT
BASE_DIR = '/srv/takahe'
class CacheBackendUrl(AnyUrl): class CacheBackendUrl(AnyUrl):
@ -59,10 +62,10 @@ class Settings(BaseSettings):
#: The currently running environment, used for things such as sentry #: The currently running environment, used for things such as sentry
#: error reporting. #: error reporting.
ENVIRONMENT: Environments = "development" ENVIRONMENT: Environments = "production"
#: Should django run in debug mode? #: Should django run in debug mode?
DEBUG: bool = False DEBUG: bool = True
#: Set a secret key used for signing values such as sessions. Randomized #: Set a secret key used for signing values such as sessions. Randomized
#: by default, so you'll logout everytime the process restarts. #: by default, so you'll logout everytime the process restarts.
@ -99,7 +102,7 @@ class Settings(BaseSettings):
ERROR_EMAILS: list[EmailStr] | None = None ERROR_EMAILS: list[EmailStr] | None = None
MEDIA_URL: str = "/media/" MEDIA_URL: str = "/media/"
MEDIA_ROOT: str = str(BASE_DIR / "media") MEDIA_ROOT: str = os.path.join(BASE_DIR, "media")
MEDIA_BACKEND: MediaBackendUrl | None = None MEDIA_BACKEND: MediaBackendUrl | None = None
#: Maximum filesize when uploading images. Increasing this may increase memory utilization #: Maximum filesize when uploading images. Increasing this may increase memory utilization
@ -147,7 +150,7 @@ class Settings(BaseSettings):
class Config: class Config:
env_prefix = "TAKAHE_" env_prefix = "TAKAHE_"
env_file = str(BASE_DIR / TAKAHE_ENV_FILE) env_file = '/etc/sysconfig/takahe'
env_file_encoding = "utf-8" env_file_encoding = "utf-8"
# Case sensitivity doesn't work on Windows, so might as well be # Case sensitivity doesn't work on Windows, so might as well be
# consistent from the get-go. # consistent from the get-go.
@ -179,16 +182,17 @@ INSTALLED_APPS = [
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django_htmx", "django_htmx",
"corsheaders", "corsheaders",
"core", "takahe.core",
"activities", "takahe.activities",
"api", "takahe.api",
"mediaproxy", "takahe.mediaproxy",
"stator", "takahe.stator",
"users", "takahe.users",
"djangosaml2",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"core.middleware.SentryTaggingMiddleware", "takahe.core.middleware.SentryTaggingMiddleware",
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"corsheaders.middleware.CorsMiddleware", "corsheaders.middleware.CorsMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware",
@ -199,19 +203,20 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_htmx.middleware.HtmxMiddleware", "django_htmx.middleware.HtmxMiddleware",
"core.middleware.HeadersMiddleware", "takahe.core.middleware.HeadersMiddleware",
"core.middleware.ConfigLoadingMiddleware", "takahe.core.middleware.ConfigLoadingMiddleware",
"api.middleware.ApiTokenMiddleware", "takahe.api.middleware.ApiTokenMiddleware",
"users.middleware.IdentityMiddleware", "takahe.users.middleware.IdentityMiddleware",
"activities.middleware.EmojiDefaultsLoadingMiddleware", "takahe.activities.middleware.EmojiDefaultsLoadingMiddleware",
"djangosaml2.middleware.SamlSessionMiddleware",
] ]
ROOT_URLCONF = "takahe.urls" ROOT_URLCONF = "takahe.takahe.urls"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"], "DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"context_processors": [ "context_processors": [
@ -219,13 +224,13 @@ TEMPLATES = [
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"core.context.config_context", "takahe.core.context.config_context",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = "takahe.wsgi.application" WSGI_APPLICATION = "takahe.takahe.wsgi.application"
if SETUP.DATABASE_SERVER: if SETUP.DATABASE_SERVER:
DATABASES = { DATABASES = {
@ -272,7 +277,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = "users.User" AUTH_USER_MODEL = "users.User"
LOGIN_URL = "/auth/login/" LOGIN_URL = "/saml2/login/"
LOGOUT_URL = "/auth/logout/" LOGOUT_URL = "/auth/logout/"
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/" LOGOUT_REDIRECT_URL = "/"
@ -282,7 +287,7 @@ STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.AppDirectoriesFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder",
] ]
STATICFILES_DIRS = [BASE_DIR / "static"] STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
@ -290,7 +295,7 @@ SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
WHITENOISE_MAX_AGE = 3600 WHITENOISE_MAX_AGE = 3600
STATIC_ROOT = BASE_DIR / "static-collected" STATIC_ROOT = os.path.join(BASE_DIR, "static-collected")
ALLOWED_HOSTS = SETUP.ALLOWED_HOSTS ALLOWED_HOSTS = SETUP.ALLOWED_HOSTS
@ -355,14 +360,14 @@ if SETUP.MEDIA_BACKEND:
parsed = urllib.parse.urlparse(SETUP.MEDIA_BACKEND) parsed = urllib.parse.urlparse(SETUP.MEDIA_BACKEND)
query = urllib.parse.parse_qs(parsed.query) query = urllib.parse.parse_qs(parsed.query)
if parsed.scheme == "gs": if parsed.scheme == "gs":
DEFAULT_FILE_STORAGE = "core.uploads.TakaheGoogleCloudStorage" DEFAULT_FILE_STORAGE = "takahe.core.uploads.TakaheGoogleCloudStorage"
GS_BUCKET_NAME = parsed.path.lstrip("/") GS_BUCKET_NAME = parsed.path.lstrip("/")
GS_QUERYSTRING_AUTH = False GS_QUERYSTRING_AUTH = False
if parsed.hostname is not None: if parsed.hostname is not None:
port = parsed.port or 443 port = parsed.port or 443
GS_CUSTOM_ENDPOINT = f"https://{parsed.hostname}:{port}" GS_CUSTOM_ENDPOINT = f"https://{parsed.hostname}:{port}"
elif parsed.scheme == "s3": elif parsed.scheme == "s3":
DEFAULT_FILE_STORAGE = "core.uploads.TakaheS3Storage" DEFAULT_FILE_STORAGE = "takahe.core.uploads.TakaheS3Storage"
AWS_STORAGE_BUCKET_NAME = parsed.path.lstrip("/") AWS_STORAGE_BUCKET_NAME = parsed.path.lstrip("/")
AWS_QUERYSTRING_AUTH = False AWS_QUERYSTRING_AUTH = False
AWS_DEFAULT_ACL = "public-read" AWS_DEFAULT_ACL = "public-read"
@ -393,3 +398,97 @@ TAKAHE_USER_AGENT = (
f"python-httpx/{httpx.__version__} " f"python-httpx/{httpx.__version__} "
f"(Takahe/{__version__}; +https://{SETUP.MAIN_DOMAIN}/)" f"(Takahe/{__version__}; +https://{SETUP.MAIN_DOMAIN}/)"
) )
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'djangosaml2.backends.Saml2Backend',
)
SAML_SESSION_COOKIE_NAME = 'takahe_test_session'
SESSION_COOKIE_SECURE = False
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
SAML_ATTRIBUTE_MAPPING = {
'email': ('email', ),
}
SAML_CONFIG = {
# full path to the xmlsec1 binary programm
'xmlsec_binary': '/usr/bin/xmlsec1',
# your entity id, usually your subdomain plus the url to the metadata view
'entityid': 'https://__REPLACE_ME__/saml2/metadata/',
'attribute_map_dir': os.path.join(BASE_DIR, 'attribute-maps'),
'allow_unknown_attributes': False,
'service': {
'sp' : {
'name': 'Federated Takahe sample SP',
'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT,
'endpoints': {
'assertion_consumer_service': [
('https://__REPLACE_ME__/saml2/acs/',
saml2.BINDING_HTTP_POST),
],
'single_logout_service': [
('https://__REPLACE_ME__/saml2/ls/',
saml2.BINDING_HTTP_REDIRECT),
('https://__REPLACE_ME__/saml2/ls/post',
saml2.BINDING_HTTP_POST),
],
},
'signing_algorithm': saml2.xmldsig.SIG_RSA_SHA256,
'digest_algorithm': saml2.xmldsig.DIGEST_SHA256,
'force_authn': False,
'name_id_format_allow_create': False,
'required_attributes': ['uid',
'email'],
'want_response_signed': True,
'authn_requests_signed': True,
'logout_requests_signed': True,
'want_assertions_signed': True,
'only_use_keys_in_metadata': True,
'allow_unsolicited': False,
'metadata': {
# in production, use local file
# 'local': [os.path.join(BASE_DIR, 'remote_metadata.xml')],
'remote': [{"url": "https://libsso.net/realms/LibertaCasa/protocol/saml/descriptor"},],
},
'debug': 1,
'key_file': '__REPLACE_ME__', # private part
'cert_file': '__REPLACE_ME__', # public part
# Encryption
# 'encryption_keypairs': [{
# 'key_file': '__REPLACE_ME__', # private part
# 'cert_file': '__REPLACE_ME__', # public part
# }],
}
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'DEBUG',
},
}

View File

@ -9,6 +9,9 @@ from mediaproxy import views as mediaproxy
from stator import views as stator from stator import views as stator
from users.views import activitypub, admin, auth, identity, report, settings from users.views import activitypub, admin, auth, identity, report, settings
from django.conf.urls import include
from django.views.generic.base import RedirectView
urlpatterns = [ urlpatterns = [
path("", core.homepage), path("", core.homepage),
path("manifest.json", core.AppManifest.as_view()), path("manifest.json", core.AppManifest.as_view()),
@ -174,7 +177,7 @@ urlpatterns = [
path("@<handle>/posts/<int:post_id>/report/", report.SubmitReport.as_view()), path("@<handle>/posts/<int:post_id>/report/", report.SubmitReport.as_view()),
path("@<handle>/posts/<int:post_id>/edit/", compose.Compose.as_view()), path("@<handle>/posts/<int:post_id>/edit/", compose.Compose.as_view()),
# Authentication # Authentication
path("auth/login/", auth.Login.as_view(), name="login"), path("auth/login/", RedirectView.as_view(url='/saml2/login', permanent=False), name='login'),
path("auth/logout/", auth.Logout.as_view(), name="logout"), path("auth/logout/", auth.Logout.as_view(), name="logout"),
path("auth/signup/", auth.Signup.as_view(), name="signup"), path("auth/signup/", auth.Signup.as_view(), name="signup"),
path("auth/reset/", auth.TriggerReset.as_view(), name="trigger_reset"), path("auth/reset/", auth.TriggerReset.as_view(), name="trigger_reset"),
@ -248,4 +251,5 @@ urlpatterns = [
core.custom_static_serve, core.custom_static_serve,
kwargs={"document_root": djsettings.MEDIA_ROOT}, kwargs={"document_root": djsettings.MEDIA_ROOT},
), ),
path('saml2/', include('djangosaml2.urls')),
] ]