Beispiel #1
0
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))
Beispiel #2
0
    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)
Beispiel #3
0
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
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #7
0
    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
Beispiel #8
0
    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,
        )
Beispiel #9
0
    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)
Beispiel #10
0
    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
Beispiel #13
0
    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)
Beispiel #14
0
    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'])
Beispiel #16
0
    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)
Beispiel #18
0
    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)
Beispiel #19
0
    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
Beispiel #20
0
    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, [])
Beispiel #21
0
    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)
Beispiel #22
0
    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
Beispiel #23
0
    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)
Beispiel #24
0
    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'])
Beispiel #25
0
    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)
Beispiel #26
0
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)
Beispiel #27
0
    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
Beispiel #28
0
    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)
Beispiel #30
0
    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)