* @author Joel Bout */ class DataBaseAccess extends ConfigurableService { const SERVICE_ID = 'taoDacSimple/DataBaseAccess'; const OPTION_PERSISTENCE = 'persistence'; const COLUMN_USER_ID = 'user_id'; const COLUMN_RESOURCE_ID = 'resource_id'; const COLUMN_PRIVILEGE = 'privilege'; const TABLE_PRIVILEGES_NAME = 'data_privileges'; const INDEX_RESOURCE_ID = 'data_privileges_resource_id_index'; private $persistence; /** * @return EventManager */ protected function getEventManager() { return $this->getServiceLocator()->get(EventManager::SERVICE_ID); } /** * We can know which users have a privilege on a resource * @param array $resourceIds * @return array list of users */ public function getUsersWithPermissions($resourceIds) { $inQuery = implode(',', array_fill(0, count($resourceIds), '?')); $query = sprintf( 'SELECT %s, %s, %s FROM %s WHERE %s IN (%s)', self::COLUMN_RESOURCE_ID, self::COLUMN_USER_ID, self::COLUMN_PRIVILEGE, self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID, $inQuery ); return $this->fetchQuery($query, $resourceIds); } /** * Get the permissions for a list of resources and users * * @access public * @param array $userIds * @param array $resourceIds * @return array */ public function getPermissions($userIds, array $resourceIds) { // permission request for empty resources must return an empty array if (!count($resourceIds)) { return []; } // get privileges for a user/roles and a resource $inQueryResource = implode(',', array_fill(0, count($resourceIds), '?')); $inQueryUser = implode(',', array_fill(0, count($userIds), '?')); $query = sprintf( 'SELECT %s, %s FROM %s WHERE %s IN (%s) AND %s IN (%s)', self::COLUMN_RESOURCE_ID, self::COLUMN_PRIVILEGE, self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID, $inQueryResource, self::COLUMN_USER_ID, $inQueryUser ); $params = array_merge(array_values($resourceIds), array_values($userIds)); //If resource doesn't have permission don't return null $returnValue = array_fill_keys($resourceIds, []); $results = $this->fetchQuery($query, $params); foreach ($results as $result) { $returnValue[$result[self::COLUMN_RESOURCE_ID]][] = $result[self::COLUMN_PRIVILEGE]; } return $returnValue; } public function getResourcesPermissions(array $resourceIds) { //If resource doesn't have permission don't return null $returnValue = array_fill_keys($resourceIds, []); $results = $this->getUsersWithPermissions($resourceIds); foreach ($results as $result) { $returnValue[$result[self::COLUMN_RESOURCE_ID]][$result[self::COLUMN_USER_ID]][] = $result[self::COLUMN_PRIVILEGE]; } return $returnValue; } /** * add permissions of a user to a resource * * @access public * @param string $user * @param string $resourceId * @param array $rights * * @return bool */ public function addPermissions($user, $resourceId, $rights) { foreach ($rights as $privilege) { // add a line with user URI, resource Id and privilege $this->getPersistence()->insert( self::TABLE_PRIVILEGES_NAME, [self::COLUMN_USER_ID => $user, self::COLUMN_RESOURCE_ID => $resourceId, self::COLUMN_PRIVILEGE => $privilege] ); } $this->getEventManager()->trigger(new DacAddedEvent($user, $resourceId, (array)$rights)); return true; } /** * add batch permissions * * @access public * @param array $permissionData * @return void */ public function addMultiplePermissions(array $permissionData) { $insert = []; foreach ($permissionData as $permissionItem) { foreach ($permissionItem['permissions'] as $userId => $privilegeIds) { if (!empty($privilegeIds)) { foreach ($privilegeIds as $privilegeId) { $insert [] = [ self::COLUMN_USER_ID => $userId, self::COLUMN_RESOURCE_ID => $permissionItem['resource']->getUri(), self::COLUMN_PRIVILEGE => $privilegeId ]; } } } } $this->getPersistence()->insertMultiple(self::TABLE_PRIVILEGES_NAME, $insert); foreach ($insert as $inserted) { $this->getEventManager()->trigger(new DacAddedEvent( $inserted[self::COLUMN_USER_ID], $inserted[self::COLUMN_RESOURCE_ID], (array)$inserted[self::COLUMN_PRIVILEGE] )); } } /** * Get the permissions to resource * * @access public * @param string $resourceId * @return array */ public function getResourcePermissions($resourceId) { // get privileges for a user/roles and a resource $query = sprintf( 'SELECT %s, %s FROM %s WHERE %s = ?', self::COLUMN_USER_ID, self::COLUMN_PRIVILEGE, self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID ); $results = $this->fetchQuery($query, [$resourceId]); foreach ($results as $result) { $returnValue[$result[self::COLUMN_USER_ID]][] = $result[self::COLUMN_PRIVILEGE]; } return $returnValue ?? []; } /** * remove permissions to a resource for a user * * @access public * @param string $user * @param string $resourceId * @param array $rights * @return boolean */ public function removePermissions($user, $resourceId, $rights) { //get all entries that match (user,resourceId) and remove them $inQueryPrivilege = implode(',', array_fill(0, count($rights), ' ? ')); $query = sprintf( 'DELETE FROM %s WHERE %s = ? AND %s IN (%s) AND %s = ?', self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID, self::COLUMN_PRIVILEGE, $inQueryPrivilege, self::COLUMN_USER_ID ); $params = array_merge([$resourceId], array_values($rights), [$user]); $this->getPersistence()->exec($query, $params); $this->getEventManager()->trigger(new DacRemovedEvent($user, $resourceId, $rights)); return true; } /** * remove batch permissions * * @access public * @param array $data * @return void */ public function removeMultiplePermissions(array $data) { $groupedRemove = []; $eventsData = []; foreach ($data as $permissionItem) { foreach ($permissionItem['permissions'] as $userId => $privilegeIds) { if (!empty($privilegeIds)) { $groupedRemove[$userId][implode($privilegeIds)]['resources'][] = $permissionItem['resource']->getUri(); $groupedRemove[$userId][implode($privilegeIds)]['privileges'] = $privilegeIds; $eventsData[] = ['userId' => $userId, 'resourceId' => $permissionItem['resource']->getUri(), 'privileges' => $privilegeIds]; } } } foreach ($groupedRemove as $userId => $resources) { foreach ($resources as $permissions) { $inQueryPrivilege = implode(',', array_fill(0, count($permissions['privileges']), ' ? ')); $inQueryResources = implode(',', array_fill(0, count($permissions['resources']), ' ? ')); $query = sprintf( 'DELETE FROM %s WHERE %s IN (%s) AND %s IN (%s) AND %s = ?', self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID, $inQueryResources, self::COLUMN_PRIVILEGE, $inQueryPrivilege, self::COLUMN_USER_ID ); $params = array_merge(array_values($permissions['resources']), array_values($permissions['privileges']), [$userId]); $this->getPersistence()->exec($query, $params); } } foreach ($eventsData as $eventData) { $this->getEventManager()->trigger(new DacRemovedEvent($eventData['userId'], $eventData['resourceId'], $eventData['privileges'])); } } /** * Remove all permissions from a resource * * @access public * @param array $resourceIds * @return boolean */ public function removeAllPermissions($resourceIds) { //get all entries that match (resourceId) and remove them $inQuery = implode(',', array_fill(0, count($resourceIds), ' ? ')); $query = sprintf( 'DELETE FROM %s WHERE %s IN (%s)', self::TABLE_PRIVILEGES_NAME, self::COLUMN_RESOURCE_ID, $inQuery ); $this->getPersistence()->exec($query, $resourceIds); foreach ($resourceIds as $resourceId) { $this->getEventManager()->trigger(new DacRemovedEvent('-', $resourceId, '-')); } return true; } /** * Filter users\roles that have no permissions * * @access public * @param array $userIds * @return array */ public function checkPermissions($userIds) { $inQueryUser = implode(',', array_fill(0, count($userIds), ' ? ')); $query = sprintf( 'SELECT %s FROM %s WHERE %s IN (%s)', self::COLUMN_USER_ID, self::TABLE_PRIVILEGES_NAME, self::COLUMN_USER_ID, $inQueryUser ); $results = $this->fetchQuery($query, array_values($userIds)); foreach ($results as $result) { $existsUsers[$result[self::COLUMN_USER_ID]] = $result[self::COLUMN_USER_ID]; } return $existsUsers ?? []; } /** * @return common_persistence_SqlPersistence */ private function getPersistence() { if (!$this->persistence) { $this->persistence = $this->getServiceLocator()->get(PersistenceManager::SERVICE_ID) ->getPersistenceById($this->getOption(self::OPTION_PERSISTENCE)); } return $this->persistence; } /** * @param string $query * @param array $params * @return array */ private function fetchQuery($query, $params) { $statement = $this->getPersistence()->query($query, $params); return $statement->fetchAll(PDO::FETCH_ASSOC); } public function createTables() { $schemaManager = $this->getPersistence()->getDriver()->getSchemaManager(); $schema = $schemaManager->createSchema(); $fromSchema = clone $schema; $table = $schema->createtable(self::TABLE_PRIVILEGES_NAME); $table->addColumn(self::COLUMN_USER_ID, 'string', ['notnull' => null, 'length' => 255]); $table->addColumn(self::COLUMN_RESOURCE_ID, 'string', ['notnull' => null, 'length' => 255]); $table->addColumn(self::COLUMN_PRIVILEGE, 'string', ['notnull' => null, 'length' => 255]); $table->setPrimaryKey([self::COLUMN_USER_ID, self::COLUMN_RESOURCE_ID, self::COLUMN_PRIVILEGE]); $table->addIndex([self::COLUMN_RESOURCE_ID], self::INDEX_RESOURCE_ID); $queries = $this->getPersistence()->getPlatform()->getMigrateSchemaSql($fromSchema, $schema); foreach ($queries as $query) { $this->getPersistence()->exec($query); } } public function removeTables() { $persistence = $this->getPersistence(); $schema = $persistence->getDriver()->getSchemaManager()->createSchema(); $fromSchema = clone $schema; $table = $schema->dropTable(self::TABLE_PRIVILEGES_NAME); $queries = $persistence->getPlatform()->getMigrateSchemaSql($fromSchema, $schema); foreach ($queries as $query) { $persistence->exec($query); } } }