瀏覽代碼

Improved user rights management: SELECT filtered on objects authorized for the current user (not yet fully implemented)

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@681 a333f486-631f-4898-b8df-5754b55c2be0
romainq 15 年之前
父節點
當前提交
4e549d589d

+ 6 - 0
addons/userrights/userrightsmatrix.class.inc.php

@@ -274,6 +274,12 @@ class UserRightsMatrix extends UserRightsAddOnAPI
 		return $oNullFilter;
 	}
 
+	public function GetSelectFilter($oUser, $sClass)
+	{
+		$oNullFilter  = new DBObjectSearch($sClass);
+		return $oNullFilter;
+	}
+
 	public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
 	{
 		if (!array_key_exists($iActionCode, self::$m_aActionCodes))

+ 6 - 0
addons/userrights/userrightsnull.class.inc.php

@@ -53,6 +53,12 @@ class UserRightsNull extends UserRightsAddOnAPI
 		return $oNullFilter;
 	}
 
+	public function GetSelectFilter($oUser, $sClass)
+	{
+		$oNullFilter  = new DBObjectSearch($sClass);
+		return $oNullFilter;
+	}
+
 	public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
 	{
 		return UR_ALLOWED_YES;

+ 138 - 19
addons/userrights/userrightsprofile.class.inc.php

@@ -228,7 +228,7 @@ class URP_Dimensions extends UserRightsBaseClass
 			// Evaluate wether it is a constant or not
 			try
 			{
-				$oObjectSearch = DBObjectSearch::FromOQL($sExpression);
+				$oObjectSearch = DBObjectSearch::FromOQL_AllData($sExpression);
 
 				$sTargetClass = $oObjectSearch->GetClass();
 			}
@@ -366,7 +366,7 @@ class URP_ProfileProjection extends UserRightsBaseClass
 			}
 			
 		}
-		elseif ($sExpr == '<any>')
+		elseif (($sExpr == '<any>') || ($sExpr == ''))
 		{
 			$aRes = null;
 		}
@@ -374,7 +374,7 @@ class URP_ProfileProjection extends UserRightsBaseClass
 		{ 
 			$sColumn = $this->Get('attribute');
 			// SELECT...
-			$oValueSetDef = new ValueSetObjects($sExpr, $sColumn);
+			$oValueSetDef = new ValueSetObjects($sExpr, $sColumn, array(), true /*allow all data*/);
 			$aRes = $oValueSetDef->GetValues(array('user' => $oUser), '');
 		}
 		else
@@ -437,7 +437,7 @@ class URP_ClassProjection extends UserRightsBaseClass
 			}
 			
 		}
-		elseif ($sExpr == '<any>')
+		elseif (($sExpr == '<any>') || ($sExpr == ''))
 		{
 			$aRes = null;
 		}
@@ -445,13 +445,9 @@ class URP_ClassProjection extends UserRightsBaseClass
 		{ 
 			$sColumn = $this->Get('attribute');
 			// SELECT...
-			$oValueSetDef = new ValueSetObjects($sExpr, $sColumn);
+			$oValueSetDef = new ValueSetObjects($sExpr, $sColumn, array(), true /*allow all data*/);
 			$aRes = $oValueSetDef->GetValues(array('this' => $oObject), '');
 		}
-		elseif ($sExpr == '<any>')
-		{
-			$aRes = null;
-		}
 		else
 		{
 			// Constant value(s)
@@ -459,6 +455,7 @@ class URP_ClassProjection extends UserRightsBaseClass
 		}
 		return $aRes;
 	}
+
 }
 
 
@@ -678,28 +675,28 @@ class UserRightsProfile extends UserRightsAddOnAPI
 	{
 		// Could be loaded in a shared memory (?)
 
-		$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions"));
+		$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Dimensions"));
 		$this->m_aDimensions = array(); 
 		while ($oDimension = $oDimensionSet->Fetch())
 		{
 			$this->m_aDimensions[$oDimension->GetKey()] = $oDimension; 
 		}
 		
-		$oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection"));
+		$oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_ClassProjection"));
 		$this->m_aClassProjs = array(); 
 		while ($oClassProj = $oClassProjSet->Fetch())
 		{
 			$this->m_aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj; 
 		}
 
-		$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles"));
+		$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
 		$this->m_aProfiles = array(); 
 		while ($oProfile = $oProfileSet->Fetch())
 		{
 			$this->m_aProfiles[$oProfile->GetKey()] = $oProfile; 
 		}
 
-		$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile"));
+		$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserProfile"));
 		$this->m_aUserProfiles = array();
 		$this->m_aAdmins = array();
 		while ($oUserProfile = $oUserProfileSet->Fetch())
@@ -711,7 +708,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
 			}
 		}
 
-		$oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection"));
+		$oProProSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_ProfileProjection"));
 		$this->m_aProPros = array(); 
 		while ($oProPro = $oProProSet->Fetch())
 		{
@@ -738,6 +735,90 @@ exit;
 		return $oNullFilter;
 	}
 
