| 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 | |
| 26 | from __future__ import with_statement |
| 27 | |
| 28 | import agpl as AGPL |
| 29 | import cmdutil as CU |
| 30 | import dbmaint as D |
| 31 | from output import OUT, PRINT |
| 32 | import subcommand as SC |
| 33 | import util as U |
| 34 | |
| 35 | @SC.subcommand( |
| 36 | 'listusers', ['admin'], 'List the existing users.', |
| 37 | opts = [SC.Opt('service', '-s', '--service', |
| 38 | 'list users with SERVICE accounts', |
| 39 | argname = 'SERVICE')], |
| 40 | oparams = [SC.Arg('pat')]) |
| 41 | def cmd_listuser(service = None, pat = None): |
| 42 | CU.format_list(CU.list_users(service, pat), |
| 43 | [CU.column('USER', "~={0.user}A", 3), |
| 44 | CU.column('EMAIL', "~={0.email}:[---~;~={0.email}A~]")]) |
| 45 | |
| 46 | @SC.subcommand( |
| 47 | 'adduser', ['admin'], 'Add a user to the master database.', |
| 48 | opts = [SC.Opt('email', '-e', '--email', |
| 49 | "email address for the new user", |
| 50 | argname = 'EMAIL')], |
| 51 | params = [SC.Arg('user')]) |
| 52 | def cmd_adduser(user, email = None): |
| 53 | with D.DB: |
| 54 | CU.check_user(user, False) |
| 55 | D.DB.execute("INSERT INTO users (user, email) VALUES ($user, $email)", |
| 56 | user = user, email = email) |
| 57 | D.DB.execute("""INSERT INTO services (user, service) |
| 58 | VALUES ($user, $service)""", |
| 59 | user = user, service = 'master') |
| 60 | |
| 61 | @SC.subcommand( |
| 62 | 'deluser', ['admin'], 'Remove USER from the master database.', |
| 63 | params = [SC.Arg('user')]) |
| 64 | def cmd_deluser(user, email = None): |
| 65 | with D.DB: |
| 66 | CU.check_user(user) |
| 67 | D.DB.execute("DELETE FROM users WHERE user = $user", user = user) |
| 68 | |
| 69 | @SC.subcommand( |
| 70 | 'edituser', ['admin'], 'Modify a user record.', |
| 71 | opts = [SC.Opt('email', '-e', '--email', |
| 72 | "change USER's email address", |
| 73 | argname = 'EMAIL'), |
| 74 | SC.Opt('noemail', '-E', '--no-email', |
| 75 | "forget USER's email address"), |
| 76 | SC.Opt('rename', '-r', '--rename', |
| 77 | "rename USER", |
| 78 | argname = 'NEW-NAME')], |
| 79 | params = [SC.Arg('user')]) |
| 80 | def cmd_edituser(user, email = None, noemail = False, rename = None): |
| 81 | with D.DB: |
| 82 | CU.check_user(user) |
| 83 | if rename is not None: check_user(rename, False) |
| 84 | CU.edit_records('users', 'user = $user', |
| 85 | [('email', email, noemail), |
| 86 | ('user', rename, False)], |
| 87 | user = user) |
| 88 | |
| 89 | @SC.subcommand( |
| 90 | 'delsvc', ['admin'], "Remove all records for SERVICE.", |
| 91 | params = [SC.Arg('service')]) |
| 92 | def cmd_delsvc(service): |
| 93 | with D.DB: |
| 94 | CU.check_service(service, must_config_p = False, must_records_p = True) |
| 95 | D.DB.execute("DELETE FROM services WHERE service = $service", |
| 96 | service = service) |
| 97 | |
| 98 | @SC.subcommand( |
| 99 | 'editsvc', ['admin'], "Edit a given SERVICE.", |
| 100 | opts = [SC.Opt('rename', '-r', '--rename', "rename the SERVICE", |
| 101 | argname = 'NEW-NAME')], |
| 102 | params = [SC.Arg('service')]) |
| 103 | def cmd_editsvc(service, rename = None): |
| 104 | with D.DB: |
| 105 | if service == 'master': |
| 106 | raise U.ExpectedError, (400, "Can't edit the master service") |
| 107 | if rename is None: |
| 108 | CU.check_service(service, must_config_p = True, must_records_p = True) |
| 109 | else: |
| 110 | CU.check_service(service, must_config_p = False, must_records_p = True) |
| 111 | CU.check_service(rename, must_config_p = True, must_records_p = False) |
| 112 | CU.edit_records('services', 'service = $service', |
| 113 | [('service', rename, False)], |
| 114 | service = service) |
| 115 | |
| 116 | @SC.subcommand( |
| 117 | 'addacct', ['admin'], 'Add an account for a user.', |
| 118 | opts = [SC.Opt('alias', '-a', '--alias', |
| 119 | "alias by which USER is known to SERVICE", |
| 120 | argname = 'ALIAS')], |
| 121 | params = [SC.Arg('user'), SC.Arg('service')]) |
| 122 | def cmd_addacct(user, service, alias = None): |
| 123 | with D.DB: |
| 124 | CU.check_user(user) |
| 125 | CU.check_service(service) |
| 126 | D.DB.execute("""SELECT 1 FROM services |
| 127 | WHERE user = $user AND service = $service""", |
| 128 | user = user, service = service) |
| 129 | if D.DB.fetchone() is not None: |
| 130 | raise U.ExpectedError, ( |
| 131 | 400, "User `%s' already has `%s' account" % (user, service)) |
| 132 | D.DB.execute("""INSERT INTO services (service, user, alias) |
| 133 | VALUES ($service, $user, $alias)""", |
| 134 | service = service, user = user, alias = alias) |
| 135 | |
| 136 | @SC.subcommand( |
| 137 | 'delacct', ['admin'], "Remove USER's SERVICE account.", |
| 138 | params = [SC.Arg('user'), SC.Arg('service')]) |
| 139 | def cmd_delacct(user, service): |
| 140 | with D.DB: |
| 141 | CU.resolve_account(service, user) |
| 142 | if service == 'master': |
| 143 | raise U.ExpectedError, \ |
| 144 | (400, "Can't delete master accounts: use `deluser'") |
| 145 | D.DB.execute("""DELETE FROM services |
| 146 | WHERE service = $service AND user = $user""", |
| 147 | service = service, user = user) |
| 148 | |
| 149 | @SC.subcommand( |
| 150 | 'editacct', ['admin'], "Modify USER's SERVICE account record.", |
| 151 | opts = [SC.Opt('alias', '-a', '--alias', |
| 152 | "change USER's login name for SERVICE", |
| 153 | argname = 'ALIAS'), |
| 154 | SC.Opt('noalias', '-A', '--no-alias', |
| 155 | "use USER's master login name")], |
| 156 | params = [SC.Arg('user'), SC.Arg('service')]) |
| 157 | def cmd_editacct(user, service, alias = None, noalias = False): |
| 158 | with D.DB: |
| 159 | CU.resolve_account(service, user) |
| 160 | if service == 'master': |
| 161 | raise U.ExpectedError, (400, "Can't edit master accounts") |
| 162 | CU.edit_records('services', 'user = $user AND service = $service', |
| 163 | [('alias', alias, noalias)], |
| 164 | user = user, service = service) |
| 165 | |
| 166 | @SC.subcommand( |
| 167 | 'source', ['admin', 'userv', 'remote'], """\ |
| 168 | Write source code (in `.tar.gz' format) to standard output.""") |
| 169 | def cmd_source_admin(): |
| 170 | AGPL.source(OUT) |
| 171 | |
| 172 | ###----- That's all, folks -------------------------------------------------- |