lasuite-docs: init at 3.3.0

+259
+109
pkgs/by-name/la/lasuite-docs/environment_variables.patch
···
··· 1 + From dd7d54e64bbdb853ff60162908f142cb34034cdd Mon Sep 17 00:00:00 2001 2 + From: soyouzpanda <soyouzpanda@soyouzpanda.fr> 3 + Date: Mon, 28 Apr 2025 18:18:39 +0200 4 + Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8(backend)=20support=20`=5FFILE`=20?= 5 + =?UTF-8?q?environment=20variables=20for=20secrets?= 6 + MIME-Version: 1.0 7 + Content-Type: text/plain; charset=UTF-8 8 + Content-Transfer-Encoding: 8bit 9 + 10 + Allow configuration variables that handles secrets, like 11 + `DJANGO_SECRET_KEY` to be able to read from a file which is given 12 + through an environment file. 13 + 14 + For example, if `DJANGO_SECRET_KEY_FILE` is set to 15 + `/var/lib/docs/django-secret-key`, the value of `DJANGO_SECRET_KEY` will 16 + be the content of `/var/lib/docs/django-secret-key`. 17 + --- 18 + src/backend/impress/settings.py | 19 ++++++++++--------- 19 + 1 files changed, 10 insertions(+), 9 deletions(-) 20 + 21 + diff --git a/impress/settings.py b/impress/settings.py 22 + index 571d7052..23c75a98 100755 23 + --- a/impress/settings.py 24 + +++ b/impress/settings.py 25 + @@ -18,6 +18,7 @@ from django.utils.translation import gettext_lazy as _ 26 + 27 + import sentry_sdk 28 + from configurations import Configuration, values 29 + +from lasuite.configuration.values import SecretFileValue 30 + from sentry_sdk.integrations.django import DjangoIntegration 31 + from sentry_sdk.integrations.logging import ignore_logger 32 + 33 + @@ -65,7 +66,7 @@ class Base(Configuration): 34 + 35 + # Security 36 + ALLOWED_HOSTS = values.ListValue([]) 37 + - SECRET_KEY = values.Value(None) 38 + + SECRET_KEY = SecretFileValue(None) 39 + SERVER_TO_SERVER_API_TOKENS = values.ListValue([]) 40 + 41 + # Application definition 42 + @@ -84,7 +85,7 @@ class Base(Configuration): 43 + "impress", environ_name="DB_NAME", environ_prefix=None 44 + ), 45 + "USER": values.Value("dinum", environ_name="DB_USER", environ_prefix=None), 46 + - "PASSWORD": values.Value( 47 + + "PASSWORD": SecretFileValue( 48 + "pass", environ_name="DB_PASSWORD", environ_prefix=None 49 + ), 50 + "HOST": values.Value( 51 + @@ -122,10 +123,10 @@ class Base(Configuration): 52 + AWS_S3_ENDPOINT_URL = values.Value( 53 + environ_name="AWS_S3_ENDPOINT_URL", environ_prefix=None 54 + ) 55 + - AWS_S3_ACCESS_KEY_ID = values.Value( 56 + + AWS_S3_ACCESS_KEY_ID = SecretFileValue( 57 + environ_name="AWS_S3_ACCESS_KEY_ID", environ_prefix=None 58 + ) 59 + - AWS_S3_SECRET_ACCESS_KEY = values.Value( 60 + + AWS_S3_SECRET_ACCESS_KEY = SecretFileValue( 61 + environ_name="AWS_S3_SECRET_ACCESS_KEY", environ_prefix=None 62 + ) 63 + AWS_S3_REGION_NAME = values.Value( 64 + @@ -384,7 +385,7 @@ class Base(Configuration): 65 + EMAIL_BRAND_NAME = values.Value(None) 66 + EMAIL_HOST = values.Value(None) 67 + EMAIL_HOST_USER = values.Value(None) 68 + - EMAIL_HOST_PASSWORD = values.Value(None) 69 + + EMAIL_HOST_PASSWORD = SecretFileValue(None) 70 + EMAIL_LOGO_IMG = values.Value(None) 71 + EMAIL_PORT = values.PositiveIntegerValue(None) 72 + EMAIL_USE_TLS = values.BooleanValue(False) 73 + @@ -407,7 +408,7 @@ class Base(Configuration): 74 + COLLABORATION_API_URL = values.Value( 75 + None, environ_name="COLLABORATION_API_URL", environ_prefix=None 76 + ) 77 + - COLLABORATION_SERVER_SECRET = values.Value( 78 + + COLLABORATION_SERVER_SECRET = SecretFileValue( 79 + None, environ_name="COLLABORATION_SERVER_SECRET", environ_prefix=None 80 + ) 81 + COLLABORATION_WS_URL = values.Value( 82 + @@ -477,7 +478,7 @@ class Base(Configuration): 83 + OIDC_RP_CLIENT_ID = values.Value( 84 + "impress", environ_name="OIDC_RP_CLIENT_ID", environ_prefix=None 85 + ) 86 + - OIDC_RP_CLIENT_SECRET = values.Value( 87 + + OIDC_RP_CLIENT_SECRET = SecretFileValue( 88 + None, 89 + environ_name="OIDC_RP_CLIENT_SECRET", 90 + environ_prefix=None, 91 + @@ -592,7 +593,7 @@ class Base(Configuration): 92 + AI_FEATURE_ENABLED = values.BooleanValue( 93 + default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None 94 + ) 95 + - AI_API_KEY = values.Value(None, environ_name="AI_API_KEY", environ_prefix=None) 96 + + AI_API_KEY = SecretFileValue(None, environ_name="AI_API_KEY", environ_prefix=None) 97 + AI_BASE_URL = values.Value(None, environ_name="AI_BASE_URL", environ_prefix=None) 98 + AI_MODEL = values.Value(None, environ_name="AI_MODEL", environ_prefix=None) 99 + AI_ALLOW_REACH_FROM = values.Value( 100 + @@ -613,7 +614,7 @@ class Base(Configuration): 101 + } 102 + 103 + # Y provider microservice 104 + - Y_PROVIDER_API_KEY = values.Value( 105 + + Y_PROVIDER_API_KEY = SecretFileValue( 106 + environ_name="Y_PROVIDER_API_KEY", 107 + environ_prefix=None, 108 + ) 109 +
+114
pkgs/by-name/la/lasuite-docs/package.nix
···
··· 1 + { 2 + lib, 3 + python3, 4 + fetchFromGitHub, 5 + }: 6 + let 7 + python = python3.override { 8 + self = python3; 9 + packageOverrides = self: super: { 10 + django = super.django_5_2; 11 + }; 12 + }; 13 + in 14 + 15 + python.pkgs.buildPythonApplication rec { 16 + pname = "lasuite-docs"; 17 + version = "3.3.0"; 18 + pyproject = true; 19 + 20 + src = fetchFromGitHub { 21 + owner = "suitenumerique"; 22 + repo = "docs"; 23 + tag = "v${version}"; 24 + hash = "sha256-SLTNkK578YhsDtVBS4vH0E/rXx+rXZIyXMhqwr95QEA="; 25 + }; 26 + 27 + sourceRoot = "source/src/backend"; 28 + 29 + patches = [ 30 + # Support for $ENVIRONMENT_VARIABLE_FILE to be able to pass secret files 31 + # See: https://github.com/suitenumerique/docs/pull/912 32 + ./environment_variables.patch 33 + # Support configuration throught environment variables for SECURE_* 34 + ./secure_settings.patch 35 + ]; 36 + 37 + build-system = with python.pkgs; [ setuptools ]; 38 + 39 + dependencies = with python.pkgs; [ 40 + beautifulsoup4 41 + boto3 42 + celery 43 + django 44 + django-configurations 45 + django-cors-headers 46 + django-countries 47 + django-extensions 48 + django-filter 49 + django-lasuite 50 + django-parler 51 + django-redis 52 + django-storages 53 + django-timezone-field 54 + django-treebeard 55 + djangorestframework 56 + drf-spectacular 57 + drf-spectacular-sidecar 58 + dockerflow 59 + easy-thumbnails 60 + factory-boy 61 + gunicorn 62 + jsonschema 63 + lxml 64 + markdown 65 + mozilla-django-oidc 66 + nested-multipart-parser 67 + openai 68 + psycopg 69 + pycrdt 70 + pyjwt 71 + pyopenssl 72 + python-magic 73 + redis 74 + requests 75 + sentry-sdk 76 + whitenoise 77 + ]; 78 + 79 + pythonRelaxDeps = true; 80 + 81 + postBuild = '' 82 + export DATA_DIR=$(pwd)/data 83 + ${python.pythonOnBuildForHost.interpreter} manage.py collectstatic --no-input --clear 84 + ''; 85 + 86 + postInstall = 87 + let 88 + pythonPath = python.pkgs.makePythonPath dependencies; 89 + in 90 + '' 91 + mkdir -p $out/{bin,share} 92 + 93 + cp ./manage.py $out/bin/.manage.py 94 + cp -r data/static $out/share 95 + chmod +x $out/bin/.manage.py 96 + 97 + makeWrapper $out/bin/.manage.py $out/bin/docs \ 98 + --prefix PYTHONPATH : "${pythonPath}" 99 + makeWrapper ${lib.getExe python.pkgs.celery} $out/bin/celery \ 100 + --prefix PYTHONPATH : "${pythonPath}:$out/${python.sitePackages}" 101 + makeWrapper ${lib.getExe python.pkgs.gunicorn} $out/bin/gunicorn \ 102 + --prefix PYTHONPATH : "${pythonPath}:$out/${python.sitePackages}" 103 + ''; 104 + 105 + meta = { 106 + description = "A collaborative note taking, wiki and documentation platform that scales. Built with Django and React. Opensource alternative to Notion or Outline"; 107 + homepage = "https://github.com/suitenumerique/docs"; 108 + changelog = "https://github.com/suitenumerique/docs/blob/${src.tag}/CHANGELOG.md"; 109 + license = lib.licenses.mit; 110 + maintainers = with lib.maintainers; [ soyouzpanda ]; 111 + mainProgram = "docs"; 112 + platforms = lib.platforms.all; 113 + }; 114 + }
+36
pkgs/by-name/la/lasuite-docs/secure_settings.patch
···
··· 1 + diff --git a/impress/settings.py b/impress/settings.py 2 + index 9d825095..518aca7f 100755 3 + --- a/impress/settings.py 4 + +++ b/impress/settings.py 5 + @@ -822,19 +822,24 @@ class Production(Base): 6 + # 7 + # In other cases, you should comment the following line to avoid security issues. 8 + # SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") 9 + - SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") 10 + - SECURE_HSTS_SECONDS = 60 11 + - SECURE_HSTS_PRELOAD = True 12 + - SECURE_HSTS_INCLUDE_SUBDOMAINS = True 13 + - SECURE_SSL_REDIRECT = True 14 + + SECURE_PROXY_SSL_HEADER = values.TupleValue(("HTTP_X_FORWARDED_PROTO", "https"), 15 + + environ_name="SECURE_PROXY_SSL_HEADER") 16 + + SECURE_HSTS_SECONDS = values.IntegerValue( 17 + + 60, environ_name="SECURE_HSTS_SECONDS") 18 + + SECURE_HSTS_PRELOAD = values.BooleanValue( 19 + + True, environ_name="SECURE_HSTS_PRELOAD") 20 + + SECURE_HSTS_INCLUDE_SUBDOMAINS = values.BooleanValue( 21 + + True, environ_name="SECURE_HSTS_INCLUDE_SUBDOMAINS") 22 + + SECURE_SSL_REDIRECT = values.BooleanValue( 23 + + True, environ_name="SECURE_SSL_REDIRECT") 24 + SECURE_REDIRECT_EXEMPT = [ 25 + "^__lbheartbeat__", 26 + "^__heartbeat__", 27 + ] 28 + 29 + # Modern browsers require to have the `secure` attribute on cookies with `Samesite=none` 30 + - CSRF_COOKIE_SECURE = True 31 + - SESSION_COOKIE_SECURE = True 32 + + CSRF_COOKIE_SECURE = values.BooleanValue(True, environ_name="CSRF_COOKIE_SECURE") 33 + + SESSION_COOKIE_SECURE = values.BooleanValue(True, environ_name="SESSION_COOKIE_SECURE") 34 + 35 + # Privacy 36 + SECURE_REFERRER_POLICY = "same-origin"