Пример #1
0
 /**
  * Checks to see if there is a valid logged in user.
  * @throws \DreamFactory\Core\Exceptions\UnauthorizedException
  */
 private static function checkUser()
 {
     $userId = SessionUtility::getCurrentUserId();
     if (empty($userId)) {
         throw new UnauthorizedException('There is no valid session for the current request.');
     }
 }
Пример #2
0
 /**
  * Builds the selection criteria from request and returns it.
  *
  * @return array
  */
 protected function getSelectionCriteria()
 {
     /** @type TableSchema $schema */
     $schema = $this->getModel()->getTableSchema();
     $criteria = ['params' => []];
     if (null !== ($value = $this->request->getParameter(ApiOptions::FIELDS))) {
         $criteria['select'] = explode(',', $value);
     } else {
         $criteria['select'] = ['*'];
     }
     if (null !== ($value = $this->request->getPayloadData(ApiOptions::PARAMS))) {
         $criteria['params'] = $value;
     }
     if (null !== ($value = $this->request->getParameter(ApiOptions::FILTER))) {
         $native = $this->convertFilterToNative($value, $criteria['params'], [], $schema->columns);
         $criteria['condition'] = $native['where'];
         if (is_array($native['params'])) {
             if (is_array($criteria['params'])) {
                 $criteria['params'] = array_merge($criteria['params'], $native['params']);
             } else {
                 $criteria['params'] = $native['params'];
             }
         }
         //	Add current user ID into parameter array if in condition, but not specified.
         if (false !== stripos($value, ':user_id')) {
             if (!isset($criteria['params'][':user_id'])) {
                 $criteria['params'][':user_id'] = SessionUtility::getCurrentUserId();
             }
         }
     }
     $value = intval($this->request->getParameter(ApiOptions::LIMIT));
     $maxAllowed = $this->getMaxRecordsReturnedLimit();
     if ($value < 1 || $value > $maxAllowed) {
         // impose a limit to protect server
         $value = $maxAllowed;
     }
     $criteria['limit'] = $value;
     // merge in possible payload options
     $optionNames = [ApiOptions::OFFSET, ApiOptions::ORDER, ApiOptions::GROUP];
     foreach ($optionNames as $option) {
         if (null !== ($value = $this->request->getParameter($option))) {
             $criteria[$option] = $value;
         } elseif (!empty($otherNames = ApiOptions::getAliases($option))) {
             foreach ($otherNames as $other) {
                 if (null !== ($value = $this->request->getParameter($other))) {
                     $criteria[$option] = $value;
                 } elseif (null !== ($value = $this->request->getPayloadData($other))) {
                     $criteria[$option] = $value;
                 }
             }
         }
         if (!isset($criteria[$option]) && null !== ($value = $this->request->getPayloadData($option))) {
             $criteria[$option] = $value;
         }
     }
     return $criteria;
 }
Пример #3
0
 /**
  * @param       $id
  * @param       $record
  * @param array $params
  *
  * @return array
  * @throws \DreamFactory\Core\Exceptions\BadRequestException
  * @throws \DreamFactory\Core\Exceptions\InternalServerErrorException
  * @throws \DreamFactory\Core\Exceptions\NotFoundException
  */
 public static function deleteInternal($id, $record, $params = [])
 {
     if (empty($record)) {
         throw new BadRequestException('There are no fields in the record to create . ');
     }
     if (empty($id)) {
         //Todo:perform logging below
         //Log::error( 'Update request with no id supplied: ' . print_r( $record, true ) );
         throw new BadRequestException('Identifying field "id" can not be empty for update request . ');
     }
     $userId = SessionUtility::getCurrentUserId();
     $model = static::whereUserId($userId)->whereName($id)->first();
     if (!$model instanceof Model) {
         throw new NotFoundException('No resource found for ' . $id);
     }
     try {
         $result = static::buildResult($model, $params);
         $model->delete();
         return $result;
     } catch (\Exception $ex) {
         throw new InternalServerErrorException('Failed to delete resource: ' . $ex->getMessage());
     }
 }
