N°2060 [WIP] Initialisation of the portal application:

- Simplify PortalUrlMaker to avoid necessity to copy most of the code. Drawback: BC break, check migration notes.
- Fix multiple portal instances (in the same running process)
- Refactor portal constants into env. vars
- Fix cache path for services (ScopeValidator & LifecycleValidator)
- Change evalution order of the portal id ($_ENV['PORTAL_ID'] > $_GET('portal_id'] > PORTAL_ID)
This commit is contained in:
Molkobain
2019-07-08 15:44:39 +02:00
parent 322ea1870d
commit 5ab059c404
8 changed files with 235 additions and 157 deletions

View File

@@ -79,22 +79,18 @@ if ($_SERVER['APP_DEBUG']) {
}
}
// If PORTAL_ID is not already defined, we look for it in a parameter
if(!defined('PORTAL_ID'))
if(isset($_ENV['PORTAL_ID']))
{
// Retrieving portal id from request params
$sPortalId = utils::ReadParam('portal_id', '', true);
if ($sPortalId == '')
{
echo "Missing argument 'portal_id'";
exit;
}
// Defining portal constants
define('PORTAL_ID', $sPortalId);
// Nothing to do
}
else
// Note: Default value is set to "false" to differentiate an empty value from a non given parameter
elseif($sPortalId = utils::ReadParam('portal_id', false, true)) {
$_ENV['PORTAL_ID'] = $sPortalId;
}
elseif(defined('PORTAL_ID'))
{
$_ENV['PORTAL_ID'] = PORTAL_ID;
@trigger_error(
sprintf(
'Usage of legacy "PORTAL_ID" constant ("%s") is deprecated. You should pass "portal_id" in the URL as GET parameter.',
@@ -104,12 +100,16 @@ else
);
}
define('PORTAL_CACHE_PATH', utils::GetCachePath() . '/portals/' . PORTAL_ID . '/');
if(empty($_ENV['PORTAL_ID']))
{
echo "Missing argument 'portal_id'";
exit;
}
// Constants to be used in templates and others
define('COMBODO_CURRENT_ENVIRONMENT', utils::GetCurrentEnvironment());
define('COMBODO_ABSOLUTE_URL', utils::GetAbsoluteUrlAppRoot());
define('COMBODO_MODULES_ABSOLUTE_URL', utils::GetAbsoluteUrlModulesRoot());
define('COMBODO_PORTAL_BASE_ABSOLUTE_URL', utils::GetAbsoluteUrlModulesRoot() . 'itop-portal-base/portal/public/');
define('COMBODO_PORTAL_BASE_ABSOLUTE_PATH', MODULESROOT . '/itop-portal-base/portal/public/');
define('COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL', utils::GetAbsoluteUrlModulesRoot() . PORTAL_ID . '/');
// Env. vars to be used in templates and others
$_ENV['COMBODO_CURRENT_ENVIRONMENT'] = utils::GetCurrentEnvironment();
$_ENV['COMBODO_ABSOLUTE_URL'] = utils::GetAbsoluteUrlAppRoot();
$_ENV['COMBODO_MODULES_ABSOLUTE_URL'] = utils::GetAbsoluteUrlModulesRoot();
$_ENV['COMBODO_PORTAL_BASE_ABSOLUTE_URL'] = utils::GetAbsoluteUrlModulesRoot() . 'itop-portal-base/portal/public/';
$_ENV['COMBODO_PORTAL_BASE_ABSOLUTE_PATH'] = MODULESROOT . '/itop-portal-base/portal/public/';
$_ENV['COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL'] = utils::GetAbsoluteUrlModulesRoot() . $_ENV['PORTAL_ID'] . '/';

View File

@@ -27,7 +27,7 @@ use Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfig
// Note: ModuleDesign service is not available yet as this script is processed before service generation,
// that's why we have to instantiate it manually.
$oModuleDesign = new ModuleDesign(PORTAL_ID);
$oModuleDesign = new ModuleDesign($_ENV['PORTAL_ID']);
// TODO: The following code needs to be refactored to more independent and atomic services.
@@ -44,20 +44,20 @@ $oListsCompat = new Lists($oModuleDesign);
$oListsCompat->Process($container);
// - Generating CSS files
$aImportPaths = array(COMBODO_PORTAL_BASE_ABSOLUTE_PATH.'css/');
$aImportPaths = array($_ENV['COMBODO_PORTAL_BASE_ABSOLUTE_PATH'].'css/');
$aPortalConf = $container->getParameter('combodo.portal.instance.conf');
foreach ($aPortalConf['properties']['themes'] as $key => $value)
{
if (!is_array($value))
{
$aPortalConf['properties']['themes'][$key] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value, $aImportPaths);
$aPortalConf['properties']['themes'][$key] = $_ENV['COMBODO_ABSOLUTE_URL'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value, $aImportPaths);
}
else
{
$aValues = array();
foreach ($value as $sSubValue)
{
$aValues[] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubValue, $aImportPaths);
$aValues[] = $_ENV['COMBODO_ABSOLUTE_URL'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubValue, $aImportPaths);
}
$aPortalConf['properties']['themes'][$key] = $aValues;
}