+	public function GetSelectFilter($oUser, $sClass)
+	{
+		$aConditions = array();
+		foreach ($this->m_aDimensions as $iDimension => $oDimension)
+		{
+			$oClassProj = @$this->m_aClassProjs[$sClass][$iDimension];
+			if (is_null($oClassProj))
+			{
+				// Authorize any for this dimension, then no additional criteria is required
+				continue;
+			}
+ 
+			// 1 - Get class projection info
+			//
+			$oExpression = null;
+			$sExpr = $oClassProj->Get('value');
+			if ($sExpr == '<this>')
+			{
+				$sColumn = $oClassProj->Get('attribute');
+				if (empty($sColumn))
+				{
+					$oExpression = new FieldExpression('id', $sClass);
+				}
+				else
+				{
+					$oExpression = new FieldExpression($sColumn, $sClass);
+				}
+			}
+			elseif (($sExpr == '<any>') || ($sExpr == ''))
+			{
+				// Authorize any for this dimension, then no additional criteria is required
+				continue;
+			}
+			elseif (strtolower(substr($sExpr, 0, 6)) == 'select')
+			{
+				throw new CoreException('Sorry, projections by the mean of OQL are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr)); 
+			}
+			else
+			{
+				// Constant value(s)
+				// unsupported
+				throw new CoreException('Sorry, constant projections are not supported currently, please specify an attribute instead', array('class' => $sClass, 'expression' => $sExpr)); 
+//				$aRes = explode(';', trim($sExpr));
+			}
+
+			// 2 - Get profile projection info and use it if needed
+			//
+			$aProjections = self::GetReadableProjectionsByDim($oUser, $sClass, $oDimension);
+			if (is_null($aProjections))
+			{
+				// Authorize any for this dimension, then no additional criteria is required
+				continue;
+			}
+			elseif (count($aProjections) == 0)
+			{
+				// Authorize none, then exit as quickly as possible
+				return false;
+			}
+			else
+			{
+				// Authorize the given set of values
+				$oListExpr = ListExpression::FromScalars($aProjections);
+				$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr);
+				$aConditions[] = $oCondition;
+			}
+		}
+
+		if (count($aConditions) == 0)
+		{
+			// allow all
+			return true;
+		}
+		else
+		{
+			$oFilter  = new DBObjectSearch($sClass);
+			foreach($aConditions as $oCondition)
+			{
+				$oFilter->AddConditionExpression($oCondition);
+			}
+			//return true;
+			return $oFilter;
+		}
+	}
+
 	// This verb has been made public to allow the development of an accurate feedback for the current configuration
 	public function GetClassActionGrant($iProfile, $sClass, $sAction)
 	{
@@ -747,7 +828,7 @@ exit;
 		}
 
 		// Get the permission for this profile/class/action
-		$oSearch = DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile AND permission = 'yes'");
+		$oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile AND permission = 'yes'");
 		$oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfile));
 		if ($oSet->Count() >= 1)
 		{
@@ -802,7 +883,7 @@ exit;
 
 				// update the list of attributes with those allowed for this profile
 				//
-				$oSearch = DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid");
+				$oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid");
 				$oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $oGrantRecord->GetKey()));
 				$aProfileAttributes = $oSet->GetColumnAsArray('attcode', false);
 				if (count($aProfileAttributes) == 0)
@@ -928,7 +1009,7 @@ exit;
 		}
 
 		// Get the permission for this profile/class/stimulus
-		$oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'");
+		$oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'");
 		$oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile));
 		if ($oSet->Count() >= 1)
 		{
@@ -999,6 +1080,44 @@ exit;
 		}
 	}
 
