forked from tinode/chat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmacros.py
339 lines (272 loc) · 12 KB
/
macros.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
"""Tinode command line macro definitions."""
import argparse
import tn_globals
from tn_globals import stdoutln
class Macro:
"""Macro base class. The external callers are expected to access
* self.parser - an instance of argparse.Argument parser which attempts to
turn a list of tokens into the corresponding argparse command.
* self.run() - executes the macro as instructed by the user."""
def __init__(self):
self.parser = argparse.ArgumentParser(prog=self.name(), description=self.description())
self.add_parser_args()
# Explain argument.
self.parser.add_argument('--explain', action='store_true', help='Only print out expanded macro')
def name(self):
"""Macro name."""
pass
def description(self):
"""Macro description."""
pass
def add_parser_args(self):
"""Method which adds custom command line arguments."""
pass
def expand(self, id, cmd, args):
"""Expands the macro to a list of basic Tinode CLI commands."""
pass
def run(self, id, cmd, args):
"""Expands the macro and returns the list of commands to actually execute to the caller
depending on the presence of the --explain argument.
"""
cmds = self.expand(id, cmd, args)
if cmd.explain:
if cmds is None:
return None
for item in cmds:
stdoutln(item)
return []
return cmds
class Usermod(Macro):
"""Modifies user account. The following modes are available:
* suspend/unsuspend account.
* change user's theCard (public name, description, avatar, private comment).
This macro requires root privileges."""
def name(self):
return "usermod"
def description(self):
return 'Modify user account (requires root privileges)'
def add_parser_args(self):
self.parser.add_argument('userid', help='user to update')
self.parser.add_argument('-L', '--suspend', action='store_true', help='Suspend account')
self.parser.add_argument('-U', '--unsuspend', action='store_true', help='Unsuspend account')
self.parser.add_argument('--name', help='Public name')
self.parser.add_argument('--avatar', help='Avatar file name')
self.parser.add_argument('--comment', help='Private comment on account')
self.parser.add_argument('--note', help='Account description')
self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
# Suspend/unsuspend user.
if cmd.suspend or cmd.unsuspend:
if cmd.suspend and cmd.unsuspend:
stdoutln("Cannot both suspend and unsuspend account")
return None
new_cmd = 'acc --user %s' % cmd.userid
if cmd.suspend:
new_cmd += ' --suspend true'
if cmd.unsuspend:
new_cmd += ' --suspend false'
return [new_cmd]
# Change theCard.
varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp'
set_cmd = '.must ' + varname + ' set me'
if cmd.name is not None:
set_cmd += ' --fn="%s"' % cmd.name
if cmd.avatar is not None:
set_cmd += ' --photo="%s"' % cmd.avatar
if cmd.comment is not None:
set_cmd += ' --private="%s"' % cmd.comment
if cmd.note is not None:
set_cmd += ' --note="%s"' % cmd.note
if cmd.trusted is not None:
set_cmd += ' --trusted="%s"' % cmd.trusted
old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else ''
return ['.use --user %s' % cmd.userid,
'.must sub me',
set_cmd,
'.must leave me',
'.use --user "%s"' % old_user]
class Resolve(Macro):
"""Looks up user id by login name and prints it."""
def name(self):
return "resolve"
def description(self):
return "Resolve login and print the corresponding user id"
def add_parser_args(self):
self.parser.add_argument('login', help='login to resolve')
def expand(self, id, cmd, args):
if not cmd.login:
return None
varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp'
return ['.must sub fnd',
'.must set fnd --public=basic:%s' % cmd.login,
'.must %s get fnd --sub' % varname,
'.must leave fnd',
'.log %s.sub[0].user_id' % varname]
class Passwd(Macro):
"""Sets user's password (requires root privileges)."""
def name(self):
return "passwd"
def description(self):
return "Set user's password (requires root privileges)"
def add_parser_args(self):
self.parser.add_argument('userid', help='Id of the user')
self.parser.add_argument('-P', '--password', help='New password')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
if not cmd.password:
stdoutln("Password (-P) not specified")
return None
return ['acc --user %s --scheme basic --secret :%s' % (cmd.userid, cmd.password)]
class Useradd(Macro):
"""Creates a new user account."""
def name(self):
return "useradd"
def description(self):
return "Create a new user account"
def add_parser_args(self):
self.parser.add_argument('login', help='User login')
self.parser.add_argument('-P', '--password', help='Password')
self.parser.add_argument('--cred', help='List of comma-separated credentials in format "(email|tel):value1,(email|tel):value2,..."')
self.parser.add_argument('--name', help='Public name of the user')
self.parser.add_argument('--comment', help='Private comment')
self.parser.add_argument('--note', help='Public description')
self.parser.add_argument('--trusted', help='Add/remove trusted marker: verified, staff, danger')
self.parser.add_argument('--tags', help='Comma-separated list of tags')
self.parser.add_argument('--avatar', help='Path to avatar file')
self.parser.add_argument('--auth', help='Default auth acs')
self.parser.add_argument('--anon', help='Default anon acs')
def expand(self, id, cmd, args):
if not cmd.login:
return None
if not cmd.password:
stdoutln("Password --password must be specified")
return None
if not cmd.cred:
stdoutln("Must specify at least one credential: --cred.")
return None
varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp'
new_cmd = '.must ' + varname + ' acc --scheme basic --secret="%s:%s" --cred="%s"' % (cmd.login, cmd.password, cmd.cred)
if cmd.name:
new_cmd += ' --fn="%s"' % cmd.name
if cmd.comment:
new_cmd += ' --private="%s"' % cmd.comment
if cmd.note is not None:
set_cmd += ' --note="%s"' % cmd.note
if cmd.trusted is not None:
set_cmd += ' --trusted="%s"' % cmd.trusted
if cmd.tags:
new_cmd += ' --tags="%s"' % cmd.tags
if cmd.avatar:
new_cmd += ' --photo="%s"' % cmd.avatar
if cmd.auth:
new_cmd += ' --auth="%s"' % cmd.auth
if cmd.anon:
new_cmd += ' --anon="%s"' % cmd.anon
return [new_cmd]
class Chacs(Macro):
"""Modifies default acs (permissions) on a user account."""
def name(self):
return "chacs"
def description(self):
return "Change default permissions/acs for a user (requires root privileges)"
def add_parser_args(self):
self.parser.add_argument('userid', help='User id')
self.parser.add_argument('--auth', help='New auth acs value')
self.parser.add_argument('--anon', help='New anon acs value')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
if not cmd.auth and not cmd.anon:
stdoutln('Must specify at least either of --auth, --anon')
return None
set_cmd = '.must set me'
if cmd.auth:
set_cmd += ' --auth=%s' % cmd.auth
if cmd.anon:
set_cmd += ' --anon=%s' % cmd.anon
old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else ''
return ['.use --user %s' % cmd.userid,
'.must sub me',
set_cmd,
'.must leave me',
'.use --user "%s"' % old_user]
class Userdel(Macro):
"""Deletes a user account."""
def name(self):
return "userdel"
def description(self):
return "Delete user account (requires root privileges)"
def add_parser_args(self):
self.parser.add_argument('userid', help='User id')
self.parser.add_argument('--hard', action='store_true', help='Hard delete')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
del_cmd = 'del user --user %s' % cmd.userid
if cmd.hard:
del_cmd += ' --hard'
return [del_cmd]
class Chcred(Macro):
"""Adds, deletes or validates credentials for a user account."""
def name(self):
return "chcred"
def description(self):
return "Add/delete/validate credentials (requires root privileges)"
def add_parser_args(self):
self.parser.add_argument('userid', help='User id')
self.parser.add_argument('cred', help='Affected credential in formt method:value, e.g. email: [email protected], tel:17771112233')
self.parser.add_argument('--add', action='store_true', help='Add credential')
self.parser.add_argument('--rm', action='store_true', help='Delete credential')
self.parser.add_argument('--validate', action='store_true', help='Validate credential')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
if not cmd.cred:
stdoutln('Must specify cred')
return None
num_actions = (1 if cmd.add else 0) + (1 if cmd.rm else 0) + (1 if cmd.validate else 0)
if num_actions == 0 or num_actions > 1:
stdoutln('Must specify exactly one action: --add, --rm, --validate')
return None
if cmd.add:
cred_cmd = '.must set me --cred %s' % cmd.cred
if cmd.rm:
cred_cmd = '.must del --topic me --cred %s cred' % cmd.cred
old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else ''
return ['.use --user %s' % cmd.userid,
'.must sub me',
cred_cmd,
'.must leave me',
'.use --user "%s"' % old_user]
class Thecard(Macro):
"""Prints user's theCard."""
def name(self):
return "thecard"
def description(self):
return "Print theCard for a user (requires root privileges)"
def add_parser_args(self):
self.parser.add_argument('userid', help='User id')
self.parser.add_argument('--what', choices=['desc', 'cred'], required=True, help='Type of data to print (desc - public/private data, cred - list of credentials.')
def expand(self, id, cmd, args):
if not cmd.userid:
return None
varname = cmd.varname if hasattr(cmd, 'varname') and cmd.varname else '$temp'
old_user = tn_globals.DefaultUser if tn_globals.DefaultUser else ''
return ['.use --user %s' % cmd.userid,
'.must sub me',
'.must %s get me --%s' % (varname, cmd.what),
'.must leave me',
'.use --user "%s"' % old_user,
'.log %s' % varname]
def parse_macro(parts):
"""Attempts to find a parser for the provided sequence of tokens."""
global Macros
# parts[0] is the command.
macro = Macros.get(parts[0])
if not macro:
return None
return macro.parser
Macros = {x.name(): x for x in [Usermod(), Resolve(), Passwd(), Useradd(), Chacs(), Userdel(), Chcred(), Thecard()]}