Пример #4
0
 /**
  * {@inheritdoc}
  */
 public static function deleteInternal($id, $record, $params = [])
 {
     if (empty($record)) {
         throw new BadRequestException('There are no fields in the record to create . ');
     }
     if (empty($id)) {
         //Todo:perform logging below
         //Log::error( 'Update request with no id supplied: ' . print_r( $record, true ) );
         throw new BadRequestException('Identifying field "id" can not be empty for update request . ');
     }
     /** @type User $model */
     $model = static::find($id);
     if (!$model instanceof Model) {
         throw new NotFoundException('No resource found for ' . $id);
     }
     try {
         if ($model->is_sys_admin && !ArrayUtils::getBool($params, 'admin')) {
             throw new ForbiddenException('Not allowed to delete an admin user.');
         } elseif (ArrayUtils::getBool($params, 'admin') && !$model->is_sys_admin) {
             throw new BadRequestException('Cannot delete a non-admin user.');
         } elseif (Session::getCurrentUserId() === $model->id) {
             throw new ForbiddenException('Cannot delete your account.');
         }
         $result = static::buildResult($model, $params);
         $model->delete();
         return $result;
     } catch (\Exception $ex) {
         if (!$ex instanceof ForbiddenException && !$ex instanceof BadRequestException) {
             throw new InternalServerErrorException('Failed to delete resource: ' . $ex->getMessage());
         } else {
             throw $ex;
         }
     }
 }
Пример #5
0
 /**
  * Handle an incoming request.
  *
  * @param  \Illuminate\Http\Request $request
  * @param  \Closure                 $next
  *
  * @return mixed
  */
 public function handle($request, Closure $next)
 {
     //Get the Console API Key
     $consoleApiKey = AccessCheck::getConsoleApiKey($request);
     // Get limits
     if (config('df.standalone') === true || $consoleApiKey === Managed::getConsoleKey()) {
         return $next($request);
     } else {
         $limits = Managed::getLimits();
         // The limits array comes across from the console as a bunch of Std Objects, need to turn it back
         // into an array
         $limits['api'] = (array) $limits['api'];
         foreach (array_keys($limits['api']) as $key) {
             $limits['api'][$key] = (array) $limits['api'][$key];
         }
     }
     if (!empty($limits) && is_null($this->_getServiceName()) === false) {
         $this->_inUnitTest = \Config::get('api_limits_test');
         $userName = $this->_getUser(Session::getCurrentUserId());
         $userRole = $this->_getRole(Session::getRoleId());
         $apiName = $this->_getApiKey(Session::getApiKey());
         $serviceName = $this->_getServiceName();
         $clusterName = Managed::getClusterName();
         // Build the list of API Hits to check
         $apiKeysToCheck = ['cluster.default' => 0, 'instance.default' => 0];
         $serviceKeys[$serviceName] = 0;
         if (is_null($userRole) === false) {
             $serviceKeys[$serviceName . '.' . $userRole] = 0;
         }
         if (is_null($userName) === false) {
             $serviceKeys[$serviceName . '.' . $userName] = 0;
         }
         if (is_null($apiName) === false) {
             $apiKeysToCheck[$apiName] = 0;
             if (is_null($userRole) === false) {
                 $apiKeysToCheck[$apiName . '.' . $userRole] = 0;
             }
             if (is_null($userName) === false) {
                 $apiKeysToCheck[$apiName . '.' . $userName] = 0;
             }
             foreach ($serviceKeys as $key => $value) {
                 $apiKeysToCheck[$apiName . '.' . $key] = $value;
             }
         }
         if (is_null($clusterName) === false) {
             $apiKeysToCheck[$clusterName] = 0;
             if (is_null($userRole) === false) {
                 $apiKeysToCheck[$clusterName . '.' . $userRole] = 0;
             }
             if (is_null($userName) === false) {
                 $apiKeysToCheck[$clusterName . '.' . $userName] = 0;
             }
             foreach ($serviceKeys as $key => $value) {
                 $apiKeysToCheck[$clusterName . '.' . $key] = $value;
             }
         }
         if (is_null($userName) === false) {
             $apiKeysToCheck[$userName] = 0;
         }
         if (is_null($userRole) === false) {
             $apiKeysToCheck[$userRole] = 0;
         }
         $apiKeysToCheck = array_merge($apiKeysToCheck, $serviceKeys);
         $timePeriods = ['minute', 'hour', 'day', '7-day', '30-day'];
         $overLimit = false;
         try {
             foreach (array_keys($apiKeysToCheck) as $key) {
                 foreach ($timePeriods as $period) {
                     $keyToCheck = $key . '.' . $period;
                     if (array_key_exists($keyToCheck, $limits['api']) === true) {
                         $cacheValue = \Cache::get($keyToCheck, 0);
                         $cacheValue++;
                         \Cache::put($keyToCheck, $cacheValue, $limits['api'][$keyToCheck]['period']);
                         if ($cacheValue > $limits['api'][$keyToCheck]['limit']) {
                             $overLimit = true;
                         }
                     }
                 }
             }
         } catch (\Exception $e) {
             return ResponseFactory::getException(new InternalServerErrorException('Unable to update cache'), $request);
         }
         if ($overLimit === true) {
             return ResponseFactory::getException(new TooManyRequestsException('Specified connection limit exceeded'), $request);
         }
     }
     return $next($request);
 }