+	// Copied from GetMatchingProfilesByDim()
+	// adapted to the optimized implementation of GetSelectFilter()
+	// Note: shares the cache m_aProPros with GetMatchingProfilesByDim()
+	// Returns   null if any object is readable
+	//           an array of allowed projections otherwise (could be an empty array if none is allowed)
+	protected function GetReadableProjectionsByDim($oUser, $sClass, $oDimension)
+	{
+		//
+		// Given a dimension, lists the values for which the user will be allowed to read the objects
+		//
+		$iUser = $oUser->GetKey();
+		$iDimension = $oDimension->GetKey();
+
+		$aRes = array();
+		if (array_key_exists($iUser, $this->m_aUserProfiles))
+		{
+			foreach ($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile)
+			{
+				// user projection to be cached on a given page !
+				if (!array_key_exists($iDimension, $this->m_aProPros[$iProfile]))
+				{
+					// No projection for a given profile: default to 'any'
+					return null;
+				}
+
+				$aUserProjection = $this->m_aProPros[$iProfile][$iDimension]->ProjectUser($oUser);
+				if (is_null($aUserProjection))
+				{
+					// No projection for a given profile: default to 'any'
+					return null;
+				}
+				$aRes = array_merge($aRes, $aUserProjection);
+			}
+		}
+		return $aRes;
+	}
+
+	// Note: shares the cache m_aProPros with GetReadableProjectionsByDim()
 	protected function GetMatchingProfilesByDim($oUser, $oObject, $oDimension)
 	{
 		//
@@ -1245,7 +1364,7 @@ class SetupProfiles
 	
 		// Project in every dimension
 		//
-		$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions"));
+		$oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Dimensions"));
 		while ($oDimension = $oDimensionSet->Fetch())
 		{
 			$iDimension = $oDimension->GetKey();
@@ -1393,7 +1512,7 @@ class SetupProfiles
 			'Problem' => MetaModel::GetClasses('problemmgmt'),
 			'Change' => MetaModel::GetClasses('changemgmt'),
 			'Service' => MetaModel::GetClasses('servicemgmt'),
-			'Call' => MetaModel::GetClasses('callmgmt'),
+			'Call' => MetaModel::GetClasses('requestmgmt'),
 			'KnownError' => MetaModel::GetClasses('knownerrormgmt'),
 		);
 		

+ 1 - 1
application/user.preferences.class.inc.php

@@ -143,7 +143,7 @@ class appUserPreferences extends DBObject
 	{
 		$aParams = array
 		(
-			"category" => "gui",
+			"category" => "gui,alwaysreadable",
 			"key_type" => "autoincrement",
 			"name_attcode" => "userid",
 			"state_attcode" => "",

+ 24 - 0
core/dbobjectsearch.class.php

@@ -34,6 +34,10 @@ class DBObjectSearch
 	private $m_aReferencedBy;
 	private $m_aRelatedTo;
 
+	// By default, some information may be hidden to the current user
+	// But it may happen that we need to disable that feature
+	private $m_bAllowAllData = false;
+
 	public function __construct($sClass, $sClassAlias = null)
 	{
 		if (is_null($sClassAlias)) $sClassAlias = $sClass;
@@ -51,6 +55,9 @@ class DBObjectSearch
 		$this->m_aRelatedTo = array();
 	}
 
+	public function AllowAllData() {$this->m_bAllowAllData = true;}
+	public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
+
 	public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
 	public function GetJoinedClasses() {return $this->m_aClasses;}
 
@@ -681,8 +688,25 @@ class DBObjectSearch
 		}
 	}
 
+	// Create a search definition that leads to 0 result, still a valid search object
+	static public function FromEmptySet($sClass)
+	{
+		$oResultFilter = new DBObjectSearch($sClass);
+		$oResultFilter->m_oSearchCondition = new FalseExpression;
+		return $oResultFilter;
+	}
+
 	static protected $m_aOQLQueries = array();
 
+	// Do not filter out depending on user rights
+	// In particular when we are currently in the process of evaluating the user rights...
+	static public function FromOQL_AllData($sQuery)
+	{
+		$oRes = self::FromOQL($sQuery);
+		$oRes->AllowAllData();
+		return $oRes;
+	}
+
 	static public function FromOQL($sQuery)
 	{
 		if (empty($sQuery)) return null;

+ 23 - 0
core/expression.class.inc.php

@@ -230,6 +230,19 @@ class TrueExpression extends ScalarExpression
 	}
 }
 
+class FalseExpression extends ScalarExpression
+{
+	public function __construct()
+	{
+		parent::__construct(0);
+	}
+
+	public function IsTrue()
+	{
+		return false;
+	}
+}
+
 class FieldExpression extends UnaryExpression
 {
 	protected $m_sParent;
@@ -348,6 +361,16 @@ class ListExpression extends Expression
 		$this->m_aExpressions = $aExpressions;
 	}
 
+	public static function FromScalars($aScalars)
+	{
+		$aExpressions = array();
+		foreach($aScalars as $value)
+		{
+			$aExpressions[] = new ScalarExpression($value);
+		}
+		return new ListExpression($aExpressions);
+	}
+
 	public function IsTrue()
 	{
 		// return true if we are certain that it will be true

+ 21 - 0
core/metamodel.class.php

@@ -1509,6 +1509,27 @@ abstract class MetaModel
 
 	public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array())
 	{
+		// Hide objects that are not visible to the current user
+		//
+		if (!$oFilter->IsAllDataAllowed())
+		{
+			$oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass());
+			if ($oVisibleObjects === false)
+			{
+				// Make sure this is a valid search object, saying NO for all
+				$oVisibleObjects = DBObjectSearch::FromEmptySet($oFilter->GetClass());
+			}
+			if (is_object($oVisibleObjects))
+			{
+				$oFilter->MergeWith($oVisibleObjects);
+			}
+			else
+			{
+				// should be true at this point, meaning that no additional filtering
+				// is required
+			}
+		}
+
 		// Query caching
 		//
 		$bQueryCacheEnabled = true;

+ 29 - 0
core/userrights.class.inc.php

@@ -57,6 +57,8 @@ abstract class UserRightsAddOnAPI
 
 	// Cf UserContext...
 	abstract public function GetFilter($sLogin, $sClass); // returns a filter object
+	// Used to build select queries showing only objects visible for the given user
+	abstract public function GetSelectFilter($sLogin, $sClass); // returns a filter object
 
 	abstract public function IsActionAllowed($oUser, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null);
 	abstract public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null);
@@ -494,6 +496,9 @@ class UserRights
 
 	public static function GetFilter($sClass)
 	{
+		// #@# to cleanup !
+		return new DBObjectSearch($sClass);
+
 		if (!self::CheckLogin()) return false;
 		if (self::IsAdministrator()) return new DBObjectSearch($sClass);
 
@@ -506,11 +511,35 @@ class UserRights
 		return self::$m_oAddOn->GetFilter(self::$m_oUser->GetKey(), $sClass);
 	}
 
