def purge_inquiries(logger): """Purge Inquiries that have exceeded their configured TTL At the moment, Inquiries do not have their own database model, so this function effectively is another, more specialized GC for executions. It will look for executions with a 'pending' status that use the 'inquirer' runner, which is the current definition for an Inquiry. Then it will mark those that have a nonzero TTL have existed longer than their TTL as "timed out". It will then request that the parent workflow(s) resume, where the failure can be handled as the user desires. """ # Get all existing Inquiries filters = {'runner__name': 'inquirer', 'status': action_constants.LIVEACTION_STATUS_PENDING} inquiries = list(ActionExecution.query(**filters)) gc_count = 0 # Inspect each Inquiry, and determine if TTL is expired for inquiry in inquiries: ttl = int(inquiry.result.get('ttl')) if ttl <= 0: logger.debug("Inquiry %s has a TTL of %s. Skipping." % (inquiry.id, ttl)) continue min_since_creation = int( (get_datetime_utc_now() - inquiry.start_timestamp).total_seconds() / 60 ) logger.debug("Inquiry %s has a TTL of %s and was started %s minute(s) ago" % ( inquiry.id, ttl, min_since_creation)) if min_since_creation > ttl: gc_count += 1 logger.info("TTL expired for Inquiry %s. Marking as timed out." % inquiry.id) liveaction_db = action_utils.update_liveaction_status( status=action_constants.LIVEACTION_STATUS_TIMED_OUT, result=inquiry.result, liveaction_id=inquiry.liveaction.get('id')) executions.update_execution(liveaction_db) # Call Inquiry runner's post_run to trigger callback to workflow action_db = get_action_by_ref(liveaction_db.action) invoke_post_run(liveaction_db=liveaction_db, action_db=action_db) if liveaction_db.context.get("parent"): # Request that root workflow resumes root_liveaction = action_service.get_root_liveaction(liveaction_db) action_service.request_resume( root_liveaction, UserDB(cfg.CONF.system_user.user) ) logger.info('Marked %s ttl-expired Inquiries as "timed out".' % (gc_count))
def post(self, api_key_api, requester_user): """ Create a new entry. """ permission_type = PermissionType.API_KEY_CREATE rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_api_permission( user_db=requester_user, resource_api=api_key_api, permission_type=permission_type) api_key_db = None api_key = None try: if not getattr(api_key_api, 'user', None): if requester_user: api_key_api.user = requester_user.name else: api_key_api.user = cfg.CONF.system_user.user try: User.get_by_name(api_key_api.user) except StackStormDBObjectNotFoundError: user_db = UserDB(name=api_key_api.user) User.add_or_update(user_db) extra = {'username': api_key_api.user, 'user': user_db} LOG.audit('Registered new user "%s".' % (api_key_api.user), extra=extra) # If key_hash is provided use that and do not create a new key. The assumption # is user already has the original api-key if not getattr(api_key_api, 'key_hash', None): api_key, api_key_hash = auth_util.generate_api_key_and_hash() # store key_hash in DB api_key_api.key_hash = api_key_hash api_key_db = ApiKey.add_or_update(ApiKeyAPI.to_model(api_key_api)) except (ValidationError, ValueError) as e: LOG.exception('Validation failed for api_key data=%s.', api_key_api) abort(http_client.BAD_REQUEST, six.text_type(e)) extra = {'api_key_db': api_key_db} LOG.audit('ApiKey created. ApiKey.id=%s' % (api_key_db.id), extra=extra) api_key_create_response_api = ApiKeyCreateResponseAPI.from_model( api_key_db) # Return real api_key back to user. A one-way hash of the api_key is stored in the DB # only the real value only returned at create time. Also, no masking of key here since # the user needs to see this value atleast once. api_key_create_response_api.key = api_key return Response(json=api_key_create_response_api, status=http_client.CREATED)
def create_token(username, ttl=None, metadata=None, add_missing_user=True): """ :param username: Username of the user to create the token for. If the account for this user doesn't exist yet it will be created. :type username: ``str`` :param ttl: Token TTL (in seconds). :type ttl: ``int`` :param metadata: Optional metadata to associate with the token. :type metadata: ``dict`` :param add_missing_user: Add the user given by `username` if they don't exist :type add_missing_user: ``bool`` """ if ttl: if ttl > cfg.CONF.auth.token_ttl: msg = 'TTL specified %s is greater than max allowed %s.' % ( ttl, cfg.CONF.auth.token_ttl) raise TTLTooLargeException(msg) else: ttl = cfg.CONF.auth.token_ttl if username: try: User.get_by_name(username) except: if add_missing_user: user_db = UserDB(name=username) User.add_or_update(user_db) extra = {'username': username, 'user': user_db} LOG.audit('Registered new user "%s".' % (username), extra=extra) else: raise UserNotFoundError() token = uuid.uuid4().hex expiry = date_utils.get_datetime_utc_now() + datetime.timedelta( seconds=ttl) token = TokenDB(user=username, token=token, expiry=expiry, metadata=metadata) Token.add_or_update(token) username_string = username if username else 'an anonymous user' token_expire_string = isotime.format(expiry, offset=False) extra = {'username': username, 'token_expiration': token_expire_string} LOG.audit('Access granted to "%s" with the token set to expire at "%s".' % (username_string, token_expire_string), extra=extra) return token
def sync_users_role_assignments(self, role_assignment_apis): """ Synchronize role assignments for all the users in the database. :param role_assignment_apis: Role assignments API objects for the assignments loaded from the files. :type role_assignment_apis: ``list`` of :class:`UserRoleAssignmentFileFormatAPI` :return: Dictionary with created and removed role assignments for each user. :rtype: ``dict`` """ LOG.info('Synchronizing users role assignments...') user_dbs = User.get_all() username_to_user_db_map = dict([(user_db.name, user_db) for user_db in user_dbs]) username_to_role_assignment_api_map = dict([ (role_assignment_api.username, role_assignment_api) for role_assignment_api in role_assignment_apis ]) # Note: We process assignments for all the users (ones specified in the assignment files # and ones which are in the databse). We want to make sure assignments are correctly # deleted from the databse for users which existing in the databse, but have no assignment # file on disk. all_usernames = (username_to_user_db_map.keys() + username_to_role_assignment_api_map.keys()) all_usernames = list(set(all_usernames)) results = {} for username in all_usernames: role_assignment_api = username_to_role_assignment_api_map.get( username, None) user_db = username_to_user_db_map.get(username, None) if not user_db: # Note: We allow assignments to be created for the users which don't exist in the # DB yet because user creation in StackStorm is lazy (we only create UserDB) object # when user first logs in. user_db = UserDB(name=username) LOG.debug(( 'User "%s" doesn\'t exist in the DB, creating assignment anyway' % (username))) role_assignment_dbs = rbac_services.get_role_assignments_for_user( user_db=user_db, include_remote=False) result = self._sync_user_role_assignments( user_db=user_db, role_assignment_dbs=role_assignment_dbs, role_assignment_api=role_assignment_api) results[username] = result LOG.info('User role assignments synchronized') return results
def _handle_schedule_execution(self, liveaction_api, requester_user, context_string=None, show_secrets=False): """ :param liveaction: LiveActionAPI object. :type liveaction: :class:`LiveActionAPI` """ if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) # Assert action ref is valid action_ref = liveaction_api.action action_db = action_utils.get_action_by_ref(action_ref) if not action_db: message = 'Action "%s" cannot be found.' % action_ref LOG.warning(message) abort(http_client.BAD_REQUEST, message) # Assert the permissions assert_user_has_resource_db_permission( user_db=requester_user, resource_db=action_db, permission_type=PermissionType.ACTION_EXECUTE) # Validate that the authenticated user is admin if user query param is provided user = liveaction_api.user or requester_user.name assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) try: return self._schedule_execution(liveaction=liveaction_api, requester_user=requester_user, user=user, context_string=context_string, show_secrets=show_secrets, pack=action_db.pack) except ValueError as e: LOG.exception('Unable to execute action.') abort(http_client.BAD_REQUEST, str(e)) except jsonschema.ValidationError as e: LOG.exception( 'Unable to execute action. Parameter validation failed.') abort(http_client.BAD_REQUEST, re.sub("u'([^']*)'", r"'\1'", e.message)) except trace_exc.TraceNotFoundException as e: abort(http_client.BAD_REQUEST, str(e)) except validation_exc.ValueValidationException as e: raise e except Exception as e: LOG.exception( 'Unable to execute action. Unexpected error encountered.') abort(http_client.INTERNAL_SERVER_ERROR, str(e))
def setUp(self): super(RBACServicesTestCase, self).setUp() # TODO: Share mocks self.users = {} self.roles = {} self.resources = {} # Create some mock users user_1_db = UserDB(name='admin') user_1_db = User.add_or_update(user_1_db) self.users['admin'] = user_1_db user_2_db = UserDB(name='observer') user_2_db = User.add_or_update(user_2_db) self.users['observer'] = user_2_db user_3_db = UserDB(name='no_roles') user_3_db = User.add_or_update(user_3_db) self.users['no_roles'] = user_3_db user_4_db = UserDB(name='custom_role') user_4_db = User.add_or_update(user_4_db) self.users['1_custom_role'] = user_4_db # Create some mock roles role_1_db = rbac_services.create_role(name='custom_role_1') role_2_db = rbac_services.create_role(name='custom_role_2', description='custom role 2') self.roles['custom_role_1'] = role_1_db self.roles['custom_role_2'] = role_2_db # Create some mock role assignments role_assignment_1 = UserRoleAssignmentDB(user=self.users['1_custom_role'].name, role=self.roles['custom_role_1'].name) role_assignment_1 = UserRoleAssignment.add_or_update(role_assignment_1) # Create some mock resources on which permissions can be granted rule_1_db = RuleDB(pack='test1', name='rule1', ref='test1.rule1') rule_1_db = Rule.add_or_update(rule_1_db) self.resources['rule_1'] = rule_1_db
def get_one( self, id, output_type="all", output_format="raw", existing_only=False, requester_user=None, show_secrets=False, ): # Special case for id == "last" if id == "last": execution_db = ActionExecution.query().order_by("-id").limit( 1).first() if not execution_db: raise ValueError("No executions found in the database") id = str(execution_db.id) if not requester_user: requester_user = UserDB(name=cfg.CONF.system_user.user) from_model_kwargs = { "mask_secrets": self._get_mask_secrets(requester_user, show_secrets=show_secrets) } execution_db = self._get_one_by_id( id=id, requester_user=requester_user, from_model_kwargs=from_model_kwargs, permission_type=PermissionType.EXECUTION_VIEW, ) execution_id = str(execution_db.id) query_filters = {} if output_type and output_type != "all": query_filters["output_type"] = output_type def existing_output_iter(): # Consume and return all of the existing lines # pylint: disable=no-member output_dbs = ActionExecutionOutput.query(execution_id=execution_id, **query_filters) output = "".join([output_db.data for output_db in output_dbs]) yield six.binary_type(output.encode("utf-8")) def make_response(): app_iter = existing_output_iter() res = Response(content_type="text/plain", app_iter=app_iter) return res res = make_response() return res
def test_admin_permissions_for_user_scoped_kvps(self): resolver = KeyValuePermissionsResolver() admin_user_db = self.users["admin"] # Setup users. No explicit grant, role, and assignment records should be # required for user to access their KVPs user_1_db = UserDB(name="user105") user_1_db = User.add_or_update(user_1_db) self.users[user_1_db.name] = user_1_db # Insert user scoped key value pairs for user1. key_1_name = "mykey5" key_1_ref = get_key_reference(FULL_USER_SCOPE, key_1_name, user_1_db.name) kvp_1_db = KeyValuePairDB( uid="%s:%s:%s" % (ResourceType.KEY_VALUE_PAIR, FULL_USER_SCOPE, key_1_ref), scope=FULL_USER_SCOPE, name=key_1_ref, value="myval5", ) kvp_1_db = KeyValuePair.add_or_update(kvp_1_db) self.resources[kvp_1_db.uid] = kvp_1_db # Admin user should have general list permissions on user1's kvps. self.assertUserHasResourceDbPermission( resolver=resolver, user_db=admin_user_db, resource_db=KeyValuePairDB(scope="%s:%s" % (FULL_USER_SCOPE, user_1_db.name)), permission_type=PermissionType.KEY_VALUE_PAIR_LIST, ) # Admin user should have all permissions to another user1's kvp. self.assertUserHasResourceDbPermission( resolver=resolver, user_db=admin_user_db, resource_db=kvp_1_db, permission_type=PermissionType.KEY_VALUE_PAIR_ALL, ) self.assertUserHasResourceDbPermissions( resolver=resolver, user_db=admin_user_db, resource_db=kvp_1_db, permission_types=self.read_permission_types, ) self.assertUserHasResourceDbPermissions( resolver=resolver, user_db=admin_user_db, resource_db=kvp_1_db, permission_types=self.write_permission_types, )
def delete(self, name, scope=FULL_SYSTEM_SCOPE, requester_user=None, user=None): """ Delete the key value pair. Handles requests: DELETE /keys/1 """ if not scope: scope = FULL_SYSTEM_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) lock_name = self._get_lock_name_for_key(name=key_ref, scope=scope) # Note: We use lock to avoid a race with self._coordinator.get_lock(lock_name): from_model_kwargs = {'mask_secrets': True} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) kvp_db = KeyValuePairAPI.to_model(kvp_api) LOG.debug( 'DELETE /keys/ lookup with scope=%s name=%s found object: %s', scope, name, kvp_db) try: KeyValuePair.delete(kvp_db) except Exception as e: LOG.exception( 'Database delete encountered exception during ' 'delete of name="%s". ', name) abort(http_client.INTERNAL_SERVER_ERROR, str(e)) return extra = {'kvp_db': kvp_db} LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra) return Response(status=http_client.NO_CONTENT)
def setUp(self): super(ActionControllerRBACTestCase, self).setUp() self.fixtures_loader.save_fixtures_to_db(fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_FIXTURES) file_name = 'action1.yaml' ActionControllerRBACTestCase.ACTION_1 = self.fixtures_loader.load_fixtures( fixtures_pack=FIXTURES_PACK, fixtures_dict={'actions': [file_name]})['actions'][file_name] # Insert mock users, roles and assignments self = self self.users = {} self.roles = {} # Users user_1_db = UserDB(name='no_permissions') user_1_db = User.add_or_update(user_1_db) self.users['no_permissions'] = user_1_db user_2_db = UserDB(name='action_create') user_2_db = User.add_or_update(user_2_db) self.users['action_create'] = user_2_db # Roles # action_create grant on parent pack grant_db = PermissionGrantDB( resource_uid='pack:examples', resource_type=ResourceType.PACK, permission_types=[PermissionType.ACTION_CREATE]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='action_create', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['action_create'] = role_1_db # Role assignments user_db = self.users['action_create'] role_assignment_db = UserRoleAssignmentDB( user=user_db.name, role=self.roles['action_create'].name) UserRoleAssignment.add_or_update(role_assignment_db)
def _insert_common_mock_users(self): # Insert common mock users user_1_db = UserDB(name='admin') user_1_db = User.add_or_update(user_1_db) self.users['admin'] = user_1_db user_2_db = UserDB(name='observer') user_2_db = User.add_or_update(user_2_db) self.users['observer'] = user_2_db user_3_db = UserDB(name='no_roles') user_3_db = User.add_or_update(user_3_db) self.users['no_roles'] = user_3_db user_4_db = UserDB(name='1_custom_role_no_permissions') user_4_db = User.add_or_update(user_4_db) self.users['1_custom_role_no_permissions'] = user_4_db user_5_db = UserDB(name='1_role_pack_grant') user_5_db = User.add_or_update(user_5_db) self.users['custom_role_pack_grant'] = user_5_db
def _insert_mock_data(self): # Insert some mock users user_1_db = UserDB(name='user_1') user_1_db = User.add_or_update(user_1_db) self.users['user_1'] = user_1_db user_2_db = UserDB(name='user_2') user_2_db = User.add_or_update(user_2_db) self.users['user_2'] = user_2_db user_3_db = UserDB(name='user_3') user_3_db = User.add_or_update(user_3_db) self.users['user_3'] = user_3_db user_5_db = UserDB(name='user_5') user_5_db = User.add_or_update(user_5_db) self.users['user_5'] = user_5_db user_6_db = UserDB(name='user_6') user_6_db = User.add_or_update(user_6_db) self.users['user_6'] = user_6_db
def setUp(self): super(BaseAPIControllerWithRBACTestCase, self).setUp() self.users = {} self.roles = {} # Run RBAC migrations run_all_rbac_migrations() # Insert mock users with default role assignments role_names = [ SystemRole.SYSTEM_ADMIN, SystemRole.ADMIN, SystemRole.OBSERVER ] for role_name in role_names: user_db = UserDB(name=role_name) user_db = User.add_or_update(user_db) self.users[role_name] = user_db role_assignment_db = UserRoleAssignmentDB( user=user_db.name, role=role_name, source='assignments/%s.yaml' % user_db.name) UserRoleAssignment.add_or_update(role_assignment_db) # Insert a user with no permissions and role assignments user_1_db = UserDB(name='no_permissions') user_1_db = User.add_or_update(user_1_db) self.users['no_permissions'] = user_1_db # Insert special system user user_2_db = UserDB(name='system_user') user_2_db = User.add_or_update(user_2_db) self.users['system_user'] = user_2_db role_assignment_db = UserRoleAssignmentDB( user=user_2_db.name, role=SystemRole.ADMIN, source='assignments/%s.yaml' % user_2_db.name) UserRoleAssignment.add_or_update(role_assignment_db)
def delete(self, id, requester_user, show_secrets=False): """ Stops a single execution. Handles requests: DELETE /executions/<id> """ if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) from_model_kwargs = { 'mask_secrets': self._get_mask_secrets(requester_user, show_secrets=show_secrets) } execution_api = self._get_one_by_id(id=id, requester_user=requester_user, from_model_kwargs=from_model_kwargs, permission_type=PermissionType.EXECUTION_STOP) if not execution_api: abort(http_client.NOT_FOUND, 'Execution with id %s not found.' % id) liveaction_id = execution_api.liveaction['id'] if not liveaction_id: abort(http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) try: liveaction_db = LiveAction.get_by_id(liveaction_id) except: abort(http_client.INTERNAL_SERVER_ERROR, 'Execution object missing link to liveaction %s.' % liveaction_id) if liveaction_db.status == action_constants.LIVEACTION_STATUS_CANCELED: LOG.info( 'Action %s already in "canceled" state; \ returning execution object.' % liveaction_db.id ) return execution_api if liveaction_db.status not in action_constants.LIVEACTION_CANCELABLE_STATES: abort(http_client.OK, 'Action cannot be canceled. State = %s.' % liveaction_db.status) try: (liveaction_db, execution_db) = action_service.request_cancellation( liveaction_db, requester_user.name or cfg.CONF.system_user.user) except: LOG.exception('Failed requesting cancellation for liveaction %s.', liveaction_db.id) abort(http_client.INTERNAL_SERVER_ERROR, 'Failed canceling execution.') return ActionExecutionAPI.from_model(execution_db, mask_secrets=from_model_kwargs['mask_secrets'])
def test_sync_remote_assignments_are_not_manipulated(self): # Verify remote assignments are not manipulated. syncer = RBACDefinitionsDBSyncer() self._insert_mock_roles() # Initial state, no roles user_db = UserDB(name='doesntexistwhaha') role_dbs = get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, []) # Create mock remote role assignment role_db = self.roles['role_3'] source = 'assignments/%s.yaml' % user_db.name role_assignment_db = assign_role_to_user(role_db=role_db, user_db=user_db, source=source, is_remote=True) self.assertTrue(role_assignment_db.is_remote) # Verify assignment has been created role_dbs = get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, [self.roles['role_3']]) # Do the sync with two roles defined - verify remote role assignment hasn't been # manipulated with. api = UserRoleAssignmentFileFormatAPI(username=user_db.name, roles=['role_1', 'role_2'], file_path='assignments/%s.yaml' % user_db.name) syncer.sync_users_role_assignments(role_assignment_apis=[api]) role_dbs = get_roles_for_user(user_db=user_db) self.assertEqual(len(role_dbs), 3) self.assertEqual(role_dbs[0], self.roles['role_1']) self.assertEqual(role_dbs[1], self.roles['role_2']) self.assertEqual(role_dbs[2], self.roles['role_3']) # Do sync with no roles - verify all roles except remote one are removed. api = UserRoleAssignmentFileFormatAPI(username=user_db.name, roles=[], file_path='assignments/%s.yaml' % user_db.name) syncer.sync_users_role_assignments(role_assignment_apis=[api]) role_dbs = get_roles_for_user(user_db=user_db) self.assertEqual(len(role_dbs), 1) self.assertEqual(role_dbs[0], self.roles['role_3'])
def put(self, api_key_api, api_key_id_or_key, requester_user): api_key_db = ApiKey.get_by_key_or_id(api_key_id_or_key) permission_type = PermissionType.API_KEY_MODIFY rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=api_key_db, permission_type=permission_type, ) old_api_key_db = api_key_db api_key_db = ApiKeyAPI.to_model(api_key_api) try: User.get_by_name(api_key_api.user) except StackStormDBObjectNotFoundError: user_db = UserDB(name=api_key_api.user) User.add_or_update(user_db) extra = {"username": api_key_api.user, "user": user_db} LOG.audit('Registered new user "%s".' % (api_key_api.user), extra=extra) # Passing in key_hash as MASKED_ATTRIBUTE_VALUE is expected since we do not # leak it out therefore it is expected we get the same value back. Interpret # this special code and empty value as no-change if api_key_db.key_hash == MASKED_ATTRIBUTE_VALUE or not api_key_db.key_hash: api_key_db.key_hash = old_api_key_db.key_hash # Rather than silently ignore any update to key_hash it is better to explicitly # disallow and notify user. if old_api_key_db.key_hash != api_key_db.key_hash: raise ValueError("Update of key_hash is not allowed.") api_key_db.id = old_api_key_db.id api_key_db = ApiKey.add_or_update(api_key_db) extra = { "old_api_key_db": old_api_key_db, "new_api_key_db": api_key_db } LOG.audit("API Key updated. ApiKey.id=%s." % (api_key_db.id), extra=extra) api_key_api = ApiKeyAPI.from_model(api_key_db) return api_key_api
def test_manipulate_permission_grants_unsupported_resource_type(self): # Try to manipulate permissions on an unsupported resource role_db = self.roles['custom_role_2'] resource_db = UserDB() permission_types = [PermissionType.RULE_ALL] expected_msg = 'Permissions cannot be manipulated for a resource of type' self.assertRaisesRegexp(ValueError, expected_msg, rbac_service.create_permission_grant_for_resource_db, role_db=role_db, resource_db=resource_db, permission_types=permission_types) expected_msg = 'Permissions cannot be manipulated for a resource of type' self.assertRaisesRegexp(ValueError, expected_msg, rbac_service.remove_permission_grant_for_resource_db, role_db=role_db, resource_db=resource_db, permission_types=permission_types)
def setUp(self): super(APIControllersRBACTestCase, self).setUp() # Register packs if self.register_packs: self._register_packs() self.users = {} # Users user_1_db = UserDB(name='no_permissions') user_1_db = User.add_or_update(user_1_db) self.users['no_permissions'] = user_1_db # Insert mock objects - those objects are used to test get one, edit and delete operations self.models = self.fixtures_loader.save_fixtures_to_db( fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_FIXTURES)
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=requester_user) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) return kvp_api
def test_grant_duplicate_role(self): user_db = UserDB(name='test-user-1') user_db = User.add_or_update(user_db) # Initial state, no roles role_dbs = rbac_services.get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, []) role_dbs = user_db.get_roles() self.assertItemsEqual(role_dbs, []) # Assign a role, should have one role assigned rbac_services.assign_role_to_user(role_db=self.roles['custom_role_1'], user_db=user_db, source='assignments/%s_1.yaml' % user_db.name) role_dbs = rbac_services.get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, [self.roles['custom_role_1']]) role_dbs = user_db.get_roles() self.assertItemsEqual(role_dbs, [self.roles['custom_role_1']]) # Assign the same role again. rbac_services.assign_role_to_user(role_db=self.roles['custom_role_1'], user_db=user_db, source='assignments/%s_2.yaml' % user_db.name) role_dbs_2 = rbac_services.get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs_2, [self.roles['custom_role_1']]) role_dbs = user_db.get_roles() self.assertItemsEqual(role_dbs_2, [self.roles['custom_role_1']]) # Revoke previously assigned role, should have no roles again rbac_services.revoke_role_from_user( role_db=self.roles['custom_role_1'], user_db=user_db) role_dbs = rbac_services.get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, []) role_dbs = user_db.get_roles() self.assertItemsEqual(role_dbs, [])
def setUp(self): super(ActionViewsControllerRBACTestCase, self).setUp() self.models = self.fixtures_loader.save_fixtures_to_db( fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_FIXTURES) file_name = 'a1.yaml' ActionViewsControllerRBACTestCase.ACTION_1 = self.fixtures_loader.load_fixtures( fixtures_pack=FIXTURES_PACK, fixtures_dict={'actions': [file_name]})['actions'][file_name] file_name = 'a2.yaml' ActionViewsControllerRBACTestCase.ACTION_1 = self.fixtures_loader.load_fixtures( fixtures_pack=FIXTURES_PACK, fixtures_dict={'actions': [file_name]})['actions'][file_name] # Insert mock users, roles and assignments # Users user_2_db = UserDB(name='action_view_a1') user_2_db = User.add_or_update(user_2_db) self.users['action_view_a1'] = user_2_db # Roles # action_view on a1 action_uid = self.models['actions']['a1.yaml'].get_uid() grant_db = PermissionGrantDB( resource_uid=action_uid, resource_type=ResourceType.ACTION, permission_types=[PermissionType.ACTION_VIEW]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='action_view_a1', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['action_view_a1'] = role_1_db # Role assignments role_assignment_db = UserRoleAssignmentDB( user=self.users['action_view_a1'].name, role=self.roles['action_view_a1'].name, source='assignments/%s.yaml' % self.users['action_view_a1'].name) UserRoleAssignment.add_or_update(role_assignment_db)
def sync_users_role_assignments(self, role_assignment_apis): """ Synchronize role assignments for all the users in the database. :param role_assignment_apis: Role assignments API objects for the assignments loaded from the files. :type role_assignment_apis: ``list`` of :class:`UserRoleAssignmentFileFormatAPI` :return: Dictionary with created and removed role assignments for each user. :rtype: ``dict`` """ LOG.info('Synchronizing users role assignments...') user_dbs = User.get_all() username_to_user_db_map = dict([(user_db.name, user_db) for user_db in user_dbs]) results = {} for role_assignment_api in role_assignment_apis: username = role_assignment_api.username user_db = username_to_user_db_map.get(username, None) if not user_db: # Note: We allow assignments to be created for the users which don't exist in the # DB yet because user creation in StackStorm is lazy (we only create UserDB) object # when user first logs in. user_db = UserDB(name=username) LOG.debug(( 'User "%s" doesn\'t exist in the DB, creating assignment anyway' % (username))) role_assignment_dbs = rbac_services.get_role_assignments_for_user( user_db=user_db) result = self._sync_user_role_assignments( user_db=user_db, role_assignment_dbs=role_assignment_dbs, role_assignment_api=role_assignment_api) results[username] = result LOG.info('User role assignments synchronized') return results
def setUp(self): super(RBACControllerTestCase, self).setUp() permissions = [ PermissionType.RULE_CREATE, PermissionType.RULE_VIEW, PermissionType.RULE_MODIFY, PermissionType.RULE_DELETE ] for name in permissions: user_db = UserDB(name=name) user_db = User.add_or_update(user_db) self.users[name] = user_db # Roles # action_create grant on parent pack grant_db = PermissionGrantDB(resource_uid='pack:examples', resource_type=ResourceType.PACK, permission_types=[name]) grant_db = PermissionGrant.add_or_update(grant_db) grant_2_db = PermissionGrantDB( resource_uid='action:wolfpack:action-1', resource_type=ResourceType.ACTION, permission_types=[PermissionType.ACTION_EXECUTE]) grant_2_db = PermissionGrant.add_or_update(grant_2_db) permission_grants = [str(grant_db.id), str(grant_2_db.id)] role_db = RoleDB(name=name, permission_grants=permission_grants) role_db = Role.add_or_update(role_db) self.roles[name] = role_db # Role assignments role_assignment_db = UserRoleAssignmentDB( user=user_db.name, role=role_db.name, source='assignments/%s.yaml' % user_db.name) UserRoleAssignment.add_or_update(role_assignment_db) role_assignment_db = UserRoleAssignmentDB( user='******', role='role_two', source='assignments/user_two.yaml', is_remote=True) UserRoleAssignment.add_or_update(role_assignment_db)
def test_sync_assignments_user_doesnt_exist_in_db(self): # Make sure that the assignments for the users which don't exist in the db are still saved syncer = RBACDefinitionsDBSyncer() self._insert_mock_roles() # Initial state, no roles user_db = UserDB(name='doesntexistwhaha') role_dbs = get_roles_for_user(user_db=user_db) self.assertItemsEqual(role_dbs, []) # Do the sync with two roles defined api = UserRoleAssignmentFileFormatAPI(username=user_db.name, roles=['role_1', 'role_2']) syncer.sync_users_role_assignments(role_assignment_apis=[api]) role_dbs = get_roles_for_user(user_db=user_db) self.assertEqual(len(role_dbs), 2) self.assertEqual(role_dbs[0], self.roles['role_1']) self.assertEqual(role_dbs[1], self.roles['role_2'])
def post(self, pack_uninstall_request, ref_or_id=None, requester_user=None): if ref_or_id: parameters = {"packs": [ref_or_id]} else: parameters = {"packs": pack_uninstall_request.packs} if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) new_liveaction_api = LiveActionCreateAPI( action="packs.uninstall", parameters=parameters, user=requester_user.name ) execution_resp = self._handle_schedule_execution( liveaction_api=new_liveaction_api, requester_user=requester_user ) exec_id = PackAsyncAPI(execution_id=execution_resp.json["id"]) return Response(json=exec_id, status=http_client.ACCEPTED)
def get_key(key=None, user_db=None, scope=None, decrypt=False): """ Retrieve key from KVP store """ if not isinstance(key, six.string_types): raise TypeError('Given key is not typeof string.') if not isinstance(decrypt, bool): raise TypeError('Decrypt parameter is not typeof bool.') if not user_db: # Use system user user_db = UserDB(cfg.CONF.system_user.user) scope, key_id = _derive_scope_and_key(key=key, user=user_db.name, scope=scope) scope = get_datastore_full_scope(scope) LOG.debug('get_key key_id: %s, scope: %s, user: %s, decrypt: %s' % (key_id, scope, str(user_db.name), decrypt)) _validate_scope(scope=scope) rbac_utils = get_rbac_backend().get_utils_class() is_admin = rbac_utils.user_is_admin(user_db=user_db) # User needs to be either admin or requesting item for itself _validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, user_db=user_db) # Get the key value pair by scope and name. kvp = KeyValuePair.get_by_scope_and_name(scope, key_id) # Decrypt in deserialize_key_value cannot handle NoneType. if kvp.value is None: return kvp.value return deserialize_key_value(kvp.value, decrypt)
def setUp(self): super(HandlerTestCase, self).setUp() cfg.CONF.auth.backend = 'mock' self.users = {} self.roles = {} self.role_assignments = {} # Insert some mock users user_1_db = UserDB(name='auser') user_1_db = User.add_or_update(user_1_db) self.users['user_1'] = user_1_db # Insert mock local role assignments role_db = create_role(name='mock_local_role_1') user_db = self.users['user_1'] source = 'assignments/%s.yaml' % user_db.name role_assignment_db_1 = assign_role_to_user( role_db=role_db, user_db=user_db, source=source, is_remote=False) self.roles['mock_local_role_1'] = role_db self.role_assignments['assignment_1'] = role_assignment_db_1 role_db = create_role(name='mock_local_role_2') user_db = self.users['user_1'] source = 'assignments/%s.yaml' % user_db.name role_assignment_db_2 = assign_role_to_user( role_db=role_db, user_db=user_db, source=source, is_remote=False) self.roles['mock_local_role_2'] = role_db self.role_assignments['assignment_2'] = role_assignment_db_2 role_db = create_role(name='mock_role_3') self.roles['mock_role_3'] = role_db role_db = create_role(name='mock_role_4') self.roles['mock_role_4'] = role_db role_db = create_role(name='mock_role_5') self.roles['mock_role_5'] = role_db
def post(self, pack_install_request, requester_user=None): parameters = { 'packs': pack_install_request.packs, } if pack_install_request.force: parameters['force'] = True if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) new_liveaction_api = LiveActionCreateAPI(action='packs.install', parameters=parameters, user=requester_user.name) execution_resp = self._handle_schedule_execution( liveaction_api=new_liveaction_api, requester_user=requester_user) exec_id = PackAsyncAPI(execution_id=execution_resp.json['id']) return Response(json=exec_id, status=http_client.ACCEPTED)
def test_assign_role_to_user_ignore_already_exists_error(self): user_db = UserDB(name='test-user-10') user_db = User.add_or_update(user_db) role_assignment_db_1 = rbac_service.assign_role_to_user( role_db=self.roles['custom_role_1'], user_db=user_db, source='assignments/%s_10.yaml' % user_db.name) # 1. Without ignore errors self.assertRaises(StackStormDBObjectConflictError, rbac_service.assign_role_to_user, role_db=self.roles['custom_role_1'], user_db=user_db, source='assignments/%s_10.yaml' % user_db.name) # 2. With ignore errors role_assignment_db_2 = rbac_service.assign_role_to_user( role_db=self.roles['custom_role_1'], user_db=user_db, source='assignments/%s_10.yaml' % user_db.name, ignore_already_exists_error=True) self.assertEqual(role_assignment_db_1, role_assignment_db_2) self.assertEqual(role_assignment_db_1.id, role_assignment_db_2.id) self.assertEqual(role_assignment_db_1.user, role_assignment_db_2.user) self.assertEqual(role_assignment_db_1.role, role_assignment_db_2.role)
def post(self, pack_install_request, requester_user=None): parameters = { "packs": pack_install_request.packs, } if pack_install_request.force: parameters["force"] = True if pack_install_request.skip_dependencies: parameters["skip_dependencies"] = True if not requester_user: requester_user = UserDB(name=cfg.CONF.system_user.user) new_liveaction_api = LiveActionCreateAPI(action="packs.install", parameters=parameters, user=requester_user.name) execution_resp = self._handle_schedule_execution( liveaction_api=new_liveaction_api, requester_user=requester_user) exec_id = PackAsyncAPI(execution_id=execution_resp.json["id"]) return Response(json=exec_id, status=http_client.ACCEPTED)