File manager - Edit - /home/opticamezl/www/newok/Access.tar
Back
Exception/NotAllowed.php 0000644 00000000750 15173016344 0011271 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining a not allowed access * * @since 3.6.3 */ class NotAllowed extends \RuntimeException { } Exception/AuthenticationFailed.php 0000644 00000000774 15173016344 0013313 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access\Exception; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Exception class defining an authentication failed event * * @since 4.0.0 */ class AuthenticationFailed extends \RuntimeException { } Rule.php 0000644 00000007775 15173016344 0006210 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Rule class. * * @since 2.5.0 */ class Rule { /** * A named array * * @var array * @since 1.7.0 */ protected $data = []; /** * Constructor. * * The input array must be in the form: array(-42 => true, 3 => true, 4 => false) * or an equivalent JSON encoded string. * * @param mixed $identities A JSON format string (probably from the database) or a named array. * * @since 1.7.0 */ public function __construct($identities) { // Convert string input to an array. if (\is_string($identities)) { $identities = json_decode($identities, true); } $this->mergeIdentities($identities); } /** * Get the data for the action. * * @return array A named array * * @since 1.7.0 */ public function getData() { return $this->data; } /** * Merges the identities * * @param mixed $identities An integer or array of integers representing the identities to check. * * @return void * * @since 1.7.0 */ public function mergeIdentities($identities) { if ($identities instanceof Rule) { $identities = $identities->getData(); } if (\is_array($identities)) { foreach ($identities as $identity => $allow) { $this->mergeIdentity($identity, $allow); } } } /** * Merges the values for an identity. * * @param integer $identity The identity. * @param boolean $allow The value for the identity (true == allow, false == deny). * * @return void * * @since 1.7.0 */ public function mergeIdentity($identity, $allow) { $identity = (int) $identity; $allow = (int) ((bool) $allow); // Check that the identity exists. if (isset($this->data[$identity])) { // Explicit deny always wins a merge. if ($this->data[$identity] !== 0) { $this->data[$identity] = $allow; } } else { $this->data[$identity] = $allow; } } /** * Checks that this action can be performed by an identity. * * The identity is an integer where +ve represents a user group, * and -ve represents a user. * * @param mixed $identities An integer or array of integers representing the identities to check. * * @return mixed True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ public function allow($identities) { // Implicit deny by default. $result = null; // Check that the inputs are valid. if (!empty($identities)) { if (!\is_array($identities)) { $identities = [$identities]; } foreach ($identities as $identity) { // Technically the identity just needs to be unique. $identity = (int) $identity; // Check if the identity is known. if (isset($this->data[$identity])) { $result = (bool) $this->data[$identity]; // An explicit deny wins. if ($result === false) { break; } } } } return $result; } /** * Convert this object into a JSON encoded string. * * @return string JSON encoded string * * @since 1.7.0 */ public function __toString() { return json_encode($this->data); } } Access.php 0000644 00000116126 15173016344 0006471 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\Log\Log; use Joomla\CMS\Profiler\Profiler; use Joomla\CMS\Table\Asset; use Joomla\CMS\User\User; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Class that handles all access authorisation routines. * * @since 1.7.0 */ class Access { /** * Array of view levels * * @var array * @since 1.7.0 */ protected static $viewLevels = []; /** * Array of rules for the asset * * @var array * @since 1.7.0 */ protected static $assetRules = []; /** * Array of identities for asset rules * * @var array * @since 1.7.0 */ protected static $assetRulesIdentities = []; /** * Array of the permission parent ID mappings * * @var array * @since 1.7.0 */ protected static $assetPermissionsParentIdMapping = []; /** * Array of asset types that have been preloaded * * @var array * @since 1.7.0 */ protected static $preloadedAssetTypes = []; /** * Array of loaded user identities * * @var array * @since 1.7.0 */ protected static $identities = []; /** * Array of user groups. * * @var array * @since 1.7.0 */ protected static $userGroups = []; /** * Array of user group paths. * * @var array * @since 1.7.0 */ protected static $userGroupPaths = []; /** * Array of cached groups by user. * * @var array * @since 1.7.0 */ protected static $groupsByUser = []; /** * Array of preloaded asset names and ids (key is the asset id). * * @var array * @since 3.7.0 */ protected static $preloadedAssets = []; /** * The root asset id. * * @var integer * @since 3.7.0 */ protected static $rootAssetId = null; /** * Method for clearing static caches. * * @return void * * @since 1.7.3 */ public static function clearStatics() { self::$viewLevels = []; self::$assetRules = []; self::$assetRulesIdentities = []; self::$assetPermissionsParentIdMapping = []; self::$preloadedAssetTypes = []; self::$identities = []; self::$userGroups = []; self::$userGroupPaths = []; self::$groupsByUser = []; self::$preloadedAssets = []; self::$rootAssetId = null; } /** * Method to check if a user is authorised to perform an action, optionally on an asset. * * @param integer $userId Id of the user for which to check authorisation. * @param string $action The name of the action to authorise. * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $preload Indicates whether preloading should be used. * * @return boolean|null True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ public static function check($userId, $action, $assetKey = null, $preload = true) { // Sanitise inputs. $userId = (int) $userId; $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action))); if (!isset(self::$identities[$userId])) { // Get all groups against which the user is mapped. self::$identities[$userId] = self::getGroupsByUser($userId); array_unshift(self::$identities[$userId], $userId * -1); } return self::getAssetRules($assetKey, true, true, $preload)->allow($action, self::$identities[$userId]); } /** * Method to preload the Rules object for the given asset type. * * @param integer|string|array $assetTypes The type or name of the asset (e.g. 'com_content.article', 'com_menus.menu.2'). * Also accepts the asset id. An array of asset type or a special * 'components' string to load all component assets. * @param boolean $reload Set to true to reload from database. * * @return boolean True on success. * * @since 1.6 * @note This method will return void in 4.0. */ public static function preload($assetTypes = 'components', $reload = false) { // If sent an asset id, we first get the asset type for that asset id. if (is_numeric($assetTypes)) { $assetTypes = self::getAssetType($assetTypes); } // Check for default case: $isDefault = \is_string($assetTypes) && \in_array($assetTypes, ['components', 'component']); // Preload the rules for all of the components. if ($isDefault) { self::preloadComponents(); return true; } // If we get to this point, this is a regular asset type and we'll proceed with the preloading process. if (!\is_array($assetTypes)) { $assetTypes = (array) $assetTypes; } foreach ($assetTypes as $assetType) { self::preloadPermissions($assetType, $reload); } return true; } /** * Method to recursively retrieve the list of parent Asset IDs * for a particular Asset. * * @param string $assetType The asset type, or the asset name, or the extension of the asset * (e.g. 'com_content.article', 'com_menus.menu.2', 'com_contact'). * @param integer $assetId The numeric asset id. * * @return array List of ancestor ids (includes original $assetId). * * @since 1.6 */ protected static function getAssetAncestors($assetType, $assetId) { // Get the extension name from the $assetType provided $extensionName = self::getExtensionNameFromAsset($assetType); // Holds the list of ancestors for the Asset ID: $ancestors = []; // Add in our starting Asset ID: $ancestors[] = (int) $assetId; // Initialize the variable we'll use in the loop: $id = (int) $assetId; while ($id !== 0) { if (isset(self::$assetPermissionsParentIdMapping[$extensionName][$id])) { $id = (int) self::$assetPermissionsParentIdMapping[$extensionName][$id]->parent_id; if ($id !== 0) { $ancestors[] = $id; } } else { // Add additional case to break out of the while loop automatically in // the case that the ID is non-existent in our mapping variable above. break; } } return $ancestors; } /** * Method to retrieve the Asset Rule strings for this particular * Asset Type and stores them for later usage in getAssetRules(). * Stores 2 arrays: one where the list has the Asset ID as the key * and a second one where the Asset Name is the key. * * @param string $assetType The asset type, or the asset name, or the extension of the asset * (e.g. 'com_content.article', 'com_menus.menu.2', 'com_contact'). * @param boolean $reload Reload the preloaded assets. * * @return void * * @since 1.6 */ protected static function preloadPermissions($assetType, $reload = false) { // Get the extension name from the $assetType provided $extensionName = self::getExtensionNameFromAsset($assetType); // If asset is a component, make sure that all the component assets are preloaded. if ((isset(self::$preloadedAssetTypes[$extensionName]) || isset(self::$preloadedAssetTypes[$assetType])) && !$reload) { return; } !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::preloadPermissions (' . $extensionName . ')'); // Get the database connection object. $db = Factory::getDbo(); $assetKey = $extensionName . '.%'; // Get a fresh query object. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'name', 'rules', 'parent_id'])) ->from($db->quoteName('#__assets')) ->where( [ $db->quoteName('name') . ' LIKE :asset', $db->quoteName('name') . ' = :extension', $db->quoteName('parent_id') . ' = 0', ], 'OR' ) ->bind(':extension', $extensionName) ->bind(':asset', $assetKey); // Get the permission map for all assets in the asset extension. $assets = $db->setQuery($query)->loadObjectList(); self::$assetPermissionsParentIdMapping[$extensionName] = []; foreach ($assets as $asset) { self::$assetPermissionsParentIdMapping[$extensionName][$asset->id] = $asset; self::$preloadedAssets[$asset->id] = $asset->name; } // Mark asset type and it's extension name as preloaded. self::$preloadedAssetTypes[$assetType] = true; self::$preloadedAssetTypes[$extensionName] = true; !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::preloadPermissions (' . $extensionName . ')'); } /** * Method to preload the Rules objects for all components. * * Note: This will only get the base permissions for the component. * e.g. it will get 'com_content', but not 'com_content.article.1' or * any more specific asset type rules. * * @return array Array of component names that were preloaded. * * @since 1.6 */ protected static function preloadComponents() { // If the components already been preloaded do nothing. if (isset(self::$preloadedAssetTypes['components'])) { return []; } !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::preloadComponents (all components)'); // Add root to asset names list. $components = ['root.1']; // Add enabled components to asset names list. foreach (ComponentHelper::getComponents() as $component) { if ($component->enabled) { $components[] = $component->option; } } // Get the database connection object. $db = Factory::getDbo(); // Get the asset info for all assets in asset names list. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'name', 'rules', 'parent_id'])) ->from($db->quoteName('#__assets')) ->whereIn($db->quoteName('name'), $components, ParameterType::STRING); // Get the Name Permission Map List $assets = $db->setQuery($query)->loadObjectList(); $rootAsset = null; // First add the root asset and save it to preload memory and mark it as preloaded. foreach ($assets as &$asset) { if ((int) $asset->parent_id === 0) { $rootAsset = $asset; self::$rootAssetId = $asset->id; self::$preloadedAssetTypes[$asset->name] = true; self::$preloadedAssets[$asset->id] = $asset->name; self::$assetPermissionsParentIdMapping[$asset->name][$asset->id] = $asset; unset($asset); break; } } // Now create save the components asset tree to preload memory. foreach ($assets as $asset) { if (!isset(self::$assetPermissionsParentIdMapping[$asset->name])) { self::$assetPermissionsParentIdMapping[$asset->name] = [$rootAsset->id => $rootAsset, $asset->id => $asset]; self::$preloadedAssets[$asset->id] = $asset->name; } } // Mark all components asset type as preloaded. self::$preloadedAssetTypes['components'] = true; !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::preloadComponents (all components)'); return $components; } /** * Method to check if a group is authorised to perform an action, optionally on an asset. * * @param integer $groupId The path to the group for which to check authorisation. * @param string $action The name of the action to authorise. * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $preload Indicates whether preloading should be used. * * @return boolean True if authorised. * * @since 1.7.0 */ public static function checkGroup($groupId, $action, $assetKey = null, $preload = true) { // Sanitize input. $groupId = (int) $groupId; $action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action))); return self::getAssetRules($assetKey, true, true, $preload)->allow($action, self::getGroupPath($groupId)); } /** * Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree * (including the leaf group id). * * @param mixed $groupId An integer or array of integers representing the identities to check. * * @return mixed True if allowed, false for an explicit deny, null for an implicit deny. * * @since 1.7.0 */ protected static function getGroupPath($groupId) { // Load all the groups to improve performance on intensive groups checks $groups = UserGroupsHelper::getInstance()->getAll(); if (!isset($groups[$groupId])) { return []; } return $groups[$groupId]->path; } /** * Method to return the Rules object for an asset. The returned object can optionally hold * only the rules explicitly set for the asset or the summation of all inherited rules from * parent assets and explicit rules. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * @param boolean $recursive True to return the rules object with inherited rules. * @param boolean $recursiveParentAsset True to calculate the rule also based on inherited component/extension rules. * @param boolean $preload Indicates whether preloading should be used. * * @return Rules Rules object for the asset. * * @since 1.7.0 * @note The non preloading code will be removed in 4.0. All asset rules should use asset preloading. */ public static function getAssetRules($assetKey, $recursive = false, $recursiveParentAsset = true, $preload = true) { // Auto preloads the components assets and root asset (if chosen). if ($preload) { self::preload('components'); } // When asset key is null fallback to root asset. $assetKey = self::cleanAssetKey($assetKey); // Auto preloads assets for the asset type (if chosen). if ($preload) { self::preload(self::getAssetType($assetKey)); } // Get the asset id and name. $assetId = self::getAssetId($assetKey); // If asset rules already cached em memory return it (only in full recursive mode). if ($recursive && $recursiveParentAsset && $assetId && isset(self::$assetRules[$assetId])) { return self::$assetRules[$assetId]; } // Get the asset name and the extension name. $assetName = self::getAssetName($assetKey); $extensionName = self::getExtensionNameFromAsset($assetName); // If asset id does not exist fallback to extension asset, then root asset. if (!$assetId) { if ($extensionName && $assetName !== $extensionName) { Log::add('No asset found for ' . $assetName . ', falling back to ' . $extensionName, Log::WARNING, 'assets'); return self::getAssetRules($extensionName, $recursive, $recursiveParentAsset, $preload); } if (self::$rootAssetId !== null && $assetName !== self::$preloadedAssets[self::$rootAssetId]) { Log::add('No asset found for ' . $assetName . ', falling back to ' . self::$preloadedAssets[self::$rootAssetId], Log::WARNING, 'assets'); return self::getAssetRules(self::$preloadedAssets[self::$rootAssetId], $recursive, $recursiveParentAsset, $preload); } } // Almost all calls can take advantage of preloading. if ($assetId && isset(self::$preloadedAssets[$assetId])) { !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); // Collects permissions for each asset $collected = []; // If not in any recursive mode. We only want the asset rules. if (!$recursive && !$recursiveParentAsset) { $collected = [self::$assetPermissionsParentIdMapping[$extensionName][$assetId]->rules]; } else { // If there is any type of recursive mode. $ancestors = array_reverse(self::getAssetAncestors($extensionName, $assetId)); foreach ($ancestors as $id) { // There are no rules for this ancestor if (!isset(self::$assetPermissionsParentIdMapping[$extensionName][$id])) { continue; } // If full recursive mode, but not recursive parent mode, do not add the extension asset rules. if ($recursive && !$recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name === $extensionName) { continue; } // If not full recursive mode, but recursive parent mode, do not add other recursion rules. if ( !$recursive && $recursiveParentAsset && self::$assetPermissionsParentIdMapping[$extensionName][$id]->name !== $extensionName && (int) self::$assetPermissionsParentIdMapping[$extensionName][$id]->id !== $assetId ) { continue; } // If empty asset to not add to rules. if (self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules === '{}') { continue; } $collected[] = self::$assetPermissionsParentIdMapping[$extensionName][$id]->rules; } } /** * Hashing the collected rules allows us to store * only one instance of the Rules object for * Assets that have the same exact permissions... * it's a great way to save some memory. */ $hash = md5(implode(',', $collected)); if (!isset(self::$assetRulesIdentities[$hash])) { $rules = new Rules(); $rules->mergeCollection($collected); self::$assetRulesIdentities[$hash] = $rules; } // Save asset rules to memory cache(only in full recursive mode). if ($recursive && $recursiveParentAsset) { self::$assetRules[$assetId] = self::$assetRulesIdentities[$hash]; } !JDEBUG ?: Profiler::getInstance('Application')->mark('After Access::getAssetRules (id:' . $assetId . ' name:' . $assetName . ')'); return self::$assetRulesIdentities[$hash]; } // Non preloading code. Use old slower method, slower. Only used in rare cases (if any) or without preloading chosen. Log::add('Asset ' . $assetKey . ' permissions fetch without preloading (slower method).', Log::INFO, 'assets'); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules (assetKey:' . $assetKey . ')'); // There's no need to process it with the recursive method for the Root Asset ID. if ((int) $assetKey === 1) { $recursive = false; } // Get the database connection object. $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->getQuery(true) ->select($db->quoteName($recursive ? 'b.rules' : 'a.rules', 'rules')) ->from($db->quoteName('#__assets', 'a')); // If the asset identifier is numeric assume it is a primary key, else lookup by name. if (is_numeric($assetKey)) { $query->where($db->quoteName('a.id') . ' = :asset', 'OR') ->bind(':asset', $assetKey, ParameterType::INTEGER); } else { $query->where($db->quoteName('a.name') . ' = :asset', 'OR') ->bind(':asset', $assetKey); } if ($recursiveParentAsset && ($extensionName !== $assetKey || is_numeric($assetKey))) { $query->where($db->quoteName('a.name') . ' = :extension') ->bind(':extension', $extensionName); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->where($db->quoteName('a.parent_id') . ' = 0') ->join( 'LEFT', $db->quoteName('#__assets', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ) ->order($db->quoteName('b.lft')); } // Execute the query and load the rules from the result. $result = $db->setQuery($query)->loadColumn(); // Get the root even if the asset is not found and in recursive mode if (empty($result)) { $rootId = (new Asset($db))->getRootId(); $query->clear() ->select($db->quoteName('rules')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = :rootId') ->bind(':rootId', $rootId, ParameterType::INTEGER); $result = $db->setQuery($query)->loadColumn(); } // Instantiate and return the Rules object for the asset rules. $rules = new Rules(); $rules->mergeCollection($result); !JDEBUG ?: Profiler::getInstance('Application')->mark('Before Access::getAssetRules <strong>Slower</strong> (assetKey:' . $assetKey . ')'); return $rules; } /** * Method to clean the asset key to make sure we always have something. * * @param integer|string $assetKey The asset key (asset id or asset name). null fallback to root asset. * * @return integer|string Asset id or asset name. * * @since 3.7.0 */ protected static function cleanAssetKey($assetKey = null) { // If it's a valid asset key, clean it and return it. if ($assetKey) { return strtolower(preg_replace('#[\s\-]+#', '.', trim($assetKey))); } // Return root asset id if already preloaded. if (self::$rootAssetId !== null) { return self::$rootAssetId; } // No preload. Return root asset id from Assets. $assets = new Asset(Factory::getDbo()); return $assets->getRootId(); } /** * Method to get the asset id from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return integer The asset id. * * @since 3.7.0 */ protected static function getAssetId($assetKey) { static $loaded = []; // If the asset is already an id return it. if (is_numeric($assetKey)) { return (int) $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$preloadedAssets[self::$rootAssetId]) { $loaded[$assetKey] = self::$rootAssetId; } else { $preloadedAssetsByName = array_flip(self::$preloadedAssets); // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. if (isset($preloadedAssetsByName[$assetKey])) { $loaded[$assetKey] = $preloadedAssetsByName[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table. $table = new Asset(Factory::getDbo()); $table->load(['name' => $assetKey]); $loaded[$assetKey] = $table->id ?? 0; } } } return (int) $loaded[$assetKey]; } /** * Method to get the asset name from the asset key. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset name (ex: com_content.article.8). * * @since 3.7.0 */ protected static function getAssetName($assetKey) { static $loaded = []; // If the asset is already a string return it. if (!is_numeric($assetKey)) { return $assetKey; } if (!isset($loaded[$assetKey])) { // It's the root asset. if (self::$rootAssetId !== null && $assetKey === self::$rootAssetId) { $loaded[$assetKey] = self::$preloadedAssets[self::$rootAssetId]; } elseif (isset(self::$preloadedAssets[$assetKey])) { // If we already have the asset name stored in preloading, example, a component, no need to fetch it from table. $loaded[$assetKey] = self::$preloadedAssets[$assetKey]; } else { // Else we have to do an extra db query to fetch it from the table fetch it from table. $table = new Asset(Factory::getDbo()); $table->load($assetKey); $loaded[$assetKey] = $table->name; } } return $loaded[$assetKey]; } /** * Method to get the extension name from the asset name. * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The extension name (ex: com_content). * * @since 1.6 */ public static function getExtensionNameFromAsset($assetKey) { static $loaded = []; if (!isset($loaded[$assetKey])) { $assetName = self::getAssetName($assetKey); $firstDot = strpos($assetName, '.'); if ($assetName !== 'root.1' && $firstDot !== false) { $assetName = substr($assetName, 0, $firstDot); } $loaded[$assetKey] = $assetName; } return $loaded[$assetKey]; } /** * Method to get the asset type from the asset name. * * For top level components this returns "components": * 'com_content' returns 'components' * * For other types: * 'com_content.article.1' returns 'com_content.article' * 'com_content.category.1' returns 'com_content.category' * * @param integer|string $assetKey The asset key (asset id or asset name). * * @return string The asset type (ex: com_content.article). * * @since 1.6 */ public static function getAssetType($assetKey) { // If the asset is already a string return it. $assetName = self::getAssetName($assetKey); $lastDot = strrpos($assetName, '.'); if ($assetName !== 'root.1' && $lastDot !== false) { return substr($assetName, 0, $lastDot); } return 'components'; } /** * Method to return the title of a user group * * @param integer $groupId Id of the group for which to get the title of. * * @return string The title of the group * * @since 3.5 */ public static function getGroupTitle($groupId) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Fetch the group title from the database $db = Factory::getDbo(); $query = $db->getQuery(true); $query->select($db->quoteName('title')) ->from($db->quoteName('#__usergroups')) ->where($db->quoteName('id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); return $db->loadResult(); } /** * Method to return a list of user groups mapped to a user. The returned list can optionally hold * only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited * by the user. * * @param integer $userId Id of the user for which to get the list of groups. * @param boolean $recursive True to include inherited user groups. * * @return array List of user group ids to which the user is mapped. * * @since 1.7.0 */ public static function getGroupsByUser($userId, $recursive = true) { // Cast as integer until method is typehinted. $userId = (int) $userId; // Creates a simple unique string for each parameter combination: $storeId = $userId . ':' . (int) $recursive; if (!isset(self::$groupsByUser[$storeId])) { // @todo: Uncouple this from ComponentHelper and allow for a configuration setting or value injection. $guestUsergroup = (int) ComponentHelper::getParams('com_users')->get('guest_usergroup', 1); // Guest user (if only the actually assigned group is requested) if (empty($userId) && !$recursive) { $result = [$guestUsergroup]; } else { // Registered user and guest if all groups are requested $db = Factory::getDbo(); // Build the database query to get the rules for the asset. $query = $db->getQuery(true) ->select($db->quoteName($recursive ? 'b.id' : 'a.id')); if (empty($userId)) { $query->from($db->quoteName('#__usergroups', 'a')) ->where($db->quoteName('a.id') . ' = :guest') ->bind(':guest', $guestUsergroup, ParameterType::INTEGER); } else { $query->from($db->quoteName('#__user_usergroup_map', 'map')) ->where($db->quoteName('map.user_id') . ' = :userId') ->join('LEFT', $db->quoteName('#__usergroups', 'a'), $db->quoteName('a.id') . ' = ' . $db->quoteName('map.group_id')) ->bind(':userId', $userId, ParameterType::INTEGER); } // If we want the rules cascading up to the global asset node we need a self-join. if ($recursive) { $query->join( 'LEFT', $db->quoteName('#__usergroups', 'b'), $db->quoteName('b.lft') . ' <= ' . $db->quoteName('a.lft') . ' AND ' . $db->quoteName('b.rgt') . ' >= ' . $db->quoteName('a.rgt') ); } // Execute the query and load the rules from the result. $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL or duplicate values, just in case $result = ArrayHelper::toInteger($result); if (empty($result)) { $result = [1]; } else { $result = array_unique($result); } } self::$groupsByUser[$storeId] = $result; } return self::$groupsByUser[$storeId]; } /** * Method to return a list of user Ids contained in a Group * * @param integer $groupId The group Id * @param boolean $recursive Recursively include all child groups (optional) * * @return array * * @since 1.7.0 * @todo This method should move somewhere else */ public static function getUsersByGroup($groupId, $recursive = false) { // Cast as integer until method is typehinted. $groupId = (int) $groupId; // Get a database object. $db = Factory::getDbo(); $test = $recursive ? ' >= ' : ' = '; // First find the users contained in the group $query = $db->getQuery(true) ->select('DISTINCT(' . $db->quoteName('user_id') . ')') ->from($db->quoteName('#__usergroups', 'ug1')) ->join( 'INNER', $db->quoteName('#__usergroups', 'ug2'), $db->quoteName('ug2.lft') . $test . $db->quoteName('ug1.lft') . ' AND ' . $db->quoteName('ug1.rgt') . $test . $db->quoteName('ug2.rgt') ) ->join('INNER', $db->quoteName('#__user_usergroup_map', 'm'), $db->quoteName('ug2.id') . ' = ' . $db->quoteName('m.group_id')) ->where($db->quoteName('ug1.id') . ' = :groupId') ->bind(':groupId', $groupId, ParameterType::INTEGER); $db->setQuery($query); $result = $db->loadColumn(); // Clean up any NULL values, just in case $result = ArrayHelper::toInteger($result); return $result; } /** * Method to return a list of view levels for which the user is authorised. * * @param integer $userId Id of the user for which to get the list of authorised view levels. * * @return array List of view levels for which the user is authorised. * * @since 1.7.0 */ public static function getAuthorisedViewLevels($userId) { // Only load the view levels once. if (empty(self::$viewLevels)) { // Get a database object. $db = Factory::getDbo(); // Build the base query. $query = $db->getQuery(true) ->select($db->quoteName(['id', 'rules'])) ->from($db->quoteName('#__viewlevels')); // Set the query for execution. $db->setQuery($query); // Build the view levels array. foreach ($db->loadAssocList() as $level) { self::$viewLevels[$level['id']] = (array) json_decode($level['rules']); } } // Initialise the authorised array. $authorised = [1]; // Check for the recovery mode setting and return early. $user = User::getInstance($userId); $root_user = Factory::getApplication()->get('root_user'); if (($user->username && $user->username == $root_user) || (is_numeric($root_user) && $user->id > 0 && $user->id == $root_user)) { // Find the super user levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if ($id > 0 && self::checkGroup($id, 'core.admin')) { $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } // Get all groups that the user is mapped to recursively. $groups = self::getGroupsByUser($userId); // Find the authorised levels. foreach (self::$viewLevels as $level => $rule) { foreach ($rule as $id) { if (($id < 0) && (($id * -1) == $userId)) { $authorised[] = $level; break; } elseif (($id >= 0) && \in_array($id, $groups)) { // Check to see if the group is mapped to the level. $authorised[] = $level; break; } } } return array_values(array_unique($authorised)); } /** * Method to return a list of actions from a file for which permissions can be set. * * @param string $file The path to the XML file. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/") { if (!is_file($file) || !is_readable($file)) { // If unable to find the file return false. return false; } else { // Else return the actions from the xml. $xml = simplexml_load_file($file); return self::getActionsFromData($xml, $xpath); } } /** * Method to return a list of actions from a string or from an xml for which permissions can be set. * * @param string|\SimpleXMLElement $data The XML string or an XML element. * @param string $xpath An optional xpath to search for the fields. * * @return boolean|array False if case of error or the list of actions available. * * @since 3.0.0 */ public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/") { // If the data to load isn't already an XML element or string return false. if ((!($data instanceof \SimpleXMLElement)) && (!\is_string($data))) { return false; } // Attempt to load the XML if a string. if (\is_string($data)) { try { $data = new \SimpleXMLElement($data); } catch (\Exception $e) { return false; } // Make sure the XML loaded correctly. if (!$data) { return false; } } // Initialise the actions array $actions = []; // Get the elements from the xpath $elements = $data->xpath($xpath . 'action[@name][@title]'); // If there some elements, analyse them if (!empty($elements)) { foreach ($elements as $element) { // Add the action to the actions array $action = [ 'name' => (string) $element['name'], 'title' => (string) $element['title'], ]; if (isset($element['description'])) { $action['description'] = (string) $element['description']; } $actions[] = (object) $action; } } // Finally return the actions array return $actions; } } Rules.php 0000644 00000012120 15173016344 0006347 0 ustar 00 <?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Access; use Joomla\CMS\Object\CMSObject; // phpcs:disable PSR1.Files.SideEffects \defined('JPATH_PLATFORM') or die; // phpcs:enable PSR1.Files.SideEffects /** * Access rules class. * * @since 2.5.0 */ class Rules { /** * A named array. * * @var array * @since 1.7.0 */ protected $data = []; /** * Constructor. * * The input array must be in the form: array('action' => array(-42 => true, 3 => true, 4 => false)) * or an equivalent JSON encoded string, or an object where properties are arrays. * * @param mixed $input A JSON format string (probably from the database) or a nested array. * * @since 1.7.0 */ public function __construct($input = '') { // Convert in input to an array. if (\is_string($input)) { $input = json_decode($input, true); } elseif (\is_object($input)) { $input = (array) $input; } if (\is_array($input)) { // Top level keys represent the actions. foreach ($input as $action => $identities) { $this->mergeAction($action, $identities); } } } /** * Get the data for the action. * * @return array A named array of Rule objects. * * @since 1.7.0 */ public function getData() { return $this->data; } /** * Method to merge a collection of Rules. * * @param mixed $input Rule or array of Rules * * @return void * * @since 1.7.0 */ public function mergeCollection($input) { // Check if the input is an array. if (\is_array($input)) { foreach ($input as $actions) { $this->merge($actions); } } } /** * Method to merge actions with this object. * * @param mixed $actions Rule object, an array of actions or a JSON string array of actions. * * @return void * * @since 1.7.0 */ public function merge($actions) { if (\is_string($actions)) { $actions = json_decode($actions, true); } if (\is_array($actions)) { foreach ($actions as $action => $identities) { $this->mergeAction($action, $identities); } } elseif ($actions instanceof Rules) { $data = $actions->getData(); foreach ($data as $name => $identities) { $this->mergeAction($name, $identities); } } } /** * Merges an array of identities for an action. * * @param string $action The name of the action. * @param array $identities An array of identities * * @return void * * @since 1.7.0 */ public function mergeAction($action, $identities) { if (isset($this->data[$action])) { // If exists, merge the action. $this->data[$action]->mergeIdentities($identities); } else { // If new, add the action. $this->data[$action] = new Rule($identities); } } /** * Checks that an action can be performed by an identity. * * The identity is an integer where +ve represents a user group, * and -ve represents a user. * * @param string $action The name of the action. * @param mixed $identity An integer representing the identity, or an array of identities * * @return mixed Object or null if there is no information about the action. * * @since 1.7.0 */ public function allow($action, $identity) { // Check we have information about this action. if (isset($this->data[$action])) { return $this->data[$action]->allow($identity); } } /** * Get the allowed actions for an identity. * * @param mixed $identity An integer representing the identity or an array of identities * * @return CMSObject Allowed actions for the identity or identities * * @since 1.7.0 */ public function getAllowed($identity) { // Sweep for the allowed actions. $allowed = new CMSObject(); foreach ($this->data as $name => &$action) { if ($action->allow($identity)) { $allowed->set($name, true); } } return $allowed; } /** * Magic method to convert the object to JSON string representation. * * @return string JSON representation of the actions array * * @since 1.7.0 */ public function __toString() { $temp = []; foreach ($this->data as $name => $rule) { if ($data = $rule->getData()) { $temp[$name] = $data; } } return json_encode($temp, JSON_FORCE_OBJECT); } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings