about summary refs log tree commit diff
path: root/nixos/modules/services/misc/taskserver
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2016-04-11 22:24:58 +0200
committeraszlig <aszlig@redmoonstudios.org>2016-04-11 22:24:58 +0200
commit6e10705754a790bcd44d1f46dfb629678750bb9b (patch)
treed1579957322b4444a42fedccc5aa900f91ce2c34 /nixos/modules/services/misc/taskserver
parentcf0501600abbb247585628d685f8262630c2b3c7 (diff)
downloadnixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar.gz
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar.bz2
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar.lz
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar.xz
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.tar.zst
nixlib-6e10705754a790bcd44d1f46dfb629678750bb9b.zip
nixos/taskserver: Handle declarative conf via JSON
We now no longer have the stupid --service-helper option, which silences
messages about already existing organisations, users or groups.

Instead of that option, we now have a new subcommand called
"process-json", which accepts a JSON file directly from the specified
NixOS module options and creates/deletes the users accordingly.

Note that this still has a two issues left to solve in this area:

 * Deletion is not supported yet.
 * If a user is created imperatively, the next run of process-json will
   delete it once deletion is supported.

So we need to implement deletion and a way to mark organisations, users
and groups as "imperatively managed".

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'nixos/modules/services/misc/taskserver')
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix20
-rw-r--r--nixos/modules/services/misc/taskserver/helper-tool.py89
2 files changed, 77 insertions, 32 deletions
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index b175bd6d6752..7e993627cec4 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -142,8 +142,6 @@ let
     propagatedBuildInputs = [ pkgs.pythonPackages.click ];
   };
 
