module.itop-profiles-itil.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. SetupWebPage::AddModule(
  17. __FILE__, // Path to the current file, all other file names are relative to the directory containing this file
  18. 'itop-profiles-itil/1.0.0',
  19. array(
  20. // Identification
  21. //
  22. 'label' => 'Create standard ITIL profiles',
  23. 'category' => 'create_profiles',
  24. // Setup
  25. //
  26. 'dependencies' => array(
  27. ),
  28. 'mandatory' => true,
  29. 'visible' => false,
  30. 'installer' => 'CreateITILProfilesInstaller',
  31. // Components
  32. //
  33. 'datamodel' => array(
  34. //'model.itop-profiles-itil.php',
  35. ),
  36. 'webservice' => array(
  37. //'webservices.itop-profiles-itil.php',
  38. ),
  39. 'dictionary' => array(
  40. //'en.dict.itop-profiles-itil.php',
  41. //'fr.dict.itop-profiles-itil.php',
  42. //'de.dict.itop-profiles-itil.php',
  43. ),
  44. 'data.struct' => array(
  45. //'data.struct.itop-profiles-itil.xml',
  46. ),
  47. 'data.sample' => array(
  48. //'data.sample.itop-profiles-itil.xml',
  49. ),
  50. // Documentation
  51. //
  52. 'doc.manual_setup' => '',
  53. 'doc.more_information' => '',
  54. // Default settings
  55. //
  56. 'settings' => array(
  57. //'some_setting' => 'some value',
  58. ),
  59. )
  60. );
  61. // Module installation handler
  62. //
  63. class CreateITILProfilesInstaller extends ModuleInstallerAPI
  64. {
  65. public static function BeforeWritingConfig(Config $oConfiguration)
  66. {
  67. //$oConfiguration->SetModuleSetting('user-rigths-profile', 'myoption', 'myvalue');
  68. return $oConfiguration;
  69. }
  70. public static function AfterDatabaseCreation(Config $oConfiguration)
  71. {
  72. self::ComputeITILProfiles();
  73. //self::ComputeBasicProfiles();
  74. self::DoCreateProfiles();
  75. UserRights::FlushPrivileges(true /* reset admin cache */);
  76. }
  77. protected static $m_aActions = array(
  78. UR_ACTION_READ => 'Read',
  79. UR_ACTION_MODIFY => 'Modify',
  80. UR_ACTION_DELETE => 'Delete',
  81. UR_ACTION_BULK_READ => 'Bulk Read',
  82. UR_ACTION_BULK_MODIFY => 'Bulk Modify',
  83. UR_ACTION_BULK_DELETE => 'Bulk Delete',
  84. );
  85. // Note: It is possible to specify the same class in several modules
  86. //
  87. protected static $m_aModules = array();
  88. protected static $m_aProfiles = array();
  89. protected static $m_aCacheActionGrants = null;
  90. protected static $m_aCacheStimulusGrants = null;
  91. protected static $m_aCacheProfiles = null;
  92. protected static function DoCreateActionGrant($iProfile, $iAction, $sClass, $bPermission = true)
  93. {
  94. $sAction = self::$m_aActions[$iAction];
  95. if (is_null(self::$m_aCacheActionGrants))
  96. {
  97. self::$m_aCacheActionGrants = array();
  98. $oFilterAll = new DBObjectSearch('URP_ActionGrant');
  99. $oSet = new DBObjectSet($oFilterAll);
  100. while ($oGrant = $oSet->Fetch())
  101. {
  102. self::$m_aCacheActionGrants[$oGrant->Get('profileid').'-'.$oGrant->Get('action').'-'.$oGrant->Get('class')] = $oGrant->GetKey();
  103. }
  104. }
  105. $sCacheKey = "$iProfile-$sAction-$sClass";
  106. if (isset(self::$m_aCacheActionGrants[$sCacheKey]))
  107. {
  108. return self::$m_aCacheActionGrants[$sCacheKey];
  109. }
  110. $oNewObj = MetaModel::NewObject("URP_ActionGrant");
  111. $oNewObj->Set('profileid', $iProfile);
  112. $oNewObj->Set('permission', $bPermission ? 'yes' : 'no');
  113. $oNewObj->Set('class', $sClass);
  114. $oNewObj->Set('action', $sAction);
  115. $iId = $oNewObj->DBInsertNoReload();
  116. self::$m_aCacheActionGrants[$sCacheKey] = $iId;
  117. return $iId;
  118. }
  119. protected static function DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass)
  120. {
  121. if (is_null(self::$m_aCacheStimulusGrants))
  122. {
  123. self::$m_aCacheStimulusGrants = array();
  124. $oFilterAll = new DBObjectSearch('URP_StimulusGrant');
  125. $oSet = new DBObjectSet($oFilterAll);
  126. while ($oGrant = $oSet->Fetch())
  127. {
  128. self::$m_aCacheStimulusGrants[$oGrant->Get('profileid').'-'.$oGrant->Get('stimulus').'-'.$oGrant->Get('class')] = $oGrant->GetKey();
  129. }
  130. }
  131. $sCacheKey = "$iProfile-$sStimulusCode-$sClass";
  132. if (isset(self::$m_aCacheStimulusGrants[$sCacheKey]))
  133. {
  134. return self::$m_aCacheStimulusGrants[$sCacheKey];
  135. }
  136. $oNewObj = MetaModel::NewObject("URP_StimulusGrant");
  137. $oNewObj->Set('profileid', $iProfile);
  138. $oNewObj->Set('permission', 'yes');
  139. $oNewObj->Set('class', $sClass);
  140. $oNewObj->Set('stimulus', $sStimulusCode);
  141. $iId = $oNewObj->DBInsertNoReload();
  142. self::$m_aCacheStimulusGrants[$sCacheKey] = $iId;
  143. return $iId;
  144. }
  145. protected static function DoCreateProfile($sName, $sDescription)
  146. {
  147. if (is_null(self::$m_aCacheProfiles))
  148. {
  149. self::$m_aCacheProfiles = array();
  150. $oFilterAll = new DBObjectSearch('URP_Profiles');
  151. $oSet = new DBObjectSet($oFilterAll);
  152. while ($oProfile = $oSet->Fetch())
  153. {
  154. self::$m_aCacheProfiles[$oProfile->Get('name')] = $oProfile->GetKey();
  155. }
  156. }
  157. $sCacheKey = $sName;
  158. if (isset(self::$m_aCacheProfiles[$sCacheKey]))
  159. {
  160. return self::$m_aCacheProfiles[$sCacheKey];
  161. }
  162. $oNewObj = MetaModel::NewObject("URP_Profiles");
  163. $oNewObj->Set('name', $sName);
  164. $oNewObj->Set('description', $sDescription);
  165. $iId = $oNewObj->DBInsertNoReload();
  166. self::$m_aCacheProfiles[$sCacheKey] = $iId;
  167. return $iId;
  168. }
  169. protected static function DoSetupProfile($sName, $aProfileData)
  170. {
  171. $sDescription = $aProfileData['description'];
  172. if (strlen(trim($aProfileData['write_modules'])) == 0)
  173. {
  174. $aWriteModules = array();
  175. }
  176. else
  177. {
  178. $aWriteModules = explode(',', trim($aProfileData['write_modules']));
  179. }
  180. if (strlen(trim($aProfileData['delete_modules'])) == 0)
  181. {
  182. $aDeleteModules = array();
  183. }
  184. else
  185. {
  186. $aDeleteModules = explode(',', trim($aProfileData['delete_modules']));
  187. }
  188. $aStimuli = $aProfileData['stimuli'];
  189. $iProfile = self::DoCreateProfile($sName, $sDescription);
  190. // Warning: BulkInsert is working because we will load one single class
  191. // having one single table !
  192. // the benefit is: 10 queries (1 per profile) instead of 1500
  193. // which divides the overall user rights setup process by 5
  194. DBObject::BulkInsertStart();
  195. // Grant read rights for everything
  196. //
  197. foreach (MetaModel::GetClasses('bizmodel') as $sClass)
  198. {
  199. self::DoCreateActionGrant($iProfile, UR_ACTION_READ, $sClass);
  200. self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_READ, $sClass);
  201. }
  202. // Grant write for given modules
  203. // Start by compiling the information, because some modules may overlap
  204. $aWriteableClasses = array();
  205. foreach ($aWriteModules as $sModule)
  206. {
  207. //$oPage->p('Granting write access for the module"'.$sModule.'" - '.count(self::$m_aModules[$sModule]).' classes');
  208. foreach (self::$m_aModules[$sModule] as $sClass)
  209. {
  210. $aWriteableClasses[$sClass] = true;
  211. }
  212. }
  213. foreach ($aWriteableClasses as $sClass => $foo)
  214. {
  215. if (!MetaModel::IsValidClass($sClass))
  216. {
  217. throw new CoreException("Invalid class name '$sClass'");
  218. }
  219. self::DoCreateActionGrant($iProfile, UR_ACTION_MODIFY, $sClass);
  220. self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_MODIFY, $sClass);
  221. }
  222. // Grant delete for given modules
  223. // Start by compiling the information, because some modules may overlap
  224. $aDeletableClasses = array();
  225. foreach ($aDeleteModules as $sModule)
  226. {
  227. //$oPage->p('Granting delete access for the module"'.$sModule.'" - '.count(self::$m_aModules[$sModule]).' classes');
  228. foreach (self::$m_aModules[$sModule] as $sClass)
  229. {
  230. $aDeletableClasses[$sClass] = true;
  231. }
  232. }
  233. foreach ($aDeletableClasses as $sClass => $foo)
  234. {
  235. if (!MetaModel::IsValidClass($sClass))
  236. {
  237. throw new CoreException("Invalid class name '$sClass'");
  238. }
  239. self::DoCreateActionGrant($iProfile, UR_ACTION_DELETE, $sClass);
  240. // By default, do not allow bulk deletion operations for standard users
  241. // self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_DELETE, $sClass);
  242. }
  243. // Grant stimuli for given classes
  244. foreach ($aStimuli as $sClass => $sAllowedStimuli)
  245. {
  246. if (!MetaModel::IsValidClass($sClass))
  247. {
  248. // Could be a class defined in a module that wasn't installed
  249. continue;
  250. //throw new CoreException("Invalid class name '$sClass'");
  251. }
  252. if ($sAllowedStimuli == 'any')
  253. {
  254. $aAllowedStimuli = array_keys(MetaModel::EnumStimuli($sClass));
  255. }
  256. elseif ($sAllowedStimuli == 'none')
  257. {
  258. $aAllowedStimuli = array();
  259. }
  260. else
  261. {
  262. $aAllowedStimuli = explode(',', $sAllowedStimuli);
  263. }
  264. foreach ($aAllowedStimuli as $sStimulusCode)
  265. {
  266. self::DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass);
  267. }
  268. }
  269. // Again: this is working only because action/stimulus grant are classes made of a single table!
  270. DBObject::BulkInsertFlush();
  271. }
  272. public static function DoCreateProfiles()
  273. {
  274. URP_Profiles::DoCreateAdminProfile();
  275. URP_Profiles::DoCreateUserPortalProfile();
  276. foreach(self::$m_aProfiles as $sName => $aProfileData)
  277. {
  278. self::DoSetupProfile($sName, $aProfileData);
  279. }
  280. }
  281. public static function ComputeBasicProfiles()
  282. {
  283. // In this profiling scheme, one single module represents all the classes
  284. //
  285. self::$m_aModules = array(
  286. 'UserData' => MetaModel::GetClasses('bizmodel'),
  287. );
  288. self::$m_aProfiles = array(
  289. 'Reader' => array(
  290. 'description' => 'Person having a ready-only access to the data',
  291. 'write_modules' => '',
  292. 'delete_modules' => '',
  293. 'stimuli' => array(
  294. ),
  295. ),
  296. 'Writer' => array(
  297. 'description' => 'Contributor to the contents (read + write access)',
  298. 'write_modules' => 'UserData',
  299. 'delete_modules' => 'UserData',
  300. 'stimuli' => array(
  301. // any class => 'any'
  302. ),
  303. ),
  304. );
  305. }
  306. public static function ComputeITILProfiles()
  307. {
  308. // In this profiling scheme, modules are based on ITIL recommendations
  309. //
  310. self::$m_aModules = array(
  311. 'General' => MetaModel::GetClasses('structure'),
  312. 'Documentation' => MetaModel::GetClasses('documentation'),
  313. 'Configuration' => MetaModel::GetClasses('configmgmt'),
  314. 'Incident' => MetaModel::GetClasses('incidentmgmt'),
  315. 'Problem' => MetaModel::GetClasses('problemmgmt'),
  316. 'Change' => MetaModel::GetClasses('changemgmt'),
  317. 'Service' => MetaModel::GetClasses('servicemgmt'),
  318. 'Call' => MetaModel::GetClasses('requestmgmt'),
  319. 'KnownError' => MetaModel::GetClasses('knownerrormgmt'),
  320. );
  321. self::$m_aProfiles = array(
  322. 'Configuration Manager' => array(
  323. 'description' => 'Person in charge of the documentation of the managed CIs',
  324. 'write_modules' => 'General,Documentation,Configuration',
  325. 'delete_modules' => 'General,Documentation,Configuration',
  326. 'stimuli' => array(
  327. //'Server' => 'none',
  328. //'Contract' => 'none',
  329. //'IncidentTicket' => 'none',
  330. //'ChangeTicket' => 'any',
  331. ),
  332. ),
  333. 'Service Desk Agent' => array(
  334. 'description' => 'Person in charge of creating incident reports',
  335. 'write_modules' => 'Incident,Call',
  336. 'delete_modules' => 'Incident,Call',
  337. 'stimuli' => array(
  338. 'Incident' => 'ev_assign',
  339. 'UserRequest' => 'ev_assign',
  340. ),
  341. ),
  342. 'Support Agent' => array(
  343. 'description' => 'Person analyzing and solving the current incidents',
  344. 'write_modules' => 'Incident',
  345. 'delete_modules' => 'Incident',
  346. 'stimuli' => array(
  347. 'Incident' => 'ev_assign,ev_reassign,ev_resolve,ev_close',
  348. 'UserRequest' => 'ev_assign,ev_reassign,ev_resolve,ev_close,ev_freeze',
  349. ),
  350. ),
  351. 'Problem Manager' => array(
  352. 'description' => 'Person analyzing and solving the current problems',
  353. 'write_modules' => 'Problem,KnownError',
  354. 'delete_modules' => 'Problem,KnownError',
  355. 'stimuli' => array(
  356. 'Problem' => 'ev_assign,ev_reassign,ev_resolve,ev_close',
  357. ),
  358. ),
  359. 'Change Implementor' => array(
  360. 'description' => 'Person executing the changes',
  361. 'write_modules' => 'Change',
  362. 'delete_modules' => 'Change',
  363. 'stimuli' => array(
  364. 'NormalChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
  365. 'EmergencyChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
  366. 'RoutineChange' => 'ev_plan,ev_replan,ev_implement,ev_monitor',
  367. ),
  368. ),
  369. 'Change Supervisor' => array(
  370. 'description' => 'Person responsible for the overall change execution',
  371. 'write_modules' => 'Change',
  372. 'delete_modules' => 'Change',
  373. 'stimuli' => array(
  374. 'NormalChange' => 'ev_validate,ev_reject,ev_assign,ev_reopen,ev_finish',
  375. 'EmergencyChange' => 'ev_assign,ev_reopen,ev_finish',
  376. 'RoutineChange' => 'ev_assign,ev_reopen,ev_finish',
  377. ),
  378. ),
  379. 'Change Approver' => array(
  380. 'description' => 'Person who could be impacted by some changes',
  381. 'write_modules' => 'Change',
  382. 'delete_modules' => 'Change',
  383. 'stimuli' => array(
  384. 'NormalChange' => 'ev_approve,ev_notapprove',
  385. 'EmergencyChange' => 'ev_approve,ev_notapprove',
  386. 'RoutineChange' => 'none',
  387. ),
  388. ),
  389. 'Service Manager' => array(
  390. 'description' => 'Person responsible for the service delivered to the [internal] customer',
  391. 'write_modules' => 'Service',
  392. 'delete_modules' => 'Service',
  393. 'stimuli' => array(
  394. ),
  395. ),
  396. 'Document author' => array(
  397. 'description' => 'Any person who could contribute to documentation',
  398. 'write_modules' => 'Documentation',
  399. 'delete_modules' => 'Documentation',
  400. 'stimuli' => array(
  401. ),
  402. ),
  403. );
  404. }
  405. }
  406. ?>