Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Permission/Builder.php
<?php

namespace XF\Permission;

use function
intval, is_array, is_int;

class
Builder
{
   
/**
     * @var \XF\Db\AbstractAdapter
     */
   
protected $db;

   
/**
     * @var \XF\Mvc\Entity\Manager
     */
   
protected $em;

    protected
$permissionsGrouped;

    protected
$contentHandlersMap;

    protected
$userEntries;
    protected
$groupEntries;
    protected
$systemEntries;

   
/** @var AbstractContentPermissions[] */
   
protected $contentHandlers = [];

    protected
$permissionPriority = [
       
'deny' => 1,
       
'content_allow' => 2,
       
'reset' => 3,
       
'allow' => 4,
       
'unset' => 5,
       
'use_int' => 6
   
];

    public function
__construct(\XF\Db\AbstractAdapter $db, \XF\Mvc\Entity\Manager $em, array $contentHandlersMap = [])
    {
       
$this->db = $db;
       
$this->em = $em;
       
$this->contentHandlersMap = $contentHandlersMap;

       
$this->setupData();
    }

    public function
refreshData()
    {
       
$this->setupData();

        foreach (
$this->contentHandlers AS $contentBuilder)
        {
           
$contentBuilder->setupBuildData();
        }
    }

    protected function
setupData()
    {
       
/** @var \XF\Repository\Permission $permissionRepo */
       
$permissionRepo = $this->em->getRepository('XF:Permission');
       
$this->permissionsGrouped = $permissionRepo->getPermissionsGrouped();

       
/** @var \XF\Repository\PermissionEntry $entryRepo */
       
$entryRepo = $this->em->getRepository('XF:PermissionEntry');
       
$entries = $entryRepo->getGlobalPermissionEntriesGrouped();
       
$this->userEntries = $entries['users'];
       
$this->groupEntries = $entries['groups'];
       
$this->systemEntries = $entries['system'];
    }

   
/**
     * @return AbstractContentPermissions[]
     */
   
public function getContentHandlers()
    {
        foreach (
$this->contentHandlersMap AS $contentType => $handler)
        {
            if (isset(
$this->contentHandlers[$contentType]))
            {
                continue;
            }

           
// will be cached
           
$this->getContentHandler($contentType);
        }

        return
$this->contentHandlers;
    }

    public function
getContentHandler($contentType, $throw = false)
    {
        if (!isset(
$this->contentHandlers[$contentType]))
        {
            if (!isset(
$this->contentHandlersMap[$contentType]))
            {
                if (
$throw)
                {
                    throw new \
InvalidArgumentException("No permission handler for '$contentType'");
                }
                return
null;
            }

           
$handler = $this->contentHandlersMap[$contentType];

            if (!
class_exists($handler))
            {
                if (
$throw)
                {
                    throw new \
InvalidArgumentException("Invalid permission handler for '$contentType': $handler");
                }
                return
null;
            }

           
$handler = \XF::extendClass($handler);
           
$this->contentHandlers[$contentType] = new $handler($this);
        }

        return
$this->contentHandlers[$contentType];
    }

    public function
isValidPermissionContentType($contentType)
    {
        return isset(
$this->contentHandlersMap[$contentType]);
    }

    public function
getPermissionsGrouped()
    {
        return
$this->permissionsGrouped;
    }

    public function
rebuildCombination(\XF\Entity\PermissionCombination $combination)
    {
       
$sets = $this->getApplicablePermissionSets($combination->user_group_list, $combination->user_id);
       
$calculated = $this->calculatePermissions($sets, $this->permissionsGrouped);

       
$preFinal = $this->applyPermissionDependencies($calculated, $this->permissionsGrouped);
       
$final = $this->finalizePermissionValues($preFinal);

       
$this->db->beginTransaction();
       
$combination->fastUpdate('cache_value', $final);
       
$this->rebuildCombinationContent($combination, $calculated);
       
$this->db->commit();
    }

    public function
rebuildCombinationContent(\XF\Entity\PermissionCombination $combination, array $basePerms)
    {
        foreach (
$this->getContentHandlers() AS $contentBuilder)
        {
           
$contentBuilder->rebuildCombination($combination, $basePerms);
        }
    }

    public function
analyzeCombination(\XF\Entity\PermissionCombination $combination)
    {
       
$groupIds = $combination->user_group_list;
       
$userId = $combination->user_id;

       
$sets = $this->getApplicablePermissionSets($groupIds, $userId);
       
$calculated = $this->calculatePermissions($sets, $this->permissionsGrouped);

       
$preFinal = $this->applyPermissionDependencies($calculated, $this->permissionsGrouped, $dependChanges);
       
$final = $this->finalizePermissionValues($preFinal);

       
$intermediates = $this->collectIntermediates($combination, $final, $sets);
        return
$this->getFinalAnalysis($final, $intermediates, $dependChanges);
    }

    public function
analyzeCombinationContent(\XF\Entity\PermissionCombination $combination, $contentType, $contentId)
    {
       
$contentHandlers = $this->getContentHandlers();
        if (!isset(
$contentHandlers[$contentType]))
        {
            throw new \
InvalidArgumentException("Unknown content builder '$contentType'");
        }

       
$sets = $this->getApplicablePermissionSets($combination->user_group_list, $combination->user_id);
       
$calculated = $this->calculatePermissions($sets, $this->permissionsGrouped);

       
// now unused - dependencies at the global level don't change what's passed into content
        //$preFinal = $this->applyPermissionDependencies($calculated, $this->permissionsGrouped);

       
$intermediates = $this->collectIntermediates($combination, $calculated, $sets);

       
$handler = $contentHandlers[$contentType];
        return
$handler->analyzeCombination($combination, $contentId, $calculated, $intermediates);
    }

    public function
collectIntermediates(
        \
XF\Entity\PermissionCombination $combination, array $groupedPermissions, array $sets,
       
$contentId = null, $contentTitle = null
   
)
    {
       
$groupIds = $combination->user_group_list;
       
$userId = $combination->user_id;

       
$intermediates = [];

        foreach (
$groupedPermissions AS $permissionGroupId => $permissions)
        {
            foreach (
$permissions AS $permissionId => $null)
            {
               
$localIntermediates = [];

                if (isset(
$sets['system'][$permissionGroupId][$permissionId]))
                {
                   
$localIntermediates[] = new AnalysisIntermediate(
                       
$sets['system'][$permissionGroupId][$permissionId], 'system', null,
                       
$contentId, $contentTitle
                   
);
                }

                foreach (
$groupIds AS $groupId)
                {
                    if (isset(
$sets["group-$groupId"][$permissionGroupId][$permissionId]))
                    {
                       
$intermediateValue = $sets["group-$groupId"][$permissionGroupId][$permissionId];
                    }
                    else
                    {
                       
$permission = $this->permissionsGrouped[$permissionGroupId][$permissionId];
                       
$intermediateValue = $permission->permission_type == 'integer' ? 0 : 'unset';
                    }

                   
$skipDefault = ($contentId && ($intermediateValue === 'unset' || $intermediateValue === 0));
                    if (!
$skipDefault)
                    {
                       
$localIntermediates[] = new AnalysisIntermediate(
                           
$intermediateValue, 'group', $groupId,
                           
$contentId, $contentTitle
                       
);
                    }
                }

                if (
$userId && isset($sets["user-$userId"][$permissionGroupId][$permissionId]))
                {
                   
$localIntermediates[] = new AnalysisIntermediate(
                       
$sets["user-$userId"][$permissionGroupId][$permissionId], 'user', $userId,
                       
$contentId, $contentTitle
                   
);
                }

               
$intermediates[$permissionGroupId][$permissionId] = $localIntermediates;
            }
        }

        return
$intermediates;
    }

    public function
pushIntermediates(array $existing, array $new)
    {
        foreach (
$new AS $groupId => $groups)
        {
            foreach (
$groups AS $permissionId => $intermediates)
            {
                if (!isset(
$existing[$groupId][$permissionId]))
                {
                   
$existing[$groupId][$permissionId] = [];
                }

               
$existing[$groupId][$permissionId] = array_merge($existing[$groupId][$permissionId], $intermediates);
            }
        }

        return
$existing;
    }

    public function
getFinalAnalysis(array $finalPerms, array $intermediates, array $dependChanges)
    {
       
$analysis = [];

        foreach (
$finalPerms AS $permissionGroupId => $permissions)
        {
            foreach (
$permissions AS $permissionId => $finalValue)
            {
                if (isset(
$dependChanges[$permissionGroupId][$permissionId]))
                {
                   
$dependChange = $dependChanges[$permissionGroupId][$permissionId];
                }
                else
                {
                   
$dependChange = null;
                }

                if (isset(
$intermediates[$permissionGroupId][$permissionId]))
                {
                   
$intermediate = $intermediates[$permissionGroupId][$permissionId];
                }
                else
                {
                   
$intermediate = [];
                }

               
$analysis[$permissionGroupId][$permissionId] = [
                   
'final' => $finalValue,
                   
'dependChange' => $dependChange,
                   
'intermediates' => $intermediate
               
];
            }
        }

        return
$analysis;
    }

    public function
getApplicablePermissionSets(array $userGroupIds, $userId = 0)
    {
       
$sets = [];
        foreach (
$userGroupIds AS $userGroupId)
        {
            if (isset(
$this->groupEntries[$userGroupId]))
            {
               
$sets["group-$userGroupId"] = $this->groupEntries[$userGroupId];
            }
        }
        if (
$userId && isset($this->userEntries[$userId]))
        {
           
$sets["user-$userId"] = $this->userEntries[$userId];
        }
        if (
$this->systemEntries)
        {
           
$sets['system'] = $this->systemEntries;
        }

        return
$sets;
    }

    public function
calculatePermissions(array $sets, array $availablePermissions, array $baseValues = [])
    {
       
$output = [];

        foreach (
$availablePermissions AS $groupId => $permissions)
        {
            foreach (
$permissions AS $permissionId => $permission)
            {
               
$permissionType = $permission->permission_type;

                if (isset(
$baseValues[$groupId][$permissionId]))
                {
                   
$baseValue = $baseValues[$groupId][$permissionId];
                }
                else
                {
                   
$baseValue = $permissionType == 'flag' ? 'unset' : 0;
                }

               
$values = ['base' => $baseValue];
                foreach (
$sets AS $setId => $set)
                {
                    if (isset(
$set[$groupId][$permissionId]))
                    {
                       
$values[$setId] = $set[$groupId][$permissionId];
                    }
                }

               
$output[$groupId][$permissionId] = $this->pickPermissionPriorityValue($values, $permissionType);
            }
        }

        return
$output;
    }

    public function
pickPermissionPriorityValue(array $values, $permissionType)
    {
        if (
$permissionType == 'integer')
        {
           
$highest = 0;
            foreach (
$values AS $value)
            {
                if (
$value == -1)
                {
                    return -
1;
                }
                else if (
$value > $highest)
                {
                   
$highest = $value;
                }
            }

            return
$highest;
        }
        else
        {
           
$priority = 5;
           
$priorityName = 'unset';
            foreach (
$values AS $value)
            {
                if (!isset(
$this->permissionPriority[$value]))
                {
                   
// should only happen if we have some invalid data
                   
continue;
                }
               
$thisPriority = $this->permissionPriority[$value];
                if (
$thisPriority < $priority)
                {
                   
$priority = $thisPriority;
                   
$priorityName = $value;
                }
            }

            return
$priorityName;
        }
    }

    public function
applyPermissionDependencies(array $calculated, array $availablePermissions, &$changed = [])
    {
       
$changed = [];

        foreach (
$availablePermissions AS $groupId => $permissions)
        {
            foreach (
$permissions AS $permissionId => $permission)
            {
               
$dependId = $permission->depend_permission_id;

                if (!
$dependId || !isset($calculated[$groupId][$dependId]))
                {
                    continue;
                }

               
$parentValue = $calculated[$groupId][$dependId];
                if (
$parentValue != 'allow' && $parentValue != 'content_allow')
                {
                   
$originalValue = $calculated[$groupId][$permissionId] ?? null;
                    if (
                       
$originalValue === 'allow'
                       
|| $originalValue === 'content_allow'
                       
|| (is_int($originalValue) && $originalValue != 0)
                    )
                    {
                       
$changed[$groupId][$permissionId] = [
                           
'original' => $calculated[$groupId][$permissionId],
                           
'by' => $dependId
                       
];
                    }

                   
$calculated[$groupId][$permissionId] = $permission->permission_type == 'integer' ? 0 : 'deny';
                }
            }
        }

        return
$calculated;
    }

    public function
finalizePermissionValues(array $calculated)
    {
       
$finalized = [];
        foreach (
$calculated AS $key => $value)
        {
            if (
is_array($value))
            {
               
$finalized[$key] = $this->finalizePermissionValues($value);
            }
            else
            {
                if (
is_int($value))
                {
                   
$finalized[$key] = intval($value);
                }
                else
                {
                   
$finalized[$key] = ($value == 'allow' || $value == 'content_allow');
                }

             }
        }

        return
$finalized;
    }

    public function
getAnalysisTypeData()
    {
       
$data = [];

        foreach (
$this->getContentHandlers() AS $contentType => $contentBuilder)
        {
           
$data[$contentType] = [
               
'title' => $contentBuilder->getAnalysisTypeTitle(),
               
'content' => $contentBuilder->getAnalysisContentPairs()
            ];
        }

        return
$data;
    }

    public function
db()
    {
        return
$this->db;
    }

    public function
em()
    {
        return
$this->em;
    }
}