\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh \00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€ \00 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95 59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9 C/// File Manager

File Manager

Path: /opt/imunify360/venv/lib64/python3.11/site-packages/defence360agent/feature_management/

Viewing File: utils.py

import logging
import os
from itertools import chain
from typing import List, Any

from defence360agent.contracts.config import Core
from defence360agent.feature_management import hooks
from defence360agent.feature_management.model import FeatureManagementPerms
from defence360agent.model import instance
from defence360agent.utils import (
    execute_iterable_expression,
    is_safe_subdir_name,
    rmtree,
)
from .lookup import features

logger = logging.getLogger(__name__)


async def set_feature(user: str, feature: str, value: str) -> bool:
    """Sets a `feature` to `value` for a given `user`.

    Calls appropriate hook and returns its (bool) result. Logs the result of
    setting change. If hook fails rollbacks changes to database.
    """
    with instance.db.atomic() as trx:
        perm = FeatureManagementPerms.get_perm(user)
        perm.set_feature(feature, value)
        hook = hooks.get_hook(feature)
        ok = hook(user, value)
        if ok:
            logger.info(
                "Applied setting %s=%s for user %s", feature, value, user
            )
        else:
            logger.error(
                "Failed to apply setting %s=%s for user %s",
                feature,
                value,
                user,
            )
            trx.rollback()
        return ok


async def update_users(
    feature: str, users: List[str], value: Any, existing_users: List[str]
):
    result = {"succeeded": [], "failed": []}

    for user in users:
        if user not in existing_users:
            logger.warning("No such user: %s", user)
            continue

        if await set_feature(user, feature, value):
            result["succeeded"].append(user)
        else:
            result["failed"].append(user)

    return result


async def update_default(feature: str, value: Any):
    perm = FeatureManagementPerms.get_default()
    perm.set_feature(feature, value)
    hook = hooks.get_hook(feature)
    return hook(None, value)


async def sync_users(users: List[str]) -> bool:
    """Synchronize existing permissions with panel users"""
    panel_users = set(users)

    perm_users = FeatureManagementPerms.select(FeatureManagementPerms.user)
    perm_users = set(chain(*perm_users.tuples()))

    perms_to_remove = perm_users - panel_users
    perms_to_remove.remove(FeatureManagementPerms.DEFAULT)

    if perms_to_remove:
        logger.info("Remove permissions of users %s", perms_to_remove)

        def expression(perms_to_remove):
            return FeatureManagementPerms.delete().where(
                FeatureManagementPerms.user.in_(perms_to_remove)
            )

        execute_iterable_expression(expression, list(perms_to_remove))

        for user in perms_to_remove:
            if not is_safe_subdir_name(user):
                continue
            target = os.path.join(Core.USER_CONFDIR, user)
            try:
                rmtree(target)
            except FileNotFoundError:
                pass
            except OSError as e:
                logger.warning(
                    "Failed to remove user_config dir %s: %s", target, e
                )

    perms_to_add = panel_users - perm_users

    if perms_to_add:
        logger.info("Add permissions to users %s", perms_to_add)

    for user in perms_to_add:
        perm = FeatureManagementPerms.get_perm(user)

        for feature in features:
            value = perm.get_feature(feature)
            callback = hooks.get_hook(feature)
            callback(user, value)
    return bool(perms_to_add) or bool(perms_to_remove)


async def reset_features(**features):
    """Sets feature values for all existing users in feature management
    database to given values in `features`."""
    users = list(
        chain(
            *FeatureManagementPerms.select(FeatureManagementPerms.user)
            .where(
                FeatureManagementPerms.user != FeatureManagementPerms.DEFAULT
            )
            .tuples()
        )
    )

    def expression(chunk, feature, value):
        return FeatureManagementPerms.update(**{feature: value}).where(
            FeatureManagementPerms.user.in_(chunk)
        )

    for feature, value in features.items():
        hook = hooks.get_hook(feature)
        # Hooks touch the filesystem, so they run before the write
        # transaction opens: on a server with many users, doing this per user
        # inside the transaction holds the SQLite write lock for tens of
        # seconds and starves every other writer.
        applied = []
        for user in users:
            if hook(user, value):
                applied.append(user)
            else:
                logger.error(
                    "Failed to apply setting %s=%s for user %s",
                    feature,
                    value,
                    user,
                )

        execute_iterable_expression(expression, applied, feature, value)

        for user in applied:
            logger.info(
                "Applied setting %s=%s for user %s", feature, value, user
            )