57809bf89db04254fa23b2ed10ab623c92f677dd
[osm/osmclient.git] / osmclient / cli_commands / rbac.py
1 # Copyright ETSI Contributors and Others.
2 # All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 import click
17 from osmclient.common.exceptions import ClientException
18 from osmclient.cli_commands import utils
19 from prettytable import PrettyTable
20 import json
21 import logging
22
23 logger = logging.getLogger("osmclient")
24
25
26 ##############################
27 # Role Management Operations #
28 ##############################
29
30
31 @click.command(name="role-create", short_help="creates a new role")
32 @click.argument("name")
33 @click.option("--permissions", default=None, help="role permissions using a dictionary")
34 @click.pass_context
35 def role_create(ctx, name, permissions):
36 """
37 Creates a new role.
38
39 \b
40 NAME: Name or ID of the role.
41 DEFINITION: Definition of grant/denial of access to resources.
42 """
43 logger.debug("")
44 utils.check_client_version(ctx.obj, ctx.command.name)
45 ctx.obj.role.create(name, permissions)
46
47
48 @click.command(name="role-update", short_help="updates a role")
49 @click.argument("name")
50 @click.option("--set-name", default=None, help="change name of rle")
51 @click.option(
52 "--add",
53 default=None,
54 help="yaml format dictionary with permission: True/False to access grant/denial",
55 )
56 @click.option("--remove", default=None, help="yaml format list to remove a permission")
57 @click.pass_context
58 def role_update(ctx, name, set_name, add, remove):
59 """
60 Updates a role.
61
62 \b
63 NAME: Name or ID of the role.
64 DEFINITION: Definition overwrites the old definition.
65 ADD: Grant/denial of access to resource to add.
66 REMOVE: Grant/denial of access to resource to remove.
67 """
68 logger.debug("")
69 utils.check_client_version(ctx.obj, ctx.command.name)
70 ctx.obj.role.update(name, set_name, None, add, remove)
71
72
73 @click.command(name="role-delete", short_help="deletes a role")
74 @click.argument("name")
75 @click.pass_context
76 def role_delete(ctx, name):
77 """
78 Deletes a role.
79
80 \b
81 NAME: Name or ID of the role.
82 """
83 logger.debug("")
84 utils.check_client_version(ctx.obj, ctx.command.name)
85 ctx.obj.role.delete(name)
86
87
88 @click.command(name="role-list", short_help="list all roles")
89 @click.option(
90 "--filter",
91 default=None,
92 multiple=True,
93 help="restricts the list to the projects matching the filter",
94 )
95 @click.pass_context
96 def role_list(ctx, filter):
97 """
98 List all roles.
99 """
100 logger.debug("")
101 utils.check_client_version(ctx.obj, ctx.command.name)
102 if filter:
103 filter = "&".join(filter)
104 resp = ctx.obj.role.list(filter)
105 table = PrettyTable(["name", "id"])
106 for role in resp:
107 table.add_row([role["name"], role["_id"]])
108 table.align = "l"
109 print(table)
110
111
112 @click.command(name="role-show", short_help="show specific role")
113 @click.argument("name")
114 @click.pass_context
115 def role_show(ctx, name):
116 """
117 Shows the details of a role.
118
119 \b
120 NAME: Name or ID of the role.
121 """
122 logger.debug("")
123 utils.check_client_version(ctx.obj, ctx.command.name)
124 resp = ctx.obj.role.get(name)
125
126 table = PrettyTable(["key", "attribute"])
127 for k, v in resp.items():
128 table.add_row([k, json.dumps(v, indent=2)])
129 table.align = "l"
130 print(table)
131
132
133 ####################
134 # Project mgmt operations
135 ####################
136
137
138 @click.command(name="project-create", short_help="creates a new project")
139 @click.argument("name")
140 # @click.option('--description',
141 # default='no description',
142 # help='human readable description')
143 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
144 @click.option(
145 "--quotas",
146 "quotas",
147 multiple=True,
148 default=None,
149 help="provide quotas. Can be used several times: 'quota1=number[,quota2=number,...]'. Quotas can be one "
150 "of vnfds, nsds, nsts, pdus, nsrs, nsis, vim_accounts, wim_accounts, sdns, k8sclusters, k8srepos",
151 )
152 @click.pass_context
153 def project_create(ctx, name, domain_name, quotas):
154 """Creates a new project
155
156 NAME: name of the project
157 DOMAIN_NAME: optional domain name for the project when keystone authentication is used
158 QUOTAS: set quotas for the project
159 """
160 logger.debug("")
161 project = {"name": name}
162 if domain_name:
163 project["domain_name"] = domain_name
164 quotas_dict = _process_project_quotas(quotas)
165 if quotas_dict:
166 project["quotas"] = quotas_dict
167
168 utils.check_client_version(ctx.obj, ctx.command.name)
169 ctx.obj.project.create(name, project)
170
171
172 def _process_project_quotas(quota_list):
173 quotas_dict = {}
174 if not quota_list:
175 return quotas_dict
176 try:
177 for quota in quota_list:
178 for single_quota in quota.split(","):
179 k, v = single_quota.split("=")
180 quotas_dict[k] = None if v in ("None", "null", "") else int(v)
181 except (ValueError, TypeError):
182 raise ClientException(
183 "invalid format for 'quotas'. Use 'k1=v1,v1=v2'. v must be a integer or null"
184 )
185 return quotas_dict
186
187
188 @click.command(name="project-delete", short_help="deletes a project")
189 @click.argument("name")
190 @click.pass_context
191 def project_delete(ctx, name):
192 """deletes a project
193
194 NAME: name or ID of the project to be deleted
195 """
196 logger.debug("")
197 utils.check_client_version(ctx.obj, ctx.command.name)
198 ctx.obj.project.delete(name)
199
200
201 @click.command(name="project-list", short_help="list all projects")
202 @click.option(
203 "--filter",
204 default=None,
205 multiple=True,
206 help="restricts the list to the projects matching the filter",
207 )
208 @click.pass_context
209 def project_list(ctx, filter):
210 """list all projects"""
211 logger.debug("")
212 utils.check_client_version(ctx.obj, ctx.command.name)
213 if filter:
214 filter = "&".join(filter)
215 resp = ctx.obj.project.list(filter)
216 table = PrettyTable(["name", "id"])
217 for proj in resp:
218 table.add_row([proj["name"], proj["_id"]])
219 table.align = "l"
220 print(table)
221
222
223 @click.command(name="project-show", short_help="shows the details of a project")
224 @click.argument("name")
225 @click.pass_context
226 def project_show(ctx, name):
227 """shows the details of a project
228
229 NAME: name or ID of the project
230 """
231 logger.debug("")
232 utils.check_client_version(ctx.obj, ctx.command.name)
233 resp = ctx.obj.project.get(name)
234
235 table = PrettyTable(["key", "attribute"])
236 for k, v in resp.items():
237 table.add_row([k, json.dumps(v, indent=2)])
238 table.align = "l"
239 print(table)
240
241
242 @click.command(
243 name="project-update", short_help="updates a project (only the name can be updated)"
244 )
245 @click.argument("project")
246 @click.option("--name", default=None, help="new name for the project")
247 @click.option(
248 "--quotas",
249 "quotas",
250 multiple=True,
251 default=None,
252 help="change quotas. Can be used several times: 'quota1=number|empty[,quota2=...]' "
253 "(use empty to reset quota to default",
254 )
255 @click.pass_context
256 def project_update(ctx, project, name, quotas):
257 """
258 Update a project name
259
260 :param ctx:
261 :param project: id or name of the project to modify
262 :param name: new name for the project
263 :param quotas: change quotas of the project
264 :return:
265 """
266 logger.debug("")
267 project_changes = {}
268 if name:
269 project_changes["name"] = name
270 quotas_dict = _process_project_quotas(quotas)
271 if quotas_dict:
272 project_changes["quotas"] = quotas_dict
273
274 utils.check_client_version(ctx.obj, ctx.command.name)
275 ctx.obj.project.update(project, project_changes)
276
277
278 ####################
279 # User mgmt operations
280 ####################
281
282
283 @click.command(name="user-create", short_help="creates a new user")
284 @click.argument("username")
285 @click.option(
286 "--password",
287 prompt=True,
288 hide_input=True,
289 confirmation_prompt=True,
290 help="user password",
291 )
292 @click.option(
293 "--projects",
294 # prompt="Comma separate list of projects",
295 multiple=True,
296 callback=lambda ctx, param, value: "".join(value).split(",")
297 if all(len(x) == 1 for x in value)
298 else value,
299 help="list of project ids that the user belongs to",
300 )
301 @click.option(
302 "--project-role-mappings",
303 "project_role_mappings",
304 default=None,
305 multiple=True,
306 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
307 )
308 @click.option("--domain-name", "domain_name", default=None, help="assign to a domain")
309 @click.pass_context
310 def user_create(ctx, username, password, projects, project_role_mappings, domain_name):
311 """Creates a new user
312
313 \b
314 USERNAME: name of the user
315 PASSWORD: password of the user
316 PROJECTS: projects assigned to user (internal only)
317 PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone)
318 DOMAIN_NAME: optional domain name for the user when keystone authentication is used
319 """
320 logger.debug("")
321 user = {}
322 user["username"] = username
323 user["password"] = password
324 user["projects"] = projects
325 user["project_role_mappings"] = project_role_mappings
326 if domain_name:
327 user["domain_name"] = domain_name
328
329 utils.check_client_version(ctx.obj, ctx.command.name)
330 ctx.obj.user.create(username, user)
331
332
333 @click.command(name="user-update", short_help="updates user information")
334 @click.argument("username")
335 @click.option(
336 "--password",
337 # prompt=True,
338 # hide_input=True,
339 # confirmation_prompt=True,
340 help="user password",
341 )
342 @click.option("--set-username", "set_username", default=None, help="change username")
343 @click.option(
344 "--set-project",
345 "set_project",
346 default=None,
347 multiple=True,
348 help="create/replace the roles for this project: 'project,role1[,role2,...]'",
349 )
350 @click.option(
351 "--remove-project",
352 "remove_project",
353 default=None,
354 multiple=True,
355 help="removes project from user: 'project'",
356 )
357 @click.option(
358 "--add-project-role",
359 "add_project_role",
360 default=None,
361 multiple=True,
362 help="assign role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
363 )
364 @click.option(
365 "--remove-project-role",
366 "remove_project_role",
367 default=None,
368 multiple=True,
369 help="remove role(s) in a project. Can be used several times: 'project,role1[,role2,...]'",
370 )
371 @click.option("--change_password", "change_password", help="user's current password")
372 @click.option(
373 "--new_password",
374 "new_password",
375 help="user's new password to update in expiry condition",
376 )
377 @click.pass_context
378 def user_update(
379 ctx,
380 username,
381 password,
382 set_username,
383 set_project,
384 remove_project,
385 add_project_role,
386 remove_project_role,
387 change_password,
388 new_password,
389 ):
390 """Update a user information
391
392 \b
393 USERNAME: name of the user
394 PASSWORD: new password
395 SET_USERNAME: new username
396 SET_PROJECT: creating mappings for project/role(s)
397 REMOVE_PROJECT: deleting mappings for project/role(s)
398 ADD_PROJECT_ROLE: adding mappings for project/role(s)
399 REMOVE_PROJECT_ROLE: removing mappings for project/role(s)
400 CHANGE_PASSWORD: user's current password to change
401 NEW_PASSWORD: user's new password to update in expiry condition
402 """
403 logger.debug("")
404 user = {}
405 user["password"] = password
406 user["username"] = set_username
407 user["set-project"] = set_project
408 user["remove-project"] = remove_project
409 user["add-project-role"] = add_project_role
410 user["remove-project-role"] = remove_project_role
411 user["change_password"] = change_password
412 user["new_password"] = new_password
413
414 utils.check_client_version(ctx.obj, ctx.command.name)
415 ctx.obj.user.update(username, user)
416 if not user.get("change_password"):
417 ctx.obj.user.update(username, user)
418 else:
419 ctx.obj.user.update(username, user, pwd_change=True)
420
421
422 @click.command(name="user-delete", short_help="deletes a user")
423 @click.argument("name")
424 # @click.option('--force', is_flag=True, help='forces the deletion bypassing pre-conditions')
425 @click.pass_context
426 def user_delete(ctx, name):
427 """deletes a user
428
429 \b
430 NAME: name or ID of the user to be deleted
431 """
432 logger.debug("")
433 utils.check_client_version(ctx.obj, ctx.command.name)
434 ctx.obj.user.delete(name)
435
436
437 @click.command(name="user-list", short_help="list all users")
438 @click.option(
439 "--filter",
440 default=None,
441 multiple=True,
442 help="restricts the list to the users matching the filter",
443 )
444 @click.pass_context
445 def user_list(ctx, filter):
446 """list all users"""
447 utils.check_client_version(ctx.obj, ctx.command.name)
448 if filter:
449 filter = "&".join(filter)
450 resp = ctx.obj.user.list(filter)
451 table = PrettyTable(["name", "id"])
452 for user in resp:
453 table.add_row([user["username"], user["_id"]])
454 table.align = "l"
455 print(table)
456
457
458 @click.command(name="user-show", short_help="shows the details of a user")
459 @click.argument("name")
460 @click.pass_context
461 def user_show(ctx, name):
462 """shows the details of a user
463
464 NAME: name or ID of the user
465 """
466 logger.debug("")
467 utils.check_client_version(ctx.obj, ctx.command.name)
468 resp = ctx.obj.user.get(name)
469 if "password" in resp:
470 resp["password"] = "********"
471
472 table = PrettyTable(["key", "attribute"])
473 for k, v in resp.items():
474 table.add_row([k, json.dumps(v, indent=2)])
475 table.align = "l"
476 print(table)