From 3f57f73636a7fc33d876daefaaa0e0e2b9602f13 Mon Sep 17 00:00:00 2001 From: yurio Date: Tue, 22 Oct 2013 19:30:56 +0300 Subject: [PATCH 001/248] BAP-1514: Dropdown list type --- .../Form/Type/AclPermissionSelectorType.php | 51 +++++++++++++++++++ .../Resources/config/services.yml | 6 +++ .../Resources/views/Form/fields.html.twig | 11 ++++ .../DependencyInjection/Configuration.php | 8 +-- .../Resources/views/Role/update.html.twig | 9 ++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php diff --git a/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php b/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php new file mode 100644 index 00000000000..52a9ea23fa1 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php @@ -0,0 +1,51 @@ +setDefaults(array( + 'choices' => array( + AccessLevel::NONE_LEVEL => self::NONE_LEVEL, + AccessLevel::BASIC_LEVEL => self::BASIC_LEVEL, + AccessLevel::LOCAL_LEVEL => self::LOCAL_LEVEL, + AccessLevel::DEEP_LEVEL => self::DEEP_LEVEL, + AccessLevel::GLOBAL_LEVEL => self::GLOBAL_LEVEL, + AccessLevel::SYSTEM_LEVEL => self::SYSTEM_LEVEL, + ) + )); + } +} \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index 83af970041c..c245ee13a82 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -22,6 +22,7 @@ parameters: oro_security.orm.ownership_sql_filter.class: Oro\Bundle\SecurityBundle\ORM\SqlFilter\OwnershipFilter oro_security.orm.ownership_sql_filter_builder.class: Oro\Bundle\SecurityBundle\ORM\SqlFilter\OwnershipFilterBuilder + oro_security.type.oro_acl_permission_selector.class: Oro\Bundle\SecurityBundle\Form\Type\AclPermissionSelectorType oro_security.type.oro_acl_collection.class: Oro\Bundle\SecurityBundle\Form\Type\CollectionType oro_security.type.oro_acl_object_name.class: Oro\Bundle\SecurityBundle\Form\Type\ObjectNameType oro_security.type.oro_acl_label.class: Oro\Bundle\SecurityBundle\Form\Type\ObjectLabelType @@ -189,6 +190,11 @@ services: # - { name: oro_entity.orm.sql_filter, filter_name: ownershipFilter, enabled: true } # - { name: kernel.event_listener, priority: -255, event: kernel.request, method: setUserParameter } + oro_security.form.type.oro_acl_permission_selector: + class: %oro_security.type.oro_acl_permission_selector.class% + tags: + - { name: form.type, alias: oro_acl_permission_selector } + oro_security.form.type.collection_type: class: %oro_security.type.oro_acl_collection.class% tags: diff --git a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig index 48d47efedfb..ceb033616f7 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig +++ b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig @@ -1,3 +1,14 @@ +{% block oro_acl_permission_selector_widget %} +
+ + {% for group_label, choice in choices %} + {% if choice is selectedchoice(value) %}{{ choice.label|trans({}, translation_domain) }}{% endif %} + {% endfor %} + + +
+{% endblock %} + {% block oro_acl_label_widget %} {{ value|trans }} {% endblock %} diff --git a/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php b/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php index 5c5a1c3ac62..f4c8ea140b7 100644 --- a/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php +++ b/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php @@ -58,8 +58,8 @@ public function getConfigTreeBuilder() 'label' => 'Entity', 'view_type' => 'grid', 'types' => array('entity'), - 'field_type' => 'checkbox', - 'fix_values' => true, + 'field_type' => 'oro_acl_permission_selector', + 'fix_values' => false, 'default_value' => 5, 'show_default' => true, ), @@ -67,8 +67,8 @@ public function getConfigTreeBuilder() 'label' => 'Capabilities', 'view_type' => 'list', 'types' => array('action'), - 'field_type' => 'checkbox', - 'fix_values' => true, + 'field_type' => 'oro_acl_permission_selector', + 'fix_values' => false, 'default_value' => 1, 'show_default' => false, ) diff --git a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig index 2dbf5abb696..f1747272f8a 100644 --- a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig +++ b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig @@ -115,4 +115,13 @@ } %} {{ parent() }} + {% endblock content_data %} From f876e7ed7df94e68e5dae2e620125ab789280e1f Mon Sep 17 00:00:00 2001 From: Benoit Jacquemont Date: Wed, 23 Oct 2013 15:39:05 +0200 Subject: [PATCH 002/248] Workaround, waiting for https://github.com/symfony/SwiftmailerBundle/pull/64 to be merged --- .../BatchBundle/Command/BatchCommand.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php b/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php index 3b175956b18..2710bc50853 100644 --- a/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php +++ b/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php @@ -137,6 +137,10 @@ protected function execute(InputInterface $input, OutputInterface $output) ) ); } + + // FIXME: Workaround, waiting for https://github.com/symfony/SwiftmailerBundle/pull/64 + // to be merged + $this->flushMailQueue(); } /** @@ -208,4 +212,35 @@ private function decodeConfiguration($data) throw new \InvalidArgumentException($error); } + + /** + * @see Symfony\Bundle\SwiftmailerBundle\EventListener\EmailSenderListener::onKernelTerminate + * and https://github.com/symfony/SwiftmailerBundle/pull/64 + */ + public function flushMailQueue() + { + if (!$this->getContainer()->has('mailer')) { + return; + } + + $mailers = array_keys($this->getContainer()->getParameter('swiftmailer.mailers')); + foreach ($mailers as $name) { + if ($this->getContainer() instanceof IntrospectableContainerInterface ? + $this->getContainer()->initialized(sprintf('swiftmailer.mailer.%s', $name)) : true) { + if ($this->getContainer()->getParameter(sprintf('swiftmailer.mailer.%s.spool.enabled', $name))) { + $mailer = $this->getContainer()->get(sprintf('swiftmailer.mailer.%s', $name)); + $transport = $mailer->getTransport(); + if ($transport instanceof \Swift_Transport_SpoolTransport) { + $spool = $transport->getSpool(); + if ($spool instanceof \Swift_MemorySpool) { + $spool->flushQueue( + $this->getContainer()->get(sprintf('swiftmailer.mailer.%s.transport.real', $name)) + ); + } + } + } + } + } + + } } From d53b001f7ffe61e594caf16dbe933bf99d99c4a4 Mon Sep 17 00:00:00 2001 From: yurio Date: Wed, 23 Oct 2013 20:10:50 +0300 Subject: [PATCH 003/248] BAP-1514: Dropdown list type --- .../Bundle/SecurityBundle/Acl/AccessLevel.php | 17 ++++++ .../Acl/Extension/ActionAclExtension.php | 8 +++ .../Acl/Extension/EntityAclExtension.php | 17 ++++++ .../Acl/Persistence/AclManager.php | 7 +++ .../Controller/AclPermissionController.php | 19 +++++++ .../Form/Type/AclAccessLevelSelectorType.php | 53 +++++++++++++++++++ .../Form/Type/AclPermissionSelectorType.php | 51 ------------------ .../Resources/config/oro/routing.yml | 4 ++ .../Resources/config/services.yml | 8 +-- .../Resources/views/Form/fields.html.twig | 16 +++--- .../DependencyInjection/Configuration.php | 4 +- .../Resources/views/Role/update.html.twig | 6 +-- 12 files changed, 143 insertions(+), 67 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php create mode 100644 src/Oro/Bundle/SecurityBundle/Form/Type/AclAccessLevelSelectorType.php delete mode 100644 src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php create mode 100644 src/Oro/Bundle/SecurityBundle/Resources/config/oro/routing.yml diff --git a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php index 071e7c0748a..aee24872a8f 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php @@ -77,4 +77,21 @@ public static function getAccessLevelName($value) return null; } + + /** + * Get array with access levels from NONE to $value + * + * @param int $value + * @return array + */ + public static function getAccessLevelNames($value = self::SYSTEM_LEVEL) + { + $names = array(); + for ($level = $value; $level >= self::NONE_LEVEL; $level--) { + $name = self::getAccessLevelName($level); + $names[$level] = $name ? $name : 'NONE'; + } + + return $names; + } } diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php b/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php index df06ca7d5be..064a754774b 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php @@ -29,6 +29,14 @@ public function __construct(ActionMetadataProvider $actionMetadataProvider) ); } + public function getAccessLevelNames($object) + { + return array( + AccessLevel::NONE_LEVEL => 'NONE', + AccessLevel::SYSTEM_LEVEL => AccessLevel::getAccessLevelName(AccessLevel::SYSTEM_LEVEL) + ); + } + /** * {@inheritdoc} */ diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php b/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php index 436f8214f2f..b90ebaaf441 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php @@ -301,6 +301,23 @@ public function adaptRootMask($rootMask, $object) return $rootMask; } + public function getAccessLevelNames($object) + { + return AccessLevel::getAccessLevelNames($this->getMaxAccessLevel($object)); + } + + protected function getMaxAccessLevel($object) + { + $metadata = $this->getMetadata($object); + if (!$metadata->hasOwner()) { + return AccessLevel::SYSTEM_LEVEL; + } elseif ($metadata->isOrganizationOwned()) { + return AccessLevel::GLOBAL_LEVEL; + } elseif ($metadata->isBusinessUnitOwned()) { + return AccessLevel::LOCAL_LEVEL; + } + } + /** * {@inheritdoc} */ diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php b/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php index ca9a2806654..95e84b10fc2 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php @@ -94,6 +94,13 @@ public function __construct( : 'Oro\Bundle\SecurityBundle\Acl\Persistence\AclPrivilegeRepository'; } + public function getAccessLevelsForObject($object) + { + $extension = $this->getExtensionSelector()->select($object); + + return $extension->getAccessLevelNames($object); + } + /** * Indicates whether ACL based security is enabled or not * diff --git a/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php new file mode 100644 index 00000000000..784820d2919 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php @@ -0,0 +1,19 @@ +get('oro_security.acl.manager')->getAccessLevelsForObject($this->getRequest()->get('oid'))); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Form/Type/AclAccessLevelSelectorType.php b/src/Oro/Bundle/SecurityBundle/Form/Type/AclAccessLevelSelectorType.php new file mode 100644 index 00000000000..c1d035d8873 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Form/Type/AclAccessLevelSelectorType.php @@ -0,0 +1,53 @@ +setDefaults(array( + 'choices' => AccessLevel::getAccessLevelNames(), + )); + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $parent = $form->getParent()->getParent()->getParent(); + $parentData = $parent->getData(); + if ($parentData instanceof AclPrivilege) { + $view->vars['identity'] = $parentData->getIdentity()->getId(); + $view->vars['level_label'] = AccessLevel::getAccessLevelName($form->getData()); + } + } +} \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php b/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php deleted file mode 100644 index 52a9ea23fa1..00000000000 --- a/src/Oro/Bundle/SecurityBundle/Form/Type/AclPermissionSelectorType.php +++ /dev/null @@ -1,51 +0,0 @@ -setDefaults(array( - 'choices' => array( - AccessLevel::NONE_LEVEL => self::NONE_LEVEL, - AccessLevel::BASIC_LEVEL => self::BASIC_LEVEL, - AccessLevel::LOCAL_LEVEL => self::LOCAL_LEVEL, - AccessLevel::DEEP_LEVEL => self::DEEP_LEVEL, - AccessLevel::GLOBAL_LEVEL => self::GLOBAL_LEVEL, - AccessLevel::SYSTEM_LEVEL => self::SYSTEM_LEVEL, - ) - )); - } -} \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/oro/routing.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/oro/routing.yml new file mode 100644 index 00000000000..b99dd2ac745 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/oro/routing.yml @@ -0,0 +1,4 @@ +oro_security: + resource: "@OroSecurityBundle/Controller" + type: annotation + prefix: /security diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index c245ee13a82..b97c66e4a62 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -22,7 +22,7 @@ parameters: oro_security.orm.ownership_sql_filter.class: Oro\Bundle\SecurityBundle\ORM\SqlFilter\OwnershipFilter oro_security.orm.ownership_sql_filter_builder.class: Oro\Bundle\SecurityBundle\ORM\SqlFilter\OwnershipFilterBuilder - oro_security.type.oro_acl_permission_selector.class: Oro\Bundle\SecurityBundle\Form\Type\AclPermissionSelectorType + oro_security.type.oro_acl_access_level_selector.class: Oro\Bundle\SecurityBundle\Form\Type\AclAccessLevelSelectorType oro_security.type.oro_acl_collection.class: Oro\Bundle\SecurityBundle\Form\Type\CollectionType oro_security.type.oro_acl_object_name.class: Oro\Bundle\SecurityBundle\Form\Type\ObjectNameType oro_security.type.oro_acl_label.class: Oro\Bundle\SecurityBundle\Form\Type\ObjectLabelType @@ -190,10 +190,10 @@ services: # - { name: oro_entity.orm.sql_filter, filter_name: ownershipFilter, enabled: true } # - { name: kernel.event_listener, priority: -255, event: kernel.request, method: setUserParameter } - oro_security.form.type.oro_acl_permission_selector: - class: %oro_security.type.oro_acl_permission_selector.class% + oro_security.form.type.oro_acl_access_level_selector: + class: %oro_security.type.oro_acl_access_level_selector.class% tags: - - { name: form.type, alias: oro_acl_permission_selector } + - { name: form.type, alias: oro_acl_access_level_selector } oro_security.form.type.collection_type: class: %oro_security.type.oro_acl_collection.class% diff --git a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig index ceb033616f7..b8357d04d25 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig +++ b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig @@ -1,11 +1,13 @@ -{% block oro_acl_permission_selector_widget %} -
- - {% for group_label, choice in choices %} - {% if choice is selectedchoice(value) %}{{ choice.label|trans({}, translation_domain) }}{% endif %} - {% endfor %} +{% block oro_acl_access_level_selector_widget %} + {% endblock %} diff --git a/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php b/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php index f4c8ea140b7..35eb043ee42 100644 --- a/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php +++ b/src/Oro/Bundle/UserBundle/DependencyInjection/Configuration.php @@ -58,7 +58,7 @@ public function getConfigTreeBuilder() 'label' => 'Entity', 'view_type' => 'grid', 'types' => array('entity'), - 'field_type' => 'oro_acl_permission_selector', + 'field_type' => 'oro_acl_access_level_selector', 'fix_values' => false, 'default_value' => 5, 'show_default' => true, @@ -67,7 +67,7 @@ public function getConfigTreeBuilder() 'label' => 'Capabilities', 'view_type' => 'list', 'types' => array('action'), - 'field_type' => 'oro_acl_permission_selector', + 'field_type' => 'oro_acl_access_level_selector', 'fix_values' => false, 'default_value' => 1, 'show_default' => false, diff --git a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig index f1747272f8a..8ddc1b0fdce 100644 --- a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig +++ b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig @@ -115,10 +115,10 @@ } %} {{ parent() }} - {% endblock %} {% block navButtons %} @@ -115,13 +118,4 @@ } %} {{ parent() }} - {% endblock content_data %} From a849e1587f7d6949cec688d0aba7b944bdd529ce Mon Sep 17 00:00:00 2001 From: yurio Date: Fri, 25 Oct 2013 13:56:18 +0300 Subject: [PATCH 006/248] BAP-1514: Dropdown list type --- .../SecurityBundle/Acl/Extension/NullAclExtension.php | 8 ++++++++ .../Unit/Acl/Extension/EntityAclExtensionTest.php | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Extension/NullAclExtension.php b/src/Oro/Bundle/SecurityBundle/Acl/Extension/NullAclExtension.php index c2425cd7105..abd059ab4fa 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Extension/NullAclExtension.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Extension/NullAclExtension.php @@ -156,4 +156,12 @@ public function decideIsGranting($triggeredMask, $object, TokenInterface $securi { return true; } + + /** + * {@inheritdoc} + */ + public function getAccessLevelNames($object) + { + return array(); + } } diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Extension/EntityAclExtensionTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Extension/EntityAclExtensionTest.php index b367d12abb4..89182caa4ce 100644 --- a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Extension/EntityAclExtensionTest.php +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Extension/EntityAclExtensionTest.php @@ -396,6 +396,16 @@ public function testGetAccessLevel() ); } + public function testGetAccessLevelNamesForRoot() + { + $object = new ObjectIdentity('entity', ObjectIdentityFactory::ROOT_IDENTITY_TYPE); + $this->assertEquals( + array('NONE', 'BASIC', 'LOCAL', 'DEEP', 'GLOBAL', 'SYSTEM'), + $this->extension->getAccessLevelNames($object) + ); + } + + public function decideIsGrantingProvider() { $this->org1 = new Organization('org1'); From 6e15e7383648f96f73cb2d968baed5bbc6cb54bb Mon Sep 17 00:00:00 2001 From: Benoit Jacquemont Date: Mon, 28 Oct 2013 15:39:16 +0100 Subject: [PATCH 007/248] BAP-1989 Make sure jobExecution and stepExecution are updated and that email are send at the end --- .../BatchBundle/Command/BatchCommand.php | 6 +++--- .../BatchBundle/Job/DoctrineJobRepository.php | 20 +++++++++++++++++++ src/Oro/Bundle/BatchBundle/Job/Job.php | 15 +++++++++++++- .../Job/JobRepositoryInterface.php | 19 ++++++++++++++++++ .../Bundle/BatchBundle/Step/AbstractStep.php | 6 ++++++ 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php b/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php index 2710bc50853..3b55d0281ab 100644 --- a/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php +++ b/src/Oro/Bundle/BatchBundle/Command/BatchCommand.php @@ -116,12 +116,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $job->execute($jobExecution); - $this->getEntityManager()->persist($jobInstance); - $this->getEntityManager()->flush($jobInstance); - $this->getEntityManager()->persist($jobExecution); $this->getEntityManager()->flush($jobExecution); + $this->getEntityManager()->persist($jobInstance); + $this->getEntityManager()->flush($jobInstance); + if (ExitStatus::COMPLETED === $jobExecution->getExitStatus()->getExitCode()) { $output->writeln( sprintf( diff --git a/src/Oro/Bundle/BatchBundle/Job/DoctrineJobRepository.php b/src/Oro/Bundle/BatchBundle/Job/DoctrineJobRepository.php index 2efade35aeb..34da4fae1c7 100644 --- a/src/Oro/Bundle/BatchBundle/Job/DoctrineJobRepository.php +++ b/src/Oro/Bundle/BatchBundle/Job/DoctrineJobRepository.php @@ -5,6 +5,7 @@ use Doctrine\ORM\EntityManager; use Oro\Bundle\BatchBundle\Entity\JobInstance; use Oro\Bundle\BatchBundle\Entity\JobExecution; +use Oro\Bundle\BatchBundle\Entity\StepExecution; /** * Class peristing JobExecution and StepExecution states @@ -35,4 +36,23 @@ public function createJobExecution(JobInstance $jobInstance) return $jobExecution; } + + /** + * {@inheritdoc} + */ + public function updateJobExecution(JobExecution $jobExecution) + { + $this->entityManager->persist($jobExecution); + $this->entityManager->flush($jobExecution); + } + + /** + * {@inheritdoc} + */ + public function updateStepExecution(StepExecution $stepExecution) + { + $this->entityManager->persist($stepExecution); + $this->entityManager->flush($stepExecution); + } + } diff --git a/src/Oro/Bundle/BatchBundle/Job/Job.php b/src/Oro/Bundle/BatchBundle/Job/Job.php index b07236ab32b..8b7a277887a 100644 --- a/src/Oro/Bundle/BatchBundle/Job/Job.php +++ b/src/Oro/Bundle/BatchBundle/Job/Job.php @@ -181,14 +181,15 @@ final public function execute(JobExecution $jobExecution) if ($jobExecution->getStatus()->getValue() !== BatchStatus::STOPPING) { $jobExecution->setStartTime(new \DateTime()); $this->updateStatus($jobExecution, BatchStatus::STARTED); + $this->jobRepository->updateJobExecution($jobExecution); - // Todo Listener beforeJob $this->doExecute($jobExecution); } else { // The job was already stopped before we even got this far. Deal // with it in the same way as any other interruption. $jobExecution->setStatus(new BatchStatus(BatchStatus::STOPPED)); $jobExecution->setExitStatus(new ExitStatus(ExitStatus::COMPLETED)); + $this->jobRepository->updateJobExecution($jobExecution); $this->dispatchJobExecutionEvent(EventInterface::JOB_EXECUTION_STOPPED, $jobExecution); } @@ -201,11 +202,15 @@ final public function execute(JobExecution $jobExecution) ) ); $jobExecution->addFailureException($e); + $this->jobRepository->updateJobExecution($jobExecution); + $this->dispatchJobExecutionEvent(EventInterface::JOB_EXECUTION_INTERRUPTED, $jobExecution); } catch (\Exception $e) { $jobExecution->setExitStatus($this->getDefaultExitStatusForFailure($e)); $jobExecution->setStatus(new BatchStatus(BatchStatus::FAILED)); $jobExecution->addFailureException($e); + $this->jobRepository->updateJobExecution($jobExecution); + $this->dispatchJobExecutionEvent(EventInterface::JOB_EXECUTION_FATAL_ERROR, $jobExecution); } @@ -217,9 +222,11 @@ final public function execute(JobExecution $jobExecution) $noopExitStatus = new ExitStatus(ExitStatus::NOOP); $noopExitStatus->addExitDescription("All steps already completed or no steps configured for this job."); $jobExecution->setExitStatus($exitStatus->logicalAnd($noopExitStatus)); + $this->jobRepository->updateJobExecution($jobExecution); } $jobExecution->setEndTime(new \DateTime()); + $this->jobRepository->updateJobExecution($jobExecution); $this->dispatchJobExecutionEvent(EventInterface::AFTER_JOB_EXECUTION, $jobExecution); } @@ -321,6 +328,7 @@ protected function doExecute(JobExecution $jobExecution) foreach ($this->steps as $step) { $stepExecution = $this->handleStep($step, $jobExecution); + $this->jobRepository->updateStepExecution($stepExecution); if ($stepExecution->getStatus()->getValue() !== BatchStatus::COMPLETED) { // Terminate the job if a step fails @@ -331,8 +339,10 @@ protected function doExecute(JobExecution $jobExecution) // Update the job status to be the same as the last step if ($stepExecution !== null) { $this->dispatchJobExecutionEvent(EventInterface::BEFORE_JOB_STATUS_UPGRADE, $jobExecution); + $jobExecution->upgradeStatus($stepExecution->getStatus()->getValue()); $jobExecution->setExitStatus($stepExecution->getExitStatus()); + $this->jobRepository->updateJobExecution($jobExecution); } } @@ -359,12 +369,15 @@ public function handleStep(StepInterface $step, JobExecution $jobExecution) $step->execute($stepExecution); } catch (JobInterruptedException $e) { $stepExecution->setStatus(new BatchStatus(BatchStatus::STOPPING)); + $this->jobRepository->updateStepExecution($stepExecution); throw $e; } if ($stepExecution->getStatus()->getValue() == BatchStatus::STOPPING || $stepExecution->getStatus()->getValue() == BatchStatus::STOPPED) { + $jobExecution->setStatus(new BatchStatus(BatchStatus::STOPPING)); + $this->jobRepository->updateJobExecution($jobExecution); throw new JobInterruptedException("Job interrupted by step execution"); } diff --git a/src/Oro/Bundle/BatchBundle/Job/JobRepositoryInterface.php b/src/Oro/Bundle/BatchBundle/Job/JobRepositoryInterface.php index 4e40b01a1af..f60c1e11ef1 100644 --- a/src/Oro/Bundle/BatchBundle/Job/JobRepositoryInterface.php +++ b/src/Oro/Bundle/BatchBundle/Job/JobRepositoryInterface.php @@ -4,6 +4,7 @@ use Oro\Bundle\BatchBundle\Entity\JobInstance; use Oro\Bundle\BatchBundle\Entity\JobExecution; +use Oro\Bundle\BatchBundle\Entity\StepExecution; /** * Common interface for Job repositories which should handle how job are stored, updated @@ -22,4 +23,22 @@ interface JobRepositoryInterface * @return JobExecution */ public function createJobExecution(JobInstance $job); + + /** + * Update a JobExecution + * + * @param JobExecution $jobExecution + * + * @return JobExecution + */ + public function updateJobExecution(JobExecution $job); + + /** + * Update a StepExecution + * + * @param StepExecution $stepExecution + * + * @return StepExecution + */ + public function updateStepExecution(StepExecution $job); } diff --git a/src/Oro/Bundle/BatchBundle/Step/AbstractStep.php b/src/Oro/Bundle/BatchBundle/Step/AbstractStep.php index 6a9a54359cd..f791262363b 100644 --- a/src/Oro/Bundle/BatchBundle/Step/AbstractStep.php +++ b/src/Oro/Bundle/BatchBundle/Step/AbstractStep.php @@ -136,6 +136,7 @@ final public function execute(StepExecution $stepExecution) $stepExecution->setStartTime(new \DateTime()); $stepExecution->setStatus(new BatchStatus(BatchStatus::STARTED)); + $this->jobRepository->updateStepExecution($stepExecution); // Start with a default value that will be trumped by anything $exitStatus = new ExitStatus(ExitStatus::EXECUTING); @@ -145,6 +146,9 @@ final public function execute(StepExecution $stepExecution) $exitStatus = new ExitStatus(ExitStatus::COMPLETED); $exitStatus->logicalAnd($stepExecution->getExitStatus()); + + $this->jobRepository->updateStepExecution($stepExecution); + // Check if someone is trying to stop us if ($stepExecution->isTerminateOnly()) { throw new JobInterruptedException("JobExecution interrupted."); @@ -159,6 +163,7 @@ final public function execute(StepExecution $stepExecution) $exitStatus = $exitStatus->logicalAnd($this->getDefaultExitStatusForFailure($e)); $stepExecution->addFailureException($e); + $this->jobRepository->updateStepExecution($stepExecution); if ($stepExecution->getStatus()->getValue() == BatchStatus::STOPPED) { $this->dispatchStepExecutionEvent(EventInterface::STEP_EXECUTION_INTERRUPTED, $stepExecution); @@ -169,6 +174,7 @@ final public function execute(StepExecution $stepExecution) $stepExecution->setEndTime(new \DateTime()); $stepExecution->setExitStatus($exitStatus); + $this->jobRepository->updateStepExecution($stepExecution); $this->dispatchStepExecutionEvent(EventInterface::STEP_EXECUTION_COMPLETED, $stepExecution); } From 717461c8c297458ac425e5820dc0074da31e3594 Mon Sep 17 00:00:00 2001 From: yurio Date: Fri, 1 Nov 2013 12:40:45 +0200 Subject: [PATCH 008/248] BAP-1926: ACL Acess Level Check concept --- .../ORM/SqlFilter/OwnershipFilter.php | 12 +- .../SecurityBundle/ORM/Walker/AclHelper.php | 223 ++++++++++++ .../SecurityBundle/ORM/Walker/AclWalker.php | 221 ++++++++++++ .../ORM/Walker/Condition/AclCondition.php | 79 ++++ .../Walker/Condition/AclConditionStorage.php | 93 +++++ .../ORM/Walker/Condition/JoinAclCondition.php | 49 +++ .../SubRequestAclConditionStorage.php | 42 +++ .../ORM/Walker/OwnershipFilterBuilder.php | 341 ++++++++++++++++++ .../Resources/config/services.yml | 47 +++ .../Bundle/SecurityBundle/TreeProvider.php | 48 +++ 10 files changed, 1152 insertions(+), 3 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclCondition.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclConditionStorage.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAclCondition.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/SubRequestAclConditionStorage.php create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php create mode 100644 src/Oro/Bundle/SecurityBundle/TreeProvider.php diff --git a/src/Oro/Bundle/SecurityBundle/ORM/SqlFilter/OwnershipFilter.php b/src/Oro/Bundle/SecurityBundle/ORM/SqlFilter/OwnershipFilter.php index 3d9caa94339..52f423841d2 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/SqlFilter/OwnershipFilter.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/SqlFilter/OwnershipFilter.php @@ -18,12 +18,18 @@ class OwnershipFilter extends SQLFilter */ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) { - return ''; //TODO: Removed after new acl implemented + //return ''; //TODO: Removed after new acl implemented + $table = $this->getParameter('table'); + $table = str_replace("'", '', $table); + $table = str_replace("\\\\", "\\", $table); + if ($table == $targetEntity->name) { + return $this->builder->buildFilterConstraint($targetEntity->reflClass->getName(), $targetTableAlias); + } + /*if (!($targetEntity->reflClass->getName() == "OroCRM\\Bundle\\AccountBundle\\Entity\\Account")) { return ''; }*/ - - return $this->builder->buildFilterConstraint($targetEntity->reflClass->getName(), $targetTableAlias); + return ''; } /** diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php new file mode 100644 index 00000000000..ce5b85a32ec --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php @@ -0,0 +1,223 @@ +builder = $builder; + } + + /** + * Mark query as acl protected + * + * @param Query|QueryBuilder $query + * @param string $permission + * + * @return Query + */ + public function apply($query, $permission = "VIEW") + { + if ($query instanceof QueryBuilder) { + $query = $query->getQuery(); + } + $aclQuery = $this->cloneQuery($query); + + $ast = $query->getAST(); + if ($ast instanceof SelectStatement) { + list ($whereConditions, $joinConditions) = $this->processRequest($ast, $permission); + $conditionStorage = new AclConditionStorage($whereConditions, $joinConditions); + $this->processSubRequests($ast, $conditionStorage, $permission); + + if (!$conditionStorage->isEmpty()) { + $aclQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, + array_merge( + $aclQuery->getHints(), + array(self::ORO_ACL_WALKER) + ) + ); + $aclQuery->setHint(AclWalker::ORO_ACL_CONDITION, $conditionStorage); + } + } + + return $aclQuery; + } + + /** + * @param SelectStatement $ast + * @param AclConditionStorage $storage + * @param $permission + */ + protected function processSubRequests(SelectStatement $ast, AclConditionStorage $storage, $permission) + { + $conditionalExpression = $ast->whereClause->conditionalExpression; + if ($conditionalExpression instanceof ConditionalPrimary) { + $expression = $conditionalExpression->simpleConditionalExpression; + if (isset($expression->subselect) + && $expression->subselect instanceof Subselect + ) { + $subRequestAclStorage = $this->processSubRequest($expression->subselect, $permission); + if (!$subRequestAclStorage->isEmpty()) { + $storage->setSubRequests($subRequestAclStorage); + } + } + } else { + $subQueryAcl = array(); + foreach ($conditionalExpression->conditionalFactors as $factorId => $expression) { + if (isset($expression->simpleConditionalExpression->subselect) + && $expression->simpleConditionalExpression->subselect instanceof Subselect + ) { + $subRequestAclStorage = $this->processSubRequest( + $expression->simpleConditionalExpression->subselect, + $permission + ); + if (!$subRequestAclStorage->isEmpty()) { + $subRequestAclStorage->setFactorId($factorId); + $subQueryAcl[] = $subRequestAclStorage; + } + } + } + if (!empty($subQueryAcl)) { + $storage->setSubRequests($subQueryAcl); + } + } + } + + /** + * @param Subselect $subSelect + * @param $permission + * @return SubRequestAclConditionStorage + */ + protected function processSubRequest(Subselect $subSelect, $permission) + { + list ($whereConditions, $joinConditions) = $this->processRequest($subSelect, $permission); + return new SubRequestAclConditionStorage($whereConditions, $joinConditions); + } + + /** + * @param Subselect|SelectStatement $select + * @param string $permission + * @return array + */ + protected function processRequest($select, $permission) + { + if ($select instanceof SelectStatement) { + $isSubRequest = false; + } else { + $isSubRequest = true; + } + $whereConditions = array(); + $joinConditions = array(); + $fromClause = $isSubRequest ? $select->subselectFromClause : $select->fromClause; + + foreach ($fromClause->identificationVariableDeclarations as $fromKey => $identificationVariableDeclaration) { + $condition = $this->processRangeVariableDeclaration( + $identificationVariableDeclaration->rangeVariableDeclaration, + $permission + ); + if ($condition) { + $whereConditions[] = $condition; + } + + // check joins + if (!empty($identificationVariableDeclaration->joins)) { + /** @var $join Join */ + foreach ($identificationVariableDeclaration->joins as $joinKey => $join) { + if ($join->joinAssociationDeclaration instanceof RangeVariableDeclaration) { + $condition = $this->processRangeVariableDeclaration( + $join->joinAssociationDeclaration, + $permission, + true + ); + if ($condition) { + $condition->setFromKey($fromKey); + $condition->setJoinKey($joinKey); + $joinConditions[] = $condition; + } + } + + } + } + } + + return array($whereConditions, $joinConditions); + } + + /** + * @param RangeVariableDeclaration $rangeVariableDeclaration + * @param $permission + * @param bool $isJoin + * @return null|AclCondition|JoinAclCondition + */ + protected function processRangeVariableDeclaration( + RangeVariableDeclaration $rangeVariableDeclaration, + $permission, + $isJoin = false + ) { + $entityName = $rangeVariableDeclaration->abstractSchemaName; + $entityAlias = $rangeVariableDeclaration->aliasIdentificationVariable; + + $resultData = $this->builder->getAclConditionData($entityName, $permission); + + if ($resultData && is_array($resultData)) { + list($entityField, $value) = $resultData; + if ($isJoin) { + + return new JoinAclCondition( + $entityAlias, $entityField, $value + ); + } else { + + return new AclCondition( + $entityAlias, $entityField, $value + ); + } + } + + return null; + } + + /** + * @param Query $query + * @return Query + */ + protected function cloneQuery(Query $query) + { + $aclAppliedQuery = clone $query; + $params = $query->getParameters(); + + foreach ($params as $param) { + $aclAppliedQuery->setParameter($param->getName(), $param->getValue(), $param->getType()); + } + + return $aclAppliedQuery; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php new file mode 100644 index 00000000000..1deb74ac40e --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php @@ -0,0 +1,221 @@ +_getQuery(); + if ($query->hasHint(self::ORO_ACL_CONDITION)) { + /** @var AclConditionStorage $aclCondition */ + $aclCondition = $query->getHint(self::ORO_ACL_CONDITION); + + if (!$aclCondition->isEmpty()) { + if (!is_null($aclCondition->getWhereConditions()) && count($aclCondition->getWhereConditions())) { + $this->addAclToWhereClause($AST, $aclCondition->getWhereConditions()); + } + if (!is_null($aclCondition->getJoinConditions()) && count($aclCondition->getJoinConditions())) { + $this->addAclToJoinClause($AST, $aclCondition->getJoinConditions()); + } + + $this->processSubRequests($AST, $aclCondition); + } + } + + return $AST; + } + + /** + * process subselects of query + * + * @param SelectStatement $AST + * @param AclConditionStorage $aclCondition + */ + protected function processSubRequests(SelectStatement $AST, AclConditionStorage $aclCondition) + { + if (!is_null($aclCondition->getSubRequests())) { + $subRequests = $aclCondition->getSubRequests(); + foreach ($subRequests as $subRequest) { + /** @var SubRequestAclConditionStorage $subRequest */ + $subselect = $AST + ->whereClause + ->conditionalExpression + ->conditionalFactors[$subRequest->getFactorId()] + ->simpleConditionalExpression + ->subselect; + if (!is_null($subRequest->getWhereConditions()) && count($subRequest->getWhereConditions())) { + $this->addAclToWhereClause($subselect, $subRequest->getWhereConditions()); + } + if (!is_null($subRequest->getJoinConditions()) && count($subRequest->getJoinConditions())) { + $this->addAclToJoinClause($subselect, $subRequest->getJoinConditions()); + } + } + } + } + + /** + * work with join statements of query + * + * @param SelectStatement $AST + * @param array $joinConditions + */ + protected function addAclToJoinClause($AST, array $joinConditions) + { + if ($AST instanceof Subselect) { + $fromClause = $AST->subselectFromClause; + } else { + $fromClause = $AST->fromClause; + } + foreach ($joinConditions as $condition) { + /** @var JoinAclCondition $condition */ + $conditionalFactor = $this->getConditionalFactor($condition); + + /** @var Join $join */ + $join = $fromClause->identificationVariableDeclarations[$condition->getFromKey()]->joins[$condition->getJoinKey()]; + + $aclConditionalFactors = array($conditionalFactor); + if ($join->conditionalExpression instanceof ConditionalPrimary) { + array_unshift($aclConditionalFactors, $join->conditionalExpression); + $join->conditionalExpression = new ConditionalTerm( + $aclConditionalFactors + ); + } else { + $join->conditionalExpression->conditionalFactors = array_merge( + $join->conditionalExpression->conditionalFactors, + $aclConditionalFactors + ); + } + } + } + + /** + * work with "where" statement of query + * + * @param SelectStatement $AST + * @param array $whereConditions + */ + protected function addAclToWhereClause($AST, array $whereConditions) + { + $aclConditionalFactors = array(); + + foreach ($whereConditions as $whereCondition) { + $aclConditionalFactors[] = $this->getConditionalFactor($whereCondition); + } + + if (!empty($aclConditionalFactors)) { + // we have query without 'where' part + if ($AST->whereClause === null) { + $AST->whereClause = new WhereClause(new ConditionalTerm($aclConditionalFactors)); + } else { + // 'where' part has only one condition + if ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) { + array_unshift($aclConditionalFactors, $AST->whereClause->conditionalExpression); + $AST->whereClause->conditionalExpression = new ConditionalTerm( + $aclConditionalFactors + ); + } else { + // 'where' part has more than one condition + $AST->whereClause->conditionalExpression->conditionalFactors = array_merge( + $AST->whereClause->conditionalExpression->conditionalFactors, + $aclConditionalFactors + ); + } + } + } + } + + /** + * @param AclCondition $condition + * @return ConditionalPrimary + */ + protected function getConditionalFactor(AclCondition $condition) + { + $expression = $this->getInExpression($condition); + + $resultCondition = new ConditionalPrimary(); + $resultCondition->simpleConditionalExpression = $expression; + + return $resultCondition; + } + + /** + * @param AclCondition $whereCondition + * @return InExpression + */ + protected function getInExpression(AclCondition $whereCondition) + { + $arithmeticExpression = new ArithmeticExpression(); + $arithmeticExpression->simpleArithmeticExpression = $this->getPathExpression($whereCondition); + + $expression = new InExpression($arithmeticExpression); + $expression->literals = $this->getLiterals($whereCondition); + + return $expression; + } + + /** + * @param AclCondition $whereCondition + * @return PathExpression + */ + protected function getPathExpression(AclCondition $whereCondition) + { + $pathExpression = new PathExpression( + self::EXPECTED_TYPE, + $whereCondition->getEntityAlias(), + $whereCondition->getEntityField() + ); + + $pathExpression->type = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + + return $pathExpression; + } + + /** + * @param AclCondition $whereCondition + * @return array + */ + protected function getLiterals(AclCondition $whereCondition) + { + $literals = array(); + + if (!is_array($whereCondition->getValue())) { + $whereCondition->setValue(array($whereCondition->getValue())); + } + foreach ($whereCondition->getValue() as $value) + { + $literals[] = new Literal(Literal::NUMERIC, $value); + } + + return $literals; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclCondition.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclCondition.php new file mode 100644 index 00000000000..8c21a0418f5 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclCondition.php @@ -0,0 +1,79 @@ +entityAlias = $entityAlias; + $this->entityField = $entityField; + $this->value = $value; + } + + /** + * @param string $entityAlias + */ + public function setEntityAlias($entityAlias) + { + $this->entityAlias = $entityAlias; + } + + /** + * @return string + */ + public function getEntityAlias() + { + return $this->entityAlias; + } + + /** + * @param string $entityField + */ + public function setEntityField($entityField) + { + $this->entityField = $entityField; + } + + /** + * @return string + */ + public function getEntityField() + { + return $this->entityField; + } + + /** + * @param int[] $value + */ + public function setValue($value) + { + $this->value = $value; + } + + /** + * @return int[] + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclConditionStorage.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclConditionStorage.php new file mode 100644 index 00000000000..0705b5e05f7 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/AclConditionStorage.php @@ -0,0 +1,93 @@ +whereConditions = $whereConditions; + $this->joinConditions = $joinConditions; + } + + /** + * @return bool + */ + public function isEmpty() + { + if (!empty($this->whereConditions)) { + return false; + } + if (!empty($this->joinConditions)) { + return false; + } + if ($this->subRequests) { + return false; + } + + return true; + } + + /** + * @param JoinAclCondition[] $joinConditions + */ + public function setJoinConditions($joinConditions) + { + $this->joinConditions = $joinConditions; + } + + /** + * @return JoinAclCondition[] + */ + public function getJoinConditions() + { + return $this->joinConditions; + } + + /** + * @param SubRequestAclConditionStorage|SubRequestAclConditionStorage[] $subRequests + */ + public function setSubRequests($subRequests) + { + $this->subRequests = $subRequests; + } + + /** + * @return SubRequestAclConditionStorage|SubRequestAclConditionStorage[] + */ + public function getSubRequests() + { + return $this->subRequests; + } + + /** + * @param AclCondition[] $whereConditions + */ + public function setWhereConditions($whereConditions) + { + $this->whereConditions = $whereConditions; + } + + /** + * @return AclCondition[] + */ + public function getWhereConditions() + { + return $this->whereConditions; + } +} \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAclCondition.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAclCondition.php new file mode 100644 index 00000000000..5e14ceb14f7 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAclCondition.php @@ -0,0 +1,49 @@ +fromKey; + } + + /** + * @return int + */ + public function getJoinKey() + { + return $this->joinKey; + } + + /** + * @param int $fromKey + */ + public function setFromKey($fromKey) + { + $this->fromKey = $fromKey; + } + + /** + * @param int $joinKey + */ + public function setJoinKey($joinKey) + { + $this->joinKey = $joinKey; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/SubRequestAclConditionStorage.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/SubRequestAclConditionStorage.php new file mode 100644 index 00000000000..4ce3b93ba7e --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/SubRequestAclConditionStorage.php @@ -0,0 +1,42 @@ +whereConditions)) { + return false; + } + if (!empty($this->joinConditions)) { + return false; + } + + return true; + } + + /** + * @param int $factorId + */ + public function setFactorId($factorId) + { + $this->factorId = $factorId; + } + + /** + * @return int + */ + public function getFactorId() + { + return $this->factorId; + } +} \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php new file mode 100644 index 00000000000..79b255d3df8 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php @@ -0,0 +1,341 @@ +securityContextLink = $securityContextLink; + $this->aclVoter = $aclVoter; + $this->objectIdAccessor = $objectIdAccessor; + $this->entityMetadataProvider = $entityMetadataProvider; + $this->metadataProvider = $metadataProvider; + $this->tree = $tree; + $treeProvider->fillTree($this->tree); + } + + /** + * @param $entityClassName + * @param $permissions + * @return null|array + */ + public function getAclConditionData($entityClassName, $permissions) + { + if ($this->aclVoter === null + || !$this->getUserId() + || !$this->entityMetadataProvider->isProtectedEntity($entityClassName) + ) { + null; + } + + $condition = null; + + $observer = new OneShotIsGrantedObserver(); + $this->aclVoter->addOneShotIsGrantedObserver($observer); + $isGranted = $this->getSecurityContext()->isGranted($permissions, 'entity:' . $entityClassName); + + $constraint = null; + + if ($isGranted) { + $condition = $this->buildConstraintIfAccessIsGranted( + $entityClassName, + $observer->getAccessLevel(), + $this->metadataProvider->getMetadata($entityClassName) + ); + } + + return $condition; + } + + /** + * @param string $targetEntityClassName + * @param int $accessLevel + * @param OwnershipMetadata $metadata + * @return null|array + * + * The cyclomatic complexity warning is suppressed by performance reasons + * (to avoid unnecessary cloning od arrays) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function buildConstraintIfAccessIsGranted( + $targetEntityClassName, + $accessLevel, + OwnershipMetadata $metadata + ) { + $constraint = null; + + if (AccessLevel::SYSTEM_LEVEL === $accessLevel) { + $constraint = null; + } elseif (!$metadata->hasOwner()) { + if (AccessLevel::GLOBAL_LEVEL === $accessLevel) { + if ($this->metadataProvider->getOrganizationClass() === $targetEntityClassName) { + $orgIds = $this->tree->getUserOrganizationIds($this->getUserId()); + $constraint = $this->getCondition($orgIds, $metadata, 'id'); + } else { + $constraint = null; + } + } else { + $constraint = null; + } + } else { + if (AccessLevel::BASIC_LEVEL === $accessLevel) { + if ($this->metadataProvider->getUserClass() === $targetEntityClassName) { + $constraint = $this->getCondition($this->getUserId(), $metadata, 'id'); + } elseif ($metadata->isUserOwned()) { + $constraint = $this->getCondition($this->getUserId(), $metadata); + } + } elseif (AccessLevel::LOCAL_LEVEL === $accessLevel) { + if ($this->metadataProvider->getBusinessUnitClass() === $targetEntityClassName) { + $buIds = $this->tree->getUserBusinessUnitIds($this->getUserId()); + $constraint = $this->getCondition($buIds, $metadata, 'id'); + } elseif ($metadata->isBusinessUnitOwned()) { + $buIds = $this->tree->getUserBusinessUnitIds($this->getUserId()); + $constraint = $this->getCondition($buIds, $metadata); + } elseif ($metadata->isUserOwned()) { + $userIds = array(); + $this->fillBusinessUnitUserIds($this->getUserId(), $userIds); + $constraint = $this->getCondition($userIds, $metadata); + } + } elseif (AccessLevel::DEEP_LEVEL === $accessLevel) { + if ($this->metadataProvider->getBusinessUnitClass() === $targetEntityClassName) { + $buIds = array(); + $this->fillSubordinateBusinessUnitIds($this->getUserId(), $buIds); + $constraint = $this->getCondition($buIds, $metadata, 'id'); + } elseif ($metadata->isBusinessUnitOwned()) { + $buIds = array(); + $this->fillSubordinateBusinessUnitIds($this->getUserId(), $buIds); + $constraint = $this->getCondition($buIds, $metadata); + } elseif ($metadata->isUserOwned()) { + $userIds = array(); + $this->fillSubordinateBusinessUnitUserIds($this->getUserId(), $userIds); + $constraint = $this->getCondition($userIds, $metadata); + } + } elseif (AccessLevel::GLOBAL_LEVEL === $accessLevel) { + if ($metadata->isOrganizationOwned()) { + $orgIds = $this->tree->getUserOrganizationIds($this->getUserId()); + $constraint = $this->getCondition($orgIds, $metadata); + } elseif ($metadata->isBusinessUnitOwned()) { + $buIds = array(); + $this->fillOrganizationBusinessUnitIds($this->getUserId(), $buIds); + $constraint = $this->getCondition($buIds, $metadata); + } elseif ($metadata->isUserOwned()) { + $userIds = array(); + $this->fillOrganizationUserIds($this->getUserId(), $userIds); + $constraint = $this->getCondition($userIds, $metadata); + } + } + } + + return $constraint; + } + + /** + * Gets the id of logged in user + * + * @return int|string + */ + public function getUserId() + { + $token = $this->getSecurityContext()->getToken(); + if (!$token) { + return null; + } + $user = $token->getUser(); + if (!is_object($user) || !is_a($user, $this->metadataProvider->getUserClass())) { + return null; + } + + return $this->objectIdAccessor->getId($user); + } + + /** + * Adds all business unit ids within all subordinate business units the given user is associated + * + * @param int|string $userId + * @param array $result [output] + */ + protected function fillSubordinateBusinessUnitIds($userId, array &$result) + { + $buIds = $this->tree->getUserBusinessUnitIds($userId); + $result = array_merge($buIds, array()); + foreach ($buIds as $buId) { + $diff = array_diff($this->tree->getSubordinateBusinessUnitIds($buId), $result); + if (!empty($diff)) { + $result = array_merge($result, $diff); + } + } + } + + /** + * Adds all user ids within all business units the given user is associated + * + * @param int|string $userId + * @param array $result [output] + */ + protected function fillBusinessUnitUserIds($userId, array &$result) + { + foreach ($this->tree->getUserBusinessUnitIds($userId) as $buId) { + $userIds = $this->tree->getBusinessUnitUserIds($buId); + if (!empty($userIds)) { + $result = array_merge($result, $userIds); + } + } + } + + /** + * Adds all user ids within all subordinate business units the given user is associated + * + * @param int|string $userId + * @param array $result [output] + */ + protected function fillSubordinateBusinessUnitUserIds($userId, array &$result) + { + $buIds = array(); + $this->fillSubordinateBusinessUnitIds($userId, $buIds); + foreach ($buIds as $buId) { + $userIds = $this->tree->getBusinessUnitUserIds($buId); + if (!empty($userIds)) { + $result = array_merge($result, $userIds); + } + } + } + + /** + * Adds all business unit ids within all organizations the given user is associated + * + * @param int|string $userId + * @param array $result [output] + */ + protected function fillOrganizationBusinessUnitIds($userId, array &$result) + { + foreach ($this->tree->getUserOrganizationIds($userId) as $orgId) { + $buIds = $this->tree->getOrganizationBusinessUnitIds($orgId); + if (!empty($buIds)) { + $result = array_merge($result, $buIds); + } + } + } + + /** + * Adds all user ids within all organizations the given user is associated + * + * @param int|string $userId + * @param array $result [output] + */ + protected function fillOrganizationUserIds($userId, array &$result) + { + foreach ($this->tree->getUserOrganizationIds($userId) as $orgId) { + foreach ($this->tree->getOrganizationBusinessUnitIds($orgId) as $buId) { + $userIds = $this->tree->getBusinessUnitUserIds($buId); + if (!empty($userIds)) { + $result = array_merge($result, $userIds); + } + } + } + } + + /** + * Gets SQL condition for the given owner id or ids + * + * @param int|int[]|null $idOrIds + * @param OwnershipMetadata $metadata + * @param string|null $columnName + * @return array|null + */ + protected function getCondition($idOrIds, OwnershipMetadata $metadata, $columnName = null) + { + if (!empty($idOrIds)) { + return array( + $this->getColumnName($metadata, $columnName), + $idOrIds + ); + } + + return null; + } + + /** + * Gets the name of owner column + * + * @param OwnershipMetadata $metadata + * @param null $columnName + * @return null|string + */ + protected function getColumnName(OwnershipMetadata $metadata, $columnName = null) + { + if ($columnName === null) { + $columnName = $metadata->getOwnerFieldName(); + } + + return $columnName; + } + + /** + * @return SecurityContextInterface + */ + protected function getSecurityContext() + { + return $this->securityContextLink->getService(); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index b97c66e4a62..f460534bb85 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -50,6 +50,8 @@ parameters: oro_security.encoder.mcrypt.class: Oro\Bundle\SecurityBundle\Encoder\Mcrypt + oro_security.acl.listenerclass: Oro\Bundle\SecurityBundle\EventListener\AclListener + services: oro_security.security_facade: class: %oro_security.security_facade.class% @@ -341,3 +343,48 @@ services: oro_security.encoder.mcrypt: class: %oro_security.encoder.mcrypt.class% arguments: [ %kernel.secret% ] + +# oro_security.acl.listener: +# class: %oro_security.acl.listenerclass% +# tags: +# - { name: doctrine.event_listener, event: preRemove } +# + +# oro_security.orm.acl_check_filter: +# public: false +# class: %oro_security.orm.ownership_sql_filter.class% +# arguments: +# - @doctrine.orm.entity_manager +# calls: +# - [setBuilder, [@oro_security.orm.ownership_sql_filter_builder]] +# tags: +# - { name: oro_entity.orm.sql_filter, filter_name: acl_check, enabled: false } +# - { name: kernel.event_listener, priority: -255, event: kernel.request, method: setUserParameter } + + oro_security.acl_helper: + class: Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper + arguments: + - @oro_security.orm.ownership_sql_walker_builder + +# oro_security.acl_walker: +# public: false +# class: Oro\Bundle\SecurityBundle\ORM\Walker\AclWalker +# arguments: [ @doctrine.orm.entity_manager, @security.context ] + + + oro_security.orm.ownership_sql_walker_builder: +# public: false + class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipFilterBuilder + arguments: + - @oro_entity_config.link.security_context + - @oro_security.acl.object_id_accessor + - @oro_security.entity_security_metadata_provider + - @oro_security.owner.ownership_metadata_provider + - @oro_security.owner.tree + - @?security.acl.voter.basic_permissions + - @oro_security.tree_provider + + oro_security.tree_provider: + class: Oro\Bundle\SecurityBundle\TreeProvider + arguments: + - @doctrine.orm.entity_manager \ No newline at end of file diff --git a/src/Oro/Bundle/SecurityBundle/TreeProvider.php b/src/Oro/Bundle/SecurityBundle/TreeProvider.php new file mode 100644 index 00000000000..308351fbd7c --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/TreeProvider.php @@ -0,0 +1,48 @@ +em = $em; + } + + public function fillTree(OwnerTree $tree) + { + $users = $this->em->getRepository('Oro\Bundle\UserBundle\Entity\User')->findAll(); + $businessUnits = $this->em->getRepository('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit')->findAll(); + + + + foreach ($users as $user) { + /** @var \Oro\Bundle\UserBundle\Entity\User $user */ + $tree->addUser($user->getId(), $user->getOwner()->getId()); + + } + + foreach ($businessUnits as $businessUnit) + { + /** @var \Oro\Bundle\OrganizationBundle\Entity\BusinessUnit $businessUnit */ + $tree->addBusinessUnit($businessUnit->getId(), $businessUnit->getOrganization()->getId()); + if ($businessUnit->getOwner()) { + $tree->addBusinessUnitRelation($businessUnit->getId(), $businessUnit->getOwner()->getId()); + } + } + + foreach ($users as $user) { + /** @var \Oro\Bundle\UserBundle\Entity\User $user */ + foreach ($user->getBusinessUnits() as $businessUnit) { + $tree->addUserBusinessUnit($user->getId(), $businessUnit->getId()); + } + } + } +} \ No newline at end of file From 8a5609e4c6212c1e58b4ee238832fe46f4512b22 Mon Sep 17 00:00:00 2001 From: yurio Date: Fri, 1 Nov 2013 19:59:44 +0200 Subject: [PATCH 009/248] BAP-2032: Ownership Tree cache warmap --- .../SecurityBundle/ORM/Walker/AclHelper.php | 6 +- .../SecurityBundle/ORM/Walker/AclWalker.php | 4 +- .../ORM/Walker/OwnershipFilterBuilder.php | 28 +++-- .../Owner/OwnerTreeProvider.php | 109 ++++++++++++++++++ .../Resources/config/services.yml | 39 ++++--- 5 files changed, 147 insertions(+), 39 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php index ce5b85a32ec..92b936b1844 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php @@ -90,7 +90,7 @@ protected function processSubRequests(SelectStatement $ast, AclConditionStorage } } } else { - $subQueryAcl = array(); + $subQueryAcl = []; foreach ($conditionalExpression->conditionalFactors as $factorId => $expression) { if (isset($expression->simpleConditionalExpression->subselect) && $expression->simpleConditionalExpression->subselect instanceof Subselect @@ -134,8 +134,8 @@ protected function processRequest($select, $permission) } else { $isSubRequest = true; } - $whereConditions = array(); - $joinConditions = array(); + $whereConditions = []; + $joinConditions = []; $fromClause = $isSubRequest ? $select->subselectFromClause : $select->fromClause; foreach ($fromClause->identificationVariableDeclarations as $fromKey => $identificationVariableDeclaration) { diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php index 1deb74ac40e..6e08f48f8df 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php @@ -126,7 +126,7 @@ protected function addAclToJoinClause($AST, array $joinConditions) */ protected function addAclToWhereClause($AST, array $whereConditions) { - $aclConditionalFactors = array(); + $aclConditionalFactors = []; foreach ($whereConditions as $whereCondition) { $aclConditionalFactors[] = $this->getConditionalFactor($whereCondition); @@ -206,7 +206,7 @@ protected function getPathExpression(AclCondition $whereCondition) */ protected function getLiterals(AclCondition $whereCondition) { - $literals = array(); + $literals = []; if (!is_array($whereCondition->getValue())) { $whereCondition->setValue(array($whereCondition->getValue())); diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php index 79b255d3df8..1db4baf894e 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/OwnershipFilterBuilder.php @@ -7,6 +7,7 @@ use Oro\Bundle\SecurityBundle\Owner\Metadata\OwnershipMetadata; use Oro\Bundle\SecurityBundle\Owner\Metadata\OwnershipMetadataProvider; use Oro\Bundle\SecurityBundle\Owner\OwnerTree; +use Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider; use Oro\Bundle\SecurityBundle\Acl\Domain\OneShotIsGrantedObserver; use Oro\Bundle\SecurityBundle\Acl\Domain\ObjectIdAccessor; use Oro\Bundle\SecurityBundle\Acl\AccessLevel; @@ -53,26 +54,23 @@ class OwnershipFilterBuilder * @param ObjectIdAccessor $objectIdAccessor * @param EntitySecurityMetadataProvider $entityMetadataProvider * @param OwnershipMetadataProvider $metadataProvider - * @param OwnerTree $tree - * @param AclVoter $aclVoter * @param $treeProvider + * @param AclVoter $aclVoter */ public function __construct( ServiceLink $securityContextLink, ObjectIdAccessor $objectIdAccessor, EntitySecurityMetadataProvider $entityMetadataProvider, OwnershipMetadataProvider $metadataProvider, - OwnerTree $tree, - AclVoter $aclVoter = null, - $treeProvider + OwnerTreeProvider $treeProvider, + AclVoter $aclVoter = null ) { $this->securityContextLink = $securityContextLink; $this->aclVoter = $aclVoter; $this->objectIdAccessor = $objectIdAccessor; $this->entityMetadataProvider = $entityMetadataProvider; $this->metadataProvider = $metadataProvider; - $this->tree = $tree; - $treeProvider->fillTree($this->tree); + $this->tree = $treeProvider->getTree(); } /** @@ -153,21 +151,21 @@ protected function buildConstraintIfAccessIsGranted( $buIds = $this->tree->getUserBusinessUnitIds($this->getUserId()); $constraint = $this->getCondition($buIds, $metadata); } elseif ($metadata->isUserOwned()) { - $userIds = array(); + $userIds = []; $this->fillBusinessUnitUserIds($this->getUserId(), $userIds); $constraint = $this->getCondition($userIds, $metadata); } } elseif (AccessLevel::DEEP_LEVEL === $accessLevel) { if ($this->metadataProvider->getBusinessUnitClass() === $targetEntityClassName) { - $buIds = array(); + $buIds = []; $this->fillSubordinateBusinessUnitIds($this->getUserId(), $buIds); $constraint = $this->getCondition($buIds, $metadata, 'id'); } elseif ($metadata->isBusinessUnitOwned()) { - $buIds = array(); + $buIds = []; $this->fillSubordinateBusinessUnitIds($this->getUserId(), $buIds); $constraint = $this->getCondition($buIds, $metadata); } elseif ($metadata->isUserOwned()) { - $userIds = array(); + $userIds = []; $this->fillSubordinateBusinessUnitUserIds($this->getUserId(), $userIds); $constraint = $this->getCondition($userIds, $metadata); } @@ -176,11 +174,11 @@ protected function buildConstraintIfAccessIsGranted( $orgIds = $this->tree->getUserOrganizationIds($this->getUserId()); $constraint = $this->getCondition($orgIds, $metadata); } elseif ($metadata->isBusinessUnitOwned()) { - $buIds = array(); + $buIds = []; $this->fillOrganizationBusinessUnitIds($this->getUserId(), $buIds); $constraint = $this->getCondition($buIds, $metadata); } elseif ($metadata->isUserOwned()) { - $userIds = array(); + $userIds = []; $this->fillOrganizationUserIds($this->getUserId(), $userIds); $constraint = $this->getCondition($userIds, $metadata); } @@ -218,7 +216,7 @@ public function getUserId() protected function fillSubordinateBusinessUnitIds($userId, array &$result) { $buIds = $this->tree->getUserBusinessUnitIds($userId); - $result = array_merge($buIds, array()); + $result = array_merge($buIds, []); foreach ($buIds as $buId) { $diff = array_diff($this->tree->getSubordinateBusinessUnitIds($buId), $result); if (!empty($diff)) { @@ -251,7 +249,7 @@ protected function fillBusinessUnitUserIds($userId, array &$result) */ protected function fillSubordinateBusinessUnitUserIds($userId, array &$result) { - $buIds = array(); + $buIds = []; $this->fillSubordinateBusinessUnitIds($userId, $buIds); foreach ($buIds as $buId) { $userIds = $this->tree->getBusinessUnitUserIds($buId); diff --git a/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php new file mode 100644 index 00000000000..852c2b51660 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php @@ -0,0 +1,109 @@ +cache = $cache; + $this->em = $em; + } + + /** + * @return OwnerTree + */ + public function getTree() + { + $this->ensureTreeLoaded(); + + return $this->tree; + } + + public function clear() + { + $this->cache->deleteAll(); + } + + /** + * Makes sure that tree data + */ + protected function ensureTreeLoaded() + { + if ($this->tree === null) { + $treeData = null; + if ($this->cache) { + $treeData = $this->cache->fetch(self::CACHE_KEY); + } + if (!$treeData) { + $treeData = new OwnerTree(); + $this->fillTree($treeData); + + if ($this->cache) { + $this->cache->save(self::CACHE_KEY, $treeData); + } + } + + $this->tree = $treeData; + } + } + + /** + * @param OwnerTree $tree + */ + protected function fillTree(OwnerTree $tree) + { + $users = $this->em->getRepository('Oro\Bundle\UserBundle\Entity\User')->findAll(); + $businessUnits = $this->em->getRepository('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit')->findAll(); + + foreach ($users as $user) { + /** @var \Oro\Bundle\UserBundle\Entity\User $user */ + $tree->addUser($user->getId(), $user->getOwner()->getId()); + } + + foreach ($businessUnits as $businessUnit) + { + /** @var \Oro\Bundle\OrganizationBundle\Entity\BusinessUnit $businessUnit */ + $tree->addBusinessUnit($businessUnit->getId(), $businessUnit->getOrganization()->getId()); + if ($businessUnit->getOwner()) { + $tree->addBusinessUnitRelation($businessUnit->getId(), $businessUnit->getOwner()->getId()); + } + } + + foreach ($users as $user) { + /** @var \Oro\Bundle\UserBundle\Entity\User $user */ + foreach ($user->getBusinessUnits() as $businessUnit) { + $tree->addUserBusinessUnit($user->getId(), $businessUnit->getId()); + } + } + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index f460534bb85..16b44a0156b 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -366,25 +366,26 @@ services: arguments: - @oro_security.orm.ownership_sql_walker_builder -# oro_security.acl_walker: -# public: false -# class: Oro\Bundle\SecurityBundle\ORM\Walker\AclWalker -# arguments: [ @doctrine.orm.entity_manager, @security.context ] - - oro_security.orm.ownership_sql_walker_builder: -# public: false - class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipFilterBuilder - arguments: - - @oro_entity_config.link.security_context - - @oro_security.acl.object_id_accessor - - @oro_security.entity_security_metadata_provider - - @oro_security.owner.ownership_metadata_provider - - @oro_security.owner.tree - - @?security.acl.voter.basic_permissions - - @oro_security.tree_provider +# public: false + class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipFilterBuilder + arguments: + - @oro_entity_config.link.security_context + - @oro_security.acl.object_id_accessor + - @oro_security.entity_security_metadata_provider + - @oro_security.owner.ownership_metadata_provider + - @oro_security.tree_provider + - @?security.acl.voter.basic_permissions oro_security.tree_provider: - class: Oro\Bundle\SecurityBundle\TreeProvider - arguments: - - @doctrine.orm.entity_manager \ No newline at end of file + public: false + class: Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider + arguments: + - @doctrine.orm.entity_manager + - @oro_security.ownership_tree_provider.cache + + oro_security.ownership_tree_provider.cache: + public: false + class: Doctrine\Common\Cache\FilesystemCache + arguments: + - %kernel.cache_dir%/oro_acl_owner_tree \ No newline at end of file From f600541b50c3209ff85b8ce5bea076dcac8ee477 Mon Sep 17 00:00:00 2001 From: yurio Date: Tue, 5 Nov 2013 17:48:02 +0200 Subject: [PATCH 010/248] BAP-2032: Ownership Tree cache warmap --- .../Acl/Persistence/AclManager.php | 6 + .../Cache/OwnerTreeCacheCleaner.php | 31 +++++ .../Cache/OwnerTreeCacheWarmer.php | 39 ++++++ .../EventListener/OwnerTreeListener.php | 98 +++++++++++++++ .../SecurityBundle/ORM/Walker/AclWalker.php | 4 +- .../Owner/OwnerTreeProvider.php | 17 ++- .../Resources/config/services.yml | 69 ++++++---- .../EventListener/OwnerTreeListenerTest.php | 118 ++++++++++++++++++ .../Unit/Owner/OwnerTreeProviderTest.php | 118 ++++++++++++++++++ 9 files changed, 469 insertions(+), 31 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheCleaner.php create mode 100644 src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheWarmer.php create mode 100644 src/Oro/Bundle/SecurityBundle/EventListener/OwnerTreeListener.php create mode 100644 src/Oro/Bundle/SecurityBundle/Tests/Unit/EventListener/OwnerTreeListenerTest.php create mode 100644 src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php b/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php index 95e84b10fc2..d580f5b6e20 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Persistence/AclManager.php @@ -94,6 +94,12 @@ public function __construct( : 'Oro\Bundle\SecurityBundle\Acl\Persistence\AclPrivilegeRepository'; } + /** + * Get access levels list for object. + * + * @param $object + * @return array + */ public function getAccessLevelsForObject($object) { $extension = $this->getExtensionSelector()->select($object); diff --git a/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheCleaner.php b/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheCleaner.php new file mode 100644 index 00000000000..25605dbf609 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheCleaner.php @@ -0,0 +1,31 @@ +treeProvider = $treeProvider; + } + + /** + * {inheritdoc} + */ + public function clear($cacheDir) + { + $this->treeProvider->clear(); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheWarmer.php b/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheWarmer.php new file mode 100644 index 00000000000..5930296e363 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Cache/OwnerTreeCacheWarmer.php @@ -0,0 +1,39 @@ +treeProvider = $treeProvider; + } + + /** + * {inheritdoc} + */ + public function warmUp($cacheDir) + { + $this->treeProvider->warmUpCache(); + } + + /** + * {inheritdoc} + */ + public function isOptional() + { + return true; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/EventListener/OwnerTreeListener.php b/src/Oro/Bundle/SecurityBundle/EventListener/OwnerTreeListener.php new file mode 100644 index 00000000000..e2830de3f9c --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/EventListener/OwnerTreeListener.php @@ -0,0 +1,98 @@ +treeProviderLink = $treeProviderLink; + } + + /** + * @param OnFlushEventArgs $args + */ + public function onFlush(OnFlushEventArgs $args) + { + $uow = $args->getEntityManager()->getUnitOfWork(); + $this->needWarmap = false; + if ($this->checkEntities($uow->getScheduledEntityInsertions())) { + $this->needWarmap = true; + } + if (!$this->needWarmap && $this->checkEntities($uow->getScheduledEntityUpdates())) { + $this->needWarmap = true; + } + if (!$this->needWarmap && $this->checkEntities($uow->getScheduledEntityDeletions())) { + $this->needWarmap = true; + } + + if ($this->needWarmap) { + $this->getTreeProvider()->clear(); + } + } + + /** + * @param PostFlushEventArgs $args + */ + public function postFlush(PostFlushEventArgs $args) + { + if ($this->needWarmap) { + $this->getTreeProvider()->warmUpCache(); + } + } + + /** + * @param array $entities + * @return bool + */ + protected function checkEntities(array $entities) + { + foreach ($entities as $entity) { + if (in_array(get_class($entity), $this->securityClasses)) { + + return true; + } + } + + return false; + } + + /** + * @return OwnerTreeProvider + */ + protected function getTreeProvider() + { + return $this->treeProviderLink->getService(); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php index 6e08f48f8df..ee3037364d4 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php @@ -101,7 +101,9 @@ protected function addAclToJoinClause($AST, array $joinConditions) $conditionalFactor = $this->getConditionalFactor($condition); /** @var Join $join */ - $join = $fromClause->identificationVariableDeclarations[$condition->getFromKey()]->joins[$condition->getJoinKey()]; + $join = $fromClause + ->identificationVariableDeclarations[$condition->getFromKey()] + ->joins[$condition->getJoinKey()]; $aclConditionalFactors = array($conditionalFactor); if ($join->conditionalExpression instanceof ConditionalPrimary) { diff --git a/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php index 852c2b51660..cbe73e9edab 100644 --- a/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php @@ -49,11 +49,22 @@ public function getTree() return $this->tree; } + /** + * Clear the owner tree cache + */ public function clear() { $this->cache->deleteAll(); } + /** + * Warmup owner tree cache + */ + public function warmUpCache() + { + $this->ensureTreeLoaded(); + } + /** * Makes sure that tree data */ @@ -85,11 +96,6 @@ protected function fillTree(OwnerTree $tree) $users = $this->em->getRepository('Oro\Bundle\UserBundle\Entity\User')->findAll(); $businessUnits = $this->em->getRepository('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit')->findAll(); - foreach ($users as $user) { - /** @var \Oro\Bundle\UserBundle\Entity\User $user */ - $tree->addUser($user->getId(), $user->getOwner()->getId()); - } - foreach ($businessUnits as $businessUnit) { /** @var \Oro\Bundle\OrganizationBundle\Entity\BusinessUnit $businessUnit */ @@ -101,6 +107,7 @@ protected function fillTree(OwnerTree $tree) foreach ($users as $user) { /** @var \Oro\Bundle\UserBundle\Entity\User $user */ + $tree->addUser($user->getId(), $user->getOwner()->getId()); foreach ($user->getBusinessUnits() as $businessUnit) { $tree->addUserBusinessUnit($user->getId(), $businessUnit->getId()); } diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index 16b44a0156b..f68f7827368 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -52,6 +52,14 @@ parameters: oro_security.acl.listenerclass: Oro\Bundle\SecurityBundle\EventListener\AclListener + oro_security.acl_helper.class: Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper + oro_security.orm.ownership_sql_walker_builder.class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipFilterBuilder + oro_security.ownership_tree_provider.class: Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider + oro_security.ownership_tree_provider.cache.class: Doctrine\Common\Cache\FilesystemCache + oro_security.ownership_tree.cache.warmer.class: Oro\Bundle\SecurityBundle\Cache\OwnerTreeCacheWarmer + oro_security.ownership_tree.cache.clearer.class: Oro\Bundle\SecurityBundle\Cache\OwnerTreeCacheCleaner + oro_security.ownership_tree_subscriber.class: Oro\Bundle\SecurityBundle\EventListener\OwnerTreeListener + services: oro_security.security_facade: class: %oro_security.security_facade.class% @@ -344,48 +352,59 @@ services: class: %oro_security.encoder.mcrypt.class% arguments: [ %kernel.secret% ] -# oro_security.acl.listener: -# class: %oro_security.acl.listenerclass% -# tags: -# - { name: doctrine.event_listener, event: preRemove } -# - -# oro_security.orm.acl_check_filter: -# public: false -# class: %oro_security.orm.ownership_sql_filter.class% -# arguments: -# - @doctrine.orm.entity_manager -# calls: -# - [setBuilder, [@oro_security.orm.ownership_sql_filter_builder]] -# tags: -# - { name: oro_entity.orm.sql_filter, filter_name: acl_check, enabled: false } -# - { name: kernel.event_listener, priority: -255, event: kernel.request, method: setUserParameter } - oro_security.acl_helper: - class: Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper + class: %oro_security.acl_helper.class% arguments: - @oro_security.orm.ownership_sql_walker_builder oro_security.orm.ownership_sql_walker_builder: -# public: false - class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipFilterBuilder + public: false + class: %oro_security.orm.ownership_sql_walker_builder.class% arguments: - @oro_entity_config.link.security_context - @oro_security.acl.object_id_accessor - @oro_security.entity_security_metadata_provider - @oro_security.owner.ownership_metadata_provider - - @oro_security.tree_provider + - @oro_security.ownership_tree_provider - @?security.acl.voter.basic_permissions - oro_security.tree_provider: + oro_security.ownership_tree_provider: public: false - class: Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider + class: %oro_security.ownership_tree_provider.class% arguments: - @doctrine.orm.entity_manager - @oro_security.ownership_tree_provider.cache + oro_security.link.ownership_tree_provider: + tags: + - { name: oro_service_link, service: oro_security.ownership_tree_provider } + oro_security.ownership_tree_provider.cache: public: false - class: Doctrine\Common\Cache\FilesystemCache + class: %oro_security.ownership_tree_provider.cache.class% + arguments: + - %kernel.cache_dir%/oro_acl_owner_tree + + oro_security.ownership_tree.cache.warmer: + public: false + class: %oro_security.ownership_tree.cache.warmer.class% + arguments: + - @oro_security.ownership_tree_provider + tags: + - { name: kernel.cache_warmer} + + oro_security.ownership_tree.cache.cleaner: + public: false + class: %oro_security.ownership_tree.cache.clearer.class% arguments: - - %kernel.cache_dir%/oro_acl_owner_tree \ No newline at end of file + - @oro_security.ownership_tree_provider + tags: + - { name: kernel.cache_clearer } + + oro_security.ownership_tree_subscriber: + class: %oro_security.ownership_tree_subscriber.class% + arguments: + - @oro_security.link.ownership_tree_provider + tags: + - { name: doctrine.event_listener, event: onFlush } + - { name: doctrine.event_listener, event: postFlush } diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/EventListener/OwnerTreeListenerTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/EventListener/OwnerTreeListenerTest.php new file mode 100644 index 00000000000..68a6a09961c --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/EventListener/OwnerTreeListenerTest.php @@ -0,0 +1,118 @@ +getMockBuilder('Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider') + ->disableOriginalConstructor() + ->getMock(); + $serviceLink = $this->getMockBuilder('Oro\Bundle\EntityConfigBundle\DependencyInjection\Utils\ServiceLink') + ->disableOriginalConstructor() + ->getMock(); + $serviceLink->expects($this->any()) + ->method('getService') + ->will($this->returnValue($treeProvider)); + $args = $this->getMockBuilder('Doctrine\ORM\Event\OnFlushEventArgs') + ->disableOriginalConstructor() + ->getMock(); + $em = $this->getMockBuilder('Doctrine\ORM\EntityManager') + ->disableOriginalConstructor() + ->getMock(); + $uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork') + ->disableOriginalConstructor() + ->getMock(); + $args->expects($this->once()) + ->method('getEntityManager') + ->will($this->returnValue($em)); + $em->expects($this->once()) + ->method('getUnitOfWork') + ->will($this->returnValue($uow)); + $uow->expects($this->once()) + ->method('getScheduledEntityInsertions') + ->will($this->returnValue($inserts)); + $uow->expects($this->any()) + ->method('getScheduledEntityUpdates') + ->will($this->returnValue($updates)); + $uow->expects($this->any()) + ->method('getScheduledEntityDeletions') + ->will($this->returnValue($deletions)); + if ($isExpectedCache) { + $treeProvider->expects($this->once()) + ->method('clear'); + $treeProvider->expects($this->once()) + ->method('warmUpCache'); + } else { + $treeProvider->expects($this->never()) + ->method('clear'); + $treeProvider->expects($this->never()) + ->method('warmUpCache'); + } + + $treeListener = new OwnerTreeListener($serviceLink); + $treeListener->onFlush($args); + $treeListener->postFlush( + $this->getMockBuilder('Doctrine\ORM\Event\PostFlushEventArgs') + ->disableOriginalConstructor() + ->getMock() + ); + } + + /** + * @return array + */ + public function provider() + { + return [ + [ + [new User()], + [], + [new \stdClass()], + true + ], + [ + [new User()], + [new BusinessUnit()], + [new \stdClass()], + true + ], + [ + [], + [new User()], + [], + true + ], + [ + [], + [new \stdClass()], + [new Organization()], + true + ], + [ + [new \stdClass()], + [], + [], + false + ], + [ + [], + [], + [], + false + ] + ]; + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php new file mode 100644 index 00000000000..23d8a29513a --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php @@ -0,0 +1,118 @@ +em = $this->getMockBuilder('Doctrine\ORM\EntityManager') + ->disableOriginalConstructor() + ->getMock(); + $this->cache = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider'); + + $this->cache->expects($this->any()) + ->method('fetch') + ->will($this->returnValue(false)); + + $this->cache->expects($this->any()) + ->method('save'); + + $this->treeProvider = new OwnerTreeProvider($this->em, $this->cache); + } + + public function testGetTree() + { + $userRepo = $this->getMockBuilder('Doctrine\ORM\EntityRepository') + ->disableOriginalConstructor() + ->getMock(); + + $buRepo = $this->getMockBuilder('Doctrine\ORM\EntityRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->em->expects($this->at(0)) + ->method('getRepository') + ->with($this->equalTo('Oro\Bundle\UserBundle\Entity\User')) + ->will($this->returnValue($userRepo)); + $this->em->expects($this->at(1)) + ->method('getRepository') + ->with($this->equalTo('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit')) + ->will($this->returnValue($buRepo)); + + list($users, $bUnits) = $this->getTestData(); + + $userRepo->expects($this->any()) + ->method('findAll') + ->will($this->returnValue($users)); + + $buRepo->expects($this->any()) + ->method('findAll') + ->will($this->returnValue($bUnits)); + + $this->treeProvider->warmUpCache(); + $tree = $this->treeProvider->getTree(); + var_dump($tree); + } + + protected function setId($object, $value) + { + $reflection = new \ReflectionClass($object); + $property = $reflection->getProperty('id'); + $property->setAccessible(true); + $property->setValue($reflection, $value); + } + + protected function getTestData() + { + $organization = new Organization(); + $this->setId($organization, 1); + + $mainBu = new BusinessUnit(); + $this->setId($mainBu, 1); + $mainBu->setOrganization($organization); + + $bu2 = new BusinessUnit(); + $this->setId($bu2, 2); + $bu2->setOrganization($organization); + + $childBu = new BusinessUnit(); + $this->setId($childBu, 3); + $childBu->setOrganization($organization); + $childBu->setOwner($mainBu); + + $user1 = new User(); + $this->setId($user1, 1); + $user1->setOwner($mainBu); + $user1->addBusinessUnit($mainBu); + + $user2 = new User(); + $this->setId($user2, 2); + $user2->setOwner($bu2); + $user2->addBusinessUnit($bu2); + + $user3 = new User(); + $this->setId($user3, 3); + $user3->setOwner($childBu); + $user3->addBusinessUnit($childBu); + + return [ + [$user1, $user2, $user3], + [$mainBu, $bu2, $childBu] + ]; + } +} \ No newline at end of file From 53e39f884ede82f9a72a784fe10c4daee023315a Mon Sep 17 00:00:00 2001 From: yurio Date: Tue, 5 Nov 2013 18:54:34 +0200 Subject: [PATCH 011/248] BAP-2032: Ownership Tree cache warmap --- .../Unit/Owner/OwnerTreeProviderTest.php | 7 +-- .../Bundle/SecurityBundle/TreeProvider.php | 48 ------------------- 2 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 src/Oro/Bundle/SecurityBundle/TreeProvider.php diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php index 23d8a29513a..6b7b55d5de8 100644 --- a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Owner/OwnerTreeProviderTest.php @@ -66,7 +66,8 @@ public function testGetTree() $this->treeProvider->warmUpCache(); $tree = $this->treeProvider->getTree(); - var_dump($tree); + $this->assertEquals(1, $tree->getBusinessUnitOrganizationId(1)); + $this->assertEquals([1], $tree->getUserOrganizationIds(1)); } protected function setId($object, $value) @@ -74,7 +75,7 @@ protected function setId($object, $value) $reflection = new \ReflectionClass($object); $property = $reflection->getProperty('id'); $property->setAccessible(true); - $property->setValue($reflection, $value); + $property->setValue($object, $value); } protected function getTestData() @@ -115,4 +116,4 @@ protected function getTestData() [$mainBu, $bu2, $childBu] ]; } -} \ No newline at end of file +} diff --git a/src/Oro/Bundle/SecurityBundle/TreeProvider.php b/src/Oro/Bundle/SecurityBundle/TreeProvider.php deleted file mode 100644 index 308351fbd7c..00000000000 --- a/src/Oro/Bundle/SecurityBundle/TreeProvider.php +++ /dev/null @@ -1,48 +0,0 @@ -em = $em; - } - - public function fillTree(OwnerTree $tree) - { - $users = $this->em->getRepository('Oro\Bundle\UserBundle\Entity\User')->findAll(); - $businessUnits = $this->em->getRepository('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit')->findAll(); - - - - foreach ($users as $user) { - /** @var \Oro\Bundle\UserBundle\Entity\User $user */ - $tree->addUser($user->getId(), $user->getOwner()->getId()); - - } - - foreach ($businessUnits as $businessUnit) - { - /** @var \Oro\Bundle\OrganizationBundle\Entity\BusinessUnit $businessUnit */ - $tree->addBusinessUnit($businessUnit->getId(), $businessUnit->getOrganization()->getId()); - if ($businessUnit->getOwner()) { - $tree->addBusinessUnitRelation($businessUnit->getId(), $businessUnit->getOwner()->getId()); - } - } - - foreach ($users as $user) { - /** @var \Oro\Bundle\UserBundle\Entity\User $user */ - foreach ($user->getBusinessUnits() as $businessUnit) { - $tree->addUserBusinessUnit($user->getId(), $businessUnit->getId()); - } - } - } -} \ No newline at end of file From 9299cfdc3ec8e9c24da8e5974c7fc36e8283ff42 Mon Sep 17 00:00:00 2001 From: yurio Date: Thu, 7 Nov 2013 11:37:40 +0200 Subject: [PATCH 012/248] BAP-1921: Add translations to permissions --- src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php | 6 ++++-- .../Acl/Extension/ActionAclExtension.php | 2 +- .../Acl/Extension/EntityAclExtension.php | 2 +- .../Controller/AclPermissionController.php | 12 +++++++++++- .../Resources/translations/messages.en.yml | 6 ++++++ .../Resources/views/Form/fields.html.twig | 2 +- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php index cf9258c3442..955adcbc174 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php @@ -14,6 +14,8 @@ final class AccessLevel */ public static $allAccessLevelNames = array('BASIC', 'LOCAL', 'DEEP', 'GLOBAL', 'SYSTEM'); + const NONE_LEVEL_LABEL = 'Access-level-NONE'; + /** * Unknown access level. */ @@ -72,7 +74,7 @@ public static function getConst($name) public static function getAccessLevelName($value) { if ($value > self::NONE_LEVEL) { - return self::$allAccessLevelNames[$value - 1]; + return 'Access-level-' . self::$allAccessLevelNames[$value - 1]; } return null; @@ -86,7 +88,7 @@ public static function getAccessLevelName($value) */ public static function getAccessLevelNames($value = self::BASIC_LEVEL) { - $names = array('NONE'); + $names = [self::NONE_LEVEL_LABEL]; for ($level = $value; $level <= self::SYSTEM_LEVEL; $level++) { $name = self::getAccessLevelName($level); $names[$level] = $name; diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php b/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php index 93006663548..401b6a58b6d 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Extension/ActionAclExtension.php @@ -35,7 +35,7 @@ public function __construct(ActionMetadataProvider $actionMetadataProvider) public function getAccessLevelNames($object) { return array( - AccessLevel::NONE_LEVEL => 'NONE', + AccessLevel::NONE_LEVEL => AccessLevel::NONE_LEVEL_LABEL, AccessLevel::SYSTEM_LEVEL => AccessLevel::getAccessLevelName(AccessLevel::SYSTEM_LEVEL) ); } diff --git a/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php b/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php index cc8d8b2feba..e4ed5c31e0c 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/Extension/EntityAclExtension.php @@ -162,7 +162,7 @@ public function getAccessLevelNames($object) if (!$metadata->hasOwner()) { return array( - AccessLevel::NONE_LEVEL => 'NONE', + AccessLevel::NONE_LEVEL => AccessLevel::NONE_LEVEL_LABEL, AccessLevel::SYSTEM_LEVEL => AccessLevel::getAccessLevelName(AccessLevel::SYSTEM_LEVEL) ); } elseif ($metadata->isUserOwned()) { diff --git a/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php index 784820d2919..ec113309b3f 100644 --- a/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php +++ b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php @@ -14,6 +14,16 @@ class AclPermissionController extends Controller */ public function aclPermissionAccessLevelsAction() { - return new JsonResponse($this->get('oro_security.acl.manager')->getAccessLevelsForObject($this->getRequest()->get('oid'))); + $levels = $this + ->get('oro_security.acl.manager') + ->getAccessLevelsForObject($this->getRequest()->get('oid')); + $translator = $this->get('translator'); + foreach ($levels as $id => $label) { + $levels[$id] = $translator->trans($label); + } + + return new JsonResponse( + $levels + ); } } diff --git a/src/Oro/Bundle/SecurityBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/SecurityBundle/Resources/translations/messages.en.yml index bcd5c3c7c0a..f366bd76952 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/translations/messages.en.yml @@ -5,3 +5,9 @@ EDIT: Edit DELETE: Delete ASSIGN: Assign SHARE: Share +Access-level-BASIC: Me +Access-level-LOCAL: Me, my business units +Access-level-DEEP: Me, my business units and sub business units +Access-level-GLOBAL: Me and my organization +Access-level-SYSTEM: All system +Access-level-NONE: Access denied diff --git a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig index b06e0a5485e..5838db4ccf4 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig +++ b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig @@ -10,7 +10,7 @@ {% if level_label %} {{ level_label|trans({}, translation_domain) }} {% else %} - {{ 'NONE'|trans({}, translation_domain) }} + {{ 'Access-level-NONE'|trans({}, translation_domain) }} {% endif %} {% set type = type|default('hidden') %} From 65af6073d9a287e170b999abadad4422969f6d35 Mon Sep 17 00:00:00 2001 From: yurio Date: Fri, 8 Nov 2013 19:48:10 +0200 Subject: [PATCH 013/248] BAP-1921: Check Acess Level on select queries --- .../Bundle/SecurityBundle/Acl/AccessLevel.php | 4 +- .../Controller/AclPermissionController.php | 2 +- .../SecurityBundle/ORM/Walker/AclHelper.php | 62 +++++++++++++-- .../SecurityBundle/ORM/Walker/AclWalker.php | 76 +++++++++++++++---- .../Condition/JoinAssociationCondition.php | 66 ++++++++++++++++ .../Resources/views/Form/fields.html.twig | 3 +- 6 files changed, 187 insertions(+), 26 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAssociationCondition.php diff --git a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php index 955adcbc174..a37dbe110bd 100644 --- a/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php +++ b/src/Oro/Bundle/SecurityBundle/Acl/AccessLevel.php @@ -14,7 +14,7 @@ final class AccessLevel */ public static $allAccessLevelNames = array('BASIC', 'LOCAL', 'DEEP', 'GLOBAL', 'SYSTEM'); - const NONE_LEVEL_LABEL = 'Access-level-NONE'; + const NONE_LEVEL_LABEL = 'NONE'; /** * Unknown access level. @@ -74,7 +74,7 @@ public static function getConst($name) public static function getAccessLevelName($value) { if ($value > self::NONE_LEVEL) { - return 'Access-level-' . self::$allAccessLevelNames[$value - 1]; + return self::$allAccessLevelNames[$value - 1]; } return null; diff --git a/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php index ec113309b3f..b6ce261f14e 100644 --- a/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php +++ b/src/Oro/Bundle/SecurityBundle/Controller/AclPermissionController.php @@ -19,7 +19,7 @@ public function aclPermissionAccessLevelsAction() ->getAccessLevelsForObject($this->getRequest()->get('oid')); $translator = $this->get('translator'); foreach ($levels as $id => $label) { - $levels[$id] = $translator->trans($label); + $levels[$id] = $translator->trans('Access-level-' . $label); } return new JsonResponse( diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php index 92b936b1844..bf6c6d58055 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclHelper.php @@ -9,11 +9,14 @@ use Doctrine\ORM\Query\AST\RangeVariableDeclaration; use Doctrine\ORM\Query\AST\Join; use Doctrine\ORM\Query\AST\ConditionalPrimary; +use Doctrine\ORM\Query\AST\IdentificationVariableDeclaration; +use Doctrine\ORM\EntityManager; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\AclConditionStorage; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\SubRequestAclConditionStorage; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\AclCondition; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\JoinAclCondition; +use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\JoinAssociationCondition; /** * Class ACLHelper @@ -28,6 +31,11 @@ class ACLHelper */ protected $builder; + /** + * @var EntityManager + */ + protected $em; + /** * @param $builder */ @@ -49,13 +57,17 @@ public function apply($query, $permission = "VIEW") if ($query instanceof QueryBuilder) { $query = $query->getQuery(); } + $this->em = $query->getEntityManager(); $aclQuery = $this->cloneQuery($query); $ast = $query->getAST(); if ($ast instanceof SelectStatement) { - list ($whereConditions, $joinConditions) = $this->processRequest($ast, $permission); + list ($whereConditions, $joinConditions) = $this->processRequest($ast, $permission, $query); $conditionStorage = new AclConditionStorage($whereConditions, $joinConditions); - $this->processSubRequests($ast, $conditionStorage, $permission); + if ($ast->whereClause) { + $this->processSubRequests($ast, $conditionStorage, $permission, $query); + } + if (!$conditionStorage->isEmpty()) { $aclQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, @@ -157,13 +169,16 @@ protected function processRequest($select, $permission) $permission, true ); - if ($condition) { - $condition->setFromKey($fromKey); - $condition->setJoinKey($joinKey); - $joinConditions[] = $condition; - } + } else { + $condition = $this->processJoinAssociationPathExpression( + $identificationVariableDeclaration, $joinKey, $permission + ); + } + if ($condition) { + $condition->setFromKey($fromKey); + $condition->setJoinKey($joinKey); + $joinConditions[] = $condition; } - } } } @@ -171,6 +186,37 @@ protected function processRequest($select, $permission) return array($whereConditions, $joinConditions); } + /** + * @param IdentificationVariableDeclaration $declaration + * @param $key + * @param $permission + * @return JoinAssociationCondition + */ + protected function processJoinAssociationPathExpression(IdentificationVariableDeclaration $declaration, $key, $permission) + { + $metadata = $this->em->getClassMetadata($declaration->rangeVariableDeclaration->abstractSchemaName); + /** @var Join $join */ + $join = $declaration->joins[$key]; + $fieldName = $join->joinAssociationDeclaration->joinAssociationPathExpression->associationField; + + $associationMapping = $metadata->getAssociationMapping($fieldName); + $targetEntity = $associationMapping['targetEntity']; + + $resultData = $this->builder->getAclConditionData($targetEntity, $permission); + + if ($resultData && is_array($resultData)) { + list($entityField, $value) = $resultData; + + return new JoinAssociationCondition( + $join->joinAssociationDeclaration->aliasIdentificationVariable, + $entityField, + $value, + $targetEntity, + $associationMapping['joinColumns'] + ); + } + } + /** * @param RangeVariableDeclaration $rangeVariableDeclaration * @param $permission diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php index ee3037364d4..01e7d67afa2 100644 --- a/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/AclWalker.php @@ -14,8 +14,11 @@ use Doctrine\ORM\Query\AST\WhereClause; use Doctrine\ORM\Query\AST\Join; use Doctrine\ORM\Query\AST\Subselect; +use Doctrine\ORM\Query\AST\RangeVariableDeclaration; +use Doctrine\ORM\Query\AST\ComparisonExpression; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\AclConditionStorage; +use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\JoinAssociationCondition; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\SubRequestAclConditionStorage; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\AclCondition; use Oro\Bundle\SecurityBundle\ORM\Walker\Condition\JoinAclCondition; @@ -97,29 +100,74 @@ protected function addAclToJoinClause($AST, array $joinConditions) $fromClause = $AST->fromClause; } foreach ($joinConditions as $condition) { - /** @var JoinAclCondition $condition */ - $conditionalFactor = $this->getConditionalFactor($condition); - /** @var Join $join */ $join = $fromClause ->identificationVariableDeclarations[$condition->getFromKey()] ->joins[$condition->getJoinKey()]; - - $aclConditionalFactors = array($conditionalFactor); - if ($join->conditionalExpression instanceof ConditionalPrimary) { - array_unshift($aclConditionalFactors, $join->conditionalExpression); - $join->conditionalExpression = new ConditionalTerm( - $aclConditionalFactors - ); + if (!($condition instanceof JoinAssociationCondition)) { + /** @var JoinAclCondition $condition */ + $conditionalFactor = $this->getConditionalFactor($condition); + $aclConditionalFactors = array($conditionalFactor); + if ($join->conditionalExpression instanceof ConditionalPrimary) { + array_unshift($aclConditionalFactors, $join->conditionalExpression); + $join->conditionalExpression = new ConditionalTerm( + $aclConditionalFactors + ); + } else { + $join->conditionalExpression->conditionalFactors = array_merge( + $join->conditionalExpression->conditionalFactors, + $aclConditionalFactors + ); + } } else { - $join->conditionalExpression->conditionalFactors = array_merge( - $join->conditionalExpression->conditionalFactors, - $aclConditionalFactors - ); + $fromClause + ->identificationVariableDeclarations[$condition->getFromKey()] + ->joins[$condition->getJoinKey()] = $this->getJoinFromJoinAssociationCondition($join, $condition); } } } + /** + * @param Join $join + * @param JoinAssociationCondition $condition + * @return Join + */ + protected function getJoinFromJoinAssociationCondition(Join $join, JoinAssociationCondition $condition) + { + $joinAssociationPathExpression = $join->joinAssociationDeclaration->joinAssociationPathExpression; + + $leftExpression = new ArithmeticExpression(); + $pathExpression = new PathExpression( + self::EXPECTED_TYPE, + $joinAssociationPathExpression->identificationVariable, + $joinAssociationPathExpression->associationField + ); + $pathExpression->type = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; + $leftExpression->simpleArithmeticExpression = $pathExpression; + + $conditionalFactors = []; + foreach ($condition->getJoinConditions() as $joinCondition) { + $rightExpression = new ArithmeticExpression(); + $pathExpression = new PathExpression( + self::EXPECTED_TYPE, + $condition->getEntityAlias(), + $joinCondition['referencedColumnName'] + ); + $pathExpression->type = PathExpression::TYPE_STATE_FIELD; + $rightExpression->simpleArithmeticExpression = $pathExpression; + $factor = new ConditionalPrimary(); + $factor->simpleConditionalExpression = new ComparisonExpression($leftExpression, '=', $rightExpression); + $conditionalFactors[] = $factor; + } + $conditionalFactors[] = $this->getConditionalFactor($condition); + $associationDeclaration = new RangeVariableDeclaration($condition->getEntityClass(), $condition->getEntityAlias()); + + $newJoin = new Join($join->joinType, $associationDeclaration); + $newJoin->conditionalExpression = new ConditionalTerm($conditionalFactors); + + return $newJoin; + } + /** * work with "where" statement of query * diff --git a/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAssociationCondition.php b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAssociationCondition.php new file mode 100644 index 00000000000..4638b7e4227 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/ORM/Walker/Condition/JoinAssociationCondition.php @@ -0,0 +1,66 @@ +entityClass = $entityClass; + $this->joinConditions = $joinConditions; + + parent::__construct($entityAlias, $entityField, $value); + } + + /** + * @param string $entityClass + */ + public function setEntityClass($entityClass) + { + $this->entityClass = $entityClass; + } + + /** + * @return string + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * @param array $joinConditions + */ + public function setJoinConditions($joinConditions) + { + $this->joinConditions = $joinConditions; + } + + /** + * @return array + */ + public function getJoinConditions() + { + return $this->joinConditions; + } + + +} diff --git a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig index 5838db4ccf4..ba9cd18566e 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig +++ b/src/Oro/Bundle/SecurityBundle/Resources/views/Form/fields.html.twig @@ -8,7 +8,8 @@ +{{ oro_form_js_validation(form) }} diff --git a/src/Oro/Bundle/UserBundle/composer.json b/src/Oro/Bundle/UserBundle/composer.json index 8a019060ea0..26a6850a108 100644 --- a/src/Oro/Bundle/UserBundle/composer.json +++ b/src/Oro/Bundle/UserBundle/composer.json @@ -20,7 +20,6 @@ "oro/grid-bundle": "dev-master", "oro/navigation-bundle": "dev-master", "oro/data-audit-bundle": "dev-master", - "oro/jsfv-bundle": "dev-master", "oro/form-bundle": "dev-master", "oro/email-bundle": "dev-master", "oro/organization-bundle": "dev-master", From 2a61fb49c9e64dd8c0177ef9ecfd2eba25e010a9 Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Fri, 22 Nov 2013 15:08:45 +0200 Subject: [PATCH 088/248] BAP-2337: Add form extension to provide value of data-validation attribute - removed use of JSFV --- .../CalendarBundle/Resources/views/Calendar/view.html.twig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Oro/Bundle/CalendarBundle/Resources/views/Calendar/view.html.twig b/src/Oro/Bundle/CalendarBundle/Resources/views/Calendar/view.html.twig index e1b63bfd1f2..20fd1e1ffd6 100644 --- a/src/Oro/Bundle/CalendarBundle/Resources/views/Calendar/view.html.twig +++ b/src/Oro/Bundle/CalendarBundle/Resources/views/Calendar/view.html.twig @@ -126,7 +126,6 @@ subordinate: true, containerSelector: '.calendar-events', itemFormTemplateSelector: '#template-calendar-event', - itemFormValidationScriptUrl: '{{ JSFV(event_form, true) }}', date: "{{ 'now'|date('Y-m-d') }}", firstDay: localeSettings.getCalendarFirstDayOfWeek() - 1, monthNames: localeSettings.getCalendarMonthNames('wide', true), From f138c15cf16075d6bc99b1a06d7923dec0a1881d Mon Sep 17 00:00:00 2001 From: Yevhen Shyshkin Date: Fri, 22 Nov 2013 16:57:34 +0200 Subject: [PATCH 089/248] BAP-2331: Refactor "request entity" action - fixed bug in workflow attributes form --- .../Form/Type/WorkflowAttributesType.php | 20 ++++++++++ .../Form/Type/WorkflowAttributesTypeTest.php | 39 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Form/Type/WorkflowAttributesType.php b/src/Oro/Bundle/WorkflowBundle/Form/Type/WorkflowAttributesType.php index c56d7e84bc6..a0ace2a7414 100644 --- a/src/Oro/Bundle/WorkflowBundle/Form/Type/WorkflowAttributesType.php +++ b/src/Oro/Bundle/WorkflowBundle/Form/Type/WorkflowAttributesType.php @@ -24,6 +24,11 @@ class WorkflowAttributesType extends AbstractType */ protected $workflowRegistry; + /** + * @var WorkflowData + */ + protected $workflowData; + /** * @param WorkflowRegistry $workflowRegistry */ @@ -55,18 +60,33 @@ public function buildForm(FormBuilderInterface $builder, array $options) $this->addAttributeField($builder, $attribute, $attributeOptions, $options); } + // extract only required attributes for form and create new WorkflowData based on them $builder->addEventListener( FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) { /** @var WorkflowData $data */ $data = $event->getData(); if ($data instanceof WorkflowData) { + $this->workflowData = $data; $rawData = $data->getValues(array_keys($options['attribute_fields'])); $formData = new WorkflowData($rawData); $event->setData($formData); } } ); + + // copy submitted data to existing workflow data + $builder->addEventListener( + FormEvents::SUBMIT, + function (FormEvent $event) { + /** @var WorkflowData $formData */ + $formData = $event->getData(); + if ($this->workflowData && $formData instanceof WorkflowData) { + $this->workflowData->add($formData->getValues()); + $event->setData($this->workflowData); + } + } + ); } } diff --git a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Form/Type/WorkflowAttributesTypeTest.php b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Form/Type/WorkflowAttributesTypeTest.php index fd520197183..a88fb42cef3 100644 --- a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Form/Type/WorkflowAttributesTypeTest.php +++ b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Form/Type/WorkflowAttributesTypeTest.php @@ -30,9 +30,10 @@ public function testSubmit( $submitData, $formData, array $formOptions, - array $childrenOptions + array $childrenOptions, + $sourceWorkflowData = null ) { - $form = $this->factory->create($this->type, null, $formOptions); + $form = $this->factory->create($this->type, $sourceWorkflowData, $formOptions); $this->assertSameSize($childrenOptions, $form->all()); @@ -99,6 +100,40 @@ public function submitDataProvider() 'second' => array('label' => 'Second Custom', 'required' => false), ), ), + 'partial_fields' => array( + 'submitData' => array('first' => 'first_string_modified'), + 'formData' => $this->createWorkflowData( + array( + 'first' => 'first_string_modified', + 'second' => 'second_string', + ) + ), + 'formOptions' => array( + 'workflow' => $this->createWorkflow( + 'test_workflow_with_partial_attributes', + array( + 'first' => $this->createAttribute('first', 'string', 'First'), + 'second' => $this->createAttribute('second', 'string', 'Second'), + ) + ), + 'attribute_fields' => array( + 'first' => array( + 'form_type' => 'text', + 'label' => 'First Custom', + 'options' => array('required' => true) + ), + ) + ), + 'childrenOptions' => array( + 'first' => array('label' => 'First Custom', 'required' => true), + ), + 'sourceWorkflowData' => $this->createWorkflowData( + array( + 'first' => 'first_string', + 'second' => 'second_string', + ) + ), + ), 'disable_fields' => array( 'submitData' => array('first' => 'first_string', 'second' => 'second_string'), 'formData' => $this->createWorkflowData(), From 2f3be4ff70b030147cb2d289a273502d6f80a6c5 Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 22 Nov 2013 16:57:44 +0200 Subject: [PATCH 090/248] Fixed BAP-2245 - removed event bindFirst plugin --- .../Resources/config/requirejs.yml | 5 - .../public/lib/jquery.bind-first-0.2.1.js | 106 ------------------ .../Resources/views/macros.html.twig | 4 +- 3 files changed, 2 insertions(+), 113 deletions(-) delete mode 100644 src/Oro/Bundle/EmailBundle/Resources/public/lib/jquery.bind-first-0.2.1.js diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/requirejs.yml b/src/Oro/Bundle/EmailBundle/Resources/config/requirejs.yml index 76f25f714b0..8833bc242a1 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/requirejs.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/requirejs.yml @@ -1,8 +1,4 @@ config: - shim: - 'jquery.bind-first': - deps: - - 'jquery' paths: 'oro/email/template/collection': 'bundles/oroemail/js/email/template/collection.js' 'oro/email/template/model': 'bundles/oroemail/js/email/template/model.js' @@ -10,4 +6,3 @@ config: 'oro/email/variable/model': 'bundles/oroemail/js/email/variable/model.js' 'oro/email/variable/view': 'bundles/oroemail/js/email/variable/view.js' 'oro/init-email': 'bundles/oroemail/js/init-email.js' - 'jquery.bind-first': 'bundles/oroemail/lib/jquery.bind-first-0.2.1.js' diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/lib/jquery.bind-first-0.2.1.js b/src/Oro/Bundle/EmailBundle/Resources/public/lib/jquery.bind-first-0.2.1.js deleted file mode 100644 index e617edde34d..00000000000 --- a/src/Oro/Bundle/EmailBundle/Resources/public/lib/jquery.bind-first-0.2.1.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * jQuery.bind-first library v0.2.1 - * Copyright (c) 2013 Vladimir Zhuravlev - * - * Released under MIT License - * @license - * - * Date: Thu Jun 13 21:06:55 NOVT 2013 - **/ - -(function($) { - var splitVersion = $.fn.jquery.split("."); - var major = parseInt(splitVersion[0]); - var minor = parseInt(splitVersion[1]); - - var JQ_LT_17 = (major < 1) || (major == 1 && minor < 7); - - function eventsData($el) { - return JQ_LT_17 ? $el.data('events') : $._data($el[0]).events; - } - - function moveHandlerToTop($el, eventName, isDelegated) { - var data = eventsData($el); - var events = data[eventName]; - - if (!JQ_LT_17) { - var handler = isDelegated ? events.splice(events.delegateCount - 1, 1)[0] : events.pop(); - events.splice(isDelegated ? 0 : (events.delegateCount || 0), 0, handler); - - return; - } - - if (isDelegated) { - data.live.unshift(data.live.pop()); - } else { - events.unshift(events.pop()); - } - } - - function moveEventHandlers($elems, eventsString, isDelegate) { - var events = eventsString.split(/\s+/); - $elems.each(function() { - for (var i = 0; i < events.length; ++i) { - var pureEventName = $.trim(events[i]).match(/[^\.]+/i)[0]; - moveHandlerToTop($(this), pureEventName, isDelegate); - } - }); - } - - $.fn.bindFirst = function() { - var args = $.makeArray(arguments); - var eventsString = args.shift(); - - if (eventsString) { - $.fn.bind.apply(this, arguments); - moveEventHandlers(this, eventsString); - } - - return this; - }; - - $.fn.delegateFirst = function() { - var args = $.makeArray(arguments); - var eventsString = args[1]; - - if (eventsString) { - args.splice(0, 2); - $.fn.delegate.apply(this, arguments); - moveEventHandlers(this, eventsString, true); - } - - return this; - }; - - $.fn.liveFirst = function() { - var args = $.makeArray(arguments); - - // live = delegate to document - args.unshift(this.selector); - $.fn.delegateFirst.apply($(document), args); - - return this; - }; - - if (!JQ_LT_17) { - $.fn.onFirst = function(types, selector) { - var $el = $(this); - var isDelegated = typeof selector === 'string'; - - $.fn.on.apply($el, arguments); - - // events map - if (typeof types === 'object') { - for (type in types) - if (types.hasOwnProperty(type)) { - moveEventHandlers($el, type, isDelegated); - } - } else if (typeof types === 'string') { - moveEventHandlers($el, types, isDelegated); - } - - return $el; - }; - } - -})(jQuery); diff --git a/src/Oro/Bundle/EmailBundle/Resources/views/macros.html.twig b/src/Oro/Bundle/EmailBundle/Resources/views/macros.html.twig index fc43f567704..3b42e18abb2 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/views/macros.html.twig +++ b/src/Oro/Bundle/EmailBundle/Resources/views/macros.html.twig @@ -42,7 +42,7 @@ {% macro renderPreviewDialog(formName, title) %} ': '<% print("") %>'})|raw }} + {% endmacro %} diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app.js index c2599f21aa0..f35059a6246 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/app.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app.js @@ -119,10 +119,16 @@ function($, _) { */ isEqualsLoosely: function (value1, value2) { if (!_.isObject(value1)) { - var equalsLoosely = (value1 || '') == (value2 || ''); - var eitherNumber = _.isNumber(value1) || _.isNumber(value2); - var equalsNumbers = Number(value1) == Number(value2); - return equalsLoosely || (eitherNumber && equalsNumbers); + if (_.isNumber(value1) || _.isNumber(value2)) { + var toNumber = function (v) { + if (_.isString(v) && v == '') { + return NaN; + } + return Number(v); + }; + return (toNumber(value1) == toNumber(value2)); + } + return ((value1 || '') == (value2 || '')); } else if (_.isObject(value1)) { var valueKeys = _.keys(value1); From 35f74bead67e86f5b61391017b05a4602c8235cd Mon Sep 17 00:00:00 2001 From: Vova Soroka Date: Mon, 25 Nov 2013 03:18:03 +0200 Subject: [PATCH 115/248] BAP-2130: Table reports creation wizard (simplified UI) --- .../QueryDesignerBundle/Resources/public/css/query-designer.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/query-designer.css b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/query-designer.css index c93cddd4d9a..83c17565d17 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/query-designer.css +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/query-designer.css @@ -43,6 +43,7 @@ -webkit-border-bottom-right-radius: 3px; -moz-border-radius-bottomright: 3px; } +.form-horizontal .query-designer-form .filter-box .filter-item .filter-select .select-filter-widget, .form-horizontal .query-designer-form .filter-box .filter-item .filter-criteria-hint { font-weight: normal; } From 108961fb537230f6334210c2be156f592402a458 Mon Sep 17 00:00:00 2001 From: Vova Soroka Date: Mon, 25 Nov 2013 04:14:13 +0200 Subject: [PATCH 116/248] BAP-2130: Table reports creation wizard (simplified UI) --- .../public/js/query-designer/filter/view.js | 24 +++++++++++++++++++ .../Resources/translations/messages.en.yml | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/query-designer/filter/view.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/query-designer/filter/view.js index ba0db2c5ecc..ef76b412b71 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/query-designer/filter/view.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/query-designer/filter/view.js @@ -107,6 +107,30 @@ function(_, __, AbstractView, FilterCollection, filterBuilder) { m.set('index', m.get('index') - 1); } }); + + // try to remove the deleted filter from filters logic + var filtersLogic = this.getFiltersLogic(); + var newFiltersLogic = filtersLogic; + var index = '' + model.get('index'); + if (filtersLogic == index || filtersLogic == 'NOT ' + index) { + newFiltersLogic = ''; + } else { + newFiltersLogic = newFiltersLogic.replace(new RegExp(' \\((NOT )?' + index + '\\) ') , ' '); + newFiltersLogic = newFiltersLogic.replace(new RegExp(' (AND|OR) (NOT )?' + index + ' ') , ' '); + newFiltersLogic = newFiltersLogic.replace(new RegExp(' (AND|OR) (NOT )?' + index + '$') , ''); + newFiltersLogic = newFiltersLogic.replace(new RegExp(' (NOT )?' + index + ' (AND|OR) ') , ' '); + newFiltersLogic = newFiltersLogic.replace(new RegExp('^(NOT )?' + index + ' (AND|OR) ') , ''); + } + if (newFiltersLogic != filtersLogic) { + index = Number(index); + newFiltersLogic = newFiltersLogic.replace(/(\d+)/g, function (match) { + if (Number(match) > index) { + return '' + (Number(match) - 1); + } + return match; + }); + this.setFiltersLogic(newFiltersLogic); + } }, onResetCollection: function () { diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml index a8f2e4b1bf8..83a4aa0acf1 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml @@ -30,4 +30,4 @@ oro: sorting_desc: Desc criterion: Criterion filter_logic: Filter Logic - filter_logic_tooltip: Example: (1 AND 2) OR 3 + filter_logic_tooltip: Example: 1 AND (2 OR NOT 3) From 3c960541a5c217648c7d28866fc7c49fb73c8828 Mon Sep 17 00:00:00 2001 From: yurio Date: Mon, 25 Nov 2013 10:13:24 +0200 Subject: [PATCH 117/248] BAP-2033: Fix codestyle --- .../Tests/Unit/Acl/Domain/RootBasedAclProviderTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Domain/RootBasedAclProviderTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Domain/RootBasedAclProviderTest.php index 19e42a0f85b..15f473678e9 100644 --- a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Domain/RootBasedAclProviderTest.php +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Acl/Domain/RootBasedAclProviderTest.php @@ -176,7 +176,8 @@ public function aclTestData() ]; } - protected function results1($result, $parameter) { + protected function results1($result, $parameter) + { $this->assertInstanceOf( 'Oro\Bundle\SecurityBundle\Acl\Domain\RootBasedAclWrapper', $result @@ -187,11 +188,13 @@ protected function results1($result, $parameter) { $this->assertEquals($parameter, $aclReflection->getValue($result)); } - protected function results2($result, $parameter) { + protected function results2($result, $parameter) + { $this->assertEquals($parameter, $result); } - protected function results3($result, $parameter) { + protected function results3($result, $parameter) + { $reflection = new \ReflectionClass($result); $aclReflection = $reflection->getProperty('acl'); $aclReflection->setAccessible(true); From 552f09b2d5bae1f058f723dff96f663cb9bb44b5 Mon Sep 17 00:00:00 2001 From: eu-ge-ne Date: Mon, 25 Nov 2013 12:16:50 +0200 Subject: [PATCH 118/248] BAP-1947: dependency on 'oro/registy' removed from 'oro/datagrid' --- .../Resources/public/js/datagrid-builder.js | 3 +- .../Resources/public/js/datagrid/grid.js | 59 ++++++++++++------- .../js/datagrid/listener/abstract-listener.js | 21 +++---- .../views/Email/dialog/update.html.twig | 9 +-- .../views/ImportExport/buttons.html.twig | 7 +-- 5 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid-builder.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid-builder.js index 30489a7fd49..6e8dcce3906 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid-builder.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid-builder.js @@ -84,8 +84,6 @@ function($, _, Backbone, __, tools, mediator, registry, LoadingMask, grid = new Grid(_.extend({collection: collection}, options)); this.$el.append(grid.render().$el); mediator.trigger('datagrid:created', grid, this.$el); - // @todo delete - registry.setElement('datagrid', options.name, grid); mediator.trigger('datagrid:created:' + options.name, grid); if (options.routerEnabled !== false) { @@ -185,6 +183,7 @@ function($, _, Backbone, __, tools, mediator, registry, LoadingMask, return function (builders) { var $container = $(document), $grids = $container.find(gridSelector); + $grids.each(function (i, el) { var $el = $(el); _.each(builders, function (builder) { diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid.js index 5d006d80a58..18f95a2db66 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid.js @@ -1,4 +1,5 @@ -/* global define */ +/*jslint nomen: true, vars: true*/ +/*global define*/ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'oro/loading-mask', 'oro/datagrid/header', 'oro/datagrid/body', 'oro/datagrid/toolbar', 'oro/datagrid/action-column', 'oro/datagrid/select-row-cell', 'oro/datagrid/select-all-header-cell', @@ -260,11 +261,11 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o /** * Creates action * - * @param {Function} actionPrototype + * @param {Function} ActionPrototype * @protected */ - createMassAction: function (actionPrototype) { - return new actionPrototype({ + createMassAction: function (ActionPrototype) { + return new ActionPrototype({ datagrid: this, launcherOptions: { className: 'btn' @@ -278,17 +279,24 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o * @return oro.datagrid.RefreshCollectionAction */ getRefreshAction: function () { - if (!this.refreshAction) { - this.refreshAction = new RefreshCollectionAction({ - datagrid: this, + var grid = this; + + if (!grid.refreshAction) { + grid.refreshAction = new RefreshCollectionAction({ + datagrid: grid, launcherOptions: { - label: 'Refresh', - className: 'btn', + label: 'Refresh', + className: 'btn', iconClassName: 'icon-refresh' } }); + + mediator.on('datagrid:refresh:' + grid.name, function () { + grid.refreshAction.execute(); + }); } - return this.refreshAction; + + return grid.refreshAction; }, /** @@ -297,17 +305,24 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o * @return oro.datagrid.ResetCollectionAction */ getResetAction: function () { - if (!this.resetAction) { - this.resetAction = new ResetCollectionAction({ - datagrid: this, + var grid = this; + + if (!grid.resetAction) { + grid.resetAction = new ResetCollectionAction({ + datagrid: grid, launcherOptions: { - label: 'Reset', - className: 'btn', + label: 'Reset', + className: 'btn', iconClassName: 'icon-repeat' } }); + + mediator.on('datagrid:reset:' + grid.name, function () { + grid.resetAction.execute(); + }); } - return this.resetAction; + + return grid.resetAction; }, /** @@ -323,7 +338,7 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o xhr.always = function () { always.apply(this, arguments); self._afterRequest(); - } + }; }, this); this.collection.on('remove', this._onRemove, this); @@ -417,8 +432,8 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o */ renderNoDataBlock: function () { var placeholders = {entityHint: (this.entityHint || __('oro.datagrid.entityHint')).toLowerCase()}, - template = _.isEmpty(this.collection.state.filters) ? - 'oro.datagrid.noentities' : 'oro.datagrid.noresults'; + template = _.isEmpty(this.collection.state.filters) + ? 'oro.datagrid.noentities' : 'oro.datagrid.noresults'; this.$(this.selectors.noDataBlock).html($(this.noDataTemplate({ hint: __(template, placeholders).replace('\n', '
') }))).hide(); @@ -431,7 +446,7 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o * @private */ _beforeRequest: function () { - this.requestsCount++; + this.requestsCount += 1; this.showLoading(); }, @@ -441,8 +456,8 @@ define(['jquery', 'underscore', 'backgrid', 'oro/translator', 'oro/mediator', 'o * @private */ _afterRequest: function () { - this.requestsCount--; - if (this.requestsCount == 0) { + this.requestsCount -= 1; + if (this.requestsCount === 0) { this.hideLoading(); // render block instead of update in order to change message depending on filter state this.renderNoDataBlock(); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/listener/abstract-listener.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/listener/abstract-listener.js index 8138d669585..187e25be1a8 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/listener/abstract-listener.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/listener/abstract-listener.js @@ -1,6 +1,6 @@ -/* global define */ -define(['underscore', 'backbone', 'oro/registry', 'oro/mediator'], -function(_, Backbone, registry, mediator) { +/*jslint nomen: true*/ +/*global define*/ +define(['underscore', 'backbone', 'oro/mediator'], function (_, Backbone, mediator) { 'use strict'; /** @@ -26,7 +26,7 @@ function(_, Backbone, registry, mediator) { * * @param {Object} options */ - initialize: function(options) { + initialize: function (options) { if (!_.has(options, 'columnName')) { throw new Error('Data column name is not specified'); @@ -56,13 +56,8 @@ function(_, Backbone, registry, mediator) { * @param {String} datagridName * @private */ - _assignDatagridAndSubscribe: function(datagridName) { - var datagrid = registry.getElement('datagrid', datagridName); - if (datagrid) { - this.setDatagridAndSubscribe(datagrid); - } else { - mediator.once("datagrid:created:" + datagridName, this.setDatagridAndSubscribe, this); - } + _assignDatagridAndSubscribe: function (datagridName) { + mediator.once("datagrid:created:" + datagridName, this.setDatagridAndSubscribe, this); }, /** @@ -70,7 +65,7 @@ function(_, Backbone, registry, mediator) { * * @param {oro.datagrid.Grid} datagrid */ - setDatagridAndSubscribe: function(datagrid) { + setDatagridAndSubscribe: function (datagrid) { this.datagrid = datagrid; this.datagrid.collection.on('change:' + this.columnName, this._onModelEdited, this); }, @@ -96,7 +91,7 @@ function(_, Backbone, registry, mediator) { * @protected * @abstract */ - _processValue: function(value, model) { + _processValue: function (value, model) { throw new Error('_processValue method is abstract and must be implemented'); } }); diff --git a/src/Oro/Bundle/EmailBundle/Resources/views/Email/dialog/update.html.twig b/src/Oro/Bundle/EmailBundle/Resources/views/Email/dialog/update.html.twig index f7b48517efb..6ad94bf1791 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/views/Email/dialog/update.html.twig +++ b/src/Oro/Bundle/EmailBundle/Resources/views/Email/dialog/update.html.twig @@ -11,16 +11,13 @@