Пример #6
0
 /**
  * @param array          $record
  * @param ColumnSchema[] $fields_info
  * @param array          $filter_info
  * @param bool           $for_update
  * @param array          $old_record
  *
  * @return array
  * @throws \Exception
  */
 protected function parseRecord($record, $fields_info, $filter_info = null, $for_update = false, $old_record = null)
 {
     $record = $this->interpretRecordValues($record);
     $parsed = empty($fields_info) ? $record : [];
     if (!empty($fields_info)) {
         $record = array_change_key_case($record, CASE_LOWER);
         foreach ($fields_info as $fieldInfo) {
             // add or override for specific fields
             switch ($fieldInfo->type) {
                 case 'timestamp_on_create':
                     if (!$for_update) {
                         $parsed[$fieldInfo->name] = $this->getCurrentTimestamp();
                     }
                     break;
                 case 'timestamp_on_update':
                     $parsed[$fieldInfo->name] = $this->getCurrentTimestamp();
                     break;
                 case 'user_id_on_create':
                     if (!$for_update) {
                         $userId = Session::getCurrentUserId();
                         if (isset($userId)) {
                             $parsed[$fieldInfo->name] = $userId;
                         }
                     }
                     break;
                 case 'user_id_on_update':
                     $userId = Session::getCurrentUserId();
                     if (isset($userId)) {
                         $parsed[$fieldInfo->name] = $userId;
                     }
                     break;
                 default:
                     $name = strtolower($fieldInfo->getName(true));
                     if (array_key_exists($name, $record)) {
                         $fieldVal = ArrayUtils::get($record, $name);
                         // due to conversion from XML to array, null or empty xml elements have the array value of an empty array
                         if (is_array($fieldVal) && empty($fieldVal)) {
                             $fieldVal = null;
                         }
                         // overwrite some undercover fields
                         if ($fieldInfo->autoIncrement) {
                             // should I error this?
                             // drop for now
                             unset($record[$name]);
                             continue;
                         }
                         if (is_null($fieldVal) && !$fieldInfo->allowNull) {
                             throw new BadRequestException("Field '{$name}' can not be NULL.");
                         }
                         /** validations **/
                         if (!static::validateFieldValue($fieldInfo->getName(true), $fieldVal, $fieldInfo->validation, $for_update, $fieldInfo)) {
                             // if invalid and exception not thrown, drop it
                             unset($record[$name]);
                             continue;
                         }
                         $fieldVal = $this->parseValueForSet($fieldVal, $fieldInfo);
                         $parsed[$fieldInfo->name] = $fieldVal;
                         unset($record[$name]);
                     } else {
                         // if field is required, kick back error
                         if ($fieldInfo->getRequired() && !$for_update) {
                             throw new BadRequestException("Required field '{$name}' can not be NULL.");
                         }
                         break;
                     }
                     break;
             }
         }
     }
     if (!empty($filter_info)) {
         $this->validateRecord($parsed, $filter_info, $for_update, $old_record);
     }
     return $parsed;
 }