+	public static function GetSelectFilter($sClass)
+	{
+		// Need to load some records before the login is performed (user preferences)
+		if (MetaModel::HasCategory($sClass, 'alwaysreadable')) return true;
+
+		// ne marche pas... pourquoi?
+		//if (!self::CheckLogin()) return false;
+
+		if (self::IsAdministrator()) return true;
+
+		// this module is forbidden for non admins.... BUT I NEED IT HERE TO DETERMINE USER RIGHTS
+		if (MetaModel::HasCategory($sClass, 'addon/userrights')) return true;
+
+		// the rest is allowed (#@# to be improved)
+		if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true;
+
+		return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass);
+	}
+
 	public static function IsActionAllowed($sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
 	{
 		if (!self::CheckLogin()) return false;
 		if (self::IsAdministrator($oUser)) return true;
 
+
+		// #@# Temporary?????
+		// The read access is controlled in MetaModel::MakeSelectQuery()
+		if ($iActionCode == UR_ACTION_READ) return true;
+
 		// this module is forbidden for non admins
 		if (MetaModel::HasCategory($sClass, 'addon/userrights')) return false;
 

+ 11 - 2
core/valuesetdef.class.inc.php

@@ -93,19 +93,28 @@ class ValueSetObjects extends ValueSetDefinition
 	protected $m_sFilterExpr; // in OQL
 	protected $m_sValueAttCode;
 	protected $m_aOrderBy;
+	private $m_bAllowAllData;
 
-	public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array())
+	public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false)
 	{
 		$this->m_sFilterExpr = $sFilterExp;
 		$this->m_sValueAttCode = $sValueAttCode;
 		$this->m_aOrderBy = $aOrderBy;
+		$this->m_bAllowAllData = $bAllowAllData;
 	}
 
 	protected function LoadValues($aArgs)
 	{
 		$this->m_aValues = array();
 		
-		$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr, $aArgs);
+		if ($this->m_bAllowAllData)
+		{
+			$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr, $aArgs);
+		}
+		else
+		{
+			$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr, $aArgs);
+		}
 		if (!$oFilter) return false;
 
 		$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);

+ 1 - 1
pages/usermanagement_profileproj.php

@@ -72,7 +72,7 @@ function ComputeProjections($oPage)
 	// Load users, and create a record per couple user/profile
 	//
 	$aDisplayData = array();
