/** * Check if access requires secure connection * * @param \Cake\Controller\Controller $controller Instantiating controller * @return bool true if secure connection required */ protected function _secureRequired(Controller $controller) { if (is_array($this->_config['requireSecure']) && !empty($this->_config['requireSecure'])) { $requireSecure = $this->_config['requireSecure']; if (in_array($this->_action, $requireSecure) || $requireSecure === ['*']) { if (!$this->request->is('ssl')) { throw new SecurityException('Request is not SSL and the action is required to be secure'); } } } return true; }
/** * Check if access requires secure connection * * @param Controller $controller Instantiating controller * @return bool true if secure connection required */ protected function _secureRequired(Controller $controller) { if (is_array($this->_config['requireSecure']) && !empty($this->_config['requireSecure'])) { $requireSecure = $this->_config['requireSecure']; if (in_array($this->_action, $requireSecure) || $requireSecure === ['*']) { if (!$this->request->is('ssl')) { if (!$this->blackHole($controller, 'secure')) { return null; } } } } return true; }
/** * {@inheritDoc} */ public function validate(Request $request) { if ($request->is('post')) { // The (User's) Remote Address $whatRemoteIP = env('REMOTE_ADDR') ? '&remoteip=' . env('REMOTE_ADDR') : ''; // The reCAPTCHA data is extracted from Request $gRecaptchaResponse = $request->data('g-recaptcha-response'); // Verify reCAPTCHA data $response = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=' . $this->config('secretKey') . '&response=' . $gRecaptchaResponse . $whatRemoteIP); $response = json_decode($response, true); // We return the Google server's response 'success' value return (bool) $response['success']; } return false; }
/** * The startup method of the RequestHandler enables several automatic behaviors * related to the detection of certain properties of the HTTP request, including: * * - If Router::extensions() is enabled, the layout and template type are * switched based on the parsed extension or Accept-Type header. For example, if `controller/action.xml` * is requested, the view path becomes `app/View/Controller/xml/action.ctp`. Also if * `controller/action` is requested with `Accept-Type: application/xml` in the headers * the view path will become `app/View/Controller/xml/action.ctp`. Layout and template * types will only switch to mime-types recognized by Cake\Network\Response. If you need to declare * additional mime-types, you can do so using Cake\Network\Response::type() in your controller's beforeFilter() * method. * - If a helper with the same name as the extension exists, it is added to the controller. * - If the extension is of a type that RequestHandler understands, it will set that * Content-type in the response header. * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned * to the $data property of the controller, which can then be saved to a model object. * * @param Event $event The startup event that was fired. * @return void */ public function startup(Event $event) { $controller = $event->subject(); $controller->request->params['isAjax'] = $this->request->is('ajax'); $isRecognized = !in_array($this->ext, ['html', 'htm']) && $this->response->getMimeType($this->ext); if (!empty($this->ext) && $isRecognized) { $this->renderAs($controller, $this->ext); } elseif (empty($this->ext) || in_array($this->ext, ['html', 'htm'])) { $this->respondAs('html', ['charset' => Configure::read('App.encoding')]); } foreach ($this->_inputTypeMap as $type => $handler) { if ($this->requestedWith($type)) { $input = call_user_func_array([$controller->request, 'input'], $handler); $controller->request->data = $input; } } }
/** * Authorizes current logged in user, if user belongs to the "administrator" * he/she is automatically authorized. * * @param array $user Active user data * @param \Cake\Network\Request $request Request instance * @return bool True if user is can access this request */ public function authorize($user, Request $request) { if ($request->is('userAdmin')) { return true; } $user = user(); $path = $this->requestPath($request); $cacheKey = 'permissions_' . intval($user->id); $permissions = Cache::read($cacheKey, 'permissions'); if ($permissions === false) { $permissions = []; Cache::write($cacheKey, [], 'permissions'); } if (!isset($permissions[$path])) { $allowed = $user->isAllowed($path); $permissions[$path] = $allowed; Cache::write($cacheKey, $permissions, 'permissions'); } else { $allowed = $permissions[$path]; } return $allowed; }
/** * Determines the content type of the data the client has sent (i.e. in a POST request) * * @param string|array $type Can be null (or no parameter), a string type name, or an array of types * @return mixed If a single type is supplied a boolean will be returned. If no type is provided * The mapped value of CONTENT_TYPE will be returned. If an array is supplied the first type * in the request content type will be returned. */ public function requestedWith($type = null) { if (!$this->request->is('post') && !$this->request->is('put')) { return null; } if (is_array($type)) { foreach ($type as $t) { if ($this->requestedWith($t)) { return $t; } } return false; } list($contentType) = explode(';', $this->request->env('CONTENT_TYPE')); if ($contentType === '') { list($contentType) = explode(';', $this->request->header('CONTENT_TYPE')); } if (!$type) { return $this->response->mapType($contentType); } if (is_string($type)) { return $type === $this->response->mapType($contentType); } }
/** * Setup access for origin and methods on cross origin requests * * This method allow multiple ways to setup the domains, see the examples * * ### Full URI * ``` * cors($request, 'http://www.cakephp.org'); * ``` * * ### URI with wildcard * ``` * cors($request, 'http://*.cakephp.org'); * ``` * * ### Ignoring the requested protocol * ``` * cors($request, 'www.cakephp.org'); * ``` * * ### Any URI * ``` * cors($request, '*'); * ``` * * ### Whitelist of URIs * ``` * cors($request, ['http://www.cakephp.org', '*.google.com', 'https://myproject.github.io']); * ``` * * @param \Cake\Network\Request $request Request object * @param string|array $allowedDomains List of allowed domains, see method description for more details * @param string|array $allowedMethods List of HTTP verbs allowed * @param string|array $allowedHeaders List of HTTP headers allowed * @return void */ public function cors(Request $request, $allowedDomains, $allowedMethods = [], $allowedHeaders = []) { $origin = $request->header('Origin'); if (!$origin) { return; } $allowedDomains = $this->_normalizeCorsDomains((array) $allowedDomains, $request->is('ssl')); foreach ($allowedDomains as $domain) { if (!preg_match($domain['preg'], $origin)) { continue; } $this->header('Access-Control-Allow-Origin', $domain['original'] === '*' ? '*' : $origin); $allowedMethods && $this->header('Access-Control-Allow-Methods', implode(', ', (array) $allowedMethods)); $allowedHeaders && $this->header('Access-Control-Allow-Headers', implode(', ', (array) $allowedHeaders)); break; } }
/** * Test is('requested') and isRequested() * * @return void */ public function testIsRequested() { $request = new Request(); $request->addParams(['controller' => 'posts', 'action' => 'index', 'plugin' => null, 'requested' => 1]); $this->assertTrue($request->is('requested')); $this->assertTrue($request->isRequested()); $request = new Request(); $request->addParams(['controller' => 'posts', 'action' => 'index', 'plugin' => null]); $this->assertFalse($request->is('requested')); $this->assertFalse($request->isRequested()); }
/** * Setup access for origin and methods on cross origin requests * * This method allow multiple ways to setup the domains, see the examples * * ### Full URI * ``` * cors($request, 'http://www.cakephp.org'); * ``` * * ### URI with wildcard * ``` * cors($request, 'http://*.cakephp.org'); * ``` * * ### Ignoring the requested protocol * ``` * cors($request, 'www.cakephp.org'); * ``` * * ### Any URI * ``` * cors($request, '*'); * ``` * * ### Whitelist of URIs * ``` * cors($request, ['http://www.cakephp.org', '*.google.com', 'https://myproject.github.io']); * ``` * * *Note* The `$allowedDomains`, `$allowedMethods`, `$allowedHeaders` parameters are deprecated. * Instead the builder object should be used. * * @param \Cake\Network\Request $request Request object * @param string|array $allowedDomains List of allowed domains, see method description for more details * @param string|array $allowedMethods List of HTTP verbs allowed * @param string|array $allowedHeaders List of HTTP headers allowed * @return \Cake\Network\CorsBuilder A builder object the provides a fluent interface for defining * additional CORS headers. */ public function cors(Request $request, $allowedDomains = [], $allowedMethods = [], $allowedHeaders = []) { $origin = $request->header('Origin'); $ssl = $request->is('ssl'); $builder = new CorsBuilder($this, $origin, $ssl); if (!$origin) { return $builder; } if (empty($allowedDomains) && empty($allowedMethods) && empty($allowedHeaders)) { return $builder; } $builder->allowOrigin($allowedDomains)->allowMethods((array) $allowedMethods)->allowHeaders((array) $allowedHeaders)->build(); return $builder; }
* to set as the default: * * - Route * - InflectedRoute * - DashedRoute * * If no call is made to `Router::defaultRouteClass()`, the class used is * `Route` (`Cake\Routing\Route\Route`) * * Note that `Route` does not do any inflections on URLs which will result in * inconsistently cased URLs when used with `:plugin`, `:controller` and * `:action` markers. * */ $req = new Network\Request(); if ($req->is('get')) { Router::defaultRouteClass('DashedRoute'); Router::scope('/', function ($routes) { $routes->connect('demo', ['controller' => 'User', 'action' => 'show', 'show']); $routes->connect('demo/activation', ['controller' => 'User', 'action' => 'activation']); // $routes->connect('demo', ['controller' => 'Demo', 'action' => 'display', 'display']); $routes->fallbacks('DashedRoute'); }); } elseif ($req->is('Post')) { Router::defaultRouteClass('DashedRoute'); Router::scope('/', function ($routes) { $routes->connect('demo', ['controller' => 'User', 'action' => 'show', 'show']); $routes->connect('demo/activation', ['controller' => 'User', 'action' => 'activation']); // $routes->connect('demo', ['controller' => 'Demo', 'action' => 'display', 'display']); $routes->fallbacks('DashedRoute'); });
/** * $options: * --------- * 'check_referer' indicates wether eventual existing filter for the current url must be reused only if the referer is the same url * true by default -> filter is preserved only during pagination navigation * * 'auto_wildcard_string' true by default -> automatcally appends wildcard character '%' around search terms * * @param array $options * @return \Cake\ORM\Query */ public function getFilterQuery(array $options = array()) { $default_options = ['check_referer' => true, 'keep_filter_actions' => ['add', 'copy', 'edit', 'view', 'delete'], 'auto_wildcard_string' => true]; $options = array_merge($default_options, $options); /* * Prepare Entity used to display search filters in the view */ $this->prepareSearchEntity($options); $options['modelClass'] = isset($options['modelClass']) ? $options['modelClass'] : $this->controller->modelClass; /******/ $filter_data = null; if ($this->request->is('post') || $this->request->is('put')) { if (isset($this->request->data['Filter']) && !empty($this->request->data['Filter'])) { $filter_data = $this->request->data['Filter']; } } elseif ($this->request->is('get')) { $current_path = $this->getComparisonPath($this->request->params); if ($options['check_referer']) { if ($this->filterMustBeCleared($options)) { $this->clearStoredQuery($current_path); } else { $filter_data = $this->getStoredQuery($current_path, $options); } } else { $filter_data = $this->getStoredQuery($current_path, $options); } } /******/ $query = $this->controller->{$options['modelClass']}->find(); if (isset($options['contain'])) { $query->contain($options['contain']); } /******/ if (!empty($filter_data)) { /* * Normalize $filter_data to get a flat array, even if some filters concern linked models */ $flat_filter_data = []; foreach ($filter_data as $fieldName => $value) { if (!is_array($value) || isset($value['__start__']) || isset($value['__end__'])) { /* * Conditions on main model */ $flat_filter_data[$options['modelClass']] = isset($flat_filter_data[$options['modelClass']]) ? $flat_filter_data[$options['modelClass']] : array(); $flat_filter_data[$options['modelClass']][$fieldName] = $value; } else { /* * Conditions on linked model */ $flat_filter_data[$options['modelClass'] . '.' . $fieldName] = isset($flat_filter_data[$options['modelClass'] . '.' . $fieldName]) ? $flat_filter_data[$options['modelClass'] . '.' . $fieldName] : array(); foreach ($value as $linked_model_fieldname => $linked_value) { if (!is_array($linked_value) || isset($linked_value['__start__']) || isset($linked_value['__end__'])) { /* * Case of LinkedModels.title */ $flat_filter_data[$options['modelClass'] . '.' . $fieldName][$linked_model_fieldname] = $linked_value; } else { $flat_filter_data[$options['modelClass'] . '.' . $fieldName . '.' . $linked_model_fieldname] = isset($flat_filter_data[$options['modelClass'] . '.' . $fieldName . '.' . $linked_model_fieldname]) ? $flat_filter_data[$options['modelClass'] . '.' . $fieldName . '.' . $linked_model_fieldname] : array(); foreach ($linked_value as $linked_model_2_fieldname => $linked_value_2) { if (!is_array($linked_value_2) || isset($linked_value_2['__start__']) || isset($linked_value_2['__end__'])) { /* * Case of LinkedModels.LinkedModels.title */ $flat_filter_data[$options['modelClass'] . '.' . $fieldName . '.' . $linked_model_fieldname][$linked_model_2_fieldname] = $linked_value_2; } else { throw new NotImplementedException(___('filtering on 3rd level related model is not implemented')); } } } } } } /******/ foreach ($flat_filter_data as $modelName => $conditions) { foreach ($conditions as $fieldName => $value) { $has_value = false; if (!is_array($value)) { if (!empty($value) || $value === '0') { $has_value = true; } } else { /* * Case of From - To */ if (isset($value['__start__']) && (!empty($value['__start__']) || $value['__start__'] === '0')) { $has_value = true; } if (isset($value['__end__']) && (!empty($value['__end__']) || $value['__end__'] === '0')) { $has_value = true; } } if ($has_value) { $schema = null; if (stripos($modelName, '.') !== -1) { /* * Case of linked models */ $modelNames = explode('.', $modelName); $table = null; for ($i = 0; $i < count($modelNames); $i++) { $modName = $modelNames[$i]; $nextModelName = isset($modelNames[$i + 1]) ? $modelNames[$i + 1] : null; $condition_fieldname = $modName . '.' . $fieldName; if (!isset($table)) { $table = $this->controller->{$modName}; } else { $table = $table->{$modName}; } $schema = $table->schema(); if (isset($nextModelName)) { $association = $table->association($modelNames[$i + 1]); $this->addJoin($query, $association); } } } else { /* * Main model */ $schema = $this->controller->{$options['modelClass']}->schema(); $condition_fieldname = $fieldName; } /******/ $aliases = $this->config('aliases'); if (is_array($aliases) && array_key_exists($condition_fieldname, $aliases)) { $condition_expression = $aliases[$condition_fieldname]['expression']; $columnType = isset($aliases[$condition_fieldname]['columnType']) ? $aliases[$condition_fieldname]['columnType'] : 'string'; $condition_fieldname = $condition_expression; } else { $columnType = $schema->columnType($fieldName); } /******/ switch ($columnType) { case 'integer': case 'float': $this->_addNumericCondition($query, $condition_fieldname, $value, $options); break; case 'datetime': case 'date': $this->_addDatetimeCondition($query, $condition_fieldname, $value, $options); break; case 'string': case 'text': $this->_addStringCondition($query, $condition_fieldname, $value, $options); break; case 'boolean': $this->_addBooleanCondition($query, $condition_fieldname, $value, $options); break; } } } } /* * Store Query in session in order to be able to navigate to other list pages * without loosing the filters */ $path = $this->getComparisonPath($this->request->params); $this->storeQuery($path, $filter_data); /* * Set request data if no already filled * (this is the case when navigating from page to page with pagination) */ if (!isset($this->request->data['Filter'])) { $this->request->data['Filter'] = $filter_data; } } /******/ return $query; }