/** * @covers Zend\Db\Sql\Update::getRawState */ public function testGetRawState() { $this->update->table('foo')->set(array('bar' => 'baz'))->where('x = y'); $this->assertEquals('foo', $this->update->getRawState('table')); $this->assertEquals(true, $this->update->getRawState('emptyWhereProtection')); $this->assertEquals(array('bar' => 'baz'), $this->update->getRawState('set')); $this->assertInstanceOf('Zend\\Db\\Sql\\Where', $this->update->getRawState('where')); }
/** * @param Update $update */ public function preUpdate(Update $update) { $metaColumns = $this->tableGateway->getColumns(); if (count($metaColumns)) { $metaColumns = array_flip($metaColumns); $set = $update->getRawState('set'); $set = array_intersect_key($set, $metaColumns); $update->set($set); } }
/** * @param Update $update * @return mixed * @throws Exception\RuntimeException * @throws \Directus\Acl\Exception\UnauthorizedFieldWriteException * @throws \Directus\Acl\Exception\UnauthorizedTableBigEditException * @throws \Directus\Acl\Exception\UnauthorizedTableEditException */ protected function executeUpdate(Update $update) { $currentUserId = null; if (Auth::loggedIn()) { $currentUser = Auth::getUserInfo(); $currentUserId = intval($currentUser['id']); } $updateState = $update->getRawState(); $updateTable = $this->getRawTableNameFromQueryStateTable($updateState['table']); $cmsOwnerColumn = $this->acl->getCmsOwnerColumnByTable($updateTable); /** * ACL Enforcement */ // check if it's NOT soft delete $updateFields = $updateState['set']; if (!(count($updateFields) == 2 && array_key_exists(STATUS_COLUMN_NAME, $updateFields) && $updateFields[STATUS_COLUMN_NAME] == STATUS_DELETED_NUM)) { if (!$this->acl->hasTablePrivilege($updateTable, 'bigedit')) { // Parsing for the column name is unnecessary. Zend enforces raw column names. /** * Enforce Privilege: "Big" Edit */ if (false === $cmsOwnerColumn) { // All edits are "big" edits if there is no magic owner column. $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); throw new UnauthorizedTableBigEditException($aclErrorPrefix . "The table `{$updateTable}` is missing the `user_create_column` within `directus_tables` (BigEdit Permission Forbidden)"); } else { // Who are the owners of these rows? list($resultQty, $ownerIds) = $this->acl->getCmsOwnerIdsByTableGatewayAndPredicate($this, $updateState['where']); // Enforce if (is_null($currentUserId) || count(array_diff($ownerIds, array($currentUserId)))) { $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); throw new UnauthorizedTableBigEditException($aclErrorPrefix . "Table bigedit access forbidden on {$resultQty} `{$updateTable}` table record(s) and " . count($ownerIds) . " CMS owner(s) (with ids " . implode(", ", $ownerIds) . ")."); } } /** * Enforce write field blacklist (if user lacks bigedit privileges on this table) */ $attemptOffsets = array_keys($updateState['set']); $this->acl->enforceBlacklist($updateTable, $attemptOffsets, Acl::FIELD_WRITE_BLACKLIST); } if (!$this->acl->hasTablePrivilege($updateTable, 'edit')) { /** * Enforce Privilege: "Little" Edit (I am the record CMS owner) */ if (false !== $cmsOwnerColumn) { if (!isset($predicateResultQty)) { // Who are the owners of these rows? list($predicateResultQty, $predicateOwnerIds) = $this->acl->getCmsOwnerIdsByTableGatewayAndPredicate($this, $updateState['where']); } if (in_array($currentUserId, $predicateOwnerIds)) { $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); throw new UnauthorizedTableEditException($aclErrorPrefix . "Table edit access forbidden on {$predicateResultQty} `{$updateTable}` table records owned by the authenticated CMS user (#{$currentUserId})."); } } } } try { return parent::executeUpdate($update); } catch (\Zend\Db\Adapter\Exception\InvalidQueryException $e) { if ('production' !== DIRECTUS_ENV) { // @TODO: these lines are the same as the executeInsert, // let's put it together if (strpos(strtolower($e->getMessage()), 'duplicate entry') !== FALSE) { throw new DuplicateEntryException($e->getMessage()); } throw new \RuntimeException("This query failed: " . $this->dumpSql($update), 0, $e); } // @todo send developer warning throw $e; } }
/** * @todo add $columns support * * @param Update $update * @return mixed * @throws Exception\RuntimeException */ protected function executeUpdate(Update $update) { $updateState = $update->getRawState(); if ($updateState['table'] != $this->table) { throw new Exception\RuntimeException('The table name of the provided Update object must match that of the table'); } // apply preUpdate features $this->featureSet->apply('preUpdate', array($update)); $statement = $this->sql->prepareStatementForSqlObject($update); $result = $statement->execute(); // apply postUpdate features $this->featureSet->apply('postUpdate', array($statement, $result)); return $result->getAffectedRows(); }
/** * @todo add $columns support * * @param Update $update * @return int * @throws Exception\RuntimeException */ protected function executeUpdate(Update $update) { $updateState = $update->getRawState(); if ($updateState['table'] != $this->table) { throw new Exception\RuntimeException('The table name of the provided Update object must match that of the table'); } // apply preUpdate features $this->featureSet->apply(EventFeatureEventsInterface::EVENT_PRE_UPDATE, [$update]); $unaliasedTable = false; if (is_array($updateState['table'])) { $tableData = array_values($updateState['table']); $unaliasedTable = array_shift($tableData); $update->table($unaliasedTable); } $statement = $this->sql->prepareStatementForSqlObject($update); $result = $statement->execute(); // apply postUpdate features $this->featureSet->apply(EventFeatureEventsInterface::EVENT_POST_UPDATE, [$statement, $result]); // Reset original table information in Update instance, if necessary if ($unaliasedTable) { $update->table($updateState['table']); } return $result->getAffectedRows(); }
/** * @todo add $columns support * * @param Update $update * @return mixed * @throws Exception\RuntimeException */ protected function executeUpdate(Update $update) { $updateState = $update->getRawState(); if ($updateState['table'] != $this->table) { throw new Exception\RuntimeException('The table name of the provided Update object must match that of the table'); } // apply preUpdate features $this->featureSet->apply(EventFeatureEventsInterface::EVENT_PRE_UPDATE, [$update]); $statement = $this->sql->prepareStatementForSqlObject($update); $result = $statement->execute(); // apply postUpdate features $this->featureSet->apply(EventFeatureEventsInterface::EVENT_POST_UPDATE, [$statement, $result]); return $result->getAffectedRows(); }
public function getRawState($key = null) { $rawState = parent::getRawState(); $rawState['option'] = $this->option; return isset($key) && array_key_exists($key, $rawState) ? $rawState[$key] : $rawState; }
/** * @param Update $update * @return mixed * @throws Exception\RuntimeException * @throws \Directus\Acl\Exception\UnauthorizedFieldWriteException * @throws \Directus\Acl\Exception\UnauthorizedTableBigEditException * @throws \Directus\Acl\Exception\UnauthorizedTableEditException */ protected function executeUpdate(Update $update) { $currentUserId = null; if (Auth::loggedIn()) { $currentUser = Auth::getUserInfo(); $currentUserId = intval($currentUser['id']); } $updateState = $update->getRawState(); $updateTable = $this->getRawTableNameFromQueryStateTable($updateState['table']); $cmsOwnerColumn = $this->acl->getCmsOwnerColumnByTable($updateTable); $updateData = $updateState['set']; /** * ACL Enforcement */ // check if it's NOT soft delete $updateFields = $updateState['set']; $permissionName = 'edit'; $hasStatusColumn = array_key_exists(STATUS_COLUMN_NAME, $updateFields) ? true : false; if ($hasStatusColumn && $updateFields[STATUS_COLUMN_NAME] == STATUS_DELETED_NUM) { $permissionName = 'delete'; } if (!$this->acl->hasTablePrivilege($updateTable, 'big' . $permissionName)) { // Parsing for the column name is unnecessary. Zend enforces raw column names. /** * Enforce Privilege: "Big" Edit */ if (false === $cmsOwnerColumn) { // All edits are "big" edits if there is no magic owner column. $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); throw new UnauthorizedTableBigEditException($aclErrorPrefix . 'The table `' . $updateTable . '` is missing the `user_create_column` within `directus_tables` (BigEdit Permission Forbidden)'); } else { // Who are the owners of these rows? list($resultQty, $ownerIds) = $this->acl->getCmsOwnerIdsByTableGatewayAndPredicate($this, $updateState['where']); // Enforce if (is_null($currentUserId) || count(array_diff($ownerIds, [$currentUserId]))) { // $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); // throw new UnauthorizedTableBigEditException($aclErrorPrefix . "Table bigedit access forbidden on $resultQty `$updateTable` table record(s) and " . count($ownerIds) . " CMS owner(s) (with ids " . implode(", ", $ownerIds) . ")."); $groupsTableGateway = self::makeTableGatewayFromTableName($this->acl, 'directus_groups', $this->adapter); $group = $groupsTableGateway->find($this->acl->getGroupId()); throw new UnauthorizedTableBigEditException('[' . $group['name'] . '] permissions only allow you to [' . $permissionName . '] your own items.'); } } } if (!$this->acl->hasTablePrivilege($updateTable, $permissionName)) { /** * Enforce Privilege: "Little" Edit (I am the record CMS owner) */ if (false !== $cmsOwnerColumn) { if (!isset($predicateResultQty)) { // Who are the owners of these rows? list($predicateResultQty, $predicateOwnerIds) = $this->acl->getCmsOwnerIdsByTableGatewayAndPredicate($this, $updateState['where']); } if (in_array($currentUserId, $predicateOwnerIds)) { $aclErrorPrefix = $this->acl->getErrorMessagePrefix(); throw new UnauthorizedTableEditException($aclErrorPrefix . 'Table edit access forbidden on ' . $predicateResultQty . '`' . $updateTable . '` table records owned by the authenticated CMS user (#' . $currentUserId . '.'); } } } // Enforce write field blacklist $attemptOffsets = array_keys($updateState['set']); $this->acl->enforceBlacklist($updateTable, $attemptOffsets, Acl::FIELD_WRITE_BLACKLIST); try { $this->emitter->run('table.update:before', [$updateTable, $updateData]); $this->emitter->run('table.update.' . $updateTable . ':before', [$updateData]); $result = parent::executeUpdate($update); $this->emitter->run('table.update', [$updateTable, $updateData]); $this->emitter->run('table.update:after', [$updateTable, $updateData]); $this->emitter->run('table.update.' . $updateTable, [$updateData]); $this->emitter->run('table.update.' . $updateTable . ':after', [$updateData]); return $result; } catch (\Zend\Db\Adapter\Exception\InvalidQueryException $e) { // @TODO: these lines are the same as the executeInsert, // let's put it together if (strpos(strtolower($e->getMessage()), 'duplicate entry') !== FALSE) { throw new DuplicateEntryException($e->getMessage()); } if ('production' !== DIRECTUS_ENV) { throw new \RuntimeException('This query failed: ' . $this->dumpSql($update), 0, $e); } // @todo send developer warning throw $e; } }