about summary refs log tree commit diff
path: root/nixos/modules/security/krb5/krb5-conf-format.nix
blob: 3e5e64ae0cb0496f51b122b246c4e110f65dd3c5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
{ pkgs, lib, ... }:

# Based on
# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html
# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html

let
  inherit (lib) boolToString concatMapStringsSep concatStringsSep filter
    isAttrs isBool isList mapAttrsToList mkOption singleton splitString;
  inherit (lib.types) attrsOf bool coercedTo either enum int listOf oneOf
    path str submodule;
in
{
  enableKdcACLEntries ? false
}: rec {
  sectionType = let
    relation = oneOf [
      (listOf (attrsOf value))
      (attrsOf value)
      value
    ];
    value = either (listOf atom) atom;
    atom = oneOf [int str bool];
  in attrsOf relation;

  type = let
    aclEntry = submodule {
      options = {
        principal = mkOption {
          type = str;
          description = "Which principal the rule applies to";
        };
        access = mkOption {
          type = either
            (listOf (enum ["add" "cpw" "delete" "get" "list" "modify"]))
            (enum ["all"]);
          default = "all";
          description = "The changes the principal is allowed to make.";
        };
        target = mkOption {
          type = str;
          default = "*";
          description = "The principals that 'access' applies to.";
        };
      };
    };

    realm = submodule ({ name, ... }: {
      freeformType = sectionType;
      options = {
        acl = mkOption {
          type = listOf aclEntry;
          default = [
            { principal = "*/admin"; access = "all"; }
            { principal = "admin"; access = "all"; }
          ];
          description = ''
            The privileges granted to a user.
          '';
        };
      };
    });
  in submodule {
    freeformType = attrsOf sectionType;
    options = {
      include = mkOption {
        default = [ ];
        description = ''
          Files to include in the Kerberos configuration.
        '';
        type = coercedTo path singleton (listOf path);
      };
      includedir = mkOption {
        default = [ ];
        description = ''
          Directories containing files to include in the Kerberos configuration.
        '';
        type = coercedTo path singleton (listOf path);
      };
      module = mkOption {
        default = [ ];
        description = ''
          Modules to obtain Kerberos configuration from.
        '';
        type = coercedTo path singleton (listOf path);
      };

    }
    //
    (lib.optionalAttrs enableKdcACLEntries {
      realms = mkOption {
        type = attrsOf realm;
        description = ''
          The realm(s) to serve keys for.
        '';
      };
    });
  };

  generate = let
    indent = str: concatMapStringsSep "\n" (line: "  " + line) (splitString "\n" str);

    formatToplevel = args @ {
      include ? [ ],
      includedir ? [ ],
      module ? [ ],
      ...
    }: let
      sections = removeAttrs args [ "include" "includedir" "module" ];
    in concatStringsSep "\n" (filter (x: x != "") [
      (concatStringsSep "\n" (mapAttrsToList formatSection sections))
      (concatMapStringsSep "\n" (m: "module ${m}") module)
      (concatMapStringsSep "\n" (i: "include ${i}") include)
      (concatMapStringsSep "\n" (i: "includedir ${i}") includedir)
    ]);

    formatSection = name: section: ''
      [${name}]
      ${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))}
    '';

    formatRelation = name: relation:
      if isAttrs relation
      then ''
        ${name} = {
        ${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
        }''
      else if isList relation
      then
        concatMapStringsSep "\n" (formatRelation name) relation
      else formatValue name relation;

    formatValue = name: value:
      if isList value
      then concatMapStringsSep "\n" (formatAtom name) value
      else formatAtom name value;

    formatAtom = name: atom: let
      v = if isBool atom then boolToString atom else toString atom;
    in "${name} = ${v}";
  in
    name: value: pkgs.writeText name ''
      ${formatToplevel value}
    '';
}