python312Packages.aiohomeconnect: 0.10.0 -> 0.11.0 (#374011)
[NixPkgs.git] / nixos / modules / services / networking / vdirsyncer.nix
blob692ce12c7a6c0caf71412eea38563772ed65e746
2   config,
3   lib,
4   pkgs,
5   ...
6 }:
8 with lib;
10 let
12   cfg = config.services.vdirsyncer;
14   toIniJson =
15     with generators;
16     toINI {
17       mkKeyValue = mkKeyValueDefault {
18         mkValueString = builtins.toJSON;
19       } "=";
20     };
22   toConfigFile =
23     name: cfg':
24     if cfg'.configFile != null then
25       cfg'.configFile
26     else
27       pkgs.writeText "vdirsyncer-${name}.conf" (
28         toIniJson (
29           {
30             general = cfg'.config.general // {
31               status_path =
32                 if cfg'.config.statusPath == null then "/var/lib/vdirsyncer/${name}" else cfg'.config.statusPath;
33             };
34           }
35           // (mapAttrs' (name: nameValuePair "pair ${name}") cfg'.config.pairs)
36           // (mapAttrs' (name: nameValuePair "storage ${name}") cfg'.config.storages)
37         )
38       );
40   userUnitConfig = name: cfg': {
41     serviceConfig =
42       {
43         User = if cfg'.user == null then "vdirsyncer" else cfg'.user;
44         Group = if cfg'.group == null then "vdirsyncer" else cfg'.group;
45       }
46       // (optionalAttrs (cfg'.user == null) {
47         DynamicUser = true;
48       })
49       // (optionalAttrs (cfg'.additionalGroups != [ ]) {
50         SupplementaryGroups = cfg'.additionalGroups;
51       })
52       // (optionalAttrs (cfg'.config.statusPath == null) {
53         StateDirectory = "vdirsyncer/${name}";
54         StateDirectoryMode = "0700";
55       });
56   };
58   commonUnitConfig = {
59     after = [ "network.target" ];
60     serviceConfig = {
61       Type = "oneshot";
62       # Sandboxing
63       PrivateTmp = true;
64       NoNewPrivileges = true;
65       ProtectSystem = "strict";
66       ProtectHome = true;
67       ProtectKernelTunables = true;
68       ProtectKernelModules = true;
69       ProtectControlGroups = true;
70       RestrictNamespaces = true;
71       MemoryDenyWriteExecute = true;
72       RestrictRealtime = true;
73       RestrictSUIDSGID = true;
74       RestrictAddressFamilies = "AF_INET AF_INET6";
75       LockPersonality = true;
76     };
77   };
81   options = {
82     services.vdirsyncer = {
83       enable = mkEnableOption "vdirsyncer";
85       package = mkPackageOption pkgs "vdirsyncer" { };
87       jobs = mkOption {
88         description = "vdirsyncer job configurations";
89         type = types.attrsOf (
90           types.submodule {
91             options = {
92               enable = (mkEnableOption "this vdirsyncer job") // {
93                 default = true;
94                 example = false;
95               };
97               user = mkOption {
98                 type = types.nullOr types.str;
99                 default = null;
100                 description = ''
101                   User account to run vdirsyncer as, otherwise as a systemd
102                   dynamic user
103                 '';
104               };
106               group = mkOption {
107                 type = types.nullOr types.str;
108                 default = null;
109                 description = "group to run vdirsyncer as";
110               };
112               additionalGroups = mkOption {
113                 type = types.listOf types.str;
114                 default = [ ];
115                 description = "additional groups to add the dynamic user to";
116               };
118               forceDiscover = mkOption {
119                 type = types.bool;
120                 default = false;
121                 description = ''
122                   Run `yes | vdirsyncer discover` prior to `vdirsyncer sync`
123                 '';
124               };
126               timerConfig = mkOption {
127                 type = types.attrs;
128                 default = {
129                   OnBootSec = "1h";
130                   OnUnitActiveSec = "6h";
131                 };
132                 description = "systemd timer configuration";
133               };
135               configFile = mkOption {
136                 type = types.nullOr types.path;
137                 default = null;
138                 description = "existing configuration file";
139               };
141               config = {
142                 statusPath = mkOption {
143                   type = types.nullOr types.str;
144                   default = null;
145                   defaultText = literalExpression "/var/lib/vdirsyncer/\${attrName}";
146                   description = "vdirsyncer's status path";
147                 };
149                 general = mkOption {
150                   type = types.attrs;
151                   default = { };
152                   description = "general configuration";
153                 };
155                 pairs = mkOption {
156                   type = types.attrsOf types.attrs;
157                   default = { };
158                   description = "vdirsyncer pair configurations";
159                   example = literalExpression ''
160                     {
161                       my_contacts = {
162                         a = "my_cloud_contacts";
163                         b = "my_local_contacts";
164                         collections = [ "from a" ];
165                         conflict_resolution = "a wins";
166                         metadata = [ "color" "displayname" ];
167                       };
168                     };
169                   '';
170                 };
172                 storages = mkOption {
173                   type = types.attrsOf types.attrs;
174                   default = { };
175                   description = "vdirsyncer storage configurations";
176                   example = literalExpression ''
177                     {
178                       my_cloud_contacts = {
179                         type = "carddav";
180                         url = "https://dav.example.com/";
181                         read_only = true;
182                         username = "user";
183                         "password.fetch" = [ "command" "cat" "/etc/vdirsyncer/cloud.passwd" ];
184                       };
185                       my_local_contacts = {
186                         type = "carddav";
187                         url = "https://localhost/";
188                         username = "user";
189                         "password.fetch" = [ "command" "cat" "/etc/vdirsyncer/local.passwd" ];
190                       };
191                     }
192                   '';
193                 };
194               };
195             };
196           }
197         );
198       };
199     };
200   };
202   config = mkIf cfg.enable {
203     systemd.services = mapAttrs' (
204       name: cfg':
205       nameValuePair "vdirsyncer@${name}" (
206         foldr recursiveUpdate { } [
207           commonUnitConfig
208           (userUnitConfig name cfg')
209           {
210             description = "synchronize calendars and contacts (${name})";
211             environment.VDIRSYNCER_CONFIG = toConfigFile name cfg';
212             serviceConfig.ExecStart =
213               (optional cfg'.forceDiscover (
214                 pkgs.writeShellScript "vdirsyncer-discover-yes" ''
215                   set -e
216                   yes | ${cfg.package}/bin/vdirsyncer discover
217                 ''
218               ))
219               ++ [ "${cfg.package}/bin/vdirsyncer sync" ];
220           }
221         ]
222       )
223     ) (filterAttrs (name: cfg': cfg'.enable) cfg.jobs);
225     systemd.timers = mapAttrs' (
226       name: cfg':
227       nameValuePair "vdirsyncer@${name}" {
228         wantedBy = [ "timers.target" ];
229         description = "synchronize calendars and contacts (${name})";
230         inherit (cfg') timerConfig;
231       }
232     ) cfg.jobs;
233   };