github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/acceptancetests/assess_user_grant_revoke.py (about) 1 #!/usr/bin/env python 2 """This testsuite is intended to test basic user permissions. Users 3 can be granted read or full privileges by model. Revoking those 4 privileges should remove them. 5 6 A read permission user can see things such as status and 7 perform read-only commands. A write permission user has 8 equivalent powers as an admin""" 9 10 from __future__ import print_function 11 12 import argparse 13 from collections import namedtuple 14 import copy 15 import json 16 import logging 17 import random 18 import string 19 import subprocess 20 import sys 21 22 import pexpect 23 24 from deploy_stack import ( 25 BootstrapManager, 26 ) 27 from utility import ( 28 JujuAssertionError, 29 add_basic_testing_arguments, 30 configure_logging, 31 temp_dir, 32 ) 33 34 __metaclass__ = type 35 36 37 log = logging.getLogger("assess_user_grant_revoke") 38 39 User = namedtuple('User', ['name', 'permissions', 'expect']) 40 41 42 USER_LIST_CTRL = [{"access": "superuser", "user-name": "admin", 43 "display-name": "admin"}] 44 USER_LIST_CTRL_READ = copy.deepcopy(USER_LIST_CTRL) 45 # Created user has no display name, bug 1606354 46 USER_LIST_CTRL_READ.append( 47 {"access": "login", "user-name": "readuser"}) 48 USER_LIST_CTRL_WRITE = copy.deepcopy(USER_LIST_CTRL) 49 # bug 1606354 50 USER_LIST_CTRL_WRITE.append({"access": "login", "user-name": "writeuser"}) 51 USER_LIST_CTRL_ADMIN = copy.deepcopy(USER_LIST_CTRL) 52 # bug 1606354 53 USER_LIST_CTRL_ADMIN.append( 54 {"access": "superuser", "user-name": "adminuser"}) 55 SHARE_LIST_CTRL = {"admin": {"display-name": "admin", 56 "access": "admin"}} 57 SHARE_LIST_CTRL_READ = copy.deepcopy(SHARE_LIST_CTRL) 58 SHARE_LIST_CTRL_READ["readuser"] = {"access": "read"} 59 SHARE_LIST_CTRL_WRITE = copy.deepcopy(SHARE_LIST_CTRL) 60 SHARE_LIST_CTRL_WRITE["writeuser"] = {"access": "write"} 61 SHARE_LIST_CTRL_ADMIN = copy.deepcopy(SHARE_LIST_CTRL) 62 SHARE_LIST_CTRL_ADMIN["adminuser"] = {"access": "admin"} 63 64 65 def _generate_random_string(): 66 # We prefix a letter because usernames must begin with letter 67 return 'r'.join(random.choice( 68 string.ascii_letters + string.digits) for _ in range(9)) 69 70 71 def assert_equal(found, expected): 72 found = sorted(found) 73 expected = sorted(expected) 74 if found != expected: 75 raise JujuAssertionError( 76 'Found: {}\nExpected: {}'.format(found, expected)) 77 78 79 def assert_command_fails(check_callable, command_type, permission): 80 try: 81 check_callable() 82 except subprocess.CalledProcessError: 83 pass 84 else: 85 raise JujuAssertionError( 86 'FAIL User performed {} operation with ' 87 'permission {}'.format(command_type, permission)) 88 89 90 def assert_command_succeeds(check_callable, command_type, permission): 91 try: 92 check_callable() 93 except subprocess.CalledProcessError: 94 raise JujuAssertionError( 95 'FAIL User unable to perform {} operation with ' 96 'permission {}'.format(command_type, permission)) 97 98 99 def list_users(client): 100 """Test listing all the users""" 101 users_list = json.loads(client.get_juju_output('list-users', '--format', 102 'json', include_e=False)) 103 for user in users_list: 104 user.pop("date-created", None) 105 user.pop("last-connection", None) 106 return users_list 107 108 109 def list_shares(client): 110 """Test listing users' shares""" 111 model_data = json.loads( 112 client.get_juju_output( 113 'show-model', '--format', 'json', include_e=False)) 114 share_list = model_data[client.model_name]['users'] 115 for key, value in share_list.iteritems(): 116 value.pop("last-connection", None) 117 return share_list 118 119 120 def show_user(client): 121 """Test showing a user's status""" 122 user_status = json.loads(client.get_juju_output('show-user', '--format', 123 'json', include_e=False)) 124 user_status.pop("date-created", None) 125 user_status.pop("last-connection", None) 126 return user_status 127 128 129 def assess_read_operations(client, permission, has_permission): 130 read_commands = ( 131 client.show_status, 132 lambda: client.juju('show-model', (), include_e=False), 133 ) 134 135 for command in read_commands: 136 if has_permission: 137 assert_command_succeeds(command, 'read', permission) 138 else: 139 assert_command_fails(command, 'read', permission) 140 141 142 def assess_write_operations(client, permission, has_permission): 143 tags = '"{}={}"'.format(client.env.user_name, permission) 144 write_commands = ( 145 lambda: client.set_env_option('resource-tags', tags), 146 ) 147 148 for command in write_commands: 149 if has_permission: 150 assert_command_succeeds(command, 'write', permission) 151 else: 152 assert_command_fails(command, 'write', permission) 153 154 155 def assess_admin_operations(client, permission, has_permission): 156 # Create a username for the user client to interact with 157 new_read_user = _generate_random_string() 158 new_admin_user = _generate_random_string() 159 admin_commands = ( 160 lambda: client.add_user(new_read_user), 161 lambda: client.grant(new_read_user, permission="read"), 162 lambda: client.remove_user(new_read_user), 163 lambda: client.add_user_perms(new_admin_user, permissions="admin"), 164 lambda: client.remove_user(new_admin_user), 165 ) 166 167 for command in admin_commands: 168 if has_permission: 169 assert_command_succeeds(command, 'admin', permission) 170 else: 171 assert_command_fails(command, 'admin', permission) 172 173 174 def assert_read_model(client, permission, has_permission): 175 """Test if the user has or doesn't have the read permission""" 176 log.info('Checking read model acl {}'.format(client.env.user_name)) 177 assess_read_operations(client, permission, has_permission) 178 log.info('PASS {} read acl'.format(client.env.user_name)) 179 180 181 def assert_write_model(client, permission, has_permission): 182 """Test if the user has or doesn't have the write permission""" 183 log.info('Checking write model acl {}'.format(client.env.user_name)) 184 assess_write_operations(client, permission, has_permission) 185 log.info('PASS {} write model acl'.format(client.env.user_name)) 186 187 188 def assert_admin_model(controller_client, client, permission, has_permission): 189 """Test if the user has or doesn't have the admin permission""" 190 log.info('Checking admin acl with {}'.format(client.env.user_name)) 191 assess_admin_operations(client, permission, has_permission) 192 log.info('PASS {} admin acl'.format(client.env.user_name)) 193 194 195 def assert_user_permissions(user, user_client, controller_client): 196 """Test if users' permissions are within expectations""" 197 expect = iter(user.expect) 198 permission = user.permissions 199 assert_read_model(user_client, permission, expect.next()) 200 assert_write_model(user_client, permission, expect.next()) 201 assert_admin_model( 202 controller_client, user_client, permission, expect.next()) 203 204 log.info("Revoking {} permission from {}".format( 205 user.permissions, user.name)) 206 controller_client.revoke(user.name, permissions=user.permissions) 207 log.info('Revoke accepted') 208 209 assert_read_model(user_client, permission, expect.next()) 210 assert_write_model(user_client, permission, expect.next()) 211 assert_admin_model( 212 controller_client, user_client, permission, expect.next()) 213 214 215 def assert_change_password(client, user, password): 216 """Test changing user's password""" 217 log.info('Checking change-user-password') 218 try: 219 child = client.expect('change-user-password', (user.name,), 220 include_e=False) 221 child.expect('(?i)password') 222 child.sendline(password) 223 child.expect('(?i)password') 224 child.sendline(password) 225 client._end_pexpect_session(child) 226 except pexpect.TIMEOUT: 227 log.error('Buffer: {}'.format(child.buffer)) 228 log.error('Before: {}'.format(child.before)) 229 raise JujuAssertionError( 230 'FAIL Changing user password failed: ' 231 'pexpect process exited with {}'.format(child.exitstatus)) 232 log.info('PASS change-user-password') 233 234 235 def assert_disable_enable(controller_client, user): 236 """Test disabling and enabling users""" 237 original_user_list = list_users(controller_client) 238 log.info('Checking disabled {}'.format(user.name)) 239 controller_client.disable_user(user.name) 240 log.info('Disabled {}'.format(user.name)) 241 user_list = list_users(controller_client) 242 log.info('Checking list-users {}'.format(user.name)) 243 assert_equal(user_list, USER_LIST_CTRL) 244 log.info('Checking enable {}'.format(user.name)) 245 controller_client.enable_user(user.name) 246 log.info('Enabled {}'.format(user.name)) 247 user_list = list_users(controller_client) 248 log.info('Checking list-users {}'.format(user.name)) 249 assert_equal(user_list, original_user_list) 250 251 252 def assert_user_status(client, user, expected_users, expected_shares): 253 """Test listing users and shares against expected values""" 254 log.info('Checking list-users {}'.format(user.name)) 255 user_list = list_users(client) 256 assert_equal(user_list, expected_users) 257 log.info('Checking list-shares {}'.format(user.name)) 258 share_list = list_shares(client) 259 assert_equal(share_list, expected_shares) 260 261 262 def assert_logout_login(controller_client, user_client, user, 263 fake_home, password, expected_users): 264 """Test users' login and logout""" 265 original_user_list = list_users(controller_client) 266 user_client.logout() 267 log.info('Checking list-users after logout') 268 user_list = list_users(controller_client) 269 assert_equal(user_list, expected_users) 270 log.info('Checking list-users after login') 271 user_client.login_user(user.name, password) 272 user_list = list_users(controller_client) 273 assert_equal(user_list, original_user_list) 274 275 276 def assert_read_user(controller_client, user): 277 """Assess the operations of read user""" 278 log.info('Checking read {}'.format(user.name)) 279 with temp_dir() as fake_home: 280 user_client = controller_client.register_user( 281 user, fake_home) 282 user_client.env.user_name = user.name 283 assert_user_status(controller_client, user, 284 USER_LIST_CTRL_READ, SHARE_LIST_CTRL_READ) 285 286 password = _generate_random_string() 287 assert_change_password(user_client, user, password) 288 assert_logout_login(controller_client, user_client, 289 user, fake_home, password, USER_LIST_CTRL_READ) 290 assert_user_permissions(user, user_client, controller_client) 291 assert_disable_enable(controller_client, user) 292 controller_client.remove_user(user.name) 293 log.info('PASS read {}'.format(user.name)) 294 295 296 def assert_write_user(controller_client, user): 297 """Assess the operations of write user""" 298 log.info('Checking write {}'.format(user.name)) 299 with temp_dir() as fake_home: 300 user_client = controller_client.register_user( 301 user, fake_home) 302 user_client.env.user_name = user.name 303 assert_user_status(controller_client, user, 304 USER_LIST_CTRL_WRITE, SHARE_LIST_CTRL_WRITE) 305 306 password = _generate_random_string() 307 assert_change_password(user_client, user, password) 308 assert_logout_login(controller_client, user_client, 309 user, fake_home, password, USER_LIST_CTRL_WRITE) 310 assert_user_permissions(user, user_client, controller_client) 311 assert_disable_enable(controller_client, user) 312 controller_client.remove_user(user.name) 313 log.info('PASS write {}'.format(user.name)) 314 315 316 def assert_admin_user(controller_client, user): 317 """Assess the operations of admin user""" 318 log.info('Checking admin {}'.format(user.name)) 319 with temp_dir() as fake_home: 320 user_client = controller_client.register_user( 321 user, fake_home) 322 controller_client.grant(user_name=user.name, permission="superuser") 323 user_client.env.user_name = user.name 324 assert_user_status(controller_client, user, 325 USER_LIST_CTRL_ADMIN, SHARE_LIST_CTRL_ADMIN) 326 327 password = _generate_random_string() 328 assert_change_password(user_client, user, password) 329 assert_logout_login(controller_client, user_client, 330 user, fake_home, password, USER_LIST_CTRL_ADMIN) 331 assert_user_permissions(user, user_client, controller_client) 332 assert_disable_enable(controller_client, user) 333 controller_client.remove_user(user.name) 334 log.info('PASS admin {}'.format(user.name)) 335 336 337 def assert_controller(controller_client): 338 log.info('Checking list-users admin') 339 user_list = list_users(controller_client) 340 assert_equal(user_list, USER_LIST_CTRL) 341 342 log.info('Checking list-shares admin') 343 share_list = list_shares(controller_client) 344 assert_equal(share_list, SHARE_LIST_CTRL) 345 346 log.info('Checking show-user admin') 347 user_status = show_user(controller_client) 348 assert_equal(user_status, USER_LIST_CTRL[0]) 349 350 351 def assess_user_grant_revoke(controller_client): 352 """Test multi-users functionality""" 353 log.info('STARTING grant/revoke permissions') 354 controller_client.env.user_name = 'admin' 355 log.info("Creating Users: readuser, writeuser, adminuser") 356 read_user = User('readuser', 'read', 357 [True, False, False, False, False, False]) 358 write_user = User('writeuser', 'write', 359 [True, True, False, True, False, False]) 360 admin_user = User('adminuser', 'admin', 361 [True, True, True, True, True, True]) 362 363 # check controller client 364 assert_controller(controller_client) 365 366 # check each type of user 367 assert_read_user(controller_client, read_user) 368 assert_write_user(controller_client, write_user) 369 assert_admin_user(controller_client, admin_user) 370 371 log.info('SUCCESS grant/revoke permissions') 372 373 374 def parse_args(argv): 375 """Parse all arguments.""" 376 parser = argparse.ArgumentParser( 377 description="Test grant and revoke permissions for users") 378 add_basic_testing_arguments(parser, existing=False) 379 return parser.parse_args(argv) 380 381 382 def main(argv=None): 383 args = parse_args(argv) 384 configure_logging(args.verbose) 385 bs_manager = BootstrapManager.from_args(args) 386 with bs_manager.booted_context(args.upload_tools): 387 assess_user_grant_revoke(bs_manager.client) 388 return 0 389 390 391 if __name__ == '__main__': 392 sys.exit(main())