zf2 acl doctrine 2 - doctrine-orm

Actually using Zend Framework 2, I am looking for a way to implement a performant ACL strategy based on a database.
The whole idea is to directly filter the DQL queries depending on the currently logged in user, and it's permissions.
I found an implementation of this mecanisme in Symfony 2 http://symfony.com/doc/current/cookbook/security/acl_advanced.html, in this case one table seems to store for each user if he has access to a single row, so we can easily dynamically load only allowed rows by joining this table.
To synthesize,I am looking for a way to define access rules to entities based on criterias, but want to be able to get results in a single query to be able to do some ordering, and pagination.
Are there any ZF2 modules to resolve this case ?
It looks like integrating the SF2 security component as standalone is not an option: Security component from Symfony 2.0 as standalone

You have to use doctrine filter for load things for current member
example of my codes adding the filter for member query :
$em = $sm->get('doctrine.entitymanager.orm_default');
$ormconfig = $sm->get('doctrine.configuration.orm_default');
$ormconfig->addFilter("member", "\PatrickCore\Script\ORM\Functional\MemberAccessFilter");
//
$currentUser = $membersService->getCurrentUser();
$uid = $currentUser->getId();
$filter = $em->getFilters()->enable("member");
$filter->setParameter('member', $uid);
and this file \PatrickCore\Script\ORM\Functional\MemberAccessFilter :
<?php
namespace PatrickCore\Script\ORM\Functional;
use Doctrine\ORM\Mapping\ClassMetaData,
Doctrine\ORM\Query\Filter\SQLFilter;
class MemberAccessFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
// Check if the entity implements the LocalAware interface
if (!$targetEntity->reflClass->implementsInterface('\PatrickCore\Entity\MemberAccessAware')) {
return "";
}
return $targetTableAlias.'.member_id = ' . $this->getParameter('member'); // getParameter applies quoting automatically
}
}

Related

spring data neo4j (SDN4) - find by relationship

