? Failure

User tests: Successful: Unsuccessful:

avatar csthomas
csthomas
15 May 2018

Summary of Changes

  1. Stop preloading the entire component's assets.
  2. There is no database changes.
  3. If you do not use subclass of Joomla\CMS\Access\Access then this PR should be B/C.
  4. Adds a new class Joomla\CMS\Access\AccessControl as a replacement for Joomla\CMS\Access\Access.

I would like to do it in similar way as modern router, but
only developers of 3rd party extension will decide which way want to go.
In this part I have not decided yet, I do not know if I'm doing it right.

You can still use Access class as before, but protected methods/variables may be missing.
Most 3rd party extensions, which does not use advanced access managements shouldn't see any difference.

This Access methods are proxy to the new methods in AccessControl, there are slight differences:

  • getGroupsByUser() - the new method returns items in reverse order
  • getAuthorisedViewLevels() - the new method returns array_combine($oldResult, $oldResult), it means that you get levels in the keys and values of the array.
  • getActionsFromFile() - only a proxy to the same code
  • getActionsFromData() - only a proxy to the same code
  • preloadComponents() - acts as a proxy but fills in its own protected variables, will use more memory
  • checkGroup() - acts as a proxy, cleans input data, AccessControl::checkGroup does not.

The next methods are totally rewritten in the new AccessControl class, has different parameters, use a new structure of data, get less memory:

  • check()
  • getAssetRules()

To not duplicate memory consumption I suggest to use only new methods in all extensions.

There is a new method in Joomla\CMS\User:

public function isAuthorised($action, $assetKey = null, $extension = null, $nested = true)

and the old method looks like:

public function authorise($action, $assetname = null)
{
        return $this->isAuthorised($action, $assetname);
}

The new method has a few more arguments to be more flexible.
The $extension argument (ex. com_content) is used as a fallback if $assetKey (asset_id) does not exist.
The $nested is used to optimization, for example modules always has level 1. Then we can always set $nested = false to generate simpler sql query.

Instead of load from database single row or all rows that match name LIKE 'com_content.%'
this PR use UNION and LEFT JOIN statement to emulate WITH RECURSIVE , example:

SELECT a5.id, a5.name, a5.rules, a5.parent_id, a5.level
FROM `j40_assets` AS `a0`
LEFT JOIN `j40_assets` AS `a1` ON a1.id = a0.parent_id AND a1.id != 1
LEFT JOIN `j40_assets` AS `a2` ON a2.id = a1.parent_id AND a2.id != 1
LEFT JOIN `j40_assets` AS `a3` ON a3.id = a2.parent_id AND a3.id != 1
LEFT JOIN `j40_assets` AS `a4` ON a4.id = a3.parent_id AND a4.id != 1
LEFT JOIN `j40_assets` AS `a5` ON a5.id IN (a0.id, a1.id, a2.id, a3.id, a4.id, a4.parent_id)
WHERE a0.id = 78
ORDER BY a5.lft

Result:

id	name	rules	parent_id	level
8	com_content	{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.edit":{"4":1},"core.edit.state":{"5":1},"core.execute.transition":{"6":1,"5":1}}	1	1
74	com_content.category.8	{}	8	2
78	com_content.article.3	{}	74	3

For this example the nested variable can be set even to 2 (article->category), but by default is 5.
If assets are more nested then joomla will check whether parent_id has been loaded and if not then generates the next query
based on level depth to load the rest of missing parents.

For category view to load all article assets joomla will use:

SELECT b.id, b.name, b.rules, b.parent_id, b.level
FROM (
SELECT a5.id
FROM `j40_assets` AS `a0`
LEFT JOIN `j40_assets` AS `a1` ON a1.id = a0.parent_id AND a1.id != 1
LEFT JOIN `j40_assets` AS `a2` ON a2.id = a1.parent_id AND a2.id != 1
LEFT JOIN `j40_assets` AS `a3` ON a3.id = a2.parent_id AND a3.id != 1
LEFT JOIN `j40_assets` AS `a4` ON a4.id = a3.parent_id AND a4.id != 1
LEFT JOIN `j40_assets` AS `a5` ON a5.id IN (a0.id, a1.id, a2.id, a3.id, a4.id, a4.parent_id)
WHERE a0.id = 78
UNION (
SELECT a5.id
FROM `j40_assets` AS `a0`
LEFT JOIN `j40_assets` AS `a1` ON a1.id = a0.parent_id AND a1.id != 1
LEFT JOIN `j40_assets` AS `a2` ON a2.id = a1.parent_id AND a2.id != 1
LEFT JOIN `j40_assets` AS `a3` ON a3.id = a2.parent_id AND a3.id != 1
LEFT JOIN `j40_assets` AS `a4` ON a4.id = a3.parent_id AND a4.id != 1
LEFT JOIN `j40_assets` AS `a5` ON a5.id IN (a0.id, a1.id, a2.id, a3.id, a4.id, a4.parent_id)
WHERE a0.id = 79)
UNION (
SELECT a5.id
FROM `j40_assets` AS `a0`
LEFT JOIN `j40_assets` AS `a1` ON a1.id = a0.parent_id AND a1.id != 1
LEFT JOIN `j40_assets` AS `a2` ON a2.id = a1.parent_id AND a2.id != 1
LEFT JOIN `j40_assets` AS `a3` ON a3.id = a2.parent_id AND a3.id != 1
LEFT JOIN `j40_assets` AS `a4` ON a4.id = a3.parent_id AND a4.id != 1
LEFT JOIN `j40_assets` AS `a5` ON a5.id IN (a0.id, a1.id, a2.id, a3.id, a4.id, a4.parent_id)
WHERE a0.id = 80)
UNION (
SELECT a5.id
FROM `j40_assets` AS `a0`
LEFT JOIN `j40_assets` AS `a1` ON a1.id = a0.parent_id AND a1.id != 1
LEFT JOIN `j40_assets` AS `a2` ON a2.id = a1.parent_id AND a2.id != 1
LEFT JOIN `j40_assets` AS `a3` ON a3.id = a2.parent_id AND a3.id != 1
LEFT JOIN `j40_assets` AS `a4` ON a4.id = a3.parent_id AND a4.id != 1
LEFT JOIN `j40_assets` AS `a5` ON a5.id IN (a0.id, a1.id, a2.id, a3.id, a4.id, a4.parent_id)
WHERE a0.id = 81)) AS pks
LEFT JOIN `j40_assets` AS `b` ON b.id = pks.id
ORDER BY b.lft

The query will only load assets that are required for articles.

The result:

id 	name 	rules 	parent_id 	level
8	com_content	{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.edit":{"4":1},"core.edit.sta...	1	1
74	com_content.category.8	{}	8	2
78	com_content.article.3	{}	74	3
79	com_content.article.4	{}	74	3
80	com_content.article.5	{}	74	3
81	com_content.article.6	{}	74	3

Comparission

Below table describes situation when you are logged (as not Super User).

Available Action Supported by Access Supported by AccessControl
Preload components Yes Yes
Preload all assets per component Yes No
Preload only specified asset rules, ex. for 'com_content.article.1' No. Always preload all assets per component, ex: ... WHERE name LIKE 'com_content.%'. It takes a lots of memory. Only load asset of article and parent assets (in one query)
Preload only assets for modules on visited page No. Load all asset rows. May take a lot of memory if you have a lot of modules in database Only preload selected assets (in one query)
Preload only assets for articles in category view No. Only preload all assets per component. Preload only assets for selected articles and its parents. Can preload it in one query using UNION sql statement.
Number of sql queries Low (Components + com_content.% + com_modules.% = 3 queries) In the worse case one per asset but we can use preload to load a few assets in one sql query. (Components + only required component items assets + only active modules = 3 queries
Total loaded #__assets rows Very high Low (preload components + only required rows)
Memory usage Very High Low
Speed of queries Slow because of load a big amount of rows Quite fast
Allows for custom preload No Yes - you can put a list of asset name or asset id to preload. E. g. foreach ($assets as $id) $acl->addAssetIdToPreload($id)
Article asset fallback Only directly to com_content component asset First fallback to category, if not exists then fallback to com_content component asset

Testing Instructions

Check ACL Permission (categories, articles, general) before and after patch.
Joomla should work as before.

To Do

  • Sort of deprecations
  • Better support for services
  • Check B/C for Access class
  • More tests
avatar csthomas csthomas - open - 15 May 2018
avatar csthomas csthomas - change - 15 May 2018
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 15 May 2018
Category Administration com_categories com_content com_modules Modules Front End Libraries Plugins
avatar csthomas csthomas - change - 15 May 2018
Labels Added: ?
avatar brianteeman
brianteeman - comment - 9 Aug 2018

is this still an active wip?

avatar csthomas
csthomas - comment - 10 Aug 2018

I will update it soon. Maybe next week.

avatar csthomas csthomas - change - 13 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 13 Sep 2018
avatar csthomas csthomas - change - 13 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 13 Sep 2018
avatar csthomas csthomas - change - 23 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 23 Sep 2018
avatar csthomas csthomas - change - 23 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 23 Sep 2018
avatar csthomas csthomas - change - 23 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 23 Sep 2018
avatar csthomas csthomas - change - 24 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 24 Sep 2018
avatar csthomas csthomas - change - 24 Sep 2018
The description was changed
avatar csthomas csthomas - edited - 24 Sep 2018
avatar franz-wohlkoenig franz-wohlkoenig - change - 19 Apr 2019
Title
[4.0][WIP] Add AccessControl replacement class for Access
[4.0] [WIP] Add AccessControl replacement class for Access
avatar franz-wohlkoenig franz-wohlkoenig - edited - 19 Apr 2019
avatar sanderpotjer
sanderpotjer - comment - 5 May 2019

@csthomas looks like a nice concept! Still working on this? If so, maybe change the PR to the "Draft" status (new Github option). Please close if no longer WIP. Thanks!

avatar csthomas
csthomas - comment - 5 May 2019

It is not active due to lack of time.

avatar csthomas csthomas - close - 5 May 2019
avatar csthomas csthomas - change - 5 May 2019
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2019-05-05 19:12:52
Closed_By csthomas
Labels Removed: J4 Issue

Add a Comment

Login with GitHub to post a comment