diff --git a/DataFixtures/ORM/LoadReportACL.php b/DataFixtures/ORM/LoadReportACL.php index 0cec37b42..429fac99c 100644 --- a/DataFixtures/ORM/LoadReportACL.php +++ b/DataFixtures/ORM/LoadReportACL.php @@ -44,19 +44,25 @@ class LoadReportACL extends AbstractFixture implements OrderedFixtureInterface { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { $permissionsGroup = $this->getReference($permissionsGroupRef); + printf("processing permission group %s \n", $permissionsGroup->getName()); foreach (LoadScopes::$references as $scopeRef){ $scope = $this->getReference($scopeRef); + printf("processing scope %s \n", $scope->getName()['en']); //create permission group switch ($permissionsGroup->getName()) { case 'social': if ($scope->getName()['en'] === 'administrative') { - continue; // we do not want any power on administrative + printf("denying power on administrative \n"); + break 2; // we do not want any power on administrative } + break; case 'administrative': case 'direction': if (in_array($scope->getName()['en'], array('administrative', 'social'))) { - continue; // we do not want any power on social or administrative + printf("denying power on %s\n", $scope->getName()['en']); + break 2; // we do not want any power on social or administrative } + break; } printf("Adding CHILL_REPORT_UPDATE & CHILL_REPORT_CREATE to %s " diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 35fbf3b27..82310bc37 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -17,6 +17,8 @@ services: class: Chill\ReportBundle\Timeline\TimelineReportProvider arguments: - '@doctrine.orm.entity_manager' + - '@chill.main.security.authorization.helper' + - '@security.token_storage' tags: - { name: chill.timeline, context: 'person' } diff --git a/Tests/Timeline/TimelineProviderTest.php b/Tests/Timeline/TimelineProviderTest.php index afd3afc17..080a8f288 100644 --- a/Tests/Timeline/TimelineProviderTest.php +++ b/Tests/Timeline/TimelineProviderTest.php @@ -24,6 +24,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Chill\PersonBundle\Entity\Person; use Chill\ReportBundle\Entity\Report; use Chill\MainBundle\Tests\TestHelper as MainTestHelper; +use Chill\MainBundle\Entity\Scope; /** * Test a report is shown into timeline @@ -65,23 +66,32 @@ class TimelineProviderTest extends WebTestCase $center = static::$em->getRepository('ChillMainBundle:Center') ->findOneBy(array('name' => 'Center A')); - $this->person = (new Person(new \DateTime('2015-05-01'))) + $person = (new Person(new \DateTime('2015-05-01'))) ->setGenre(Person::GENRE_WOMAN) ->setFirstName('Nelson') ->setLastName('Mandela') ->setCenter($center); - static::$em->persist($this->person); + static::$em->persist($person); + $this->person = $person; - $this->report = (new Report) + $scopesSocial = array_filter(static::$em + ->getRepository('ChillMainBundle:Scope') + ->findAll(), + function(Scope $scope) { return $scope->getName()['en'] === 'social'; }) + ; + + $report = (new Report) ->setUser(static::$em->getRepository('ChillMainBundle:User') - ->findOneByUsername('center b_social')) + ->findOneByUsername('center a_social')) ->setDate(new \DateTime('2015-05-02')) ->setPerson($this->person) ->setCFGroup($this->getHousingCustomFieldsGroup()) ->setCFData(['has_logement' => 'own_house', - 'house-desc' => 'blah blah']); + 'house-desc' => 'blah blah']) + ->setScope(end($scopesSocial)); - static::$em->persist($this->report); + static::$em->persist($report); + $this->report = $report; @@ -131,6 +141,20 @@ class TimelineProviderTest extends WebTestCase 'the page contains the mention "PropriƩtaire"'); } + public function testReportIsNotVisibleToUngrantedUsers() + { + $client = static::createClient(array(), + MainTestHelper::getAuthenticatedClientOptions('center a_administrative') + ); + + $crawler = $client->request('GET', '/fr/person/'.$this->person->getId() + .'/timeline'); + + $this->assertEquals(0, $crawler->filter('.report .summary') + ->count(), + 'the page does not contains a .report .summary element'); + } + /** * get a random custom fields group * @@ -155,7 +179,9 @@ class TimelineProviderTest extends WebTestCase public function tearDown() { - //static::$em->remove($this->person); + //static::$em->refresh($this->person); + //static::$em->refresh($this->report); + // static::$em->remove($this->person); //static::$em->remove($this->report); } } diff --git a/Timeline/TimelineReportProvider.php b/Timeline/TimelineReportProvider.php index c9138c9d4..a4d189212 100644 --- a/Timeline/TimelineReportProvider.php +++ b/Timeline/TimelineReportProvider.php @@ -22,6 +22,12 @@ namespace Chill\ReportBundle\Timeline; use Chill\MainBundle\Timeline\TimelineProviderInterface; use Doctrine\ORM\EntityManager; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Role\Role; +use Doctrine\ORM\Mapping\ClassMetadata; +use Chill\PersonBundle\Entity\Person; +use Chill\MainBundle\Entity\Scope; /** * Provide report for inclusion in timeline @@ -38,9 +44,30 @@ class TimelineReportProvider implements TimelineProviderInterface */ protected $em; - public function __construct(EntityManager $em) + /** + * + * @var AuthorizationHelper + */ + protected $helper; + + /** + * + * @var \Chill\MainBundle\Entity\User + */ + protected $user; + + public function __construct(EntityManager $em, AuthorizationHelper $helper, + TokenStorage $storage) { $this->em = $em; + $this->helper = $helper; + + if (!$storage->getToken()->getUser() instanceof \Chill\MainBundle\Entity\User) + { + throw new \RuntimeException('A user should be authenticated !'); + } + + $this->user = $storage->getToken()->getUser(); } /** @@ -51,19 +78,71 @@ class TimelineReportProvider implements TimelineProviderInterface { $this->checkContext($context); - $metadata = $this->em->getClassMetadata('ChillReportBundle:Report'); + $metadataReport = $this->em->getClassMetadata('ChillReportBundle:Report'); + $metadataPerson = $this->em->getClassMetadata('ChillPersonBundle:Person'); return array( - 'id' => $metadata->getColumnName('id'), + 'id' => $metadataReport->getTableName() + .'.'.$metadataReport->getColumnName('id'), 'type' => 'report', - 'date' => $metadata->getColumnName('date'), - 'FROM' => $metadata->getTableName(), - 'WHERE' => sprintf('%s = %d', - $metadata - ->getAssociationMapping('person')['joinColumns'][0]['name'], - $args['person']->getId()) + 'date' => $metadataReport->getTableName() + .'.'.$metadataReport->getColumnName('date'), + 'FROM' => $this->getFromClause($metadataReport, $metadataPerson), + 'WHERE' => $this->getWhereClause($metadataReport, $metadataPerson, + $args['person']) ); } + + private function getWhereClause(ClassMetadata $metadataReport, + ClassMetadata $metadataPerson, Person $person) + { + $role = new Role('CHILL_REPORT_SEE'); + $reachableCenters = $this->helper->getReachableCenters($this->user, + $role); + $associationMapping = $metadataReport->getAssociationMapping('person'); + + // we start with reports having the person_id linked to person + // (currently only context "person" is supported) + $whereClause = sprintf('%s = %d', + $associationMapping['joinColumns'][0]['name'], + $person->getId()); + + // we add acl (reachable center and scopes) + $centerAndScopeLines = array(); + foreach ($reachableCenters as $center) { + $reachablesScopesId = array_map( + function(Scope $scope) { return $scope->getId(); }, + $this->helper->getReachableScopes($this->user, $role, + $person->getCenter()) + ); + + $centerAndScopeLines[] = sprintf('(%s = %d AND %s IN (%s))', + $metadataPerson->getTableName().'.'. + $metadataPerson->getAssociationMapping('center')['joinColumns'][0]['name'], + $center->getId(), + $metadataReport->getTableName().'.'. + $metadataReport->getAssociationMapping('scope')['joinColumns'][0]['name'], + implode(',', $reachablesScopesId)); + + } + $whereClause .= ' AND ('.implode(' OR ', $centerAndScopeLines).')'; + + return $whereClause; + } + + private function getFromClause(ClassMetadata $metadataReport, + ClassMetadata $metadataPerson) + { + $associationMapping = $metadataReport->getAssociationMapping('person'); + + return $metadataReport->getTableName().' JOIN ' + .$metadataPerson->getTableName().' ON ' + .$metadataPerson->getTableName().'.'. + $associationMapping['joinColumns'][0]['referencedColumnName'] + .' = ' + .$associationMapping['joinColumns'][0]['name'] + ; + } /** *