-	$oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users"));
+	$oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("User"));
 	while ($oUser = $oUserSet->Fetch())
 	{
 		$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile WHERE userid = :user->id"), array(), array('user' => $oUser));

+ 1 - 1
pages/usermanagement_userstatus.php

@@ -305,7 +305,7 @@ else
 	$oPage->p('<p>If one profile says YES, then the answer is YES</p>');
 	
 
-	$oUser = MetaModel::GetObject('URP_Users', $iUser);
+	$oUser = MetaModel::GetObject('User', $iUser);
 
 	$oPage->p('<h2>Projections for user '.$oUser->GetName().'</h2>');
 	ComputeUserProjections($oPage, $oUser);

+ 405 - 26
setup/benchmark.php

@@ -33,13 +33,16 @@ require_once('./setuppage.class.inc.php');
 
 class BenchmarkDataCreation
 {
+	var $m_iIfByServer;
+	var $m_iIfByNWDevice;
 	var $m_aRequested;
 	var $m_aPlanned;
-	var $m_aCreated = array();
+	var $m_aCreatedByClass = array();
+	var $m_aCreatedByDesc = array();
 
 	var $m_aStatsByClass = array();
 
-	public function __construct($iPlannedCIs, $iPlannedContacts, $iPlannedContracts)
+	public function __construct($iPlannedCIs = 0, $iPlannedContacts = 0, $iPlannedContracts = 0)
 	{
 		$this->m_aRequested = array(
 			'CIs' => $iPlannedCIs,
@@ -47,10 +50,27 @@ class BenchmarkDataCreation
 			'Contracts' => $iPlannedContracts,
 		);
 
+		$this->m_iIfByServer = 2;
+		$this->m_iIfByNWDevice = 10;
+
+		$iServers = ceil($iPlannedCIs * 9 / 10);
+		$iNWDevices = ceil($iPlannedCIs / 10);
+		$iBigTicketCIs = ceil($iPlannedCIs / 10);
+		$iInterfaces = $iServers * $this->m_iIfByServer + $iNWDevices * $this->m_iIfByNWDevice;
+		$iApplications = $iServers * 10;
+		$iSolutions = $iApplications;
+		$iProcesses = $iSolutions * 2;
+
 		$this->m_aPlanned = array(
-			'Network devices' => ceil($iPlannedCIs / 2),
-			'Servers' => ceil($iPlannedCIs / 2),
-			'Interfaces' => 10 * $iPlannedCIs,
+			'Network devices' => $iNWDevices,
+			'Servers' => $iServers,
+			'Big ticket: CIs' => $iBigTicketCIs,
+			'Interfaces' => $iInterfaces,
+			'Application SW' => 2,
+			'Applications' => $iApplications,
+			'Solutions' => $iSolutions,
+			'Processes' => $iProcesses,
+
 			'Contacts' => $iPlannedContacts,
 			'Contracts' => $iPlannedContracts,
 			'Incidents' => 2 * 12 * $iPlannedCIs,
@@ -70,7 +90,7 @@ class BenchmarkDataCreation
 		return $this->m_aRequested;
 	}
 
-	protected function CreateObject($sClass, $aData, $oChange)
+	protected function CreateObject($sClass, $aData, $oChange, $sClassDesc = '')
 	{
 		$mu_t1 = MyHelpers::getmicrotime();
 
@@ -82,7 +102,9 @@ class BenchmarkDataCreation
 
 		$iId = $oMyObject->DBInsertTrackedNoReload($oChange);
 
-		$this->m_aCreated[$sClass][] = $iId;
+		$sClassId = "$sClass ($sClassDesc)";
+		$this->m_aCreatedByDesc[$sClassId][] = $iId;
+		$this->m_aCreatedByClass[$sClass][] = $iId;
 
 		$mu_t2 = MyHelpers::getmicrotime();
 		$this->m_aStatsByClass[$sClass][] = $mu_t2 - $mu_t1;
@@ -90,9 +112,10 @@ class BenchmarkDataCreation
 		return $iId;
 	}
 
-	protected function RandomId($sClass)
+	protected function RandomId($sClass, $sClassDesc = '')
 	{
-		return $this->m_aCreated[$sClass][array_rand($this->m_aCreated[$sClass])];
+		$sClassId = "$sClass ($sClassDesc)";
+		return $this->m_aCreatedByDesc[$sClassId][array_rand($this->m_aCreatedByDesc[$sClassId])];
 	}
 
 	static protected function FindId($sClass)
@@ -121,7 +144,7 @@ class BenchmarkDataCreation
 
 	protected function MakeFeedback($oP, $sClass)
 	{
-		$iSample = reset($this->m_aCreated[$sClass]);
+		$iSample = reset($this->m_aCreatedByClass[$sClass]);
 		$sSample = "<a href=\"../pages/UI.php?operation=details&class=$sClass&id=$iSample\">sample</a>";
 
 		$iDuration = number_format(array_sum($this->m_aStatsByClass[$sClass]), 3);
@@ -131,13 +154,156 @@ class BenchmarkDataCreation
 
 		$oP->add("<ul>");
 		$oP->add("<li>");
-		$oP->add("$sClass: ".count($this->m_aCreated[$sClass])." - $sSample<br/>");
+		$oP->add("$sClass: ".count($this->m_aStatsByClass[$sClass])." - $sSample<br/>");
 		$oP->add("Duration: $fDurationMin =&gt; $fDurationMax; Avg:$fDurationAverage; Total: $iDuration");
 		$oP->add("</li>");
 		$oP->add("</ul>");
 	}
 
-	public function GoProjections(WebPage $oP, $oChange)
+	public function GoProjectionsOrganization(WebPage $oP, $oChange)
+	{
+		$aClasses = MetaModel::GetClasses();
+		$aActions = array('Read', 'Bulk Read', 'Delete', 'Bulk Delete', 'Modify', 'Bulk Modify');
+		$aStdProfiles = array(2, 3, 4, 5, 6, 7, 8, 9);
+
+		////////////////////////////////////////
+		// Dimension: Organization
+		//
+		$aData = array(
+			'name' => 'organization',
+			'description' => '',
+			'type' => 'Organization',
+		);
+		$iDimLocation = $this->CreateObject('URP_Dimensions', $aData, $oChange);
+
+		////////////////////////////////////////
+		// New specific profile, given access to everything
+		//
+		$aData = array(
+			'name' => 'data guru',
+			'description' => 'could do anything, because everything is granted',
+		);
+		$iGuruProfile = $this->CreateObject('URP_Profiles', $aData, $oChange);
+		foreach($aClasses as $sClass)
+		{
+			foreach($aActions as $sAction)
+			{
+				$aData = array(
+					'profileid' => $iGuruProfile,
+					'class' => $sClass,
+					'permission' => 'yes',
+					'action' => $sAction,
+				);
+				$this->CreateObject('URP_ActionGrant', $aData, $oChange);
+			}
+		}
+		$aData = array(
+			'dimensionid' => $iDimLocation,
+			'profileid' => $iGuruProfile,
+			'value' => '<any>',
+			'attribute' => 'org_id',
+		);
+		$this->CreateObject('URP_ProfileProjection', $aData, $oChange);
+
+		// User login with super access rights
+		//
+		$aData = array(
+			'org_id' => self::FindId('Organization'),
+			'location_id' => self::FindId('Location'),
+			'first_name' => 'Jesus',
+			'name' => 'Deus',
+			'email' => '',
+		);
+		$iPerson = $this->CreateObject('Person', $aData, $oChange);
+		$aData = array(
+			'contactid' => $iPerson,
+			'login' => 'guru',
+			'password' => 'guru',
+			'language' => 'EN US',
+		);
+		$iLogin = $this->CreateObject('UserLocal', $aData, $oChange);
+
+		// Assign profiles to the new login
+		//
+		$aData = array(
+			'userid' => $iLogin,
+			'profileid' => $iGuruProfile,
+			'reason' => 'he is the one',
+		);
+		$this->CreateObject('URP_UserProfile', $aData, $oChange);
+
+		////////////////////////////////////////
+		// User login having all profiles, but seeing only his organization
+		//
+		$aData = array(
+			'org_id' => self::FindId('Organization'),
+			'location_id' => self::FindId('Location'),
+			'first_name' => 'Little ze',
+			'name' => 'Foo',
+			'email' => '',
+		);
+		$iPerson = $this->CreateObject('Person', $aData, $oChange);
+		$aData = array(
+			'contactid' => $iPerson,
+			'login' => 'foo',
+			'password' => 'foo',
+			'language' => 'EN US',
+		);
+		$iLogin = $this->CreateObject('UserLocal', $aData, $oChange);
+
+		// Assign profiles to the new login
+		//
+		foreach($aStdProfiles as $iProfileId)
+		{
+			$aData = array(
+				'userid' => $iLogin,
+				'profileid' => $iProfileId,
+				'reason' => '',
+			);
+			$this->CreateObject('URP_UserProfile', $aData, $oChange);
+		}
+
+		// Project classes
+		//
+		$aMyClassesToProject = array();
+		foreach($aClasses as $sClass)
+		{
+			if (MetaModel::IsValidAttCode($sClass, 'org_id'))
+			{
+				$aMyClassesToProject[$sClass] = 'org_id';
+			}
+		}
+		foreach($aMyClassesToProject as $sClass => $sAttCode)
+		{
+			$aData = array(
+				'dimensionid' => $iDimLocation,
+				'class' => $sClass,
+				'value' => '<this>',
+				'attribute' => $sAttCode,
+			);
+			$this->CreateObject('URP_ClassProjection', $aData, $oChange);
+		}
+
+		// Project profiles
+		//
+		foreach($aStdProfiles as $iProfileId)
+		{
+			$aData = array(
+				'dimensionid' => $iDimLocation,
+				'profileid' => $iProfileId,
+				'value' => 'SELECT Person WHERE id = :user->contactid',
+				'attribute' => 'org_id',
+			);
+			$this->CreateObject('URP_ProfileProjection', $aData, $oChange);
+		}
+
+		$oP->p('Created projections (Cf. login "foo", pwd "foo")');
+		$oP->p('* foo can do configuration management for a given customer');
+		$oP->p('* guru can do everything');
+	}
+
+	// For testing purposes -Romain
+	public function GoProjectionsLocation(WebPage $oP, $oChange)
 	{
 		// User login
 		//
@@ -156,7 +322,7 @@ class BenchmarkDataCreation
 			'profileid' => self::FindIdFromOQL("SELECT URP_Profiles WHERE name LIKE 'Configuration Manager'"),
 			'reason' => '',
 		);
-		$iFoo = $this->CreateObject('URP_UserProfile', $aData, $oChange);
+		$this->CreateObject('URP_UserProfile', $aData, $oChange);
 
 		// Dimension
 		//
@@ -178,7 +344,7 @@ class BenchmarkDataCreation
 				'value' => '<this>',
 				'attribute' => 'location_name',
 			);
-			$iFoo = $this->CreateObject('URP_ClassProjection', $aData, $oChange);
+			$this->CreateObject('URP_ClassProjection', $aData, $oChange);
 		}
 
 		// Project profiles
@@ -192,7 +358,7 @@ class BenchmarkDataCreation
 				'value' => 'Grenoble',
 				'attribute' => '',
 			);