I'm studying spring data for Neo4J and I've seen some examples where you just define a method in the repository interface following some standards (to find by a specific attribute) and it's automatically handled by spring. Ex: findByName.
It works quite straightforward with basic attributes but it doesn't seems to work when the attribute is actually a relationship.
See this example:
public class AcceptOrganizationTask extends AbstractTask {
#Relationship(type="RELATES_TO", direction = "OUTGOING")
private OrganizationInvite invitation;
...
}
In the repository interface I've defined 3 methods (All with the same goal):
List<AcceptOrganizationTask> findAllByInvitation(OrganizationInvite invite);
#Query("MATCH (i:OrganizationInvite)<-[RELATES_TO]-(t:AcceptOrganizationTask) WHERE i={invite} RETURN t")
List<AcceptOrganizationTask> getTaskByInvitation(#Param("invite") OrganizationInvite invite);
AcceptOrganizationTask findByInvitation_Id(Long invitationId);
None of them are able to retrieve the task by its invite property. But if I use use findAll() I can get the object with the property associated to the correct invitation.
Am I missing something ?
Bellow I have the Cypher code generated for this three methods:
findAllByInvitation
MATCH (n:`AcceptOrganizationTask`)
WHERE n.`invitation` = { `invitation_0` }
WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)
with params {invitation_0={entityId=15, version=0, createdOn=1484758262374, lastChanged=1484758262374, createUser=null, lastUpdatedBy=null, email=user2#acme.com, randomKey=fc940b14-12c3-4894-b2b4-728e3a6b8036, invitedUser={entityId=11, version=0, createdOn=1484758261450, lastChanged=1484758261450, createUser=null, lastUpdatedBy=null, name=User user2#acme.com, email=user2#acme.com, credentialsNonExpired=true, lastPasswordResetDate=null, authorities=null, authoritiesInDB=[], accountNonExpired=true, accountNonLocked=true, enabled=true, id=11}, id=15}}
getTasksByInvitation
MATCH (i:OrganizationInvite)<-[RELATES_TO]-(t:AcceptOrganizationTask)
WHERE i={invite} RETURN t with params {invite=15}
findByInvitation_Id
MATCH (n:`AcceptOrganizationTask`)
MATCH (m0:`OrganizationInvite`)
WHERE m0.`id` = { `invitation_id_0` }
MATCH (n)-[:`RELATES_TO`]->(m0)
WITH n
MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n)
with params {invitation_id_0=15}
All entities inherit from a common AbstractEntity with and Long id field, annotated with #GraphId.
Am I missing something ?
At the moment derived finder queries support only works to one level of nesting.
We are hoping to add this feature soon in the coming weeks though.
For now you can write a custom #Query to get around this.

How to query based on one-to-many relationships in Spring Data Neo4j

I have defined simple one-to-many relationship as:
#Relationship(type = "BELONG")
private Set<Category> categories;
I want to query all the objects based on the exact set of Category. i.e. implement something like:
Page<SomeObject> findAllByCategories(Set<Category> categories, PageRequest page);
What is the best way to do it in Spring Data Neo4j?
You have a couple of options on how you can do this.
Your calling code can simply call the following on your SomeObjectRepository:
Iterable<Long> ids = categories.stream().map(SomeObject::getId).collect(Collectors.toSet());
Iterable<SomeObject> someObjects = someObjectRepository.findAll(ids, 1);
Note this method does not support paging though.
Another way is to inject a Session into your calling code and either write a custom Cypher query to retrieve the objects you want or use the load all by instance method:
#Autowired
private Session session;
...
// Option 1: write some cypher
Map<String, Object> params = new HashMap<>():
params.put("ids", ids) // use a stream like above to collect the ids.
Iterable<SomeObject> someObjects = session.query(SomeObject.class, "MATCH (n:SomeObject) ...", params);
//Option 2: use the load by instances method which also allows pagination:
Collection<SomeObject> someObjects = session.loadAll(categories, new Pagination(0, 25));
My recommendation would be option 2 as it pretty much does exactly what you want.

Cleanest way to identify an entity with Doctrine inheritance mapping in zf2

I am using Doctrine inheritance mapping in a project which produces a set of unique entities that each extend a base entity. Because the route is not aware of which entities go with which base rows, I have to query the database twice in order to grab the row I want from the right fieldset:
// in a controller action:
// locate the event entity record and determine the event type
$entity = 'AdminEvents\Entity\Event';
$event = $this->getEntityManager()->find($entity, $eventID);
$eventType = $this->getEntityManager()->getClassMetadata(get_class($event))->discriminatorValue;
// locate the record we're really looking for in the unique extended entity
$entity = 'AdminEvents\Entity\\' . $eventType;
$event = $this->getEntityManager()->find($entity, $eventID);
Is there a cleaner way to do this?
You should probably define an \AdminEvents\Entity\AbsractEvent class, if you haven't already. Then each of your STI entities should extend this, and you can do instanceof (or other logic) to find out what concrete type you got:
// locate the record using the AbstractEntity
$entity = 'AdminEvents\Entity\AbstractEntity';
$event = $this->getEntityManager()->find($entity, $eventID);
A word of caution: the SPL function, get_class will often return the Doctrine Proxy class, so don't rely on that directly to test the return type. You can use the Doctrine class 'ClassUtils'
\Doctrine\Common\Util\ClassUtils::getRealClass(get_class($event));

Sitecore Multisite Manager and 'source' field in template builder

Is there any way to parametise the Datasource for the 'source' field in the Template Builder?
We have a multisite setup. As part of this it would save a lot of time and irritation if we could point our Droptrees and Treelists point at the appropriate locations rather than common parents.
For instance:
Content
--Site1
--Data
--Site2
--Data
Instead of having to point our site at the root Content folder I want to point it at the individual data folders, so I want to do something like:
DataSource=/sitecore/content/$sitename/Data
I can't find any articles on this. Is it something that's possible?
Not by default, but you can use this technique to code your datasources:
http://newguid.net/sitecore/2013/coded-field-datasources-in-sitecore/
You could possibly use relative paths if it fits with the rest of your site structure. It could be as simple as:
./Data
But if the fields are on random items all over the tree, that might not be helpul.
Otherwise try looking at:
How to use sitecore query in datasource location? (dynamic datasouce)
You might want to look at using a Querable Datasource Location and plugging into the getRenderingDatasource pipeline.
It's really going to depend on your use cases. The thing I like about this solution is there is no need to create a whole bunch of controls which effectively do he same thing as the default Sitecore ones, and you don't have to individually code up each datasource you require - just set the query you need to get the data. You can also just set the datasource query in the __standard values for the templates.
This is very similar to Holger's suggestion, I just think this code is neater :)
Since Sitecore 7 requires VS 2012 and our company isn't going to upgrade any time soon I was forced to find a Sitecore 6 solution to this.
Drawing on this article and this one I came up with this solution.
public class SCWTreeList : TreeList
{
protected override void OnLoad(EventArgs e)
{
if (!String.IsNullOrEmpty(Source))
this.Source = SourceQuery.Resolve(SContext.ContentDatabase.Items[ItemID], Source);
base.OnLoad(e);
}
}
This creates a custom TreeList control and passes it's Source field through to a class to handle it. All that class needs to do is resolve anything you have in the Source field into a sitecore query path which can then be reassigned to the source field. This will then go on to be handled by Sitecore's own query engine.
So for our multi-site solution it enabled paths such as this:
{A588F1CE-3BB7-46FA-AFF1-3918E8925E09}/$sitename
To resolve to paths such as this:
/sitecore/medialibrary/Product Images/Site2
Our controls will then only show items for the correct site.
This is the method that handles resolving the GUIDs and tokens:
public static string Resolve(Item item, string query)
{
// Resolve tokens
if (query.Contains("$"))
{
MatchCollection matches = Regex.Matches(query, "\\$[a-z]+");
foreach (Match match in matches)
query = query.Replace(match.Value, ResolveToken(item, match.Value));
}
// Resolve GUIDs.
MatchCollection guidMatches = Regex.Matches(query, "^{[a-zA-Z0-9-]+}");
foreach (Match match in guidMatches)
{
Guid guid = Guid.Parse(match.Value);
Item queryItem = SContext.ContentDatabase.GetItem(new ID(guid));
if (item != null)
query = query.Replace(match.Value, queryItem.Paths.FullPath);
}
return query;
}
Token handling below, as you can see it requires that any item using the $siteref token is inside an Site Folder item that we created. That allows us to use a field which contains the name that all of our multi-site content folders must follow - Site Reference. As long at that naming convention is obeyed it allows us to reference folders within the media library or any other shared content within Sitecore.
static string ResolveToken(Item root, string token)
{
switch (token)
{
case "$siteref":
string sRef = string.Empty;
Item siteFolder = root.Axes.GetAncestors().First(x => x.TemplateID.Guid == TemplateKeys.CMS.SiteFolder);
if (siteFolder != null)
sRef = siteFolder.Fields["Site Reference"].Value;
return sRef;
}
throw new Exception("Token '" + token + "' is not recognised. Please disable wishful thinking and try again.");
}
So far this works for TreeLists, DropTrees and DropLists. It would be nice to get it working with DropLinks but this method does not seem to work.
This feels like scratching the surface, I'm sure there's a lot more you could do with this approach.

Zend Framework - Doctrine2 - Repository Query Caching

I'm looking at Caching and how to use it in Doctrine.
I've got the following in my Zend Framework Bootstrap.php:
// Build Configuration
$orm_config = new \Doctrine\ORM\Configuration();
// Caching
$cacheOptions = $options['cache']['backendOptions'];
$cache = new \Doctrine\Common\Cache\MemcacheCache();
$memcache = new Memcache;
$memcache->connect($cacheOptions['servers']['host'], $cacheOptions['servers']['port']);
$cache->setMemcache($memcache);
$orm_config->setMetadataCacheImpl($cache);
$orm_config->setQueryCacheImpl($cache);
$orm_config->setResultCacheImpl($cache);
I'm running a very simple query on my DB using:
self::_instance()->_em->getRepository('UserManagement\Users')->find('1');
And I'm not sure if I'm using caching properly, because with it on (as
per the above config) the query seems to take twice as long to execute
as with it disabled, is this right?
Thanks in advance,
Steve
I seem to have sorted this myself, sort of related to enter link description here. Basically, from what I understand a repository query like:
self::_instance()->_em->getRepository('UserManagement\Users')->find('1');
Will not cache the results. If the same query is executed again throughout the script processing, it will not perform the search and use the result it has in memory - this isn't the same as real caching, in my case using Memcache.
The only way to achieve this, is to override the Doctrine EntityRepository find() method in a custom repository with something like:
public function find($id)
{
// Retrieve an instance of the Entity Manager
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
->from('UserManagement\Users', 'u')
->where('u.id = :id')
->setParameter('id', $id);
$query = $qb->getQuery();
$query->useResultCache(TRUE);
$result = $query->getSingleResult();
return $result;
}
Notably, the most important line from the above is $query->useResultCache(TRUE); - this informs the Application to cache the results.
Hope this helps.