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>
This commit is contained in:
parent
cf0501600a
commit
6e10705754
@ -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";
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user