midpoint-cli 1.6.0.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- midpoint_cli/__init__.py +234 -0
- midpoint_cli/client/__init__.py +199 -0
- midpoint_cli/client/audit.py +178 -0
- midpoint_cli/client/audit_query.py +247 -0
- midpoint_cli/client/configuration.py +15 -0
- midpoint_cli/client/constants.py +37 -0
- midpoint_cli/client/errors.py +74 -0
- midpoint_cli/client/logging_config.py +122 -0
- midpoint_cli/client/midpoint_client.py +866 -0
- midpoint_cli/client/object_query.py +230 -0
- midpoint_cli/client/objects.py +814 -0
- midpoint_cli/client/observer.py +29 -0
- midpoint_cli/client/patch.py +113 -0
- midpoint_cli/client/progress.py +80 -0
- midpoint_cli/client/rest.py +490 -0
- midpoint_cli/client/safe_xml.py +24 -0
- midpoint_cli/client/server_info.py +130 -0
- midpoint_cli/client/server_version.py +74 -0
- midpoint_cli/client/session.py +142 -0
- midpoint_cli/client/task_definitions.py +110 -0
- midpoint_cli/prompt/__init__.py +196 -0
- midpoint_cli/prompt/audit.py +253 -0
- midpoint_cli/prompt/base.py +218 -0
- midpoint_cli/prompt/complete.py +291 -0
- midpoint_cli/prompt/configuration.py +258 -0
- midpoint_cli/prompt/connector.py +45 -0
- midpoint_cli/prompt/console.py +818 -0
- midpoint_cli/prompt/delete.py +89 -0
- midpoint_cli/prompt/get.py +66 -0
- midpoint_cli/prompt/info.py +53 -0
- midpoint_cli/prompt/log.py +98 -0
- midpoint_cli/prompt/org.py +122 -0
- midpoint_cli/prompt/output.py +33 -0
- midpoint_cli/prompt/put.py +71 -0
- midpoint_cli/prompt/report.py +69 -0
- midpoint_cli/prompt/resource.py +217 -0
- midpoint_cli/prompt/role.py +96 -0
- midpoint_cli/prompt/script.py +154 -0
- midpoint_cli/prompt/search.py +51 -0
- midpoint_cli/prompt/shadow.py +87 -0
- midpoint_cli/prompt/task.py +296 -0
- midpoint_cli/prompt/user.py +406 -0
- midpoint_cli-1.6.0.dev3.dist-info/METADATA +1334 -0
- midpoint_cli-1.6.0.dev3.dist-info/RECORD +47 -0
- midpoint_cli-1.6.0.dev3.dist-info/WHEEL +4 -0
- midpoint_cli-1.6.0.dev3.dist-info/entry_points.txt +3 -0
- midpoint_cli-1.6.0.dev3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from midpoint_cli.client import (
|
|
8
|
+
MidpointObjectList,
|
|
9
|
+
MidpointObjectType,
|
|
10
|
+
MidpointObjectTypes,
|
|
11
|
+
MidpointOrganization,
|
|
12
|
+
MidpointRole,
|
|
13
|
+
MidpointServerError,
|
|
14
|
+
MidpointShadow,
|
|
15
|
+
MidpointUser,
|
|
16
|
+
)
|
|
17
|
+
from midpoint_cli.client.objects import MidpointObject
|
|
18
|
+
|
|
19
|
+
# User command wrapper parser
|
|
20
|
+
from midpoint_cli.prompt.base import PromptBase, add_listing_arguments
|
|
21
|
+
from midpoint_cli.prompt.complete import ROLE, USER, completes
|
|
22
|
+
from midpoint_cli.prompt.console import (
|
|
23
|
+
create_table,
|
|
24
|
+
get_console,
|
|
25
|
+
is_machine_output,
|
|
26
|
+
print_details,
|
|
27
|
+
print_error,
|
|
28
|
+
print_info,
|
|
29
|
+
print_rows,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
user_parser = ArgumentParser(
|
|
33
|
+
formatter_class=RawTextHelpFormatter,
|
|
34
|
+
prog='user',
|
|
35
|
+
description='Manage server users.',
|
|
36
|
+
epilog="""
|
|
37
|
+
Available commands:
|
|
38
|
+
ls List all users.
|
|
39
|
+
search Search for a user.
|
|
40
|
+
get Get user details by OID or name.
|
|
41
|
+
assign Assign a role, org, archetype or service to a user.
|
|
42
|
+
unassign Remove a role, org, archetype or service assignment from a user.
|
|
43
|
+
enable Enable users.
|
|
44
|
+
disable Disable users.
|
|
45
|
+
passwd Set the password of a user.
|
|
46
|
+
""",
|
|
47
|
+
)
|
|
48
|
+
user_parser.add_argument('command', help='User command to execute.')
|
|
49
|
+
user_parser.add_argument('arg', help='Optional command arguments.', nargs='*')
|
|
50
|
+
|
|
51
|
+
user_ls_parser = add_listing_arguments(
|
|
52
|
+
ArgumentParser(
|
|
53
|
+
formatter_class=RawTextHelpFormatter,
|
|
54
|
+
prog='user ls',
|
|
55
|
+
description='List users.',
|
|
56
|
+
),
|
|
57
|
+
'users',
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# User search parser
|
|
61
|
+
|
|
62
|
+
user_search_parser = ArgumentParser(
|
|
63
|
+
formatter_class=RawTextHelpFormatter,
|
|
64
|
+
prog='user search',
|
|
65
|
+
description='Search for users by substring of their name, full name or email address, ignoring case.\n'
|
|
66
|
+
'Terms are combined with OR. Prefix a term with + to make it mandatory (AND).',
|
|
67
|
+
)
|
|
68
|
+
user_search_parser.add_argument(
|
|
69
|
+
'searchquery',
|
|
70
|
+
help='String fragments to search in user data. Use +term to require a match (AND); plain terms are optional (OR).',
|
|
71
|
+
nargs='+',
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
user_get_parser = ArgumentParser(
|
|
75
|
+
formatter_class=RawTextHelpFormatter,
|
|
76
|
+
prog='user get',
|
|
77
|
+
description='Search for users by OID.',
|
|
78
|
+
)
|
|
79
|
+
completes(user_get_parser.add_argument('oid', help='An OID or name value.'), USER)
|
|
80
|
+
|
|
81
|
+
# The assignment target types `user assign/unassign` accept, by option.
|
|
82
|
+
ASSIGNMENT_TARGET_TYPES: dict[str, tuple[MidpointObjectType, str]] = {
|
|
83
|
+
'role': (MidpointObjectTypes.ROLE.value, 'Role'),
|
|
84
|
+
'org': (MidpointObjectTypes.ORG.value, 'Org'),
|
|
85
|
+
'archetype': (MidpointObjectTypes.ARCHETYPE.value, 'Archetype'),
|
|
86
|
+
'service': (MidpointObjectTypes.SERVICE.value, 'Service'),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _assignment_parser(prog: str, description: str) -> ArgumentParser:
|
|
91
|
+
parser = ArgumentParser(
|
|
92
|
+
formatter_class=RawTextHelpFormatter,
|
|
93
|
+
prog=prog,
|
|
94
|
+
description=description,
|
|
95
|
+
epilog='The target is a role unless --org, --archetype or --service says otherwise.',
|
|
96
|
+
)
|
|
97
|
+
completes(parser.add_argument('user', help='User OID or name.'), USER)
|
|
98
|
+
completes(
|
|
99
|
+
parser.add_argument('target', help='Role (or org, archetype, service) OID or name.'),
|
|
100
|
+
ROLE,
|
|
101
|
+
)
|
|
102
|
+
target_type = parser.add_mutually_exclusive_group()
|
|
103
|
+
for option in ASSIGNMENT_TARGET_TYPES:
|
|
104
|
+
article = 'an' if option[0] in 'aeiou' else 'a'
|
|
105
|
+
target_type.add_argument(
|
|
106
|
+
f'--{option}',
|
|
107
|
+
dest='target_type',
|
|
108
|
+
action='store_const',
|
|
109
|
+
const=option,
|
|
110
|
+
help=f'The target is {article} {option}.',
|
|
111
|
+
)
|
|
112
|
+
parser.set_defaults(target_type='role')
|
|
113
|
+
return parser
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
user_assign_parser = _assignment_parser('user assign', 'Assign a role, org, archetype or service to a user.')
|
|
117
|
+
user_unassign_parser = _assignment_parser(
|
|
118
|
+
'user unassign', 'Remove the assignment of a role, org, archetype or service from a user.'
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
user_enable_parser = ArgumentParser(
|
|
122
|
+
formatter_class=RawTextHelpFormatter,
|
|
123
|
+
prog='user enable',
|
|
124
|
+
description='Enable users: set their administrative status to enabled.',
|
|
125
|
+
)
|
|
126
|
+
completes(user_enable_parser.add_argument('user', help='User OID or name.', nargs='+'), USER)
|
|
127
|
+
|
|
128
|
+
user_disable_parser = ArgumentParser(
|
|
129
|
+
formatter_class=RawTextHelpFormatter,
|
|
130
|
+
prog='user disable',
|
|
131
|
+
description='Disable users: set their administrative status to disabled.',
|
|
132
|
+
)
|
|
133
|
+
completes(user_disable_parser.add_argument('user', help='User OID or name.', nargs='+'), USER)
|
|
134
|
+
|
|
135
|
+
user_passwd_parser = ArgumentParser(
|
|
136
|
+
formatter_class=RawTextHelpFormatter,
|
|
137
|
+
prog='user passwd',
|
|
138
|
+
description='Set the password of a user.',
|
|
139
|
+
epilog='On a terminal, the new password is asked twice and not echoed. Otherwise it is read from\n'
|
|
140
|
+
'the first line of standard input, e.g. printf \'%s\\n\' "$PW" | midpoint-cli user passwd jdoe\n'
|
|
141
|
+
'(in a command script piped to midpoint-cli, the line after the command).\n'
|
|
142
|
+
'The password is never printed or logged; the server checks it against its password policy.',
|
|
143
|
+
)
|
|
144
|
+
completes(user_passwd_parser.add_argument('user', help='User OID or name.'), USER)
|
|
145
|
+
user_passwd_parser.add_argument(
|
|
146
|
+
'--password-env',
|
|
147
|
+
metavar='VAR',
|
|
148
|
+
help='Read the new password from the environment variable VAR instead.',
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Each subcommand with its parser; None for those taking no arguments.
|
|
152
|
+
user_subparsers: dict[str, ArgumentParser | None] = {
|
|
153
|
+
'ls': user_ls_parser,
|
|
154
|
+
'search': user_search_parser,
|
|
155
|
+
'get': user_get_parser,
|
|
156
|
+
'assign': user_assign_parser,
|
|
157
|
+
'unassign': user_unassign_parser,
|
|
158
|
+
'enable': user_enable_parser,
|
|
159
|
+
'disable': user_disable_parser,
|
|
160
|
+
'passwd': user_passwd_parser,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# The records shown with a user, by key in the machine formats, with their table title and columns.
|
|
164
|
+
USER_SECTION_TITLES = {'Roles': 'Role Assignments', 'Orgs': 'Org Memberships', 'Projections': 'Projections'}
|
|
165
|
+
USER_SECTION_COLUMNS: dict[str, tuple[str, ...]] = {
|
|
166
|
+
'Roles': ('OID', 'Name', 'Status'),
|
|
167
|
+
'Orgs': ('OID', 'Name', 'Status'),
|
|
168
|
+
'Projections': ('Shadow OID', 'Resource', 'Account Name', 'Kind', 'Intent'),
|
|
169
|
+
}
|
|
170
|
+
_SECTION_COLUMN_STYLES = {
|
|
171
|
+
'OID': 'blue',
|
|
172
|
+
'Shadow OID': 'blue',
|
|
173
|
+
'Name': 'bright_cyan',
|
|
174
|
+
'Resource': 'bright_cyan',
|
|
175
|
+
'Account Name': 'bright_cyan',
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _text(value: object) -> str:
|
|
180
|
+
return '' if value is None else str(value)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _assignment_row(target: MidpointObject) -> dict[str, str]:
|
|
184
|
+
"""A role or org of a user, as a row of its section."""
|
|
185
|
+
return {
|
|
186
|
+
'OID': _text(target.get('OID')),
|
|
187
|
+
'Name': _text(target.get('DisplayName') or target.get('Name')),
|
|
188
|
+
'Status': _text(target.get('Status')),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _projection_row(shadow: MidpointShadow) -> dict[str, str]:
|
|
193
|
+
"""A shadow linked to a user, as a row of the Projections section."""
|
|
194
|
+
return {
|
|
195
|
+
'Shadow OID': _text(shadow.get('OID')),
|
|
196
|
+
'Resource': _text(shadow.get('ResourceName') or shadow.get('ResourceOID')),
|
|
197
|
+
'Account Name': _text(shadow.get('Name')),
|
|
198
|
+
'Kind': _text(shadow.get('Kind')),
|
|
199
|
+
'Intent': _text(shadow.get('Intent')),
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def user_details(user: MidpointUser, sections: dict[str, list[dict[str, str]]]) -> dict[str, Any]:
|
|
204
|
+
"""A user as one detail object: its OID, every attribute shown in the table, then its sections.
|
|
205
|
+
|
|
206
|
+
An attribute present several times (a multi-valued property) becomes a list of values.
|
|
207
|
+
"""
|
|
208
|
+
details: dict[str, Any] = {'OID': user.get_oid()}
|
|
209
|
+
for name, value in user.get_all_attributes():
|
|
210
|
+
if name not in details:
|
|
211
|
+
details[name] = value
|
|
212
|
+
elif isinstance(details[name], list):
|
|
213
|
+
details[name].append(value)
|
|
214
|
+
else:
|
|
215
|
+
details[name] = [details[name], value]
|
|
216
|
+
details.update(sections)
|
|
217
|
+
return details
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class UserClientPrompt(PromptBase):
|
|
221
|
+
subparsers_user = user_subparsers
|
|
222
|
+
|
|
223
|
+
def do_user(self, inp: str) -> None:
|
|
224
|
+
"""Manage users (subcommands: ls, search, get, assign, unassign, enable, disable, passwd)."""
|
|
225
|
+
self._dispatch_subcommand(
|
|
226
|
+
inp,
|
|
227
|
+
user_parser,
|
|
228
|
+
user_subparsers,
|
|
229
|
+
{
|
|
230
|
+
'ls': self._user_ls,
|
|
231
|
+
'search': self._user_search,
|
|
232
|
+
'get': self._user_get,
|
|
233
|
+
'assign': lambda args: self._assignment(True, user_assign_parser.parse_args(args)),
|
|
234
|
+
'unassign': lambda args: self._assignment(False, user_unassign_parser.parse_args(args)),
|
|
235
|
+
'enable': lambda args: self._set_enabled(True, user_enable_parser.parse_args(args).user),
|
|
236
|
+
'disable': lambda args: self._set_enabled(False, user_disable_parser.parse_args(args).user),
|
|
237
|
+
'passwd': self._user_passwd,
|
|
238
|
+
},
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def _user_ls(self, args: list[str]) -> None:
|
|
242
|
+
ls_ns = user_ls_parser.parse_args(args)
|
|
243
|
+
self.print_users(self.client.get_users(limit=ls_ns.limit, name_filter=ls_ns.filter))
|
|
244
|
+
|
|
245
|
+
def _user_search(self, args: list[str]) -> None:
|
|
246
|
+
search_ns = user_search_parser.parse_args(args)
|
|
247
|
+
self.print_users(self.client.search_users(search_ns.searchquery))
|
|
248
|
+
|
|
249
|
+
def _user_get(self, args: list[str]) -> None:
|
|
250
|
+
get_ns = user_get_parser.parse_args(args)
|
|
251
|
+
user = self.client.get_user(self.client.resolve_oid(MidpointObjectTypes.USER.value, get_ns.oid))
|
|
252
|
+
|
|
253
|
+
roles: list[MidpointRole] = []
|
|
254
|
+
for oid in user.get_role_assignment_oids():
|
|
255
|
+
try:
|
|
256
|
+
roles.append(self.client.get_role(oid))
|
|
257
|
+
except MidpointServerError:
|
|
258
|
+
roles.append(MidpointRole.error_stub(oid))
|
|
259
|
+
|
|
260
|
+
orgs: list[MidpointOrganization] = []
|
|
261
|
+
for oid in user.get_org_assignment_oids():
|
|
262
|
+
try:
|
|
263
|
+
orgs.append(self.client.get_org(oid))
|
|
264
|
+
except MidpointServerError:
|
|
265
|
+
orgs.append(MidpointOrganization.error_stub(oid))
|
|
266
|
+
|
|
267
|
+
shadows: list[MidpointShadow] = []
|
|
268
|
+
for oid in user.get_link_ref_oids():
|
|
269
|
+
try:
|
|
270
|
+
shadow = self.client.get_shadow(oid)
|
|
271
|
+
resource_oid = shadow.get('ResourceOID')
|
|
272
|
+
if resource_oid:
|
|
273
|
+
try:
|
|
274
|
+
resource = self.client.get_resource(resource_oid)
|
|
275
|
+
shadow['ResourceName'] = resource.get('Name') or resource_oid
|
|
276
|
+
except MidpointServerError:
|
|
277
|
+
shadow['ResourceName'] = resource_oid
|
|
278
|
+
shadows.append(shadow)
|
|
279
|
+
except MidpointServerError:
|
|
280
|
+
shadows.append(MidpointShadow.error_stub(oid))
|
|
281
|
+
|
|
282
|
+
self.print_user(user, roles=roles, orgs=orgs, shadows=shadows)
|
|
283
|
+
|
|
284
|
+
def _user_passwd(self, args: list[str]) -> None:
|
|
285
|
+
passwd_ns = user_passwd_parser.parse_args(args)
|
|
286
|
+
oid = self.client.resolve_oid(MidpointObjectTypes.USER.value, passwd_ns.user)
|
|
287
|
+
password = self._read_new_password(passwd_ns.password_env)
|
|
288
|
+
if password is None:
|
|
289
|
+
return
|
|
290
|
+
self.client.set_user_password(oid, password)
|
|
291
|
+
print_info(f'Password of user {passwd_ns.user} changed')
|
|
292
|
+
|
|
293
|
+
def _assignment(self, assign: bool, ns: Namespace) -> None:
|
|
294
|
+
"""user assign/unassign: add or remove the assignment of a role, org, archetype or service."""
|
|
295
|
+
target_type, label = ASSIGNMENT_TARGET_TYPES[ns.target_type]
|
|
296
|
+
user_obj = self._require_object(MidpointObjectTypes.USER.value, ns.user, 'User')
|
|
297
|
+
if user_obj is None:
|
|
298
|
+
return
|
|
299
|
+
target_obj = self._require_object(target_type, ns.target, label)
|
|
300
|
+
if target_obj is None:
|
|
301
|
+
return
|
|
302
|
+
user_oid = user_obj.get_oid()
|
|
303
|
+
target_oid = target_obj.get_oid()
|
|
304
|
+
if user_oid is None or target_oid is None:
|
|
305
|
+
raise AssertionError
|
|
306
|
+
if assign:
|
|
307
|
+
self.client.assign(user_oid, target_type, target_oid)
|
|
308
|
+
print_info(f"{label} '{target_obj.get_name()}' assigned to '{user_obj.get_name()}'")
|
|
309
|
+
else:
|
|
310
|
+
self.client.unassign(user_oid, target_type, target_oid)
|
|
311
|
+
print_info(f"{label} '{target_obj.get_name()}' unassigned from '{user_obj.get_name()}'")
|
|
312
|
+
|
|
313
|
+
def _set_enabled(self, enabled: bool, references: list[str]) -> None:
|
|
314
|
+
"""user enable/disable: set the administrative status of each user, reporting each failure."""
|
|
315
|
+
for reference in references:
|
|
316
|
+
user_obj = self._find_reference(MidpointObjectTypes.USER.value, reference, 'User')
|
|
317
|
+
if user_obj is None:
|
|
318
|
+
continue
|
|
319
|
+
user_oid = user_obj.get_oid()
|
|
320
|
+
if user_oid is None:
|
|
321
|
+
raise AssertionError
|
|
322
|
+
try:
|
|
323
|
+
self.client.set_user_enabled(user_oid, enabled)
|
|
324
|
+
print_info(f"User '{user_obj.get_name()}' {'enabled' if enabled else 'disabled'}")
|
|
325
|
+
except MidpointServerError as e:
|
|
326
|
+
self._fail()
|
|
327
|
+
print_error(str(e))
|
|
328
|
+
|
|
329
|
+
def _read_new_password(self, environment_variable: str | None) -> str | None:
|
|
330
|
+
"""The new password for user passwd, or None after setting the error. It is never echoed."""
|
|
331
|
+
if environment_variable is not None:
|
|
332
|
+
password = os.environ.get(environment_variable)
|
|
333
|
+
if password is None:
|
|
334
|
+
self._fail(f'Environment variable {environment_variable} is not set; password not changed')
|
|
335
|
+
return None
|
|
336
|
+
elif sys.stdin.isatty():
|
|
337
|
+
password = getpass.getpass('New password: ')
|
|
338
|
+
if getpass.getpass('Retype new password: ') != password:
|
|
339
|
+
self._fail('The passwords do not match; password not changed')
|
|
340
|
+
return None
|
|
341
|
+
else:
|
|
342
|
+
line = sys.stdin.readline()
|
|
343
|
+
if not line:
|
|
344
|
+
self._fail('No password on standard input; password not changed')
|
|
345
|
+
return None
|
|
346
|
+
password = line.rstrip('\r\n')
|
|
347
|
+
if not password:
|
|
348
|
+
self._fail('Empty password refused; password not changed')
|
|
349
|
+
return None
|
|
350
|
+
return password
|
|
351
|
+
|
|
352
|
+
@staticmethod
|
|
353
|
+
def print_user(
|
|
354
|
+
user: MidpointUser,
|
|
355
|
+
roles: list[MidpointRole],
|
|
356
|
+
orgs: list[MidpointOrganization],
|
|
357
|
+
shadows: list[MidpointShadow],
|
|
358
|
+
) -> None:
|
|
359
|
+
"""Print a single user's details with related records, in the selected output format."""
|
|
360
|
+
sections = {
|
|
361
|
+
'Roles': [_assignment_row(role) for role in roles],
|
|
362
|
+
'Orgs': [_assignment_row(org) for org in orgs],
|
|
363
|
+
'Projections': [_projection_row(shadow) for shadow in shadows],
|
|
364
|
+
}
|
|
365
|
+
if is_machine_output():
|
|
366
|
+
print_details([user_details(user, sections)], as_list=False)
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
console = get_console()
|
|
370
|
+
table = create_table(title='User Details')
|
|
371
|
+
table.add_column('Attribute', style='cyan')
|
|
372
|
+
table.add_column('Value', style='white')
|
|
373
|
+
|
|
374
|
+
for attr in user.get_all_attributes():
|
|
375
|
+
table.add_row(str(attr[0]), str(attr[1]))
|
|
376
|
+
|
|
377
|
+
console.print(table)
|
|
378
|
+
|
|
379
|
+
for key, title in USER_SECTION_TITLES.items():
|
|
380
|
+
section_table = create_table(title=title)
|
|
381
|
+
columns = USER_SECTION_COLUMNS[key]
|
|
382
|
+
for column in columns:
|
|
383
|
+
section_table.add_column(column, style=_SECTION_COLUMN_STYLES.get(column, 'white'))
|
|
384
|
+
for row in sections[key]:
|
|
385
|
+
section_table.add_row(*(row[column] for column in columns))
|
|
386
|
+
if not sections[key]:
|
|
387
|
+
# '(none)' in the name column.
|
|
388
|
+
section_table.add_row(*('(none)' if 'Name' in column else '' for column in columns))
|
|
389
|
+
console.print(section_table)
|
|
390
|
+
|
|
391
|
+
@staticmethod
|
|
392
|
+
def print_users(users: MidpointObjectList) -> None:
|
|
393
|
+
"""Print a list of users in the selected output format."""
|
|
394
|
+
print_rows(users, title='Server Users', noun='users')
|
|
395
|
+
|
|
396
|
+
@staticmethod
|
|
397
|
+
def help_user() -> None:
|
|
398
|
+
user_parser.print_help()
|
|
399
|
+
|
|
400
|
+
def do_users(self, inp: str) -> None:
|
|
401
|
+
"""Shortcut for 'user ls' — list all users."""
|
|
402
|
+
self._list_shortcut(inp, 'users', 'user', self.do_user)
|
|
403
|
+
|
|
404
|
+
@staticmethod
|
|
405
|
+
def help_users() -> None:
|
|
406
|
+
print('List all server users. This is a shortcut for "user ls"')
|