diff --git a/doc/core/maintenance.md b/doc/core/maintenance.md new file mode 100644 index 00000000..7a2c1392 --- /dev/null +++ b/doc/core/maintenance.md @@ -0,0 +1,18 @@ +Maintenance +===== + +Pum allow you to easily switch your website to a maintenance mode. +Your application will be closed except for people whitelisted by their IP address. + +You can either add some IP to the configuration, or from the Pum global parameters form. + +Even in maintenance mode, the backend side will still be available for administrators. + +##Configuration +``` +pum_core: + maintenance: + template: 'pum://my_maintenance.html.twig' + whiteIps: [ 127.0.0.1 ] + whitePaths: [ 'login' ] +``` \ No newline at end of file diff --git a/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.en.po b/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.en.po index c3b52744..7a56f6d9 100644 --- a/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.en.po +++ b/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.en.po @@ -255,6 +255,8 @@ msgstr "All rights" ################################################# Settings ##################################################### msgid "pum.form.config.tabs.woodwork.label" msgstr "Woodwork" +msgid "pum.form.config.tabs.maintenance.label" +msgstr "Maintenance" msgid "pum.form.config.tabs.project.admin.label" msgstr "Project Admin" msgid "pum.form.config.tabs.type.extra.label" @@ -281,6 +283,10 @@ msgid "pum.form.config.tabs.woodwork.ww.show.export.import.button.label" msgstr "Show export/import button" msgid "pum.form.config.tabs.woodwork.ww.show.clone.button.label" msgstr "Show clone button" +msgid "pum.form.config.tabs.maintenance.maintenance.mode.label" +msgstr "Maintenance mode" +msgid "pum.form.config.tabs.maintenance.maintenance.restriction.ips.label" +msgstr "Allowed IPs" msgid "pum.form.config.tabs.project.admin.pa.default.tableview.truncatecols.value.label" msgstr "Max columns showed in default Tableview" msgid "pum.form.config.tabs.project.admin.pa.disable.default.tableview.truncatecols.label" diff --git a/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.fr.po b/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.fr.po index b2737e01..bdbca960 100644 --- a/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.fr.po +++ b/src/Pum/Bundle/AppBundle/Resources/translations/pum_form.fr.po @@ -257,6 +257,8 @@ msgstr "Tous les droits" ################################################# Settings ##################################################### msgid "pum.form.config.tabs.woodwork.label" msgstr "Woodwork" +msgid "pum.form.config.tabs.maintenance.label" +msgstr "Maintenance" msgid "pum.form.config.tabs.project.admin.label" msgstr "Project Admin" msgid "pum.form.config.tabs.type.extra.label" @@ -283,6 +285,10 @@ msgid "pum.form.config.tabs.woodwork.ww.show.export.import.button.label" msgstr "Afficher les boutons d'import/export" msgid "pum.form.config.tabs.woodwork.ww.show.clone.button.label" msgstr "Afficher les boutons de clonage" +msgid "pum.form.config.tabs.maintenance.maintenance.mode.label" +msgstr "Mettre en maintenance" +msgid "pum.form.config.tabs.maintenance.maintenance.restriction.ips.label" +msgstr "Adresses IP autorisées" msgid "pum.form.config.tabs.project.admin.pa.default.tableview.truncatecols.value.label" msgstr "Nombre de colonnes maximum affichées dans la Tableview par défaut" msgid "pum.form.config.tabs.project.admin.pa.disable.default.tableview.truncatecols.label" diff --git a/src/Pum/Bundle/CoreBundle/DependencyInjection/Configuration.php b/src/Pum/Bundle/CoreBundle/DependencyInjection/Configuration.php index 21c81fa0..28239a72 100644 --- a/src/Pum/Bundle/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Pum/Bundle/CoreBundle/DependencyInjection/Configuration.php @@ -13,68 +13,85 @@ public function getConfigTreeBuilder() $builder ->root('pum_core') - ->children() - ->booleanNode('validation')->defaultTrue()->end() - ->booleanNode('em_factory')->defaultFalse()->end() - ->arrayNode('doctrine') - ->useAttributeAsKey('name') - ->treatNullLike(array()) - ->prototype('scalar')->end() - ->defaultValue(array('_pum' => array('dql' => array()))) - ->prototype('array') - ->beforeNormalization() - ->ifTrue(function($v) { - return !isset($v['dql']); - }) - ->then(function($v) { - return array('dql' => array()); - }) - ->end() - ->children() - ->arrayNode('dql') - ->fixXmlConfig('string_function') - ->fixXmlConfig('numeric_function') - ->fixXmlConfig('datetime_function') - ->children() - ->arrayNode('string_functions') - ->useAttributeAsKey('name') - ->prototype('scalar')->end() - ->end() - ->arrayNode('numeric_functions') - ->useAttributeAsKey('name') - ->prototype('scalar')->end() - ->end() - ->arrayNode('datetime_functions') - ->useAttributeAsKey('name') - ->prototype('scalar')->end() + ->children() + ->booleanNode('validation')->defaultTrue()->end() + ->booleanNode('em_factory')->defaultFalse()->end() + ->arrayNode('doctrine') + ->useAttributeAsKey('name') + ->treatNullLike(array()) + ->prototype('scalar')->end() + ->defaultValue(array('_pum' => array('dql' => array()))) + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function($v) { + return !isset($v['dql']); + }) + ->then(function($v) { + return array('dql' => array()); + }) + ->end() + ->children() + ->arrayNode('dql') + ->fixXmlConfig('string_function') + ->fixXmlConfig('numeric_function') + ->fixXmlConfig('datetime_function') + ->children() + ->arrayNode('string_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('numeric_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() + ->arrayNode('datetime_functions') + ->useAttributeAsKey('name') + ->prototype('scalar')->end() + ->end() ->end() ->end() ->end() ->end() ->end() - ->end() - ->arrayNode('view') - ->addDefaultsIfNotSet() - ->children() - ->booleanNode('enabled')->defaultFalse()->end() - ->arrayNode('mode') - ->prototype('scalar')->end() - ->defaultValue(array('filesystem')) + ->arrayNode('view') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultFalse()->end() + ->arrayNode('mode') + ->prototype('scalar')->end() + ->defaultValue(array('filesystem')) + ->end() ->end() ->end() - ->end() - ->arrayNode('notification') - ->addDefaultsIfNotSet() - ->children() - ->scalarNode('from')->defaultValue('notification@kitea.fr')->end() - ->arrayNode('content') - ->children() - ->scalarNode('title')->end() + ->arrayNode('notification') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('from')->defaultValue('notification@kitea.fr')->end() + ->arrayNode('content') + ->children() + ->scalarNode('title')->end() + ->end() ->end() ->end() ->end() + ->arrayNode('maintenance') + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('template')->defaultValue('pum://maintenance.html.twig')->end() + ->arrayNode('whitePaths') + ->prototype('scalar') + ->end() + ->end() + ->arrayNode('whiteIps') + ->prototype('scalar') + ->end() + ->end() + ->end() + ->end() + ->arrayNode('assetic_bundles') + ->prototype('scalar') + ->end() ->end() - ->arrayNode('assetic_bundles')->prototype('scalar')->end()->end() ; return $builder; diff --git a/src/Pum/Bundle/CoreBundle/DependencyInjection/PumCoreExtension.php b/src/Pum/Bundle/CoreBundle/DependencyInjection/PumCoreExtension.php index 8f3cf417..ed90d391 100644 --- a/src/Pum/Bundle/CoreBundle/DependencyInjection/PumCoreExtension.php +++ b/src/Pum/Bundle/CoreBundle/DependencyInjection/PumCoreExtension.php @@ -53,6 +53,7 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('translation.xml'); $loader->load('templating.xml'); $loader->load('mailer.xml'); + $loader->load('maintenance.xml'); $loader->load('notification.xml'); $container->setParameter('pum_core.assetic_bundles', $config['assetic_bundles']); @@ -97,6 +98,22 @@ public function load(array $configs, ContainerBuilder $container) if ($config['notification']) { $container->setParameter('pum_core.notification', $config['notification']); } + + if ($config['maintenance']) { + $whiteIps = array(); + if (isset($config['maintenance']['whiteIps'])) { + $whiteIps = $config['maintenance']['whiteIps']; + } + + $whitePaths = array(); + if (isset($config['maintenance']['whitePaths'])) { + $whitePaths = $config['maintenance']['whitePaths']; + } + + $container->setParameter('pum_core.maintenance.template', $config['maintenance']['template']); + $container->setParameter('pum_core.maintenance.whiteIps', $whiteIps); + $container->setParameter('pum_core.maintenance.whitePaths', $whitePaths); + } } private function registerPumViewFolders(ContainerBuilder $container) @@ -105,7 +122,6 @@ private function registerPumViewFolders(ContainerBuilder $container) $folders = array(); foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$bundle.'/pum_views')) { $folders[$bundle] = $dir; } @@ -116,6 +132,8 @@ private function registerPumViewFolders(ContainerBuilder $container) } } + $folders = array_reverse($folders); + $container->setParameter('pum_core.view.folders', $folders); } } diff --git a/src/Pum/Bundle/CoreBundle/EventListener/MaintenanceListener.php b/src/Pum/Bundle/CoreBundle/EventListener/MaintenanceListener.php new file mode 100644 index 00000000..b0570046 --- /dev/null +++ b/src/Pum/Bundle/CoreBundle/EventListener/MaintenanceListener.php @@ -0,0 +1,71 @@ +config = $config; + $this->tokenStorage = $tokenStorage; + $this->twigEngine = $twigEngine; + + $this->template = $template; + $this->whiteIps = array_unique(array_merge($whiteIps, $this->config->get('maintenance_restriction_ips', array()))); + $this->whitePaths = array_unique(array_merge($whitePaths, array('_profiler', '_wdt', 'pum-login', 'pum-login-check', 'js', 'css', 'images'))); + } + + public function onKernelRequest(GetResponseEvent $event) + { + $this->isUnderMaintenance = $this->config->get('maintenance_mode', false); + + if ($this->isUnderMaintenance) { + $clientIp = $event->getRequest()->getClientIp(); + + // is IP authorized + if (!empty($this->whiteIps)) { + foreach ($this->whiteIps as $ip) { + if ($ip === $clientIp) { + return true; + } + } + } + + // is User connected and authorized + $token = $this->tokenStorage->getToken(); + if ($token != null) { + $user = $token->getUser(); + if ($user instanceof User && $user->getGroup()->isAdmin()) { + return true; + } + } + + // is Path authorized + $path = trim($event->getRequest()->getPathInfo(), '/'); + foreach ($this->whitePaths as $pattern) { + if (preg_match('/^' . $pattern . '/', $path)) { + return true; + } + } + + $event->setResponse(new Response($this->twigEngine->render($this->template), 503)); + $event->stopPropagation(); + } + } +} diff --git a/src/Pum/Bundle/CoreBundle/Form/TypeExtension/MaintenanceConfigTypeExtension.php b/src/Pum/Bundle/CoreBundle/Form/TypeExtension/MaintenanceConfigTypeExtension.php new file mode 100644 index 00000000..7a2ea7f5 --- /dev/null +++ b/src/Pum/Bundle/CoreBundle/Form/TypeExtension/MaintenanceConfigTypeExtension.php @@ -0,0 +1,52 @@ +get('tabs') + ->add( + $builder->create('maintenance', 'pum_tab') + ->add('maintenance_mode', 'checkbox', array('required' => false)) + ->add('maintenance_restriction_ips', 'collection', array( + 'type' => 'text', + 'max_length' => 45, // IPv6 + 'allow_add' => true, + 'allow_delete' => true, + )) + ); + } + + /** + * {@inheritdoc} + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'translation_domain' => 'pum_form' + )); + } + + /** + * {@inheritdoc} + */ + public function getExtendedType() + { + return 'pum_config'; + } +} diff --git a/src/Pum/Bundle/CoreBundle/Resources/config/form.xml b/src/Pum/Bundle/CoreBundle/Resources/config/form.xml index c46da3d8..c555ff66 100644 --- a/src/Pum/Bundle/CoreBundle/Resources/config/form.xml +++ b/src/Pum/Bundle/CoreBundle/Resources/config/form.xml @@ -24,6 +24,10 @@ + + + + diff --git a/src/Pum/Bundle/CoreBundle/Resources/config/maintenance.xml b/src/Pum/Bundle/CoreBundle/Resources/config/maintenance.xml new file mode 100644 index 00000000..8a8b6266 --- /dev/null +++ b/src/Pum/Bundle/CoreBundle/Resources/config/maintenance.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + %pum_core.maintenance.template% + %pum_core.maintenance.whiteIps% + %pum_core.maintenance.whitePaths% + + + + + diff --git a/src/Pum/Bundle/CoreBundle/Resources/pum_views/maintenance.html.twig b/src/Pum/Bundle/CoreBundle/Resources/pum_views/maintenance.html.twig new file mode 100644 index 00000000..48e37879 --- /dev/null +++ b/src/Pum/Bundle/CoreBundle/Resources/pum_views/maintenance.html.twig @@ -0,0 +1,8 @@ + + + + + + Site en maintenance + + \ No newline at end of file