-			$iFoo = $this->CreateObject('URP_ProfileProjection', $aData, $oChange);
+			$this->CreateObject('URP_ProfileProjection', $aData, $oChange);
 		}
 
 		$oP->p('Created projections (Cf. login "foo", pwd "foo")');
@@ -216,7 +382,7 @@ class BenchmarkDataCreation
 		//
 		$aData = array(
 			'org_id' => $iOrg,
-			'name' => 'Rio',
+			'name' => 'Rio de Janeiro',
 		);
 		$iLoc = $this->CreateObject('Location', $aData, $oChange);
 		$this->MakeFeedback($oP, 'Location');
@@ -247,7 +413,15 @@ class BenchmarkDataCreation
 				'name' => 'Ningem #'.$i,
 				'email' => 'foo'.$i.'@nowhere.fr',
 			);
-			$this->CreateObject('Person', $aData, $oChange);
+			$iPerson = $this->CreateObject('Person', $aData, $oChange);
+
+			// Contract/Infra
+			//
+			$aData = array(
+				'contact_id' => $iPerson,
+				'team_id' => $this->RandomId('Team'),
+			);
+			$this->CreateObject('lnkTeamToContact', $aData, $oChange);
 		}
 		$this->MakeFeedback($oP, 'Person');
 		
@@ -287,6 +461,19 @@ class BenchmarkDataCreation
 				'support_team_id' => $this->RandomId('Team'),
 			);
 			$iContract = $this->CreateObject('CustomerContract', $aData, $oChange);
+
+			// Contract/Contact (10% of contacts)
+			//
+			$iContactCount = ceil($this->m_aPlanned['Contracts'] / 10);
+			for($iLinked = 0 ; $iLinked < $iContactCount ; $iLinked++)
+			{
+				$aData = array(
+					'contact_id' => $this->RandomId('Person'),
+					'contract_id' => $iContract,
+					'role' => 'role '.$iLinked,
+				);
+				$this->CreateObject('lnkContractToContact', $aData, $oChange);
+			}
 		}
 		$this->MakeFeedback($oP, 'CustomerContract');
 		
@@ -300,6 +487,7 @@ class BenchmarkDataCreation
 				'org_id' => $iOrg,
 				'location_id' => $iLoc,
 				'name' => 'server'.$i,
+				'status' => 'production',
 			);
 			$iServer = $this->CreateObject('Server', $aData, $oChange);
 
