| 1 | ### -*-python-*- |
| 2 | ### |
| 3 | ### User 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 getpass as GP |
| 29 | import sys as SYS |
| 30 | |
| 31 | import cmdutil as CU |
| 32 | import dbmaint as D |
| 33 | import operation as OP |
| 34 | from output import PRINT |
| 35 | import service as S |
| 36 | import subcommand as SC |
| 37 | import util as U |
| 38 | |
| 39 | OMSG = [None, |
| 40 | "Partial failure", |
| 41 | "Operation failed", |
| 42 | "No services selected"] |
| 43 | |
| 44 | def operate(op, accts, *args, **kw): |
| 45 | """ |
| 46 | Perform a request as indicated by the arguments (see `operation.operate' |
| 47 | for the full details), and report the results. |
| 48 | """ |
| 49 | |
| 50 | ## Collect the results. |
| 51 | o, ii, rq, ops = OP.operate(op, accts, *args, **kw) |
| 52 | |
| 53 | ## Report any additional information collected. |
| 54 | if o.nwin and ii: |
| 55 | CU.format_list(ii, |
| 56 | [CU.column('RESULT', "~={0.desc}A"), |
| 57 | CU.column('VALUE', "~={0.value}A")]) |
| 58 | PRINT() |
| 59 | |
| 60 | ## Report the outcomes of the indvidual operations. |
| 61 | if ops: |
| 62 | CU.format_list(ops, |
| 63 | [CU.column('SERVICE', |
| 64 | "~={0.svc.friendly}A"), |
| 65 | CU.column('RESULT', "~={0.error}:[" |
| 66 | "OK~={0.result}@[ ~A~]~;" |
| 67 | "FAILED: ~={0.error.msg}A~]")]) |
| 68 | |
| 69 | ## If it failed, report an appropriate error. |
| 70 | if o.rc: |
| 71 | if o.nlose: PRINT() |
| 72 | raise U.ExpectedError, (400, OMSG[o.rc]) |
| 73 | |
| 74 | @SC.subcommand( |
| 75 | 'set', ['userv'], |
| 76 | """Sets the password for the SERVICES to a given string. If standard input |
| 77 | is a terminal, read the password interactively, with prompts, disabling echo, |
| 78 | and asking for confirmation to catch typos. Otherwise, just read one line |
| 79 | and use the result as the password.""", |
| 80 | rparam = SC.Arg('services')) |
| 81 | def cmd_set_userv(services): |
| 82 | accts = CU.resolve_accounts(CU.USER, services) |
| 83 | if not SYS.stdin.isatty(): |
| 84 | new = U.readline('new password') |
| 85 | else: |
| 86 | first = GP.getpass('Enter new password: ') |
| 87 | second = GP.getpass('Confirm new password: ') |
| 88 | if first != second: raise U.ExpectedError, (400, "Passwords don't match") |
| 89 | new = first |
| 90 | operate('set', accts, new) |
| 91 | |
| 92 | @SC.subcommand( |
| 93 | 'reset', ['userv'], |
| 94 | """Resets the password for the SERVICES.""", |
| 95 | rparam = SC.Arg('services')) |
| 96 | def cmd_reset_userv(services): |
| 97 | accts = CU.resolve_accounts(CU.USER, services) |
| 98 | operate('reset', accts) |
| 99 | |
| 100 | @SC.subcommand( |
| 101 | 'clear', ['userv'], |
| 102 | """Clears the password for the SERVICES, preventing access. This doesn't |
| 103 | work for all services, depending on how passwords are represented.""", |
| 104 | rparam = SC.Arg('services')) |
| 105 | def cmd_clear_userv(services): |
| 106 | accts = CU.resolve_accounts(CU.USER, services) |
| 107 | operate('clear', accts) |
| 108 | |
| 109 | @SC.subcommand('list', ['userv'], 'List available accounts') |
| 110 | def cmd_list_userv(): |
| 111 | CU.format_list(CU.list_accounts(CU.USER), |
| 112 | [CU.column('NAME', "~={0.service}A"), |
| 113 | CU.column('DESCRIPTION', "~={0.friendly}A"), |
| 114 | CU.column('LOGIN', "~={0.alias}:[---~;~={0.alias}A~]")]) |
| 115 | |
| 116 | ###----- That's all, folks -------------------------------------------------- |