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())