View File

@@ -28,14 +28,14 @@ parameters:
router.options.generator_base_class: Combodo\iTop\Portal\Routing\UrlGenerator
# Used in templates
combodo.current_environment: !php/const COMBODO_CURRENT_ENVIRONMENT
combodo.absolute_url: !php/const COMBODO_ABSOLUTE_URL
combodo.modules.absolute_url: !php/const COMBODO_MODULES_ABSOLUTE_URL
combodo.current_environment: '%env(string:COMBODO_CURRENT_ENVIRONMENT)%'
combodo.absolute_url: '%env(string:COMBODO_ABSOLUTE_URL)%'
combodo.modules.absolute_url: '%env(string:COMBODO_MODULES_ABSOLUTE_URL)%'
combodo.modules.absolute_path: !php/const MODULESROOT
combodo.portal.base.absolute_url: !php/const COMBODO_PORTAL_BASE_ABSOLUTE_URL
combodo.portal.base.absolute_path: !php/const COMBODO_PORTAL_BASE_ABSOLUTE_PATH
combodo.portal.instance.absolute_url: !php/const COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL
combodo.portal.instance.id: !php/const PORTAL_ID
combodo.portal.base.absolute_url: '%env(string:COMBODO_PORTAL_BASE_ABSOLUTE_URL)%'
combodo.portal.base.absolute_path: '%env(string:COMBODO_PORTAL_BASE_ABSOLUTE_PATH)%'
combodo.portal.instance.absolute_url: '%env(string:COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL)%'
combodo.portal.instance.id: '%env(string:PORTAL_ID)%'
services:
# Default configuration for services in *this* file
@@ -47,10 +47,10 @@ services:
# The best practice is to be explicit about your dependencies anyway.
bind:
$bDebug: '%kernel.debug%'
$sPortalCachePath: !php/const PORTAL_CACHE_PATH
$sPortalId: !php/const PORTAL_ID
$sPortalCachePath: '%kernel.cache_dir%/'
$sPortalId: '%env(string:PORTAL_ID)%'
$aCombodoPortalInstanceConf: '%combodo.portal.instance.conf%'
$sCombodoPortalInstanceAbsoluteUrl: !php/const COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL
$sCombodoPortalInstanceAbsoluteUrl: '%env(string:COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL)%'
# Makes classes in src/ available to be used as services
# This creates a service per class whose id is the fully-qualified class name

View File

