diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md index f9a5846c1f384..a62387cb6e56e 100644 --- a/nixos/doc/manual/release-notes/rl-2411.section.md +++ b/nixos/doc/manual/release-notes/rl-2411.section.md @@ -529,6 +529,16 @@ - The kubelet configuration file can now be amended with arbitrary additional content using the `services.kubernetes.kubelet.extraConfig` option. +- The `services.seafile` module was updated to major version 11. + - As part of this upgrade, the database backend will be migrated to MySQL. + This process should be automatic, but in case of a botched migration, + old sqlite files are not removed and can be used to manually migrate the database. + - Additionally, the updated CSRF protection may prevent some users from logging in. + Specific origin addresses can be whitelisted using the `services.seafile.seahubExtraConf` option + (e.g. `services.seafile.seahubExtraConf = ''CSRF_TRUSTED_ORIGINS = ["https://example.com"]'';`). + Note that first solution of the [official FAQ answer](https://cloud.seatable.io/dtable/external-links/7b976c85f504491cbe8e/?tid=0000&vid=0000&row-id=BQhH-2HSQs68Nq2EW91DBA) + is not allowed by the `services.nginx` module's config-checker. + - To facilitate dependency injection, the `imgui` package now builds a static archive using vcpkg' CMake rules. The derivation now installs "impl" headers selectively instead of by a wildcard. Use `imgui.src` if you just want to access the unpacked sources. diff --git a/nixos/modules/services/networking/seafile.nix b/nixos/modules/services/networking/seafile.nix index 486bc145cd5d6..356bcfe04419d 100644 --- a/nixos/modules/services/networking/seafile.nix +++ b/nixos/modules/services/networking/seafile.nix @@ -1,19 +1,42 @@ -{ config, lib, pkgs, ... }: -with lib; +{ + config, + lib, + pkgs, + ... +}: let cfg = config.services.seafile; settingsFormat = pkgs.formats.ini { }; - ccnetConf = settingsFormat.generate "ccnet.conf" cfg.ccnetSettings; + ccnetConf = settingsFormat.generate "ccnet.conf" ( + lib.attrsets.recursiveUpdate { + Database = { + ENGINE = "mysql"; + UNIX_SOCKET = "/var/run/mysqld/mysqld.sock"; + DB = "ccnet_db"; + CONNECTION_CHARSET = "utf8"; + }; + } cfg.ccnetSettings + ); - seafileConf = settingsFormat.generate "seafile.conf" cfg.seafileSettings; + seafileConf = settingsFormat.generate "seafile.conf" ( + lib.attrsets.recursiveUpdate { + database = { + type = "mysql"; + unix_socket = "/var/run/mysqld/mysqld.sock"; + db_name = "seafile_db"; + connection_charset = "utf8"; + }; + } cfg.seafileSettings + ); seahubSettings = pkgs.writeText "seahub_settings.py" '' FILE_SERVER_ROOT = '${cfg.ccnetSettings.General.SERVICE_URL}/seafhttp' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '${seahubDir}/seahub.db', + 'ENGINE': 'django.db.backends.mysql', + 'NAME' : 'seahub_db', + 'HOST' : '/var/run/mysqld/mysqld.sock', } } MEDIA_ROOT = '${seahubDir}/media/' @@ -21,23 +44,25 @@ let SERVICE_URL = '${cfg.ccnetSettings.General.SERVICE_URL}' + CSRF_TRUSTED_ORIGINS = ["${cfg.ccnetSettings.General.SERVICE_URL}"] + with open('${seafRoot}/.seahubSecret') as f: SECRET_KEY = f.readline().rstrip() ${cfg.seahubExtraConf} ''; - seafRoot = "/var/lib/seafile"; # hardcode it due to dynamicuser + seafRoot = "/var/lib/seafile"; ccnetDir = "${seafRoot}/ccnet"; - dataDir = "${seafRoot}/data"; seahubDir = "${seafRoot}/seahub"; + defaultUser = "seafile"; in { ###### Interface - options.services.seafile = { + options.services.seafile = with lib; { enable = mkEnableOption "Seafile server"; ccnetSettings = mkOption { @@ -47,7 +72,7 @@ in options = { General = { SERVICE_URL = mkOption { - type = types.str; + type = types.singleLineStr; example = "https://www.example.com"; description = '' Seahub public URL. @@ -78,11 +103,17 @@ in ''; }; host = mkOption { - type = types.str; - default = "127.0.0.1"; - example = "0.0.0.0"; + type = types.singleLineStr; + default = "ipv4:127.0.0.1"; + example = "unix:/run/seafile/server.sock"; description = '' - The binding address used by seafile fileserver. + The bind address used by seafile fileserver. + + The addr can be defined as one of the following: + - ipv6: for binding to an IPv6 address. + - unix: for binding to a unix named socket + - ipv4: for binding to an ipv4 address + Otherwise the addr is assumed to be ipv4. ''; }; }; @@ -96,6 +127,19 @@ in ''; }; + seahubAddress = mkOption { + type = types.singleLineStr; + default = "unix:/run/seahub/gunicorn.sock"; + example = "[::1]:8083"; + description = '' + Which address to bind the seahub server to, of the form: + - HOST + - HOST:PORT + - unix:PATH. + IPv6 HOSTs must be wrapped in brackets. + ''; + }; + workers = mkOption { type = types.int; default = 4; @@ -107,7 +151,7 @@ in adminEmail = mkOption { example = "john@example.com"; - type = types.str; + type = types.singleLineStr; description = '' Seafile Seahub Admin Account Email. ''; @@ -115,17 +159,79 @@ in initialAdminPassword = mkOption { example = "someStrongPass"; - type = types.str; + type = types.singleLineStr; description = '' Seafile Seahub Admin Account initial password. - Should be change via Seahub web front-end. + Should be changed via Seahub web front-end. ''; }; - seafilePackage = mkPackageOption pkgs "seafile-server" { }; + seahubPackage = mkPackageOption pkgs "seahub" { }; + + user = mkOption { + type = types.singleLineStr; + default = defaultUser; + description = "User account under which seafile runs."; + }; + + group = mkOption { + type = types.singleLineStr; + default = defaultUser; + description = "Group under which seafile runs."; + }; + + dataDir = mkOption { + type = types.path; + default = "${seafRoot}/data"; + description = "Path in which to store user data"; + }; + + gc = { + enable = mkEnableOption "automatic garbage collection on stored data blocks"; + + dates = mkOption { + type = types.listOf types.singleLineStr; + default = [ "Sun 03:00:00" ]; + description = '' + When to run garbage collection on stored data blocks. + The time format is described in {manpage}`systemd.time(7)`. + ''; + }; + + randomizedDelaySec = mkOption { + default = "0"; + type = types.singleLineStr; + example = "45min"; + description = '' + Add a randomized delay before each garbage collection. + The delay will be chosen between zero and this value. + This value must be a time span in the format specified by + {manpage}`systemd.time(7)` + ''; + }; + + persistent = mkOption { + default = true; + type = types.bool; + example = false; + description = '' + Takes a boolean argument. If true, the time when the service + unit was last triggered is stored on disk. When the timer is + activated, the service unit is triggered immediately if it + would have been triggered at least once during the time when + the timer was inactive. Such triggering is nonetheless + subject to the delay imposed by RandomizedDelaySec=. This is + useful to catch up on missed runs of the service when the + system was powered down. + ''; + }; + }; seahubExtraConf = mkOption { default = ""; + example = '' + CSRF_TRUSTED_ORIGINS = ["https://example.com"] + ''; type = types.lines; description = '' Extra config to append to `seahub_settings.py` file. @@ -137,12 +243,40 @@ in ###### Implementation - config = mkIf cfg.enable { + config = lib.mkIf cfg.enable { + services.mysql = { + enable = true; + package = lib.mkDefault pkgs.mariadb; + ensureDatabases = [ + "ccnet_db" + "seafile_db" + "seahub_db" + ]; + ensureUsers = [ + { + name = cfg.user; + ensurePermissions = { + "ccnet_db.*" = "ALL PRIVILEGES"; + "seafile_db.*" = "ALL PRIVILEGES"; + "seahub_db.*" = "ALL PRIVILEGES"; + }; + } + ]; + }; environment.etc."seafile/ccnet.conf".source = ccnetConf; environment.etc."seafile/seafile.conf".source = seafileConf; environment.etc."seafile/seahub_settings.py".source = seahubSettings; + users.users = lib.optionalAttrs (cfg.user == defaultUser) { + "${defaultUser}" = { + group = cfg.group; + isSystemUser = true; + }; + }; + + users.groups = lib.optionalAttrs (cfg.group == defaultUser) { "${defaultUser}" = { }; }; + systemd.targets.seafile = { wantedBy = [ "multi-user.target" ]; description = "Seafile components"; @@ -150,10 +284,12 @@ in systemd.services = let - securityOptions = { + serviceOptions = { ProtectHome = true; PrivateUsers = true; PrivateDevices = true; + PrivateTmp = true; + ProtectSystem = "strict"; ProtectClock = true; ProtectHostname = true; ProtectProc = "invisible"; @@ -162,36 +298,49 @@ in ProtectKernelLogs = true; ProtectControlGroups = true; RestrictNamespaces = true; + RemoveIPC = true; LockPersonality = true; RestrictRealtime = true; RestrictSUIDSGID = true; + NoNewPrivileges = true; MemoryDenyWriteExecute = true; SystemCallArchitectures = "native"; - RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" ]; + RestrictAddressFamilies = [ + "AF_UNIX" + "AF_INET" + ]; + + User = cfg.user; + Group = cfg.group; + StateDirectory = "seafile"; + RuntimeDirectory = "seafile"; + LogsDirectory = "seafile"; + ConfigurationDirectory = "seafile"; + ReadWritePaths = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; }; in { seaf-server = { description = "Seafile server"; partOf = [ "seafile.target" ]; - after = [ "network.target" ]; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + requires = [ "mysql.service" ]; + after = [ + "network.target" + "mysql.service" + ]; wantedBy = [ "seafile.target" ]; - restartTriggers = [ ccnetConf seafileConf ]; - path = [ pkgs.sqlite ]; - serviceConfig = securityOptions // { - User = "seafile"; - Group = "seafile"; - DynamicUser = true; - StateDirectory = "seafile"; - RuntimeDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; + restartTriggers = [ + ccnetConf + seafileConf + ]; + serviceConfig = serviceOptions // { ExecStart = '' - ${cfg.seafilePackage}/bin/seaf-server \ + ${lib.getExe cfg.seahubPackage.seafile-server} \ --foreground \ -F /etc/seafile \ -c ${ccnetDir} \ - -d ${dataDir} \ + -d ${cfg.dataDir} \ -l /var/log/seafile/server.log \ -P /run/seafile/server.pid \ -p /run/seafile @@ -199,100 +348,201 @@ in }; preStart = '' if [ ! -f "${seafRoot}/server-setup" ]; then - mkdir -p ${dataDir}/library-template - mkdir -p ${ccnetDir}/{GroupMgr,misc,OrgMgr,PeerMgr} - sqlite3 ${ccnetDir}/GroupMgr/groupmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/groupmgr.sql" - sqlite3 ${ccnetDir}/misc/config.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/config.sql" - sqlite3 ${ccnetDir}/OrgMgr/orgmgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/org.sql" - sqlite3 ${ccnetDir}/PeerMgr/usermgr.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/user.sql" - sqlite3 ${dataDir}/seafile.db ".read ${cfg.seafilePackage}/share/seafile/sql/sqlite/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + mkdir -p ${cfg.dataDir}/library-template + # Load schema on first install + ${pkgs.mariadb.client}/bin/mysql --database=ccnet_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/ccnet.sql + ${pkgs.mariadb.client}/bin/mysql --database=seafile_db < ${cfg.seahubPackage.seafile-server}/share/seafile/sql/mysql/seafile.sql + echo "${cfg.seahubPackage.seafile-server.version}-mysql" > "${seafRoot}"/server-setup + echo Loaded MySQL schemas for first install fi # checking for upgrades and handling them installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) - pkgMajor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f1) - pkgMinor=$(echo "${cfg.seafilePackage.version}" | cut -d"." -f2) + pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1) + pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2) if [[ $installedMajor == $pkgMajor && $installedMinor == $pkgMinor ]]; then : - elif [[ $installedMajor == 8 && $installedMinor == 0 && $pkgMajor == 9 && $pkgMinor == 0 ]]; then - # Upgrade from 8.0 to 9.0 - sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/9.0.0/sqlite3/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup - elif [[ $installedMajor == 9 && $installedMinor == 0 && $pkgMajor == 10 && $pkgMinor == 0 ]]; then - # Upgrade from 9.0 to 10.0 - sqlite3 ${dataDir}/seafile.db ".read ${pkgs.seahub}/scripts/upgrade/sql/10.0.0/sqlite3/seafile.sql" - echo "${cfg.seafilePackage.version}-sqlite" > "${seafRoot}"/server-setup + elif [[ $installedMajor == 10 && $installedMinor == 0 && $pkgMajor == 11 && $pkgMinor == 0 ]]; then + # Upgrade from 10.0 to 11.0: migrate to mysql + echo Migrating from version 10 to 11 + + # From https://github.com/haiwen/seahub/blob/e12f941bfef7191795d8c72a7d339c01062964b2/scripts/sqlite2mysql.sh + + echo Migrating ccnet database to MySQL + ${lib.getExe pkgs.sqlite} ${ccnetDir}/PeerMgr/usermgr.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py > ${ccnetDir}/ccnet.sql + ${lib.getExe pkgs.sqlite} ${ccnetDir}/GroupMgr/groupmgr.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py >> ${ccnetDir}/ccnet.sql + sed 's/ctime INTEGER/ctime BIGINT/g' -i ${ccnetDir}/ccnet.sql + sed 's/email TEXT, role TEXT/email VARCHAR(255), role TEXT/g' -i ${ccnetDir}/ccnet.sql + ${pkgs.mariadb.client}/bin/mysql --database=ccnet_db < ${ccnetDir}/ccnet.sql + + echo Migrating seafile database to MySQL + ${lib.getExe pkgs.sqlite} ${cfg.dataDir}/seafile.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py > ${cfg.dataDir}/seafile.sql + sed 's/owner_id TEXT/owner_id VARCHAR(255)/g' -i ${cfg.dataDir}/seafile.sql + sed 's/user_name TEXT/user_name VARCHAR(255)/g' -i ${cfg.dataDir}/seafile.sql + ${pkgs.mariadb.client}/bin/mysql --database=seafile_db < ${cfg.dataDir}/seafile.sql + + echo Migrating seahub database to MySQL + echo 'SET FOREIGN_KEY_CHECKS=0;' > ${seahubDir}/seahub.sql + ${lib.getExe pkgs.sqlite} ${seahubDir}/seahub.db ".dump" | \ + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/scripts/sqlite2mysql.py >> ${seahubDir}/seahub.sql + sed 's/`permission` , `reporter` text NOT NULL/`permission` longtext NOT NULL/g' -i ${seahubDir}/seahub.sql + sed 's/varchar(256) NOT NULL UNIQUE/varchar(255) NOT NULL UNIQUE/g' -i ${seahubDir}/seahub.sql + sed 's/, UNIQUE (`user_email`, `contact_email`)//g' -i ${seahubDir}/seahub.sql + sed '/INSERT INTO `base_dirfileslastmodifiedinfo`/d' -i ${seahubDir}/seahub.sql + sed '/INSERT INTO `notifications_usernotification`/d' -i ${seahubDir}/seahub.sql + sed 's/DEFERRABLE INITIALLY DEFERRED//g' -i ${seahubDir}/seahub.sql + ${pkgs.mariadb.client}/bin/mysql --database=seahub_db < ${seahubDir}/seahub.sql + + echo "${cfg.seahubPackage.seafile-server.version}-mysql" > "${seafRoot}"/server-setup + echo Migration complete else - echo "Unsupported upgrade" >&2 + echo "Unsupported upgrade: $installedMajor.$installedMinor to $pkgMajor.$pkgMinor" >&2 exit 1 fi ''; + + # Fix unix socket permissions + postStart = ( + lib.strings.optionalString (lib.strings.hasPrefix "unix:" cfg.seafileSettings.fileserver.host) '' + while [[ ! -S "${lib.strings.removePrefix "unix:" cfg.seafileSettings.fileserver.host}" ]]; do + sleep 1 + done + chmod 666 "${lib.strings.removePrefix "unix:" cfg.seafileSettings.fileserver.host}" + '' + ); }; seahub = { description = "Seafile Server Web Frontend"; wantedBy = [ "seafile.target" ]; partOf = [ "seafile.target" ]; - after = [ "network.target" "seaf-server.service" ]; - requires = [ "seaf-server.service" ]; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + requires = [ + "mysql.service" + "seaf-server.service" + ]; + after = [ + "network.target" + "mysql.service" + "seaf-server.service" + ]; restartTriggers = [ seahubSettings ]; environment = { - PYTHONPATH = "${pkgs.seahub.pythonPath}:${pkgs.seahub}/thirdpart:${pkgs.seahub}"; + PYTHONPATH = "${cfg.seahubPackage.pythonPath}:${cfg.seahubPackage}/thirdpart:${cfg.seahubPackage}"; DJANGO_SETTINGS_MODULE = "seahub.settings"; CCNET_CONF_DIR = ccnetDir; - SEAFILE_CONF_DIR = dataDir; + SEAFILE_CONF_DIR = cfg.dataDir; SEAFILE_CENTRAL_CONF_DIR = "/etc/seafile"; SEAFILE_RPC_PIPE_PATH = "/run/seafile"; SEAHUB_LOG_DIR = "/var/log/seafile"; }; - serviceConfig = securityOptions // { - User = "seafile"; - Group = "seafile"; - DynamicUser = true; + serviceConfig = serviceOptions // { RuntimeDirectory = "seahub"; - StateDirectory = "seafile"; - LogsDirectory = "seafile"; - ConfigurationDirectory = "seafile"; ExecStart = '' - ${pkgs.seahub.python.pkgs.gunicorn}/bin/gunicorn seahub.wsgi:application \ + ${lib.getExe cfg.seahubPackage.python3.pkgs.gunicorn} seahub.wsgi:application \ --name seahub \ --workers ${toString cfg.workers} \ --log-level=info \ --preload \ --timeout=1200 \ --limit-request-line=8190 \ - --bind unix:/run/seahub/gunicorn.sock + --bind ${cfg.seahubAddress} ''; }; preStart = '' mkdir -p ${seahubDir}/media # Link all media except avatars - for m in `find ${pkgs.seahub}/media/ -maxdepth 1 -not -name "avatars"`; do + for m in `find ${cfg.seahubPackage}/media/ -maxdepth 1 -not -name "avatars"`; do ln -sf $m ${seahubDir}/media/ done if [ ! -e "${seafRoot}/.seahubSecret" ]; then - ${pkgs.seahub.python}/bin/python ${pkgs.seahub}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret - chmod 400 ${seafRoot}/.seahubSecret + ( + umask 377 && + ${lib.getExe cfg.seahubPackage.python3} ${cfg.seahubPackage}/tools/secret_key_generator.py > ${seafRoot}/.seahubSecret + ) fi if [ ! -f "${seafRoot}/seahub-setup" ]; then # avatars directory should be writable - install -D -t ${seahubDir}/media/avatars/ ${pkgs.seahub}/media/avatars/default.png - install -D -t ${seahubDir}/media/avatars/groups ${pkgs.seahub}/media/avatars/groups/default.png + install -D -t ${seahubDir}/media/avatars/ ${cfg.seahubPackage}/media/avatars/default.png + install -D -t ${seahubDir}/media/avatars/groups ${cfg.seahubPackage}/media/avatars/groups/default.png # init database - ${pkgs.seahub}/manage.py migrate + ${cfg.seahubPackage}/manage.py migrate # create admin account - ${pkgs.expect}/bin/expect -c 'spawn ${pkgs.seahub}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."' - echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + ${lib.getExe pkgs.expect} -c 'spawn ${cfg.seahubPackage}/manage.py createsuperuser --email=${cfg.adminEmail}; expect "Password: "; send "${cfg.initialAdminPassword}\r"; expect "Password (again): "; send "${cfg.initialAdminPassword}\r"; expect "Superuser created successfully."' + echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup" fi if [ $(cat "${seafRoot}/seahub-setup" | cut -d"-" -f1) != "${pkgs.seahub.version}" ]; then - # update database - ${pkgs.seahub}/manage.py migrate - echo "${pkgs.seahub.version}-sqlite" > "${seafRoot}/seahub-setup" + # run django migrations + ${cfg.seahubPackage}/manage.py migrate + echo "${cfg.seahubPackage.version}-mysql" > "${seafRoot}/seahub-setup" + fi + ''; + }; + + seaf-gc = { + description = "Seafile storage garbage collection"; + conflicts = [ + "seaf-server.service" + "seahub.service" + ]; + after = [ + "seaf-server.service" + "seahub.service" + ]; + unitConfig.RequiresMountsFor = lib.lists.optional (cfg.dataDir != "${seafRoot}/data") cfg.dataDir; + onSuccess = [ + "seaf-server.service" + "seahub.service" + ]; + onFailure = [ + "seaf-server.service" + "seahub.service" + ]; + startAt = lib.lists.optionals cfg.gc.enable cfg.gc.dates; + serviceConfig = serviceOptions // { + Type = "oneshot"; + }; + script = '' + if [ ! -f "${seafRoot}/server-setup" ]; then + echo "Server not setup yet, GC not needed" >&2 + exit + fi + + # checking for pending upgrades + installedMajor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f1) + installedMinor=$(cat "${seafRoot}/server-setup" | cut -d"-" -f1 | cut -d"." -f2) + pkgMajor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f1) + pkgMinor=$(echo "${cfg.seahubPackage.seafile-server.version}" | cut -d"." -f2) + + if [[ $installedMajor != $pkgMajor || $installedMinor != $pkgMinor ]]; then + echo "Server not upgraded yet" >&2 + exit fi + + # Clean up user-deleted blocks and libraries + ${cfg.seahubPackage.seafile-server}/bin/seafserv-gc \ + -F /etc/seafile \ + -c ${ccnetDir} \ + -d ${cfg.dataDir} \ + --rm-fs ''; }; }; + + systemd.timers.seaf-gc = lib.mkIf cfg.gc.enable { + timerConfig = { + randomizedDelaySec = cfg.gc.randomizedDelaySec; + Persistent = cfg.gc.persistent; + }; + }; }; + + meta.maintainers = with lib.maintainers; [ + greizgh + schmittlauch + ]; } diff --git a/nixos/tests/seafile.nix b/nixos/tests/seafile.nix index 78e735f4fed73..7784d5fddaedd 100644 --- a/nixos/tests/seafile.nix +++ b/nixos/tests/seafile.nix @@ -14,6 +14,7 @@ import ./make-test-python.nix ({ pkgs, ... }: services.seafile = { enable = true; ccnetSettings.General.SERVICE_URL = "http://server"; + seafileSettings.fileserver.host = "unix:/run/seafile/server.sock"; adminEmail = "admin@example.com"; initialAdminPassword = "seafile_password"; }; @@ -22,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }: virtualHosts."server" = { locations."/".proxyPass = "http://unix:/run/seahub/gunicorn.sock"; locations."/seafhttp" = { - proxyPass = "http://127.0.0.1:8082"; + proxyPass = "http://unix:/run/seafile/server.sock"; extraConfig = '' rewrite ^/seafhttp(.*)$ $1 break; client_max_body_size 0; diff --git a/pkgs/servers/seafile-server/libevhtp.nix b/pkgs/by-name/se/seafile-server/libevhtp.nix similarity index 80% rename from pkgs/servers/seafile-server/libevhtp.nix rename to pkgs/by-name/se/seafile-server/libevhtp.nix index 97cdc0804faa7..8eb1b3e22ff52 100644 --- a/pkgs/servers/seafile-server/libevhtp.nix +++ b/pkgs/by-name/se/seafile-server/libevhtp.nix @@ -1,4 +1,10 @@ -{ stdenv, lib, fetchFromGitHub, cmake, libevent }: +{ + stdenv, + lib, + fetchFromGitHub, + cmake, + libevent, +}: stdenv.mkDerivation rec { pname = "libevhtp"; @@ -24,6 +30,10 @@ stdenv.mkDerivation rec { description = "Create extremely-fast and secure embedded HTTP servers with ease"; homepage = "https://github.com/criticalstack/libevhtp"; license = licenses.bsd3; - maintainers = with maintainers; [ greizgh schmittlauch ]; + maintainers = with maintainers; [ + greizgh + schmittlauch + melvyn2 + ]; }; } diff --git a/pkgs/servers/seafile-server/default.nix b/pkgs/by-name/se/seafile-server/package.nix similarity index 57% rename from pkgs/servers/seafile-server/default.nix rename to pkgs/by-name/se/seafile-server/package.nix index 109484c467b4d..f9510c3c5fb6c 100644 --- a/pkgs/servers/seafile-server/default.nix +++ b/pkgs/by-name/se/seafile-server/package.nix @@ -1,50 +1,61 @@ -{ stdenv -, lib -, fetchFromGitHub -, pkg-config -, python3 -, autoreconfHook -, libuuid -, sqlite -, glib -, libevent -, libsearpc -, openssl -, fuse -, libarchive -, libjwt -, curl -, which -, vala -, cmake -, oniguruma -, nixosTests +{ + stdenv, + lib, + fetchFromGitHub, + pkg-config, + python3, + autoreconfHook, + libuuid, + libmysqlclient, + sqlite, + glib, + libevent, + libsearpc, + openssl, + fuse, + libarchive, + libjwt, + curl, + which, + vala, + cmake, + oniguruma, + nixosTests, }: let # seafile-server relies on a specific version of libevhtp. # It contains non upstreamed patches and is forked off an outdated version. libevhtp = import ./libevhtp.nix { - inherit stdenv lib fetchFromGitHub cmake libevent; + inherit + stdenv + lib + fetchFromGitHub + cmake + libevent + ; }; in -stdenv.mkDerivation rec { +stdenv.mkDerivation { pname = "seafile-server"; - version = "10.0.1"; + version = "11.0.12"; src = fetchFromGitHub { owner = "haiwen"; repo = "seafile-server"; - rev = "db09baec1b88fc131bf4453a808ab63a3fc714c9"; # using a fixed revision because upstream may re-tag releases :/ - sha256 = "sha256-a5vtJcbnaYzq6/3xmhbWk23BZ+Wil/Tb/q22ML4bDqs="; + rev = "5e6c0974e6abe5d92b8ba1db41c6ddbc1029f2d5"; # using a fixed revision because upstream may re-tag releases :/ + hash = "sha256-BVa4QZiHPkqRB5FvDlCSbEVxdnyxVy2KuCDb2orRMuI="; }; - nativeBuildInputs = [ autoreconfHook pkg-config ]; + nativeBuildInputs = [ + autoreconfHook + pkg-config + ]; buildInputs = [ libuuid + libmysqlclient sqlite - openssl glib libsearpc libevent @@ -52,7 +63,6 @@ stdenv.mkDerivation rec { fuse libarchive libjwt - curl which vala libevhtp @@ -73,6 +83,11 @@ stdenv.mkDerivation rec { homepage = "https://github.com/haiwen/seafile-server"; license = licenses.agpl3Plus; platforms = platforms.linux; - maintainers = with maintainers; [ greizgh schmittlauch ]; + maintainers = with maintainers; [ + greizgh + schmittlauch + melvyn2 + ]; + mainProgram = "seaf-server"; }; } diff --git a/pkgs/applications/networking/seahub/default.nix b/pkgs/by-name/se/seahub/package.nix similarity index 55% rename from pkgs/applications/networking/seahub/default.nix rename to pkgs/by-name/se/seahub/package.nix index 604ba4772710a..b1df123cd8147 100644 --- a/pkgs/applications/networking/seahub/default.nix +++ b/pkgs/by-name/se/seahub/package.nix @@ -1,47 +1,30 @@ -{ lib -, fetchFromGitHub -, fetchpatch -, python3 -, makeWrapper -, nixosTests +{ + lib, + fetchFromGitHub, + python3, + makeWrapper, + nixosTests, + seafile-server, }: -let - python = python3.override { - self = python; - packageOverrides = self: super: { - django = super.django_3; - }; - }; -in -python.pkgs.buildPythonApplication rec { +python3.pkgs.buildPythonApplication rec { pname = "seahub"; - version = "10.0.1"; + version = "11.0.12"; pyproject = false; src = fetchFromGitHub { owner = "haiwen"; repo = "seahub"; - rev = "e8c02236c0eaca6dde009872745f089da4b77e6e"; # using a fixed revision because upstream may re-tag releases :/ - sha256 = "sha256-7JXWKEFqCsC+ZByhvyP8AmDpajT3hpgyYDNUqc3wXyg="; + rev = "d998361dd890cac3f6d6ebec3af47a589e0332bc"; # using a fixed revision because upstream may re-tag releases :/ + hash = "sha256-n56sRZ9TVb37JA0+12ZoF2Mt7dADjaYk7V0PmdBY0EU="; }; - patches = [ - (fetchpatch { - # PIL update fix - url = "https://patch-diff.githubusercontent.com/raw/haiwen/seahub/pull/5570.patch"; - sha256 = "sha256-7V2aRlacJ7Qhdi9k4Bs+t/Emx+EAM/NNCI+K40bMwLA="; - }) - ]; - dontBuild = true; doCheck = false; # disabled because it requires a ccnet environment - nativeBuildInputs = [ - makeWrapper - ]; + nativeBuildInputs = [ makeWrapper ]; - propagatedBuildInputs = with python.pkgs; [ + propagatedBuildInputs = with python3.pkgs; [ django future django-compressor @@ -50,6 +33,7 @@ python.pkgs.buildPythonApplication rec { django-simple-captcha django-picklefield django-formtools + djangosaml2 mysqlclient pillow python-dateutil @@ -60,14 +44,21 @@ python.pkgs.buildPythonApplication rec { chardet pyjwt pycryptodome + pyopenssl + python-ldap qrcode pysearpc - seaserv gunicorn markdown bleach + + (python3.pkgs.toPythonModule (seafile-server.override { inherit python3; })) ]; + postPatch = '' + substituteInPlace seahub/settings.py --replace-fail "SEAFILE_VERSION = '6.3.3'" "SEAFILE_VERSION = '${version}'" + ''; + installPhase = '' cp -dr --no-preserve='ownership' . $out/ wrapProgram $out/manage.py \ @@ -75,18 +66,23 @@ python.pkgs.buildPythonApplication rec { ''; passthru = { - inherit python; - pythonPath = python.pkgs.makePythonPath propagatedBuildInputs; + inherit python3; + pythonPath = python3.pkgs.makePythonPath propagatedBuildInputs; tests = { inherit (nixosTests) seafile; }; + inherit seafile-server; }; meta = with lib; { description = "Web end of seafile server"; homepage = "https://github.com/haiwen/seahub"; license = licenses.asl20; - maintainers = with maintainers; [ greizgh schmittlauch ]; + maintainers = with maintainers; [ + greizgh + schmittlauch + melvyn2 + ]; platforms = platforms.linux; }; } diff --git a/pkgs/development/python-modules/djangosaml2/default.nix b/pkgs/development/python-modules/djangosaml2/default.nix new file mode 100644 index 0000000000000..ff484907e69f7 --- /dev/null +++ b/pkgs/development/python-modules/djangosaml2/default.nix @@ -0,0 +1,54 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + defusedxml, + django, + pysaml2, + pythonOlder, + setuptools, +}: + +buildPythonPackage rec { + pname = "djangosaml2"; + version = "1.9.3"; + pyproject = true; + + disabled = pythonOlder "3.8"; + + src = fetchFromGitHub { + owner = "IdentityPython"; + repo = "djangosaml2"; + rev = "refs/tags/v${version}"; + hash = "sha256-rbmEJuG2mgozpCFOXZUJFxv8v52IRQeaeAKfeUDACeU="; + }; + + build-system = [ setuptools ]; + + dependencies = [ + django + defusedxml + pysaml2 + ]; + + # Falsely complains that 'defusedxml>=0.4.1 not satisfied by version 0.8.0rc2' + pythonRelaxDeps = [ "defusedxml" ]; + + pythonImportsCheck = [ "djangosaml2" ]; + + checkPhase = '' + runHook preCheck + + python tests/run_tests.py + + runHook postCheck + ''; + + meta = { + description = "Django SAML2 Service Provider based on pySAML2"; + homepage = "https://github.com/IdentityPython/djangosaml2"; + changelog = "https://github.com/IdentityPython/djangosaml2/blob/v${version}/CHANGES"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [ melvyn2 ]; + }; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index b79143c50a5fc..021b4c9e3abf0 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -32654,8 +32654,6 @@ with pkgs; seafile-client = libsForQt5.callPackage ../applications/networking/seafile-client { }; - seahub = callPackage ../applications/networking/seahub { }; - seatd = callPackage ../applications/misc/seatd { }; secrets-extractor = callPackage ../tools/security/secrets-extractor { }; @@ -38250,8 +38248,6 @@ with pkgs; sct = callPackage ../tools/X11/sct { }; - seafile-server = callPackage ../servers/seafile-server { }; - seafile-shared = callPackage ../misc/seafile-shared { }; ser2net = callPackage ../servers/ser2net { }; diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index 5f4c18055d164..2042869e6b0b9 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -3512,6 +3512,8 @@ self: super: with self; { django-reversion = callPackage ../development/python-modules/django-reversion { }; + djangosaml2 = callPackage ../development/python-modules/djangosaml2 { }; + django-sekizai = callPackage ../development/python-modules/django-sekizai { }; django-sesame = callPackage ../development/python-modules/django-sesame { };