@@ -317,16 +505,16 @@ class BenchmarkDataCreation
 
 			// Interfaces
 			//
-			$iInterfaceCount = 5; // See how aPlanned['Interfaces'] is computed
-			for($iLinked = 0 ; $iLinked < $iInterfaceCount ; $iLinked++)
+			for($iLinked = 0 ; $iLinked < $this->m_iIfByServer ; $iLinked++)
 			{
 				$aData = array(
 					'name' => "eth$iLinked",
 					'status' => 'implementation',
 					'org_id' => $iOrg,
 					'device_id' => $iServer,
+					'status' => 'production',
 				);
-				$this->CreateObject('NetworkInterface', $aData, $oChange);
+				$this->CreateObject('NetworkInterface', $aData, $oChange, 'server if');
 			}
 		}
 		$this->MakeFeedback($oP, 'Server');
@@ -340,7 +528,8 @@ class BenchmarkDataCreation
 			$aData = array(
 				'org_id' => $iOrg,
 				'location_id' => $iLoc,
-				'name' => 'server'.$i,
+				'name' => 'equipment #'.$i,
+				'status' => 'production',
 			);
 			$iNWDevice = $this->CreateObject('NetworkDevice', $aData, $oChange);
 
@@ -358,16 +547,17 @@ class BenchmarkDataCreation
 
 			// Interfaces
 			//
-			$iInterfaceCount = 5; // See how aPlanned['Interfaces'] is computed
-			for($iLinked = 0 ; $iLinked < $iInterfaceCount ; $iLinked++)
+			for($iLinked = 0 ; $iLinked < $this->m_iIfByNWDevice ; $iLinked++)
 			{
 				$aData = array(
 					'name' => "eth$iLinked",
 					'status' => 'implementation',
 					'org_id' => $iOrg,
 					'device_id' => $iNWDevice,
+					'connected_if' => $this->RandomId('NetworkInterface', 'server if'),
+					'status' => 'production',
 				);
-				$this->CreateObject('NetworkInterface', $aData, $oChange);
+				$this->CreateObject('NetworkInterface', $aData, $oChange, 'equipment if');
 			}
 		}
 		$this->MakeFeedback($oP, 'NetworkDevice');
@@ -375,6 +565,102 @@ class BenchmarkDataCreation
 
 		/////////////////////////
 		//
+		// Application Software
+		//
+		for($i = 0 ; $i < $this->m_aPlanned['Application SW'] ; $i++)
+		{
+			$aData = array(
+				'name' => 'Software #'.$i,
+			);
+			$iNWDevice = $this->CreateObject('Application', $aData, $oChange);
+		}
+		$this->MakeFeedback($oP, 'Application');
+
+		/////////////////////////
+		//
+		// Applications
+		//
+		for($i = 0 ; $i < $this->m_aPlanned['Applications'] ; $i++)
+		{
+			$aData = array(
+				'org_id' => $iOrg,
+				'device_id' => $this->RandomId('Server'),
+				'software_id' => $this->RandomId('Application'),
+				'name' => 'Application #'.$i,
+				'status' => 'production',
+			);
+			$iNWDevice = $this->CreateObject('ApplicationInstance', $aData, $oChange);
+
+			// Contract/Infra
+			//
+			$iContractCount = 1;
+			for($iLinked = 0 ; $iLinked < $iContractCount ; $iLinked++)
+			{
+				$aData = array(
+					'contract_id' => $this->RandomId('CustomerContract'),
+					'ci_id' => $iNWDevice,
+				);
+				$this->CreateObject('lnkContractToCI', $aData, $oChange);
+			}
+		}
+		$this->MakeFeedback($oP, 'ApplicationInstance');
+
+		/////////////////////////
+		//
+		// Application Solution
+		//
+		for($i = 0 ; $i < $this->m_aPlanned['Solutions'] ; $i++)
+		{
+			$aData = array(
+				'org_id' => $iOrg,
+				'name' => 'Solution #'.$i,
+				'status' => 'production',
+			);
+			$iNWDevice = $this->CreateObject('ApplicationSolution', $aData, $oChange);
+
+			// Contract/Infra
+			//
+			$iContractCount = 1;
+			for($iLinked = 0 ; $iLinked < $iContractCount ; $iLinked++)
+			{
+				$aData = array(
+					'contract_id' => $this->RandomId('CustomerContract'),
+					'ci_id' => $iNWDevice,
+				);
+				$this->CreateObject('lnkContractToCI', $aData, $oChange);
+			}
+		}
+		$this->MakeFeedback($oP, 'ApplicationSolution');
+
+		/////////////////////////
+		//
+		// Business Process
+		//
+		for($i = 0 ; $i < $this->m_aPlanned['Processes'] ; $i++)
+		{
+			$aData = array(
+				'org_id' => $iOrg,
+				'name' => 'Process #'.$i,
+				'status' => 'production',
+			);
+			$iNWDevice = $this->CreateObject('BusinessProcess', $aData, $oChange);
+
+			// Contract/Infra
+			//
+			$iContractCount = 1;
+			for($iLinked = 0 ; $iLinked < $iContractCount ; $iLinked++)
+			{
+				$aData = array(
+					'contract_id' => $this->RandomId('CustomerContract'),
+					'ci_id' => $iNWDevice,
+				);
+				$this->CreateObject('lnkContractToCI', $aData, $oChange);
+			}
+		}
+		$this->MakeFeedback($oP, 'BusinessProcess');
+
+		/////////////////////////
+		//
 		// Incident Tickets
 		//
 		for($i = 0 ; $i < $this->m_aPlanned['Incidents'] ; $i++)