@@ -78,7 +78,7 @@ class Basic extends AbstractConfiguration
{
$aPortalConf = array(
'properties' => array(
'id' => PORTAL_ID,
'id' => $_ENV['PORTAL_ID'],
'name' => 'Page:DefaultTitle',
'logo' => (file_exists(MODULESROOT.'branding/portal-logo.png')) ? utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png' : '../images/logo-itop-dark-bg.svg',
'themes' => array(

View File

@@ -41,9 +41,9 @@ class UserProvider implements ContainerAwareInterface
{
/** @var \ModuleDesign */
private $oModuleDesign;
/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
/** @var string $sPortalId */
private $sPortalId;
/** @var \Symfony\Component\DependencyInjection\ContainerInterface */
private $container;
/**
@@ -52,9 +52,10 @@ class UserProvider implements ContainerAwareInterface
* @param \ModuleDesign $oModuleDesign
* @param \User $oUser
*/
public function __construct(ModuleDesign $oModuleDesign)
public function __construct(ModuleDesign $oModuleDesign, $sPortalId)
{
$this->oModuleDesign = $oModuleDesign;
$this->sPortalId = $sPortalId;
}
/**
@@ -68,7 +69,7 @@ class UserProvider implements ContainerAwareInterface
// Note: At this point the Exception handler is not registered, so we can't use $oApp::abort() method, hence the die().
// - Checking user rights and prompt if needed (401 HTTP code returned if XHR request)
$iExitMethod = ($oGetResponseEvent->getRequest()->isXmlHttpRequest()) ? LoginWebPage::EXIT_RETURN : LoginWebPage::EXIT_PROMPT;
$iLogonRes = LoginWebPage::DoLoginEx(PORTAL_ID, false, $iExitMethod);
$iLogonRes = LoginWebPage::DoLoginEx($this->sPortalId, false, $iExitMethod);
if( ($iExitMethod === LoginWebPage::EXIT_RETURN) && ($iLogonRes != 0) )
{
die(Dict::S('Portal:ErrorUserLoggedOut'));
@@ -90,6 +91,8 @@ class UserProvider implements ContainerAwareInterface
/**
* Sets the container.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface|null $container
*/
public function setContainer(ContainerInterface $container = null)
{

View File

@@ -18,14 +18,14 @@ class Kernel extends BaseKernel
public function getCacheDir()
{
$cacheDir = PORTAL_ID . '-' . $this->environment;
$cacheDir = $_ENV['PORTAL_ID'] . '-' . $this->environment;
return utils::GetCachePath() . "/portals/$cacheDir";
}
public function getLogDir()
{
$logDir = PORTAL_ID . '-' . $this->environment;
$logDir = $_ENV['PORTAL_ID'] . '-' . $this->environment;
return utils::GetLogPath() . "/portals/$logDir";
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* Copyright (C) 2013-2019 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*
*
*/
namespace Combodo\iTop\Portal\UrlMaker;
use Combodo\iTop\Portal\Kernel;
use ContextTag;
use CoreException;
use Exception;
use iDBObjectURLMaker;
use utils;
/**
* AbstractPortalUrlMaker
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @author Bruno Da Silva <bruno.dasilva@combodo.com>
* @since 2.3.0
*/
abstract class AbstractPortalUrlMaker implements iDBObjectURLMaker
{
/** @var string PORTAL_ID */
const PORTAL_ID = '';
/** @var \Combodo\iTop\Portal\Kernel[] $aKernels */
private static $aKernels = array();
/**
* Generate an (absolute) URL to an object, either in view or edit mode.
* Returns null if the current user is not allowed to view / edit object.
*
* @param string $sClass The class of the object
* @param int $iId The identifier of the object
* @param string $sMode edit|view
*
* @return string | null
*
* @throws Exception
* @throws CoreException
*/
public static function PrepareObjectURL($sClass, $iId, $sMode)
{
$sPreviousPortalId = (isset($_ENV['PORTAL_ID'])) ? $_ENV['PORTAL_ID'] : '';
$_ENV['PORTAL_ID'] = static::PORTAL_ID;
require MODULESROOT . 'itop-portal-base/portal/config/bootstrap.php';
$sUrl = self::DoPrepareObjectURL($sClass, $iId, $sMode);
if(!empty($sPreviousPortalId))
{
$_ENV['PORTAL_ID'] = $sPreviousPortalId;
}
return $sUrl;
}
/**
* @param string $sClass
* @param int $iId
* @param string $sMode
*
* @return string|null
* @throws \CoreException
*/
private static function DoPrepareObjectURL($sClass, $iId, $sMode)
{
$oKernel = self::GetKernelInstance();
$oContainer = $oKernel->getContainer();
/** @var string $sPortalId */
$sPortalId = $oContainer->getParameter('combodo.portal.instance.id');
/** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */
$oUrlGenerator = $oContainer->get('url_generator');
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $oContainer->get('security_helper');
// The object is reachable in the specified mode (edit/view)
//
// Note: Scopes only apply when URL check is triggered from the portal GUI.
$sObjectQueryString = null;
switch ($sMode)
{
case 'view':
if (!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
break;
case 'edit':
default:
// Checking if user is allowed to edit object, if not we check if it can at least view it.
if (!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
elseif (!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
break;
}
$sPortalAbsoluteUrl = utils::GetAbsoluteUrlModulePage('itop-portal-base', 'index.php', array('portal_id' => $sPortalId));
if ($sObjectQueryString === null)
{
$sUrl = null;
}
elseif (strpos($sPortalAbsoluteUrl, '?') !== false)
{
// Removing generated url query parameters so it can be replaced with those from the absolute url
// Mostly necessary when iTop instance has multiple portals
if (strpos($sObjectQueryString, '?') !== false)
{
$sObjectQueryString = substr($sObjectQueryString, 0, strpos($sObjectQueryString, '?'));
}
$sUrl = substr($sPortalAbsoluteUrl, 0, strpos($sPortalAbsoluteUrl, '?')).$sObjectQueryString.substr($sPortalAbsoluteUrl,
strpos($sPortalAbsoluteUrl, '?'));
}
else
{
$sUrl = $sPortalAbsoluteUrl.$sObjectQueryString;
}
return $sUrl;
}
/**
* Returns the kernel singleton
*
* @return \Combodo\iTop\Portal\Kernel
* @since 2.7.0
*/
private static function GetKernelInstance()
{
if(!array_key_exists(static::PORTAL_ID, self::$aKernels))
{
self::$aKernels[static::PORTAL_ID] = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
self::$aKernels[static::PORTAL_ID]->boot();
}
return self::$aKernels[static::PORTAL_ID];
}
/**
* @param $sClass
* @param $iId
*
* @return null|string
*
* @throws CoreException
*/
public static function MakeObjectURL($sClass, $iId)
{
return static::PrepareObjectURL($sClass, $iId, 'edit');
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* Copyright (C) 2013-2019 Combodo SARL
*
@@ -20,7 +19,9 @@
*
*/
use Combodo\iTop\Portal\Kernel;
use Combodo\iTop\Portal\UrlMaker\AbstractPortalUrlMaker;
require_once APPROOT.'/lib/composer-vendor/autoload.php';
/**
* iTopPortalEditUrlMaker
@@ -29,119 +30,10 @@ use Combodo\iTop\Portal\Kernel;
* @author Bruno Da Silva <bruno.dasilva@combodo.com>
* @since 2.3.0
*/
class iTopPortalEditUrlMaker implements iDBObjectURLMaker
class iTopPortalEditUrlMaker extends AbstractPortalUrlMaker
{
private static $oKernel;
/**
* Generate an (absolute) URL to an object, either in view or edit mode.
* Returns null if the current user is not allowed to view / edit object.
*
* @param string $sClass The class of the object
* @param int $iId The identifier of the object
* @param string $sMode edit|view
*
* @return string | null
*
* @throws Exception
* @throws CoreException
*/
public static function PrepareObjectURL($sClass, $iId, $sMode)
{
require_once APPROOT . 'lib/composer-vendor/autoload.php';
require_once MODULESROOT . 'itop-portal-base/portal/config/bootstrap.php';
$oKernel = self::GetKernelInstance();
$oContainer = $oKernel->getContainer();
/** @var string $sPortalId */
$sPortalId = $oContainer->getParameter('combodo.portal.instance.id');
/** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */
$oUrlGenerator = $oContainer->get('url_generator');
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
$oSecurityHelper = $oContainer->get('security_helper');
// The object is reachable in the specified mode (edit/view)
//
// Note: Scopes only apply when URL check is triggered from the portal GUI.
$sObjectQueryString = null;
switch($sMode)
{
case 'view':
if(!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
break;
case 'edit':
default:
// Checking if user is allowed to edit object, if not we check if it can at least view it.
if(!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
elseif(!ContextTag::Check('GUI:Portal') || $oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sClass, $iId))
{
$sObjectQueryString = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $sClass, 'sObjectId' => $iId));
}
break;
}
$sPortalAbsoluteUrl = utils::GetAbsoluteUrlModulePage($sPortalId, 'index.php');
if($sObjectQueryString === null)
{
$sUrl = null;
}
elseif (strpos($sPortalAbsoluteUrl, '?') !== false)
{
// Removing generated url query parameters so it can be replaced with those from the absolute url
// Mostly necessary when iTop instance has multiple portals
if(strpos($sObjectQueryString, '?') !== false)
{
$sObjectQueryString = substr($sObjectQueryString, 0, strpos($sObjectQueryString, '?'));
}
$sUrl = substr($sPortalAbsoluteUrl, 0, strpos($sPortalAbsoluteUrl, '?')).$sObjectQueryString.substr($sPortalAbsoluteUrl, strpos($sPortalAbsoluteUrl, '?'));
}
else
{
$sUrl = $sPortalAbsoluteUrl.$sObjectQueryString;
}
return $sUrl;
}
/**
* @param $sClass
* @param $iId
*
* @return null|string
*
* @throws CoreException
*/
public static function MakeObjectURL($sClass, $iId)
{
return static::PrepareObjectURL($sClass, $iId, 'edit');
}
/**
* Returns the kernel singleton
*
* @return \Combodo\iTop\Portal\Kernel
* @since 2.7.0
*/
private static function GetKernelInstance()
{
if(self::$oKernel === null)
{
self::$oKernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
self::$oKernel->boot();
}
return self::$oKernel;
}
/** @var string PORTAL_ID */
const PORTAL_ID = 'itop-portal';
}
/**
@@ -153,6 +45,9 @@ class iTopPortalEditUrlMaker implements iDBObjectURLMaker
*/
class iTopPortalViewUrlMaker extends iTopPortalEditUrlMaker
{
/**
* @inheritDoc
*/
public static function MakeObjectURL($sClass, $iId)
{
return static::PrepareObjectURL($sClass, $iId, 'view');