chiark / gitweb /
backend.py: Use configured delimiter for joining fields.
[chopwood] / cmd-admin.py
CommitLineData
a2916c06
MW
1### -*-python-*-
2###
3### Administrative commands
4###
5### (c) 2013 Mark Wooding
6###
7
8###----- Licensing notice ---------------------------------------------------
9###
10### This file is part of Chopwood: a password-changing service.
11###
12### Chopwood is free software; you can redistribute it and/or modify
13### it under the terms of the GNU Affero General Public License as
14### published by the Free Software Foundation; either version 3 of the
15### License, or (at your option) any later version.
16###
17### Chopwood is distributed in the hope that it will be useful,
18### but WITHOUT ANY WARRANTY; without even the implied warranty of
19### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20### GNU Affero General Public License for more details.
21###
22### You should have received a copy of the GNU Affero General Public
23### License along with Chopwood; if not, see
24### <http://www.gnu.org/licenses/>.
25
26from __future__ import with_statement
27
28import agpl as AGPL
82d4f64b 29import backend as BE
a2916c06 30import cmdutil as CU
82d4f64b 31import config as CONF; CFG = CONF.CFG
a2916c06 32import dbmaint as D
82d4f64b 33import operation as OP
a2916c06 34from output import OUT, PRINT
82d4f64b 35import service as S
a2916c06
MW
36import subcommand as SC
37import util as U
38
39@SC.subcommand(
40 'listusers', ['admin'], 'List the existing users.',
41 opts = [SC.Opt('service', '-s', '--service',
42 'list users with SERVICE accounts',
43 argname = 'SERVICE')],
44 oparams = [SC.Arg('pat')])
45def cmd_listuser(service = None, pat = None):
46 CU.format_list(CU.list_users(service, pat),
47 [CU.column('USER', "~={0.user}A", 3),
48 CU.column('EMAIL', "~={0.email}:[---~;~={0.email}A~]")])
49
50@SC.subcommand(
51 'adduser', ['admin'], 'Add a user to the master database.',
52 opts = [SC.Opt('email', '-e', '--email',
53 "email address for the new user",
54 argname = 'EMAIL')],
55 params = [SC.Arg('user')])
56def cmd_adduser(user, email = None):
57 with D.DB:
58 CU.check_user(user, False)
59 D.DB.execute("INSERT INTO users (user, email) VALUES ($user, $email)",
60 user = user, email = email)
61 D.DB.execute("""INSERT INTO services (user, service)
62 VALUES ($user, $service)""",
63 user = user, service = 'master')
64
65@SC.subcommand(
66 'deluser', ['admin'], 'Remove USER from the master database.',
67 params = [SC.Arg('user')])
68def cmd_deluser(user, email = None):
69 with D.DB:
70 CU.check_user(user)
82d4f64b
MW
71 for service, alias in D.DB.execute(
72 "SELECT service, alias FROM services WHERE user = $user",
73 user = user):
74 if service == 'master': continue
75 try:
76 svc = S.SERVICES[service]
77 except KeyError:
78 OUT.warn("User `%s' has account for unknown service `%s'" %
79 (user, service))
80 else:
81 if svc.manage_pwent_p:
82 if alias is None: alias = user
83 svc.rmpwent(alias)
a2916c06
MW
84 D.DB.execute("DELETE FROM users WHERE user = $user", user = user)
85
86@SC.subcommand(
87 'edituser', ['admin'], 'Modify a user record.',
88 opts = [SC.Opt('email', '-e', '--email',
89 "change USER's email address",
90 argname = 'EMAIL'),
91 SC.Opt('noemail', '-E', '--no-email',
92 "forget USER's email address"),
93 SC.Opt('rename', '-r', '--rename',
94 "rename USER",
95 argname = 'NEW-NAME')],
96 params = [SC.Arg('user')])
97def cmd_edituser(user, email = None, noemail = False, rename = None):
98 with D.DB:
99 CU.check_user(user)
100 if rename is not None: check_user(rename, False)
101 CU.edit_records('users', 'user = $user',
102 [('email', email, noemail),
103 ('user', rename, False)],
104 user = user)
105
106@SC.subcommand(
107 'delsvc', ['admin'], "Remove all records for SERVICE.",
108 params = [SC.Arg('service')])
109def cmd_delsvc(service):
110 with D.DB:
111 CU.check_service(service, must_config_p = False, must_records_p = True)
112 D.DB.execute("DELETE FROM services WHERE service = $service",
113 service = service)
114
115@SC.subcommand(
116 'editsvc', ['admin'], "Edit a given SERVICE.",
117 opts = [SC.Opt('rename', '-r', '--rename', "rename the SERVICE",
118 argname = 'NEW-NAME')],
119 params = [SC.Arg('service')])
120def cmd_editsvc(service, rename = None):
121 with D.DB:
122 if service == 'master':
123 raise U.ExpectedError, (400, "Can't edit the master service")
124 if rename is None:
125 CU.check_service(service, must_config_p = True, must_records_p = True)
126 else:
127 CU.check_service(service, must_config_p = False, must_records_p = True)
128 CU.check_service(rename, must_config_p = True, must_records_p = False)
129 CU.edit_records('services', 'service = $service',
130 [('service', rename, False)],
131 service = service)
132
133@SC.subcommand(
134 'addacct', ['admin'], 'Add an account for a user.',
135 opts = [SC.Opt('alias', '-a', '--alias',
136 "alias by which USER is known to SERVICE",
137 argname = 'ALIAS')],
82d4f64b
MW
138 params = [SC.Arg('user'), SC.Arg('service')],
139 rparam = SC.Arg('fields'))
140def cmd_addacct(user, service, fields, alias = None):
a2916c06
MW
141 with D.DB:
142 CU.check_user(user)
82d4f64b 143 svc = CU.check_service(service)
a2916c06
MW
144 D.DB.execute("""SELECT 1 FROM services
145 WHERE user = $user AND service = $service""",
146 user = user, service = service)
147 if D.DB.fetchone() is not None:
148 raise U.ExpectedError, (
74b87214 149 400, "User `%s' already has a `%s' account" % (user, service))
a2916c06
MW
150 D.DB.execute("""INSERT INTO services (service, user, alias)
151 VALUES ($service, $user, $alias)""",
152 service = service, user = user, alias = alias)
153
82d4f64b
MW
154 if svc.manage_pwent_p:
155 if alias is None: alias = user
156 passwd = CFG.RQCLASS.reset([OP.acct(svc, alias)]).pwgen()
157 svc.mkpwent(alias, passwd, fields)
158 elif fields:
159 raise U.ExpectedError, (
160 400, "Password entry fields supplied, "
161 "but `%s' entries must be created manually" % service)
162
a2916c06
MW
163@SC.subcommand(
164 'delacct', ['admin'], "Remove USER's SERVICE account.",
165 params = [SC.Arg('user'), SC.Arg('service')])
166def cmd_delacct(user, service):
167 with D.DB:
82d4f64b 168 svc, alias = CU.resolve_account(service, user)
a2916c06
MW
169 if service == 'master':
170 raise U.ExpectedError, \
171 (400, "Can't delete master accounts: use `deluser'")
172 D.DB.execute("""DELETE FROM services
173 WHERE service = $service AND user = $user""",
174 service = service, user = user)
82d4f64b
MW
175 if svc.manage_pwent_p:
176 svc.rmpwent(alias)
177 else:
178 OUT.warn("You must remove the `%s' password entry for `%s' by hand" %
179 (service, user))
a2916c06
MW
180
181@SC.subcommand(
182 'editacct', ['admin'], "Modify USER's SERVICE account record.",
183 opts = [SC.Opt('alias', '-a', '--alias',
184 "change USER's login name for SERVICE",
185 argname = 'ALIAS'),
186 SC.Opt('noalias', '-A', '--no-alias',
187 "use USER's master login name")],
188 params = [SC.Arg('user'), SC.Arg('service')])
189def cmd_editacct(user, service, alias = None, noalias = False):
190 with D.DB:
191 CU.resolve_account(service, user)
192 if service == 'master':
193 raise U.ExpectedError, (400, "Can't edit master accounts")
194 CU.edit_records('services', 'user = $user AND service = $service',
195 [('alias', alias, noalias)],
196 user = user, service = service)
197
198@SC.subcommand(
4f09f456 199 'source', ['admin', 'userv', 'remote'], """\
a2916c06
MW
200Write source code (in `.tar.gz' format) to standard output.""")
201def cmd_source_admin():
202 AGPL.source(OUT)
203
204###----- That's all, folks --------------------------------------------------