Пример #7
0
 /**
  * Update the model in the database.
  *
  * @param array $attributes
  *
  * @return bool|int
  * @throws \Exception
  */
 public function update(array $attributes = [])
 {
     $relations = [];
     $transaction = false;
     foreach ($attributes as $key => $value) {
         if ($this->isRelationMapped($key)) {
             $relations[$key] = $value;
             unset($attributes[$key]);
         }
     }
     if (count($relations) > 0) {
         DB::beginTransaction();
         $transaction = true;
     }
     try {
         $userId = SessionUtility::getCurrentUserId();
         if ($userId && static::isField('last_modified_by_id')) {
             $this->last_modified_by_id = $userId;
         }
         $updated = parent::update($attributes);
         if ($updated && $this->exists && count($relations) > 0) {
             foreach ($relations as $name => $value) {
                 $relatedModel = $this->getReferencingModel($name);
                 if (RelationSchema::HAS_MANY === $this->getReferencingType($name)) {
                     $hasMany = $this->getHasManyByRelationName($name);
                     $this->saveHasManyData($relatedModel, $hasMany, $value, $name);
                 }
             }
         }
         if ($transaction) {
             DB::commit();
         }
     } catch (\Exception $e) {
         if ($transaction) {
             DB::rollBack();
         }
         throw $e;
     }
     return $updated;
 }
 /**
  * Handle an incoming request.
  *
  * @param  \Illuminate\Http\Request $request
  * @param  \Closure                 $next
  *
  * @return mixed
  */
 public function handle(Request $request, Closure $next)
 {
     /**
      * It is assumed, if you get this far, that ClusterServiceProvider was registered via
      * the ManagedInstance bootstrapper. If not, you're in a world of shit.
      *
      * We use provider's service() method because Facades are not loaded yet
      */
     $_cluster = ClusterServiceProvider::service();
     //  Get limits or bail
     if (!$_cluster instanceof ProvidesManagedLimits || empty($limits = $_cluster->getLimits())) {
         return $next($request);
     }
     $this->testing = config('api_limits_test', 'testing' == env('APP_ENV'));
     if (!empty($limits)) {
         $userName = $this->getUser(Session::getCurrentUserId());
         $userRole = $this->getRole(Session::getRoleId());
         $apiName = $this->getApiKey(Session::getApiKey());
         $clusterName = $_cluster->getClusterId();
         $instanceName = $_cluster->getInstanceName();
         $serviceName = $this->getServiceName($request);
         $limits = json_encode($limits);
         //TODO: Update dfe-console to properly set this, but right now, we want to touch as few files as possible
         if (!$this->testing) {
             $limits = str_replace(['cluster.default', 'instance.default'], [$clusterName, $clusterName . '.' . $instanceName], $limits);
         }
         //  Convert to an array
         $limits = json_decode($limits, true);
         //  Build the list of API Hits to check
         $apiKeysToCheck = [$clusterName . '.' . $instanceName => 0];
         $serviceKeys = [];
         if ($serviceName) {
             $serviceKeys[$serviceName] = 0;
             $userRole && ($serviceKeys[$serviceName . '.' . $userRole] = 0);
             $userName && ($serviceKeys[$serviceName . '.' . $userName] = 0);
         }
         if ($apiName) {
             $apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $apiName] = 0;
             $userRole && ($apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $apiName . '.' . $userRole] = 0);
             $userName && ($apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $apiName . '.' . $userName] = 0);
             foreach ($serviceKeys as $key => $value) {
                 $apiKeysToCheck[$apiName . '.' . $key] = $value;
             }
         }
         if ($clusterName) {
             $apiKeysToCheck[$clusterName] = 0;
             $userRole && ($apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $userRole] = 0);
             $userName && ($apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $userName] = 0);
             foreach ($serviceKeys as $key => $value) {
                 $apiKeysToCheck[$clusterName . '.' . $instanceName . '.' . $key] = $value;
             }
         }
         /* Per Ben, we want to increment every limit they hit, not stop after the first one */
         $overLimit = [];
         try {
             foreach (array_keys($apiKeysToCheck) as $key) {
                 foreach ($this->periods as $period) {
                     $_checkKey = $key . '.' . $period;
                     /** @noinspection PhpUndefinedMethodInspection */
                     if (array_key_exists($_checkKey, $limits['api'])) {
                         $_limit = $limits['api'][$_checkKey];
                         // For any cache drivers that make use of the cache prefix, we need to make sure we use
                         // a prefix that every instance can see.  But first, grab the current value
                         $dfCachePrefix = env('DF_CACHE_PREFIX');
                         putenv('DF_CACHE_PREFIX' . '=' . 'df_limits');
                         $_ENV['DF_CACHE_PREFIX'] = $_SERVER['DF_CACHE_PREFIX'] = 'df_limits';
                         //  Increment counter
                         $cacheValue = $this->cache()->get($_checkKey, 0);
                         $cacheValue++;
                         if ($cacheValue > $_limit['limit']) {
                             // Push the name of the rule onto the over-limit array so we can give the name in the 429 error message
                             $overLimit[] = array_get($_limit, 'name', $_checkKey);
                         } else {
                             // Only increment the counter if we are not over the limit.  Fixes DFE-205
                             $this->cache()->put($_checkKey, $cacheValue, $_limit['period']);
                         }
                         // And now set it back
                         putenv('DF_CACHE_PREFIX' . '=' . $dfCachePrefix);
                         $_ENV['DF_CACHE_PREFIX'] = $_SERVER['DF_CACHE_PREFIX'] = $dfCachePrefix;
                     }
                 }
             }
         } catch (\Exception $_ex) {
             return ResponseFactory::getException(new InternalServerErrorException('Unable to update cache: ' . $_ex->getMessage()), $request);
         }
         if ($overLimit) {
             /* Per Ben, we want to increment every limit they hit, not stop after the first one */
             return ResponseFactory::getException(new TooManyRequestsException('API limit(s) exceeded: ' . implode(', ', $overLimit)), $request);
         }
     }
     return $next($request);
 }