Oh, sorry... I assumed you tested the query.
Is my implementation of findpolicy() correct or is there something wrong?
<?php class modTest extends modAccessibleSimpleObject { public function findPolicy($context = '') { $policy = array(); $context = !empty($context) ? $context : $this->xpdo->context->get('key'); if (empty($this->_policies) || !isset($this->_policies[$context])) { $accessTable = $this->xpdo->getTableName('modAccessTest'); $policyTable = $this->xpdo->getTableName('modAccessPolicy'); $sql = "SELECT acl.target, acl.principal, acl.authority, acl.policy, p.data FROM {$accessTable} acl " . "LEFT JOIN {$policyTable} p ON p.id = acl.policy " . "WHERE acl.principal_class = 'modUserGroup' " . "AND acl.target = :test " . "GROUP BY acl.target, acl.principal, acl.authority, acl.policy"; $bindings = array( ':test' => $this->get('id') ); $query = new xPDOCriteria($this->xpdo, $sql, $bindings); if ($query->stmt && $query->stmt->execute()) { while ($row = $query->stmt->fetch(PDO::FETCH_ASSOC)) { $policy['modAccessTest'][$row['target']][] = array( 'principal' => $row['principal'], 'authority' => $row['authority'], 'policy' => $row['data'] ? xPDO :: fromJSON($row['data'], true) : array(), ); } } $this->_policies[$context] = $policy; } else { $policy = $this->_policies[$context]; } return $policy; } } ?>
Array ( [modx.user.contextTokens] => Array() [modx.user..attributes] => Array ( [web] => Array ( [modAccessContext] => Array() [modAccessResourceGroup] => Array() [modAccessCategory] => Array() [modAccessTest] => Array() ( [2] => Array ( [0] => Array ( [principal] => 0 [authority] => 0 [policy] => Array ( ) ... ) [webDocgroups] => Array() )
No; modPrincipal is simply an abstract class that both modUser and modUserGroup extend. At the moment, all of the core only supports a principal_class of modUserGroup, but your findPolicy()/loadAttributes()/getAttributes() implementations can support either. This is why you should extend modUser rather than hacking on the modPrincipal/modUser implementations of loadAttributes()/getAttributes(); you can override their behavior in your modUser derivative however you choose.
– Should it be possible to use modPrincipal as the principal_class in the table? Because if I use modPrincipal it doesn’t consider the permissions that I set.
Yep; in order to be in any way efficient, the user policies are loaded into session and those policies must be explicitly reloaded or users must be logged out and back in if they change. In addition, Resources and Elements cache their policy data as well, so clearing the cache after a change to any ACL is also necessary.
- When I change the permissions from a database entry while I’m logged in i need to "flush all sessions" and log in again to see the changes. Is this normal?
Are you sure you don’t have an empty row in your modAccessTest table? The second switch statement in modUser::loadAttributes() looks for such rows to assign anonymous policies.
– When I’m logged out I get this: (Shouldn’t the modAccessTest-Array be empty in this state?)
you can override their behavior in your modUser derivative however you chooseCan I extend modUser in my main table class with loadAttributes()/getAttributes() like I did it with findpolicy()?
the user policies are loaded into session and those policies must be explicitly reloaded or users must be logged out and back in if they changeSay, I create a web-application that shares data to different users. When I add a new database record it should be immediately visible (withoud logout and re-login) for assigned users. Is it recommended to use $modx->user->getAttributes(’modTest’, ’web’, true); before each database-call? I tried it and it works, but I don’t know if it’s meant for this case...
$modx->user->getAttributes('modTest', 'web', true); $tests = $modx->getCollection('modTest'); foreach ($tests as $test) { echo $test->get('name')."<br />"; }
No, you can’t extend modAccessibleObject and modUser in the same class. It has to be a separate modUser derivative only.
you can override their behavior in your modUser derivative however you chooseCan I extend modUser in my main table class with loadAttributes()/getAttributes() like I did it with findpolicy()?
A system error? What do you mean? If the record is not accessible, getObject() will return null.
the user policies are loaded into session and those policies must be explicitly reloaded or users must be logged out and back in if they changeSay, I create a web-application that shares data to different users. When I add a new database record it should be immediately visible (withoud logout and re-login) for assigned users. Is it recommended to use $modx->user->getAttributes(’modTest’, ’web’, true); before each database-call? I tried it and it works, but I don’t know if it’s meant for this case...
$modx->user->getAttributes('modTest', 'web', true); $tests = $modx->getCollection('modTest'); foreach ($tests as $test) { echo $test->get('name')."<br />"; }
Is there a method to simply check if the user has access to a specific database record? Because if I use getObject(’modTest’,3) with an id that I don’t have access to, I get a System Error instead of a custom error. If the id is accessible everything works fine.
As for changing ACLs on the fly, well, it’s not designed for that.Are there any disadvantages when I use: $modx->user->getAttributes(’modTest’, ’web’, true) before each database call?
That’s the error I get: Fatal error: Call to a member function get() on a non-object in /home/httpd/vhosts/test.com/modx/httpdocs/core/cache/elements/modsnippet/23.include.cache.php on line 69
<?php $object = $modx->getObject('ClassName', 3); if (!$object) { // handle error here somehow $modx->sendUnauthorizedPage(); } // code that tries to use the object if it is accessible
Well, having to reload the user policies from the database is definitely slower than being able to just use the data loaded from the session.
As for changing ACLs on the fly, well, it’s not designed for that.Are there any disadvantages when I use: $modx->user->getAttributes(’modTest’, ’web’, true) before each database call?
It’s just a "related object" that serves as a container for your main object in a one-to-many relationship, i.e. one modTestGroup to many modTest instances. You can see how this is done in the modResourceGroup queries in loadAttributes() and the findPolicy() implementation in modResource. You just have to "find" your policies for modTest by modTestGroup and "load" them for your user the same way so they can be compared by the primary key of the modTestGroup (instead of modTest).
Is there already a tutorial about how to create something like the organizational entity like in Resource Groups? Is it easy to achieve?
<?xml version="1.0" encoding="UTF-8"?> <model package="hotels" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" tablePrefix="tw_" phpdoc-package="hotels" phpdoc-subpackage="model"> <object class="modAccessHotels" table="access_hotels" extends="modAccess"> <aggregate alias="Target" class="modHotels" local="target" foreign="id" owner="foreign" cardinality="one" /> </object> <object class="modHotelsGroup" table="hotel_groups" extends="xPDOSimpleObject"> <field key="hotels_group" dbtype="int" precision="10" phptype="integer" null="false" default="0" index="index" /> <field key="hotels" dbtype="int" precision="10" phptype="integer" null="false" default="0" index="index" /> <aggregate alias="Hotels" class="modHotels" key="id" local="document" foreign="id" cardinality="one" owner="foreign" /> </object> <object class="modHotels" table="hotels" extends="modAccessibleSimpleObject"> <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default="" index="index" /> <field key="address" dbtype="varchar" precision="255" phptype="string" null="false" default="" /> <field key="city" dbtype="varchar" precision="255" phptype="string" null="false" default="" /> <field key="country" dbtype="varchar" precision="20" phptype="string" null="false" default="" /> <composite alias="HotelsGroup" class="modHotelsGroup" local="id" foreign="hotels" cardinality="many" owner="local" /> <composite alias="Acls" class="modAccessHotels" local="id" foreign="target" owner="local" cardinality="many" /> </object> </model>
<?xml version="1.0" encoding="UTF-8"?> <model package="hotels" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" tablePrefix="tw_" phpdoc-package="hotels" phpdoc-subpackage="mysql"> <object class="twAccessHotel" table="access_hotels" extends="modAccess"> <aggregate alias="Target" class="twHotel" local="target" foreign="id" owner="foreign" cardinality="one" /> </object> <object class="twHotelGroup" table="hotel_groups" extends="xPDOSimpleObject"> <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default="" index="unique" /> <composite alias="Hotels" class="twHotelGroups" local="id" foreign="group" cardinality="many" owner="local" /> </object> <object class="twHotelGroupHotel" table="hotel_group_hotels" extends="xPDOObject"> <field key="group" dbtype="int" precision="10" phptype="integer" null="false" default="0" index="pk" /> <field key="hotel" dbtype="int" precision="10" phptype="integer" null="false" default="0" index="pk" /> <aggregate alias="Group" class="twHotelGroup" local="group" foreign="id" cardinality="one" owner="foreign" /> <aggregate alias="Hotel" class="twHotel" local="hotel" foreign="id" cardinality="one" owner="foreign" /> </object> <object class="twHotel" table="hotels" extends="modAccessibleSimpleObject"> <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default="" index="index" /> <field key="address" dbtype="varchar" precision="255" phptype="string" null="false" default="" /> <field key="city" dbtype="varchar" precision="255" phptype="string" null="false" default="" /> <field key="country" dbtype="varchar" precision="20" phptype="string" null="false" default="" /> <composite alias="Groups" class="twHotelGroupHotel" local="id" foreign="hotel" cardinality="many" owner="local" /> <composite alias="Acls" class="twAccessHotel" local="id" foreign="target" owner="local" cardinality="many" /> </object> </model>