1<?php 2/** 3 * Copyright since 2007 PrestaShop SA and Contributors 4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA 5 * 6 * NOTICE OF LICENSE 7 * 8 * This source file is subject to the Open Software License (OSL 3.0) 9 * that is bundled with this package in the file LICENSE.md. 10 * It is also available through the world-wide-web at this URL: 11 * https://opensource.org/licenses/OSL-3.0 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@prestashop.com so we can send you a copy immediately. 15 * 16 * DISCLAIMER 17 * 18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer 19 * versions in the future. If you wish to customize PrestaShop for your 20 * needs please refer to https://devdocs.prestashop.com/ for more information. 21 * 22 * @author PrestaShop SA and Contributors <contact@prestashop.com> 23 * @copyright Since 2007 PrestaShop SA and Contributors 24 * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) 25 */ 26 27/** 28 * Class AccessCore. 29 */ 30class AccessCore extends ObjectModel 31{ 32 /** @var int Profile id which address belongs to */ 33 public $id_profile = null; 34 35 /** @var int AuthorizationRole id which address belongs to */ 36 public $id_authorization_role = null; 37 38 /** 39 * @see ObjectModel::$definition 40 */ 41 public static $definition = [ 42 'table' => 'access', 43 'primary' => 'id_profile', 44 'fields' => [ 45 'id_profile' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false], 46 'id_authorization_role' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false], 47 ], 48 ]; 49 50 /** 51 * Is access granted to this Role? 52 * 53 * @param string $role Role name ("Superadministrator", "sales", "translator", etc.) 54 * @param int $idProfile Profile ID 55 * 56 * @return bool Whether access is granted 57 * 58 * @throws Exception 59 */ 60 public static function isGranted($role, $idProfile) 61 { 62 foreach ((array) $role as $currentRole) { 63 preg_match( 64 '/ROLE_MOD_(?P<type>[A-Z]+)_(?P<name>[A-Z0-9_]+)_(?P<auth>[A-Z]+)/', 65 $currentRole, 66 $matches 67 ); 68 69 if (isset($matches['type']) && $matches['type'] == 'TAB') { 70 $joinTable = _DB_PREFIX_ . 'access'; 71 } elseif (isset($matches['type']) && $matches['type'] == 'MODULE') { 72 $joinTable = _DB_PREFIX_ . 'module_access'; 73 } else { 74 throw new Exception('The slug ' . $currentRole . ' is invalid'); 75 } 76 77 $currentRole = Db::getInstance()->escape($currentRole); 78 79 $isCurrentGranted = (bool) Db::getInstance()->getRow(' 80 SELECT t.`id_authorization_role` 81 FROM `' . _DB_PREFIX_ . 'authorization_role` t 82 LEFT JOIN ' . $joinTable . ' j 83 ON j.`id_authorization_role` = t.`id_authorization_role` 84 WHERE `slug` = "' . $currentRole . '" 85 AND j.`id_profile` = "' . (int) $idProfile . '" 86 '); 87 88 if (!$isCurrentGranted) { 89 return false; 90 } 91 } 92 93 return true; 94 } 95 96 /** 97 * Get all roles for the Profile ID. 98 * 99 * @param int $idProfile Profile ID 100 * 101 * @return array Roles 102 */ 103 public static function getRoles($idProfile) 104 { 105 $idProfile = (int) $idProfile; 106 107 $accesses = Db::getInstance()->executeS(' 108 SELECT r.`slug` 109 FROM `' . _DB_PREFIX_ . 'authorization_role` r 110 INNER JOIN `' . _DB_PREFIX_ . 'access` a ON a.`id_authorization_role` = r.`id_authorization_role` 111 WHERE a.`id_profile` = "' . $idProfile . '" 112 '); 113 114 $accessesFromModules = Db::getInstance()->executeS(' 115 SELECT r.`slug` 116 FROM `' . _DB_PREFIX_ . 'authorization_role` r 117 INNER JOIN `' . _DB_PREFIX_ . 'module_access` ma ON ma.`id_authorization_role` = r.`id_authorization_role` 118 WHERE ma.`id_profile` = "' . $idProfile . '" 119 '); 120 121 $roles = array_merge($accesses, $accessesFromModules); 122 123 foreach ($roles as $key => $role) { 124 $roles[$key] = $role['slug']; 125 } 126 127 return $roles; 128 } 129 130 /** 131 * Find Tab ID by slug. 132 * 133 * @param string $authSlug Slug 134 * 135 * @return string Tab ID 136 * @todo: Find out if we should return an int instead. (breaking change) 137 */ 138 public static function findIdTabByAuthSlug($authSlug) 139 { 140 preg_match( 141 '/ROLE_MOD_[A-Z]+_(?P<classname>[A-Z]+)_(?P<auth>[A-Z]+)/', 142 $authSlug, 143 $matches 144 ); 145 146 $result = Db::getInstance()->getRow(' 147 SELECT `id_tab` 148 FROM `' . _DB_PREFIX_ . 'tab` 149 WHERE UCASE(`class_name`) = "' . $matches['classname'] . '" 150 '); 151 152 return $result['id_tab']; 153 } 154 155 /** 156 * Find slug by Tab ID. 157 * 158 * @param int $idTab Tab ID 159 * 160 * @return string Full module slug 161 */ 162 public static function findSlugByIdTab($idTab) 163 { 164 $result = Db::getInstance()->getRow(' 165 SELECT `class_name` 166 FROM `' . _DB_PREFIX_ . 'tab` 167 WHERE `id_tab` = "' . (int) $idTab . '" 168 '); 169 170 return self::sluggifyTab($result); 171 } 172 173 /** 174 * Find slug by Parent Tab ID. 175 * 176 * @param int $idParentTab Tab ID 177 * 178 * @return string Full module slug 179 */ 180 public static function findSlugByIdParentTab($idParentTab) 181 { 182 return Db::getInstance()->executeS(' 183 SELECT `class_name` 184 FROM `' . _DB_PREFIX_ . 'tab` 185 WHERE `id_parent` = "' . (int) $idParentTab . '" 186 '); 187 } 188 189 /** 190 * Find slug by Module ID. 191 * 192 * @param int $idModule Module ID 193 * 194 * @return string Full module slug 195 */ 196 public static function findSlugByIdModule($idModule) 197 { 198 $result = Db::getInstance()->getRow(' 199 SELECT `name` 200 FROM `' . _DB_PREFIX_ . 'module` 201 WHERE `id_module` = "' . (int) $idModule . '" 202 '); 203 204 return self::sluggifyModule($result); 205 } 206 207 /** 208 * Sluggify tab. 209 * 210 * @param string $tab Tab class name 211 * @param string $authorization 'CREATE'|'READ'|'UPDATE'|'DELETE' 212 * 213 * @return string Full slug for tab 214 */ 215 public static function sluggifyTab($tab, $authorization = '') 216 { 217 return sprintf('ROLE_MOD_TAB_%s_%s', strtoupper($tab['class_name']), $authorization); 218 } 219 220 /** 221 * Sluggify module. 222 * 223 * @param string $module Module name 224 * @param string $authorization 'CREATE'|'READ'|'UPDATE'|'DELETE' 225 * 226 * @return string Full slug for module 227 */ 228 public static function sluggifyModule($module, $authorization = '') 229 { 230 return sprintf('ROLE_MOD_MODULE_%s_%s', strtoupper($module['name']), $authorization); 231 } 232 233 /** 234 * Get legacy authorization. 235 * 236 * @param string $legacyAuth Legacy authorization 237 * 238 * @return bool|string|array Authorization 239 */ 240 public static function getAuthorizationFromLegacy($legacyAuth) 241 { 242 $auth = [ 243 'add' => 'CREATE', 244 'view' => 'READ', 245 'edit' => 'UPDATE', 246 'configure' => 'UPDATE', 247 'delete' => 'DELETE', 248 'uninstall' => 'DELETE', 249 'duplicate' => ['CREATE', 'UPDATE'], 250 'all' => ['CREATE', 'READ', 'UPDATE', 'DELETE'], 251 ]; 252 253 return isset($auth[$legacyAuth]) ? $auth[$legacyAuth] : false; 254 } 255 256 /** 257 * Add access. 258 * 259 * @param int $idProfile Profile ID 260 * @param int $idRole Role ID 261 * 262 * @return string Whether access has been successfully granted ("ok", "error") 263 */ 264 public function addAccess($idProfile, $idRole) 265 { 266 $sql = ' 267 INSERT IGNORE INTO `' . _DB_PREFIX_ . 'access` (`id_profile`, `id_authorization_role`) 268 VALUES (' . (int) $idProfile . ',' . (int) $idRole . ') 269 '; 270 271 return Db::getInstance()->execute($sql) ? 'ok' : 'error'; 272 } 273 274 /** 275 * Remove access. 276 * 277 * @param int $idProfile Profile ID 278 * @param int $idRole Role ID 279 * 280 * @return string Whether access has been successfully removed ("ok", "error") 281 */ 282 public function removeAccess($idProfile, $idRole) 283 { 284 $sql = ' 285 DELETE FROM `' . _DB_PREFIX_ . 'access` 286 WHERE `id_profile` = "' . (int) $idProfile . '" 287 AND `id_authorization_role` = "' . (int) $idRole . '" 288 '; 289 290 return Db::getInstance()->execute($sql) ? 'ok' : 'error'; 291 } 292 293 /** 294 * Add module access. 295 * 296 * @param int $idProfile Profile ID 297 * @param int $idRole Role ID 298 * 299 * @return string Whether module access has been successfully granted ("ok", "error") 300 */ 301 public function addModuleAccess($idProfile, $idRole) 302 { 303 $sql = ' 304 INSERT IGNORE INTO `' . _DB_PREFIX_ . 'module_access` (`id_profile`, `id_authorization_role`) 305 VALUES (' . (int) $idProfile . ',' . (int) $idRole . ') 306 '; 307 308 return Db::getInstance()->execute($sql) ? 'ok' : 'error'; 309 } 310 311 /** 312 * @param int $idProfile 313 * @param int $idRole 314 * 315 * @return string 'ok'|'error' 316 */ 317 public function removeModuleAccess($idProfile, $idRole) 318 { 319 $sql = ' 320 DELETE FROM `' . _DB_PREFIX_ . 'module_access` 321 WHERE `id_profile` = "' . (int) $idProfile . '" 322 AND `id_authorization_role` = "' . (int) $idRole . '" 323 '; 324 325 return Db::getInstance()->execute($sql) ? 'ok' : 'error'; 326 } 327 328 /** 329 * Update legacy access. 330 * 331 * @param int $idProfile Profile ID 332 * @param int $idTab Tab ID 333 * @param string $lgcAuth Legacy authorization 334 * @param int $enabled Whether access should be granted 335 * @param int $addFromParent Child from parents 336 * 337 * @return string Whether legacy access has been successfully updated ("ok", "error") 338 * 339 * @throws Exception 340 */ 341 public function updateLgcAccess($idProfile, $idTab, $lgcAuth, $enabled, $addFromParent = 0) 342 { 343 $idProfile = (int) $idProfile; 344 $idTab = (int) $idTab; 345 346 if ($idTab == -1) { 347 $slug = 'ROLE_MOD_TAB_%_'; 348 } else { 349 $slug = self::findSlugByIdTab($idTab); 350 } 351 352 $whereClauses = []; 353 354 foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) { 355 $slugLike = Db::getInstance()->escape($slug . $auth); 356 $whereClauses[] = ' `slug` LIKE "' . $slugLike . '"'; 357 } 358 359 if ($addFromParent == 1) { 360 foreach (self::findSlugByIdParentTab($idTab) as $child) { 361 $child = self::sluggifyTab($child); 362 foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) { 363 $slugLike = Db::getInstance()->escape($child . $auth); 364 $whereClauses[] = ' `slug` LIKE "' . $slugLike . '"'; 365 } 366 } 367 } 368 369 $roles = Db::getInstance()->executeS(' 370 SELECT `id_authorization_role` 371 FROM `' . _DB_PREFIX_ . 'authorization_role` t 372 WHERE ' . implode(' OR ', $whereClauses) . ' 373 '); 374 375 if (empty($roles)) { 376 throw new \Exception('Cannot find role slug'); 377 } 378 379 $res = []; 380 foreach ($roles as $role) { 381 if ($enabled) { 382 $res[] = $this->addAccess($idProfile, $role['id_authorization_role']); 383 } else { 384 $res[] = $this->removeAccess($idProfile, $role['id_authorization_role']); 385 } 386 } 387 388 return in_array('error', $res) ? 'error' : 'ok'; 389 } 390 391 /** 392 * Update (legacy) Module access. 393 * 394 * @param int $idProfile Profile ID 395 * @param int $idModule Module ID 396 * @param string $lgcAuth Legacy authorization 397 * @param int $enabled Whether module access should be granted 398 * 399 * @return string Whether module access has been succesfully changed ("ok", "error") 400 */ 401 public function updateLgcModuleAccess($idProfile, $idModule, $lgcAuth, $enabled) 402 { 403 $idProfile = (int) $idProfile; 404 $idModule = (int) $idModule; 405 406 if ($idModule == -1) { 407 $slug = 'ROLE_MOD_MODULE_%_'; 408 } else { 409 $slug = self::findSlugByIdModule($idModule); 410 } 411 412 $whereClauses = []; 413 414 foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) { 415 $slugLike = Db::getInstance()->escape($slug . $auth); 416 $whereClauses[] = ' `slug` LIKE "' . $slugLike . '"'; 417 } 418 419 $roles = Db::getInstance()->executeS(' 420 SELECT `id_authorization_role` 421 FROM `' . _DB_PREFIX_ . 'authorization_role` t 422 WHERE ' . implode(' OR ', $whereClauses) . ' 423 '); 424 425 $res = []; 426 foreach ($roles as $role) { 427 if ($enabled) { 428 $res[] = $this->addModuleAccess($idProfile, $role['id_authorization_role']); 429 } else { 430 $res[] = $this->removeModuleAccess($idProfile, $role['id_authorization_role']); 431 } 432 } 433 434 return in_array('error', $res) ? 'error' : 'ok'; 435 } 436} 437