@@ -420,6 +706,47 @@ class BenchmarkDataCreation
 
 		/////////////////////////
 		//
+		// Big Ticket
+		//
+		$aData = array(
+			'org_id' => $iOrg,
+			'caller_id' => $this->RandomId('Person'),
+			'workgroup_id' => $this->RandomId('Team'),
+			'agent_id' => $this->RandomId('Person'),
+			'service_id' => $this->RandomId('Service'),
+			'servicesubcategory_id' => $this->RandomId('ServiceSubcategory'),
+			'title' => 'Big ticket',
+			'ticket_log' => 'Testing...',
+		);
+		$iTicket = $this->CreateObject('Incident', $aData, $oChange);
+
+		// Incident/Infra
+		//
+		$iInfraCount = $this->m_aPlanned['Big ticket: CIs'];
+		for($iLinked = 0 ; $iLinked < $iInfraCount ; $iLinked++)
+		{
+			$aData = array(
+				'ci_id' => $this->RandomId('Server'),
+				'ticket_id' => $iTicket,
+			);
+			$this->CreateObject('lnkTicketToCI', $aData, $oChange);
+		}
+
+		// Incident/Contact
+		//
+		$iInfraCount = rand(0, 6)		;
+		for($iLinked = 0 ; $iLinked < $iInfraCount ; $iLinked++)
+		{
+			$aData = array(
+				'contact_id' => $this->RandomId('Person'),
+				'ticket_id' => $iTicket,
+				'role' => 'role '.$iLinked,
+			);
+			$this->CreateObject('lnkTicketToContact', $aData, $oChange);
+		}
+
+		/////////////////////////
+		//
 		// Change Tickets
 		//
 		for($i = 0 ; $i < $this->m_aPlanned['Changes'] ; $i++)
@@ -529,6 +856,17 @@ function DisplayStep1(SetupWebPage $oP)
 {
 	$sNextOperation = 'step2';
 	$oP->add("<h1>iTop benchmarking</h1>\n");
+
+	$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Please wait...', 10)\">\n");
+	$oP->add("<input type=\"hidden\" name=\"operation\" value=\"createprofiles_organization\">\n");
+	$oP->add("<button type=\"submit\">Create profiles!</button>\n");
+	$oP->add("</form>\n");
+
+	$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Please wait...', 10)\">\n");
+	$oP->add("<input type=\"hidden\" name=\"operation\" value=\"createprofiles_location\">\n");
+	$oP->add("<button type=\"submit\">Create profiles (unit tests)!</button>\n");
+	$oP->add("</form>\n");
+
 	$oP->add("<h2>Please specify the requested volumes</h2>\n");
 	$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Evaluating real plans...', 10)\">\n");
 	$oP->add("<fieldset><legend>Data load configuration</legend>\n");
@@ -600,11 +938,40 @@ function DisplayStep3(SetupWebPage $oP, $oDataCreation)
 	$oMyChange->Set("userinfo", "Administrator");
 	$iChangeId = $oMyChange->DBInsertNoReload();
 
-	$oDataCreation->GoProjections($oP, $oMyChange);
 	$oDataCreation->GoVolume($oP, $oMyChange);
 }
 
 /**
+ * Do create a profile management context
+ */  
+function CreateProfilesOrganization(SetupWebPage $oP, $oDataCreation)
+{
+//	$sNextOperation = 'step3';
+
+	$oMyChange = MetaModel::NewObject("CMDBChange");
+	$oMyChange->Set("date", time());
+	$oMyChange->Set("userinfo", "Administrator");
+	$iChangeId = $oMyChange->DBInsertNoReload();
+
+	$oDataCreation->GoProjectionsOrganization($oP, $oMyChange);
+}
+
+/**
+ * Do create the data set... could take some time to execute
+ */  
+function CreateProfilesLocation(SetupWebPage $oP, $oDataCreation)
+{
+//	$sNextOperation = 'step3';
+
+	$oMyChange = MetaModel::NewObject("CMDBChange");
+	$oMyChange->Set("date", time());
+	$oMyChange->Set("userinfo", "Administrator");
+	$iChangeId = $oMyChange->DBInsertNoReload();
+
+	$oDataCreation->GoProjectionsLocation($oP, $oMyChange);
+}
+
+/**
  * Main program
  */
 
@@ -620,7 +987,19 @@ try
 		case 'step1':
 		DisplayStep1($oP);
 		break;
-		
+
+		case 'createprofiles_organization':
+		$oP->no_cache();
+		$oDataCreation = new BenchmarkDataCreation();
+		CreateProfilesOrganization($oP, $oDataCreation);
+		break;
+
+		case 'createprofiles_location':
+		$oP->no_cache();
+		$oDataCreation = new BenchmarkDataCreation();
+		CreateProfilesLocation($oP, $oDataCreation);
+		break;
+
 		case 'step2':
 		$oP->no_cache();
 		$iPlannedCIs = Utils::ReadParam('plannedcis');