-  ctlcmd = "${nixos-taskserver}/bin/nixos-taskserver --service-helper";
-
   withMeta = meta: defs: mkMerge [ defs { inherit meta; } ];
 
 in {
@@ -432,20 +430,10 @@ in {
 
       environment.TASKDDATA = cfg.dataDir;
 
-      preStart = ''
-        ${concatStrings (mapAttrsToList (orgName: attrs: ''
-          ${ctlcmd} add-org ${mkShellStr orgName}
-
-          ${concatMapStrings (user: ''
-            echo Creating ${user} >&2
-            ${ctlcmd} add-user ${mkShellStr orgName} ${mkShellStr user}
-          '') attrs.users}
-
-          ${concatMapStrings (group: ''
-            ${ctlcmd} add-group ${mkShellStr orgName} ${mkShellStr user}
-          '') attrs.groups}
-        '') cfg.organisations)}
-      '';
+      preStart = let
+        jsonOrgs = builtins.toJSON cfg.organisations;
+        jsonFile = pkgs.writeText "orgs.json" jsonOrgs;
+      in "${nixos-taskserver}/bin/nixos-taskserver process-json '${jsonFile}'";
 
       serviceConfig = {
         ExecStart = "@${taskd} taskd server";
diff --git a/nixos/modules/services/misc/taskserver/helper-tool.py b/nixos/modules/services/misc/taskserver/helper-tool.py
index 7a582f6091f8..c255081f5657 100644
--- a/nixos/modules/services/misc/taskserver/helper-tool.py
+++ b/nixos/modules/services/misc/taskserver/helper-tool.py
@@ -1,4 +1,5 @@
 import grp
+import json
 import pwd
 import os
 import re
@@ -210,6 +211,13 @@ class Organisation(object):
             return newuser
         return None
 
+    def del_user(self, name):
+        """
+        Delete a user and revoke its keys.
+        """
+        sys.stderr.write("Delete user {}.".format(name))
+        # TODO: deletion!
+
     def add_group(self, name):
         """
         Create a new group.
@@ -223,6 +231,13 @@ class Organisation(object):
             return newgroup
         return None
 
+    def del_group(self, name):
+        """
+        Delete a group.
+        """
+        sys.stderr.write("Delete group {}.".format(name))
+        # TODO: deletion!
+
     def get_user(self, name):
         return self.users.get(name)
 
@@ -261,6 +276,14 @@ class Manager(object):
             return neworg
         return None
 
+    def del_org(self, name):
+        """
+        Delete and revoke keys of an organisation with all its users and
+        groups.
+        """
+        sys.stderr.write("Delete org {}.".format(name))
+        # TODO: deletion!
+
     def get_org(self, name):
         return self.orgs.get(name)
 
@@ -285,13 +308,11 @@ ORGANISATION = OrganisationType()
 
 
 @click.group()
-@click.option('--service-helper', is_flag=True)
-@click.pass_context
-def cli(ctx, service_helper):
+def cli():
     """
     Manage Taskserver users and certificates
     """
-    ctx.obj = {'is_service_helper': service_helper}
+    pass
 
 
 @cli.command("list-users")
@@ -351,14 +372,11 @@ def export_user(organisation, user):
 
 @cli.command("add-org")
 @click.argument("name")
-@click.pass_obj
-def add_org(obj, name):
+def add_org(name):
     """
     Create an organisation with the specified name.
     """
     if os.path.exists(mkpath(name)):
-        if obj['is_service_helper']:
-            return
         msg = "Organisation with name {} already exists."
         sys.exit(msg.format(name))
 
@@ -368,8 +386,7 @@ def add_org(obj, name):
 @cli.command("add-user")
 @click.argument("organisation", type=ORGANISATION)
 @click.argument("user")
-@click.pass_obj
-def add_user(obj, organisation, user):
+def add_user(organisation, user):
     """
     Create a user for the given organisation along with a client certificate
     and print the key of the new user.
@@ -379,8 +396,6 @@ def add_user(obj, organisation, user):
     """
     userobj = organisation.add_user(user)
     if userobj is None:
-        if obj['is_service_helper']:
-            return
         msg = "User {} already exists in organisation {}."
         sys.exit(msg.format(user, organisation))
 
@@ -388,18 +403,60 @@ def add_user(obj, organisation, user):
 @cli.command("add-group")
 @click.argument("organisation", type=ORGANISATION)
 @click.argument("group")
-@click.pass_obj
-def add_group(obj, organisation, group):
+def add_group(organisation, group):
     """
     Create a group for the given organisation.
     """
     userobj = organisation.add_group(group)
     if userobj is None:
-        if obj['is_service_helper']:
-            return
         msg = "Group {} already exists in organisation {}."
         sys.exit(msg.format(group, organisation))
 
 
+def add_or_delete(old, new, add_fun, del_fun):
+    """
+    Given an 'old' and 'new' list, figure out the intersections and invoke
+    'add_fun' against every element that is not in the 'old' list and 'del_fun'
+    against every element that is not in the 'new' list.
+
+    Returns a tuple where the first element is the list of elements that were
+    added and the second element consisting of elements that were deleted.
+    """
+    old_set = set(old)
+    new_set = set(new)
+    to_delete = old_set - new_set
+    to_add = new_set - old_set
+    for elem in to_delete:
+        del_fun(elem)
+    for elem in to_add:
+        add_fun(elem)
+    return to_add, to_delete
+
+
+@cli.command("process-json")
+@click.argument('json-file', type=click.File('rb'))
+def process_json(json_file):
+    """
+    Create and delete users, groups and organisations based on a JSON file.
+
+    The structure of this file is exactly the same as the
+    'services.taskserver.organisations' option of the NixOS module and is used
+    for declaratively adding and deleting users.
+
+    Hence this subcommand is not recommended outside of the scope of the NixOS
+    module.
+    """
+    data = json.load(json_file)
+
+    mgr = Manager()
+    add_or_delete(mgr.orgs.keys(), data.keys(), mgr.add_org, mgr.del_org)
+
+    for org in mgr.orgs.values():
+        add_or_delete(org.users.keys(), data[org.name]['users'],
+                      org.add_user, org.del_user)
+        add_or_delete(org.groups.keys(), data[org.name]['groups'],
+                      org.add_group, org.del_group)
+
+
 if __name__ == '__main__':
     cli()