diff options
Diffstat (limited to 'takahe')
| -rw-r--r-- | takahe/settings.py | 151 | ||||
| -rw-r--r-- | takahe/urls.py | 6 | 
2 files changed, 130 insertions, 27 deletions
| diff --git a/takahe/settings.py b/takahe/settings.py index d284e0e..6623d01 100644 --- a/takahe/settings.py +++ b/takahe/settings.py @@ -12,9 +12,12 @@ import sentry_sdk  from pydantic import AnyUrl, BaseSettings, EmailStr, Field, validator  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): @@ -59,7 +62,7 @@ class Settings(BaseSettings):      #: The currently running environment, used for things such as sentry      #: error reporting. -    ENVIRONMENT: Environments = "development" +    ENVIRONMENT: Environments = "production"      #: Should django run in debug mode?      DEBUG: bool = False @@ -99,7 +102,7 @@ class Settings(BaseSettings):      ERROR_EMAILS: list[EmailStr] | None = None      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      #: Maximum filesize when uploading images. Increasing this may increase memory utilization @@ -147,7 +150,7 @@ class Settings(BaseSettings):      class Config:          env_prefix = "TAKAHE_" -        env_file = str(BASE_DIR / TAKAHE_ENV_FILE) +        env_file = '/etc/sysconfig/takahe'          env_file_encoding = "utf-8"          # Case sensitivity doesn't work on Windows, so might as well be          # consistent from the get-go. @@ -179,16 +182,17 @@ INSTALLED_APPS = [      "django.contrib.staticfiles",      "django_htmx",      "corsheaders", -    "core", -    "activities", -    "api", -    "mediaproxy", -    "stator", -    "users", +    "takahe.core", +    "takahe.activities", +    "takahe.api", +    "takahe.mediaproxy", +    "takahe.stator", +    "takahe.users", +    "djangosaml2",  ]  MIDDLEWARE = [ -    "core.middleware.SentryTaggingMiddleware", +    "takahe.core.middleware.SentryTaggingMiddleware",      "django.middleware.security.SecurityMiddleware",      "corsheaders.middleware.CorsMiddleware",      "whitenoise.middleware.WhiteNoiseMiddleware", @@ -199,19 +203,20 @@ MIDDLEWARE = [      "django.contrib.messages.middleware.MessageMiddleware",      "django.middleware.clickjacking.XFrameOptionsMiddleware",      "django_htmx.middleware.HtmxMiddleware", -    "core.middleware.HeadersMiddleware", -    "core.middleware.ConfigLoadingMiddleware", -    "api.middleware.ApiTokenMiddleware", -    "users.middleware.IdentityMiddleware", -    "activities.middleware.EmojiDefaultsLoadingMiddleware", +    "takahe.core.middleware.HeadersMiddleware", +    "takahe.core.middleware.ConfigLoadingMiddleware", +    "takahe.api.middleware.ApiTokenMiddleware", +    "takahe.users.middleware.IdentityMiddleware", +    "takahe.activities.middleware.EmojiDefaultsLoadingMiddleware", +    "djangosaml2.middleware.SamlSessionMiddleware",  ] -ROOT_URLCONF = "takahe.urls" +ROOT_URLCONF = "takahe.takahe.urls"  TEMPLATES = [      {          "BACKEND": "django.template.backends.django.DjangoTemplates", -        "DIRS": [BASE_DIR / "templates"], +        "DIRS": [os.path.join(BASE_DIR, "templates")],          "APP_DIRS": True,          "OPTIONS": {              "context_processors": [ @@ -219,13 +224,13 @@ TEMPLATES = [                  "django.template.context_processors.request",                  "django.contrib.auth.context_processors.auth",                  "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:      DATABASES = { @@ -272,7 +277,7 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"  AUTH_USER_MODEL = "users.User" -LOGIN_URL = "/auth/login/" +LOGIN_URL = "/saml2/login/"  LOGOUT_URL = "/auth/logout/"  LOGIN_REDIRECT_URL = "/"  LOGOUT_REDIRECT_URL = "/" @@ -282,7 +287,7 @@ STATICFILES_FINDERS = [      "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" @@ -290,7 +295,7 @@ SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"  WHITENOISE_MAX_AGE = 3600 -STATIC_ROOT = BASE_DIR / "static-collected" +STATIC_ROOT = os.path.join(BASE_DIR, "static-collected")  ALLOWED_HOSTS = SETUP.ALLOWED_HOSTS @@ -355,14 +360,14 @@ if SETUP.MEDIA_BACKEND:      parsed = urllib.parse.urlparse(SETUP.MEDIA_BACKEND)      query = urllib.parse.parse_qs(parsed.query)      if parsed.scheme == "gs": -        DEFAULT_FILE_STORAGE = "core.uploads.TakaheGoogleCloudStorage" +        DEFAULT_FILE_STORAGE = "takahe.core.uploads.TakaheGoogleCloudStorage"          GS_BUCKET_NAME = parsed.path.lstrip("/")          GS_QUERYSTRING_AUTH = False          if parsed.hostname is not None:              port = parsed.port or 443              GS_CUSTOM_ENDPOINT = f"https://{parsed.hostname}:{port}"      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_QUERYSTRING_AUTH = False          AWS_DEFAULT_ACL = "public-read" @@ -393,3 +398,97 @@ TAKAHE_USER_AGENT = (      f"python-httpx/{httpx.__version__} "      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': 0, + +  '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': 'INFO', +    }, +} diff --git a/takahe/urls.py b/takahe/urls.py index d3572a9..ea205c5 100644 --- a/takahe/urls.py +++ b/takahe/urls.py @@ -9,6 +9,9 @@ from mediaproxy import views as mediaproxy  from stator import views as stator  from users.views import activitypub, admin, auth, identity, report, settings +from django.conf.urls import include +from django.views.generic.base import RedirectView +  urlpatterns = [      path("", core.homepage),      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>/edit/", compose.Compose.as_view()),      # 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/signup/", auth.Signup.as_view(), name="signup"),      path("auth/reset/", auth.TriggerReset.as_view(), name="trigger_reset"), @@ -248,4 +251,5 @@ urlpatterns = [          core.custom_static_serve,          kwargs={"document_root": djsettings.MEDIA_ROOT},      ), +    path('saml2/', include('djangosaml2.urls')),  ] | 
