/** * Add authorization check before calling any action * * @return * a HTTP status code. This method add only authorization checks * so it can return No2_HTTP::UNAUTHORIZED, No2_HTTP::FORBIDDEN or * No2_HTTP::OK. */ protected function before_filter() { // csrf check $csrf_methods = ['POST', 'PUT', 'PATCH', 'DELETE']; if ($this->check_csrf() && in_array($this->http_method, $csrf_methods)) { $req_http_headers = array_change_key_case(getallheaders(), CASE_LOWER); if (array_key_exists('x-csrf-token', $req_http_headers)) { $token = $req_http_headers['x-csrf-token']; } else { if (array_key_exists('_csrf', $_REQUEST)) { $token = $_REQUEST['_csrf']; } else { $token = ""; } } if (!csrf_token_check($token)) { No2_Logger::warn(sprintf('bad CSRF token: expected [%s] but got [%s]', csrf_token(), $token)); return No2_HTTP::BAD_REQUEST; } } // authorization check if (!$this->authorize(current_user(), $this->action)) { return current_user()->is_anonymous() ? No2_HTTP::UNAUTHORIZED : No2_HTTP::FORBIDDEN; } return parent::before_filter(); }
/** * find the associated controller to $alias. * * @param $alias * the alias to find a matching controller * * @param $action * The action (method) to perform. It will be passed to the respond_to() * method of the controller in order to check if it can handle it. * * @param $http_method * The HTTP verb, usually GET or POST. * * @return * a controller object that can handle $action. If there isn't any, null is * returned. */ public function find_route($alias, $action, $http_method) { if (!array_key_exists($alias, $this->mapping)) { return null; } $target = $this->mapping[$alias]; /* require the associated file if any */ if (!is_null($target['file'])) { require_once $target['file']; } // check if the controller responding to $alias can handle $action $klass = $target['controller']; try { $controller = new $klass($alias, $action, $http_method); } catch (Exception $e) { No2_Logger::warn(get_class($this) . '::find_route: ' . 'exception in controller ctor: ' . $e->getMessage()); return null; } return $controller; }
/** * update this model's properties. * * This method fiter some properties given in order to avoid user injection * of internal data field like id. The internal_fields() function can setup * an array of fields that should be filtered. * * @param $properties * An array of new properties. */ public function update_properties($properties) { $db_infos = $this->db_infos(); $reflect = new ReflectionClass($this); foreach ($properties as $name => $val) { /* * first try to set $name as a db field. It is expected to match * most of the time so it would be costly to test the property * reflection first (which would be throw happy). */ if (array_key_exists($name, $db_infos)) { $field_infos = $db_infos[$name]; // filter out protected properties. if (array_key_exists('protected', $field_infos) && $field_infos['protected']) { No2_Logger::warn(get_class($this) . '#update_properties: ' . "filtering out {$field} (protected)"); } else { $this->{$name} = $val; // see __set() } } else { $p = $reflect->getProperty($name); if ($p->isPublic() && !$p->isStatic()) { $p->setValue($this, $val); } else { $msg = 'Undefined property ' . get_class($this) . "::\${$name}"; throw new InvalidArgumentException($msg); } } } }
/** * execute an UPDATE instruction. set() should have been called before. * * @return * false on error, true otherwise. */ public function update() { $this->restrict_to(self::UPDATE); if (empty($this->set)) { No2_Logger::warn(get_class($this) . '::update: ' . 'called without previous set() call.'); } $klass = $this->klass; $arguments = array_merge($this->arguments, ['{__table}' => $klass::$table]); $options = ['profile' => $this->profile]; $db = static::_database_or_throw($this->profile); $updated = false; if ($db->has_returning()) { $sql = "UPDATE {__table} {$this->set} {$this->where} RETURNING *"; $updated = static::execute($sql, $arguments, $options); } else { // XXX: asume MySQL, hacky. $last_insert_id_hack = (empty($this->set) ? 'SET' : ',') . ' id = LAST_INSERT_ID(id)'; $sql = "UPDATE {__table} {$this->set} {$last_insert_id_hack} {$this->where}"; if (static::execute($sql, $arguments, $options) !== false) { $updated = static::execute('SELECT * FROM {__table} WHERE id = LAST_INSERT_ID()', $arguments, $options); } } if ($updated !== false) { return $updated; } else { No2_Logger::warn(get_class($this) . '::update: ' . "SQL query returned FALSE: {$sql}"); No2_Logger::warn('error message: ' . $db->error()); return null; } }