/** * @param array $params * @return array */ public function getList(array $params = null) { require_once 'Util/Sql.php'; require_once 'Util/Array.php'; $params = is_array($params) ? $params : array(); if (isset($params['entourage'])) { require_once 'models/AclHandler/Entourage.php'; $entourageHandler = new Default_Model_AclHandler_Entourage($this->getAcl(), $this->getAclContextUser()); $data = $entourageHandler->getList(array($this->_resourceName => $params)); return $data[$this->_resourceName]; } if (isset($params['where'])) { // use default properties to search against if none are provided if (!is_array($params['where'])) { // no where terms specified, use the defaults at the 'or' level $defaultWhereStruct = array(); foreach ($this->_defaultListWhere as $whereTerm) { $defaultWhereStruct[] = array($whereTerm => $params['where']); } $params['where'] = array($defaultWhereStruct); } } else { $params['where'] = array(); } if (!isset($params['sort']) || (!is_string($params['sort']) && !is_array($params['sort']))) { $params['sort'] = $this->_defaultListSort; } $params['sort'] = Util_Sql::generateSqlOrderBy($params['sort'], $this->getPropertyKeys()); // expected that: 0 < limit <= _listMaxLength if (!isset($params['limit']) || 0 >= $params['limit'] || $this->_listMaxLength < $params['limit']) { $params['limit'] = $this->_listMaxLength; } $params['limit'] = (integer) $params['limit']; // group by if (!isset($params['groupBy']) || !in_array($params['groupBy'], $this->getPropertyKeys())) { $params['groupBy'] = null; } // order of elements before group by, determines which row gets used // by group by from within the group, the last in the group wins if (!isset($params['condenseOn']) || (!is_string($params['condenseOn']) && !is_array($params['condenseOn']))) { $params['condenseOn'] = $this->_defaultListSort; } $params['condenseOn'] = Util_Sql::generateSqlOrderBy($params['condenseOn'], $this->getPropertyKeys()); // properties is expected to be an array of string property keys or a // string of space separated property keys // // array('id', 'discussion_id', 'comment', 'modified') // - or - // 'id discussion_id comment modified' { if (!isset($params['properties'])) { $params['properties'] = $this->getPropertyKeys(); } if (!is_array($params['properties'])) { $params['properties'] = explode(' ', $params['properties']); } $validatedProps = array_intersect($this->getPropertyKeys(), $params['properties']); if (count($validatedProps) != count($params['properties'])) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('[' . implode(', ', array_diff($params['properties'], $validatedProps)) . '] are not valid properties for ' . $this->_resourceName); } } // this is an optimization. Imposes a slight overhead performance hit, // but for queries where there is very restrictive dependency and a // large resource set being scanned, this optimization is very important if (0 < count($this->_permissionDependency)) { // go through all the dependies and make sure there are WHERE // clauses for them foreach ($this->_permissionDependency as $depResource => $depAssoc) { foreach ($depAssoc as $depId => $resourceId) { // if there is a WHERE already for the resource id // associated with the dependency, then can't optimize further if (in_array($resourceId, array_keys($params['where']))) { continue; } $parentResourceHandler = $this->_createAclHandler($depResource); $list = Util_Array::arrayFromKeyValuesOfSet($depId, $parentResourceHandler->getList(array('limit' => $this->_listMaxLength))); if (0 == count($list)) { // none of the dependencies, done return array(); } if ($this->_listMaxLength == count($list)) { // optimization isn't optimal in this situation abort // for dependency association continue; } $params['where'][$resourceId] = $list; } } } $whereSqlAndParam = Util_Sql::generateSqlWheresAndParams($params['where'], $this->getPropertyKeys()); $limit = $params['limit']; $dbLimit = floor($limit * $this->_listPermissionFilteredRate); $cumulativeRowSet = array(); $offset = 0; do { $sql = ''; // do sub select in order to handle sub ordering in group and get // the row we want $sql .= (($params['groupBy'] && $params['condenseOn']) ? ' SELECT * FROM (' : ''); // RESOURCE $sql .= $this->_getListResourceSqlFragment(); $sql .= '' // ACL . $this->_getGenericAclListJoins() // ACL . ' WHERE ' . $this->_getGenericAclListWheres() // RESOURCE . ' AND ' . $whereSqlAndParam['sql'] . ($params['groupBy'] ? (($params['condenseOn'] ? (' ORDER BY ' . implode(', ', $params['condenseOn']) . ')') : '') . ' GROUP BY ' . $params['groupBy']) : '') . ' ORDER BY ' . implode(', ', $params['sort']) . ' LIMIT ' . $dbLimit . ' OFFSET ' . $offset . ''; $query = $this->_getDbHandler()->prepare($sql); $query->execute(array_merge($this->_getGenericAclListParams(), $whereSqlAndParam['param'])); $rowSet = $query->fetchAll(PDO::FETCH_ASSOC); $countUnfiltered = count($rowSet); $this->_filterDependenciesNotAllowed($rowSet); $cumulativeRowSet += $rowSet; $countCumulativeFiltered = count($cumulativeRowSet); $offset += $dbLimit; } while ($countCumulativeFiltered < $limit && $countUnfiltered == $dbLimit); // ensure that only the desired properties are returned if (!empty($cumulativeRowSet) && count($params['properties']) != count($cumulativeRowSet[0])) { foreach ($cumulativeRowSet as &$row) { $row = array_intersect_key($row, array_flip($params['properties'])); } } return array_slice($cumulativeRowSet, 0, $limit); }
/** * Loops through the roles to check for one that is allowed for the method. * * @param string $method, same as what Zend_Acl referers to as 'privilege' but 'method' used for REST context * @return boolean */ public function isAllowed($privilege, array $roleResourceId = null, array $resourceId = null) { // for regular resources the $roleResourceId can be specified and // it is used for the resource as well. For permission type resources // $roleResourceId should be passed as null and will be determined if (null !== $roleResourceId && null === $resourceId) { $resourceId = $roleResourceId; } $user = $this->getAclContextUser(); if (null === $user) { $roleSet = $this->getRoles(); } else { $roleResourceGeneral = $this->getRoleResourceId(); // first get the possible roles this user has with the resource if (null === $resourceId) { $sql = 'SELECT role FROM resource_role' . ' WHERE user_id = :userId' . ' AND resource = :roleResourceGeneral' . ' AND resource_id IS NULL' . ''; $query = $this->_getDbHandler()->query($sql); $query->execute(array( ':userId' => $user->id, ':roleResourceGeneral' => $roleResourceGeneral, )); } else { if (null === $roleResourceId) { $sql = 'SELECT role FROM resource_role' . ' WHERE user_id = :userId' . ' AND (' . ' resource = :roleResourceGeneral' . ' AND id = :resourceId' . ' )' . ''; $query = $this->_getDbHandler()->query($sql); $query->execute(array( ':userId' => $user->id, ':roleResourceGeneral' => $roleResourceGeneral, ':resourceId' => $resourceId['id'], )); } else { // include roles from specific case $roleResourceSpecific = $this->getSpecificRoleResourceId($roleResourceId); $sql = 'SELECT role FROM resource_role' . ' WHERE user_id = :userId' . ' AND (' . ' resource = :roleResourceGeneral' . ' AND (' . ' resource_id IS NULL' . ' OR resource_id = :roleResourceSpecific' . ' )' . ' )' . ''; $query = $this->_getDbHandler()->query($sql); $query->execute(array( ':userId' => $user->id, ':roleResourceGeneral' => $roleResourceGeneral, ':roleResourceSpecific' => $roleResourceSpecific, )); } } $rowSet = $query->fetchAll(PDO::FETCH_ASSOC); $roleSet = Util_Array::arrayFromKeyValuesOfSet('role', $rowSet); } // make sure 'default' role is in there if (!in_array('default', $roleSet)) { $roleSet[] = 'default'; } $resourceGeneral = $this->getResourceId(); if (null !== $resourceId && null !== $roleResourceId) { $resourceSpecific = $this->getSpecificResourceId($resourceId); // first check if against this specific resource things are // allowed or denied $roleVarKeyLookup = array(); foreach ($roleSet as $index => $role) { $roleVarKeyLookup[':role_' . $index] = $role; } $sql = 'SELECT p.id, p.permission, p.privilege, p.resource, p.role' . ' FROM permission AS p' . ' WHERE p.role IN (' . implode(', ', array_keys($roleVarKeyLookup)) . ')' . ' AND p.resource = :resourceGeneral' . ' AND p.resource_id = :resourceSpecific' . ' AND p.privilege = :privilege' . ' ORDER BY p.permission ASC' . ''; $query = $this->_getDbHandler()->prepare($sql); $query->execute(array_merge( array( ':resourceGeneral' => $resourceGeneral, ':resourceSpecific' => $resourceSpecific, ':privilege' => $privilege, ), $roleVarKeyLookup )); $row = $query->fetch(PDO::FETCH_ASSOC); if (false !== $row) { // able to say that this specific resource is either allowed or denied return $row['permission'] == 'allow'; } } // specific resource check wasn't definitive, check the general resource // add the default role $allowed = false; foreach ($roleSet as $role) { // check if a role is accepted if ($this->getAcl()->isAllowed($role, $resourceGeneral, $privilege)) { // if any role is found that allows, the whole thing allows return true; } } // no allows found in the general resource, so permission is denied return false; }
/** * * @param mixed $entourageSetParam * @param array $resourceList * @param Rest_Model_EntourageImplementer_Interface */ protected function _entouragePopulate($entourageSetParam, &$resourceList, $resourceHandler) { require_once 'Util/Array.php'; // attach to the resource all the entourage resources if (empty($entourageSetParam)) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage resources not provided'); } // entourage wasn't passed as an array, get it set up if (!is_array($entourageSetParam)) { $entourageSetParam = array($entourageSetParam); } if (empty($resourceList)) { return; } foreach ($entourageSetParam as $name => $entourageParam) { // get the entourage set and attach the values to the matching // items in the resource list // if entourage item wasn't passed as key/value pair, load from value if (is_int($name)) { $name = $entourageParam; $entourageParam = true; } // if entourage item wasn't expanded then get it from resource if (!is_array($entourageParam)) { $entourageParam = $resourceHandler->expandEntourageAlias($name); if (null === $entourageParam) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage alias "' . $name . '" is not known'); } } // validate the input param if (!isset($entourageParam['resourceIdKey'])) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage alias "' . $name . '" does not specify a resourceIdKey'); } if (!isset($entourageParam['entourageIdKey'])) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage alias "' . $name . '" does not specify a entourageIdKey'); } if (!isset($entourageParam['entourageModel'])) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage alias "' . $name . '" does not specify a entourageModel'); } $resourceIdKey = $entourageParam['resourceIdKey']; unset($entourageParam['resourceIdKey']); // wont be passed into entourage getList $entourageIdKey = $entourageParam['entourageIdKey']; unset($entourageParam['entourageIdKey']); // wont be passed into entourage getList $entourageModel = $entourageParam['entourageModel']; unset($entourageParam['entourageModel']); // wont be passed into entourage getList // use entourage name if specified otherwise use the alias name $entourageName = isset($entourageParam['entourageName']) ? $entourageParam['entourageName'] : $name; unset($entourageParam['entourageName']); // wont be passed into entourage getList // if specified only return the first match for entourages that match, can make for a // cleaner api for some use cases $singleOnly = isset($entourageParam['singleOnly']) && $entourageParam['singleOnly'] && $entourageParam['singleOnly'] !== 'false' ? $entourageParam['singleOnly'] : false; unset($entourageParam['singleOnly']); // wont be passed into entourage getList $entourageHandler = $this->_createAclHandler($entourageModel); // get only the entourage resources needed for the resource $resourceJoinIdList = Util_Array::arrayFromKeyValuesOfSet($resourceIdKey, $resourceList); if (empty($resourceJoinIdList)) { require_once 'Rest/Model/BadRequestException.php'; throw new Rest_Model_BadRequestException('entourage alias "' . $name . '" specifies an invalid resourceIdKey "' . $resourceIdKey . '"'); } // default, assume that the sub results will be more than the limit $resultWillBeLessThanLimit = false; if ($singleOnly && empty($entourageParam['groupBy'])) { $entourageParam['groupBy'] = $entourageIdKey; $resultWillBeLessThanLimit = true; } // TODO: figure out nature of sort versus condenseOn and if // condenseOn should be presented to user, either way some // checks and better implementation of condenseOn is needed if ($singleOnly && isset($entourageParam['condenseOn']) && $entourageParam['condenseOn']) { $entourageParam['condenseOn'] = $entourageParam['condenseOn']; } if (empty($entourageParam['where'])) { $entourageParam['where'] = array(); } // load the entourage items if ($resultWillBeLessThanLimit) { $entourageParam['where'] = array_merge($entourageParam['where'], array( // because there could be duplicate ids: array_unique and reindex with array_values $entourageIdKey => array_values(array_unique($resourceJoinIdList)) )); $entourageList = $entourageHandler->getList($entourageParam); } else { $entourageList = array(); foreach (array_values(array_unique($resourceJoinIdList)) as $resourceId) { $entourageParam['where'][$entourageIdKey] = $resourceId; $entourageList = array_merge($entourageList, $entourageHandler->getList($entourageParam)); } } // attach the entourage items to the resource $entourageJoinIdList = Util_Array::arrayFromKeyValuesOfSet($entourageIdKey, $entourageList); foreach ($resourceList as &$resource) { $joinKeySet = array_keys($entourageJoinIdList, $resource[$resourceIdKey]); if ($singleOnly) { $first = current($joinKeySet); $resource[$entourageName] = $first !== false ? $entourageList[$first] : null; } else { $resource[$entourageName] = array(); foreach ($joinKeySet as $joinKey) { $resource[$entourageName][] = $entourageList[$joinKey]; } } } } }