From e1fdb6803ddd7ed3e39dc0d414d7ad59d63ca0dd Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Fri, 6 Nov 2015 15:31:49 +0100 Subject: [PATCH 01/18] Add nonce functionality --- system/src/Grav/Common/Twig/TwigExtension.php | 25 ++++- system/src/Grav/Common/Uri.php | 16 ++++ system/src/Grav/Common/Utils.php | 96 ++++++++++++++++++- 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 2da411210..97723ce40 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -94,14 +94,17 @@ class TwigExtension extends \Twig_Extension new \Twig_simpleFunction('authorize', [$this, 'authorize']), new \Twig_SimpleFunction('debug', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]), new \Twig_SimpleFunction('dump', [$this, 'dump'], ['needs_context' => true, 'needs_environment' => true]), + new \Twig_SimpleFunction('evaluate', [$this, 'evaluateFunc']), new \Twig_SimpleFunction('gist', [$this, 'gistFunc']), + new \Twig_SimpleFunction('nonce_field', [$this, 'nonceFieldFunc']), new \Twig_simpleFunction('random_string', [$this, 'randomStringFunc']), new \Twig_SimpleFunction('repeat', [$this, 'repeatFunc']), new \Twig_SimpleFunction('string', [$this, 'stringFunc']), new \Twig_simpleFunction('t', [$this, 'translate']), new \Twig_simpleFunction('ta', [$this, 'translateArray']), new \Twig_SimpleFunction('url', [$this, 'urlFunc']), - new \Twig_SimpleFunction('evaluate', [$this, 'evaluateFunc']), + + ]; } @@ -595,4 +598,24 @@ class TwigExtension extends \Twig_Extension return false; } + + /** + * Used to add a nonce to a form. Call {{ nonce_field('action') }} specifying a string representing the action. + * + * For maximum protection, ensure that the string representing the action is as specific as possible. + * + * @todo evaluate if adding referrer or not + * + * @param string action the action + * @param string nonceParamName a custom nonce param name + * + * @return string the nonce input field + */ + public function nonceFieldFunc($action, $nonceParamName = 'nonce') + { + $string = ''; + // $string += ''; + return $string; + + } } diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 0473d3938..cf9ec4793 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -572,4 +572,20 @@ class Uri return $normalized_url; } } + + /** + * Adds the nonce to a URL for a specific action + * + * @param string $url the url + * @param string $action the action + * @param string $nonceParamName the param name to use + * + * @return string the url with the nonce + */ + public static function addNonce($url, $action, $nonceParamName = 'nonce') + { + $nonce = Utils::getNonce($action); + $urlWithNonce = $url . '/' . $nonceParamName . self::getGrav()['config']->get('system.param_sep', ':') . $nonce; + return $urlWithNonce; + } } diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 8c55669a2..ae4479903 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -384,7 +384,101 @@ abstract class Utils * * @return boolean */ - public static function isPositive($value) { + public static function isPositive($value) + { return in_array($value, [true, 1, '1', 'yes', 'on', 'true'], true); } + + + /** + * Generates a nonce string to be hashed. Called by self::getNonce() + * + * @param string $action + * @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) + * + * @return string the nonce string + */ + private static function generateNonceString($action, $plusOneTick) + { + $user = self::getGrav()['user']; + $username = $user->username; + + if ( ! $username ) { + $username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; + } + + $token = session_id(); + $i = self::nonceTick(); + + if ($plusOneTick) { + $i++; + } + + return ( $i . '|' . $action . '|' . $username . '|' . $token ); + } + + /** + * Get the time-dependent variable for nonce creation. + * + * @todo now a tick lasts a day. Once the day is passed, the nonce is not valid any more. Find a better way + * to ensure nonces issued near the end of the day do not expire in that small amount of time + * + * @return int the time part of the nonce. Changes once every 24 hours + */ + private static function nonceTick() + { + $secondsInHalfADay = 60 * 60 * 12; + return (int)ceil(time() / ( $secondsInHalfADay )); + } + + /** + * Get hash of given string. Uses BCrypt. The salt is taken from system.security.default_hash + * + * @param string $data string to hash + * + * @return string hashed value of $data, cut to 10 characters + */ + private static function hash($data) + { + $hash = password_hash($data, PASSWORD_BCRYPT, ['salt' => self::getGrav()['config']->get('system.security.default_hash')]); + $hash = substr($hash, -12, 10); + return $hash; + } + + /** + * Creates a hashed nonce tied to the passed action. Tied to the current user and time. The nonce for a given + * action is the same for 12 hours. + * + * @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage) + * @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) + * + * @return string the nonce, a 10 characters string + */ + public static function getNonce($action, $plusOneTick = false) + { + $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); + return $nonce; + } + + /** + * Verify the passed nonce for the give action + * + * @param string $nonce the nonce to verify + * @param string $action the action to verify the nonce to + * + * @return boolean verified or not + */ + public static function verifyNonce($nonce, $action) + { + if ($nonce == self::getNonce($action)) { + return true; + } + + $plusOneTick = true; + if ($nonce == self::getNonce($action, $plusOneTick)) { + return true; + } + + return false; + } } From b8413cefaffd82092e2cd7287d7df6e929136667 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 10 Nov 2015 17:34:23 +0100 Subject: [PATCH 02/18] Avoid having to deal with slashes in URLs --- system/src/Grav/Common/Utils.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index ae4479903..427ec3ea1 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -457,6 +457,7 @@ abstract class Utils public static function getNonce($action, $plusOneTick = false) { $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); + $nonce = str_replace('/', '-', $nonce); // avoid having to deal with slashes in URLs return $nonce; } From da4593fdc1c99269026eb215760912498e692601 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 11 Nov 2015 17:13:54 +0100 Subject: [PATCH 03/18] Drop commented referrer code --- system/src/Grav/Common/Twig/TwigExtension.php | 1 - 1 file changed, 1 deletion(-) diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 97723ce40..f0264146a 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -614,7 +614,6 @@ class TwigExtension extends \Twig_Extension public function nonceFieldFunc($action, $nonceParamName = 'nonce') { $string = ''; - // $string += ''; return $string; } From 0725af5367a375c33a36bec42b699d4148c56b61 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Thu, 12 Nov 2015 09:17:22 +0100 Subject: [PATCH 04/18] Handle case login plugin disabled (thanks @hwmaier) --- system/src/Grav/Common/Twig/TwigExtension.php | 1 - system/src/Grav/Common/Utils.php | 10 +++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index f0264146a..aa8c4a3ee 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -615,6 +615,5 @@ class TwigExtension extends \Twig_Extension { $string = ''; return $string; - } } diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 427ec3ea1..21bf89816 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -400,10 +400,14 @@ abstract class Utils */ private static function generateNonceString($action, $plusOneTick) { - $user = self::getGrav()['user']; - $username = $user->username; + if (isset(self::getGrav()['user'])) { + $user = self::getGrav()['user']; + $username = $user->username; + } else { + $username = false; + } - if ( ! $username ) { + if (!$username) { $username = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } From dc80228f0b6976f1c28e056e983949c7feed486a Mon Sep 17 00:00:00 2001 From: yaman-jain Date: Fri, 13 Nov 2015 00:48:54 +0530 Subject: [PATCH 05/18] PackageInterface was deleted #435 --- .../src/Grav/Common/GPM/PackageInterface.php | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 system/src/Grav/Common/GPM/PackageInterface.php diff --git a/system/src/Grav/Common/GPM/PackageInterface.php b/system/src/Grav/Common/GPM/PackageInterface.php deleted file mode 100644 index ad856c08f..000000000 --- a/system/src/Grav/Common/GPM/PackageInterface.php +++ /dev/null @@ -1,58 +0,0 @@ - Date: Fri, 13 Nov 2015 17:50:04 +0100 Subject: [PATCH 06/18] Fix exception message when label is not set --- system/src/Grav/Common/Data/Blueprint.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 97ed5858a..bf1f5a32f 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -450,7 +450,8 @@ class Blueprint if (isset($field['validate']['required']) && $field['validate']['required'] === true && empty($data[$name])) { - throw new \RuntimeException("Missing required field: {$field['label']}"); + $value = isset($field['label']) ? $field['label'] : $field['name']; + throw new \RuntimeException("Missing required field: {$value}"); } } } From c22fae0d3d22f3dcc8fb698aaafdca6fe215d9fc Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 16 Nov 2015 21:33:01 -0700 Subject: [PATCH 07/18] just added a comment --- system/src/Grav/Common/Uri.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index b59b26b4c..cbae34ddb 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -139,6 +139,7 @@ class Uri $valid_page_types = implode('|', $config->get('system.pages.types')); + // Strip the file extension for valid page types if (preg_match("/\.(".$valid_page_types.")$/", $parts['basename'])) { $uri = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS). '/' .$parts['filename']; } From e96445abe343e914c85bb0226d9076846c171c2f Mon Sep 17 00:00:00 2001 From: Andy Miller Date: Mon, 16 Nov 2015 21:33:24 -0700 Subject: [PATCH 08/18] Added `append_url_extension` option to system/page headers. --- system/config/system.yaml | 155 ++++++++++++++------------- system/src/Grav/Common/Page/Page.php | 17 ++- 2 files changed, 93 insertions(+), 79 deletions(-) diff --git a/system/config/system.yaml b/system/config/system.yaml index 43b1dbba5..a53951e7c 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -1,113 +1,114 @@ -absolute_urls: false # Absolute or relative URLs for `base_url` -timezone: '' # Valid values: http://php.net/manual/en/timezones.php -default_locale: # Default locale (defaults to system) -param_sep: ':' # Parameter separator, use ';' for Apache on windows -wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform +absolute_urls: false # Absolute or relative URLs for `base_url` +timezone: '' # Valid values: http://php.net/manual/en/timezones.php +default_locale: # Default locale (defaults to system) +param_sep: ':' # Parameter separator, use ';' for Apache on windows +wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform languages: - supported: [] # List of languages supported. eg: [en, fr, de] - include_default_lang: true # Include the default lang prefix in all URLs - translations: true # Enable translations by default - translations_fallback: true # Fallback through supported translations if active lang doesn't exist - session_store_active: false # Store active language in session - http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser - override_locale: false # Override the default or system locale with language specific one + supported: [] # List of languages supported. eg: [en, fr, de] + include_default_lang: true # Include the default lang prefix in all URLs + translations: true # Enable translations by default + translations_fallback: true # Fallback through supported translations if active lang doesn't exist + session_store_active: false # Store active language in session + http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser + override_locale: false # Override the default or system locale with language specific one home: - alias: '/home' # Default path for home, ie / + alias: '/home' # Default path for home, ie / pages: - theme: antimatter # Default theme (defaults to "antimatter" theme) + theme: antimatter # Default theme (defaults to "antimatter" theme) order: - by: default # Order pages by "default", "alpha" or "date" - dir: asc # Default ordering direction, "asc" or "desc" + by: default # Order pages by "default", "alpha" or "date" + dir: asc # Default ordering direction, "asc" or "desc" list: - count: 20 # Default item count per page + count: 20 # Default item count per page dateformat: - default: # The default date format Grav expects in the `date: ` field - short: 'jS M Y' # Short date format - long: 'F jS \a\t g:ia' # Long date format - publish_dates: true # automatically publish/unpublish based on dates + default: # The default date format Grav expects in the `date: ` field + short: 'jS M Y' # Short date format + long: 'F jS \a\t g:ia' # Long date format + publish_dates: true # automatically publish/unpublish based on dates process: - markdown: true # Process Markdown - twig: false # Process Twig + markdown: true # Process Markdown + twig: false # Process Twig events: - page: true # Enable page level events - twig: true # Enable twig level events + page: true # Enable page level events + twig: true # Enable twig level events markdown: - extra: false # Enable support for Markdown Extra support (GFM by default) - auto_line_breaks: false # Enable automatic line breaks - auto_url_links: false # Enable automatic HTML links - escape_markup: false # Escape markup tags into entities - special_chars: # List of special characters to automatically convert to entities + extra: false # Enable support for Markdown Extra support (GFM by default) + auto_line_breaks: false # Enable automatic line breaks + auto_url_links: false # Enable automatic HTML links + escape_markup: false # Escape markup tags into entities + special_chars: # List of special characters to automatically convert to entities '>': 'gt' '<': 'lt' - types: [txt,xml,html,json,rss,atom] # list of valid page types - expires: 604800 # Page expires time in seconds (604800 seconds = 7 days) - last_modified: false # Set the last modified date header based on file modifcation timestamp - etag: false # Set the etag header tag - vary_accept_encoding: false # Add `Vary: Accept-Encoding` header - redirect_default_route: false # Automatically redirect to a page's default route - redirect_default_code: 301 # Default code to use for redirects - redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL - ignore_files: [.DS_Store] # Files to ignore in Pages - ignore_folders: [.git, .idea] # Folders to ignore in Pages - ignore_hidden: true # Ignore all Hidden files and folders - url_taxonomy_filters: true # Enable auto-magic URL-based taxonomy filters for page collections - fallback_types: [png,jpg,jpeg,gif] # Allowed types of files found if accessed via Page route + types: [txt,xml,html,htm,json,rss,atom] # list of valid page types + append_url_extension: '' # Append page's extension in Page urls (e.g. '.html' results in /path/page.html) + expires: 604800 # Page expires time in seconds (604800 seconds = 7 days) + last_modified: false # Set the last modified date header based on file modifcation timestamp + etag: false # Set the etag header tag + vary_accept_encoding: false # Add `Vary: Accept-Encoding` header + redirect_default_route: false # Automatically redirect to a page's default route + redirect_default_code: 301 # Default code to use for redirects + redirect_trailing_slash: true # Handle automatically or 301 redirect a trailing / URL + ignore_files: [.DS_Store] # Files to ignore in Pages + ignore_folders: [.git, .idea] # Folders to ignore in Pages + ignore_hidden: true # Ignore all Hidden files and folders + url_taxonomy_filters: true # Enable auto-magic URL-based taxonomy filters for page collections + fallback_types: [png,jpg,jpeg,gif] # Allowed types of files found if accessed via Page route cache: - enabled: true # Set to true to enable caching + enabled: true # Set to true to enable caching check: - method: file # Method to check for updates in pages: file|folder|none - driver: auto # One of: auto|file|apc|xcache|memcache|wincache - prefix: 'g' # Cache prefix string (prevents cache conflicts) - lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite) - gzip: false # GZip compress the page output + method: file # Method to check for updates in pages: file|folder|none + driver: auto # One of: auto|file|apc|xcache|memcache|wincache + prefix: 'g' # Cache prefix string (prevents cache conflicts) + lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite) + gzip: false # GZip compress the page output twig: - cache: true # Set to true to enable twig caching - debug: false # Enable Twig debug - auto_reload: true # Refresh cache on changes - autoescape: false # Autoescape Twig vars - undefined_functions: true # Allow undefined functions - undefined_filters: true # Allow undefined filters - umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775 + cache: true # Set to true to enable twig caching + debug: false # Enable Twig debug + auto_reload: true # Refresh cache on changes + autoescape: false # Autoescape Twig vars + undefined_functions: true # Allow undefined functions + undefined_filters: true # Allow undefined filters + umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775 -assets: # Configuration for Assets Manager (JS, CSS) - css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file - css_minify: true # Minify the CSS during pipelining - css_minify_windows: false # Minify Override for Windows platforms. False by default due to ThreadStackSize - css_rewrite: true # Rewrite any CSS relative URLs during pipelining - js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file - js_minify: true # Minify the JS during pipelining - enable_asset_timestamp: false # Enable asset timestamps +assets: # Configuration for Assets Manager (JS, CSS) + css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file + css_minify: true # Minify the CSS during pipelining + css_minify_windows: false # Minify Override for Windows platforms. False by default due to ThreadStackSize + css_rewrite: true # Rewrite any CSS relative URLs during pipelining + js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file + js_minify: true # Minify the JS during pipelining + enable_asset_timestamp: false # Enable asset timestamps collections: jquery: system://assets/jquery/jquery-2.1.4.min.js errors: - display: false # Display full backtrace-style error page - log: true # Log errors to /logs folder + display: false # Display full backtrace-style error page + log: true # Log errors to /logs folder debugger: - enabled: false # Enable Grav debugger and following settings + enabled: false # Enable Grav debugger and following settings shutdown: - close_connection: true # Close the connection before calling onShutdown(). false for debugging + close_connection: true # Close the connection before calling onShutdown(). false for debugging images: - default_image_quality: 85 # Default image quality to use when resampling images (85%) - cache_all: false # Cache all image by default - debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example + default_image_quality: 85 # Default image quality to use when resampling images (85%) + cache_all: false # Cache all image by default + debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example media: - enable_media_timestamp: false # Enable media timetsamps - upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited) - unsupported_inline_types: [] # Array of unsupported media file types to try to display inline + enable_media_timestamp: false # Enable media timetsamps + upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited) + unsupported_inline_types: [] # Array of unsupported media file types to try to display inline session: - enabled: true # Enable Session support - timeout: 1800 # Timeout in seconds - name: grav-site # Name prefix of the session cookie + enabled: true # Enable Session support + timeout: 1800 # Timeout in seconds + name: grav-site # Name prefix of the session cookie security: default_hash: $2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index f2c6ea2d9..0132c6b2e 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -41,6 +41,7 @@ class Page protected $folder; protected $path; protected $extension; + protected $url_extension; protected $id; protected $parent; @@ -128,6 +129,7 @@ class Page $this->modularTwig($this->slug[0] == '_'); $this->setPublishState(); $this->published(); + $this->urlExtension(); // some extension logic if (empty($extension)) { @@ -136,6 +138,7 @@ class Page $this->extension($extension); } + // extract page language from page extension $language = trim(basename($this->extension(), 'md'), '.') ?: null; $this->language($language); @@ -349,7 +352,6 @@ class Page if (isset($this->header->last_modified)) { $this->last_modified = (bool) $this->header->last_modified; } - } return $this->header; @@ -957,6 +959,17 @@ class Page return $this->extension; } + public function urlExtension() + { + // if not set in the page get the value from system config + if (empty($this->url_extension)) { + $this->url_extension = trim(isset($this->header->append_url_extension) ? $this->header->append_url_extension : self::getGrav()['config']->get('system.pages.append_url_extension', false)); + } + + return $this->url_extension; + + } + /** * Gets and sets the expires field. If not set will return the default * @@ -1262,7 +1275,7 @@ class Page $rootUrl = $uri->rootUrl($include_host) . $pages->base(); - $url = $rootUrl.'/'.trim($route, '/'); + $url = $rootUrl.'/'.trim($route, '/') . $this->urlExtension(); // trim trailing / if not root if ($url !== '/') { From a1ee3cf4e461c8a42cea036cab65461637a83ea4 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 17 Nov 2015 11:59:02 +0100 Subject: [PATCH 09/18] Avoid using our own hash, let PHP use its own for password_hash, fix #445 --- system/src/Grav/Common/Utils.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 21bf89816..74bc12af0 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -398,7 +398,7 @@ abstract class Utils * * @return string the nonce string */ - private static function generateNonceString($action, $plusOneTick) + private static function generateNonceString($action, $plusOneTick = false) { if (isset(self::getGrav()['user'])) { $user = self::getGrav()['user']; @@ -436,7 +436,7 @@ abstract class Utils } /** - * Get hash of given string. Uses BCrypt. The salt is taken from system.security.default_hash + * Get hash of given string * * @param string $data string to hash * @@ -444,8 +444,7 @@ abstract class Utils */ private static function hash($data) { - $hash = password_hash($data, PASSWORD_BCRYPT, ['salt' => self::getGrav()['config']->get('system.security.default_hash')]); - $hash = substr($hash, -12, 10); + $hash = password_hash($data, PASSWORD_DEFAULT); return $hash; } @@ -456,12 +455,11 @@ abstract class Utils * @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage) * @param bool $plusOneTick if true, generates the token for the next tick (the next 12 hours) * - * @return string the nonce, a 10 characters string + * @return string the nonce */ public static function getNonce($action, $plusOneTick = false) { $nonce = self::hash(self::generateNonceString($action, $plusOneTick)); - $nonce = str_replace('/', '-', $nonce); // avoid having to deal with slashes in URLs return $nonce; } @@ -475,15 +473,18 @@ abstract class Utils */ public static function verifyNonce($nonce, $action) { - if ($nonce == self::getNonce($action)) { + //Nonce generated 0-12 hours ago + if (password_verify(self::generateNonceString($action), $nonce)) { return true; } + //Nonce generated 12-24 hours ago $plusOneTick = true; - if ($nonce == self::getNonce($action, $plusOneTick)) { + if (password_verify(self::generateNonceString($action, $plusOneTick), $nonce)) { return true; } + //Invalid nonce return false; } } From a6bc56535661a74d772f11f0f3a8fe90a87557c7 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Tue, 17 Nov 2015 11:59:30 +0100 Subject: [PATCH 10/18] Replace forward slashes automatically to avoid problems when used in GET request --- system/src/Grav/Common/Uri.php | 3 ++- system/src/Grav/Common/Utils.php | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index cbae34ddb..98b45fcac 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -585,7 +585,8 @@ class Uri public static function addNonce($url, $action, $nonceParamName = 'nonce') { $nonce = Utils::getNonce($action); - $urlWithNonce = $url . '/' . $nonceParamName . self::getGrav()['config']->get('system.param_sep', ':') . $nonce; + $nonce = str_replace('/', 'SLASH', $nonce); + $urlWithNonce = $url . '/' . $nonceParamName . Grav::instance()['config']->get('system.param_sep', ':') . $nonce; return $urlWithNonce; } } diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index 74bc12af0..4935ee53a 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -473,6 +473,8 @@ abstract class Utils */ public static function verifyNonce($nonce, $action) { + $nonce = str_replace('SLASH', '/', $nonce); + //Nonce generated 0-12 hours ago if (password_verify(self::generateNonceString($action), $nonce)) { return true; From 3505ef046d6e9340b3ee258f452e7e5ddbeb212b Mon Sep 17 00:00:00 2001 From: Paul Bovis Date: Tue, 17 Nov 2015 11:50:47 -0700 Subject: [PATCH 11/18] Added a translation for "Validation failed:" text The English term "Validation failed:" was hard coded on line 83. Instead, I created a translation and placed this at /system/languages/en.yaml. It will need to be translated into other languages by others. --- system/src/Grav/Common/Data/Blueprint.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index bf1f5a32f..7aa51440e 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -72,12 +72,15 @@ class Blueprint { // Initialize data $this->fields(); + + // Get language class + $language = self::getGrav()['language']; try { $this->validateArray($data, $this->nested); } catch (\RuntimeException $e) { - $language = self::getGrav()['language']; - throw new \RuntimeException(sprintf('Validation failed: %s', $language->translate($e->getMessage()))); + $message = sprintf($language->translate('FORM.VALIDATION_FAIL', null, true) . ' %s', $e->getMessage()); + throw new \RuntimeException($message); } } From d5b3f070a5a47a7da888cac84ea61f16c9480ba9 Mon Sep 17 00:00:00 2001 From: Paul Bovis Date: Tue, 17 Nov 2015 11:55:31 -0700 Subject: [PATCH 12/18] Added a translation for "Invalid input in " text The English phrase "Invalid input in " was hardcoded at line 40. I created a new translation 'FORM.INVALID_INPUT' and placed it in /system/languages/en.yaml. It will need to be translated into other languages by others. --- system/src/Grav/Common/Data/Validation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index 653386fe9..c232467ba 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -38,7 +38,7 @@ class Validation $type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type']; $method = 'type'.strtr($type, '-', '_'); $name = ucfirst(isset($field['label']) ? $field['label'] : $field['name']); - $message = (string) isset($field['validate']['message']) ? $field['validate']['message'] : 'Invalid input in "' . $language->translate($name) . '""'; + $message = (string) isset($field['validate']['message']) ? $field['validate']['message'] : $language->translate('FORM.INVALID_INPUT', null, true) . ' "' . $language->translate($name) . '"'; if (method_exists(__CLASS__, $method)) { $success = self::$method($value, $validate, $field); From 280377985fc525c915cd5f5c97c2b59f540ef82c Mon Sep 17 00:00:00 2001 From: Paul Bovis Date: Tue, 17 Nov 2015 11:57:15 -0700 Subject: [PATCH 13/18] Added form validation translations These will need to be translated into other languages. --- system/languages/en.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/languages/en.yaml b/system/languages/en.yaml index 5ae01dbde..a1eab6f19 100644 --- a/system/languages/en.yaml +++ b/system/languages/en.yaml @@ -92,3 +92,6 @@ NICETIME: MO_PLURAL: mos YR_PLURAL: yrs DEC_PLURAL: decs +FORM: + VALIDATION_FAIL: Validation failed: + INVALID_INPUT: Invalid input in From 7f0eefbde55f0fa7e6021a7b8b1c7864fff1a802 Mon Sep 17 00:00:00 2001 From: yaman-jain Date: Wed, 18 Nov 2015 02:32:44 +0530 Subject: [PATCH 14/18] updated/added PhpDoc for methods --- system/src/Grav/Common/Filesystem/Folder.php | 4 ++-- system/src/Grav/Common/GPM/Remote/Plugins.php | 2 ++ system/src/Grav/Common/GPM/Remote/Themes.php | 2 ++ system/src/Grav/Common/GPM/Response.php | 6 ++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index 6026d8f04..af1b93352 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -64,8 +64,8 @@ abstract class Folder /** * Get relative path between target and base path. If path isn't relative, return full path. * - * @param string $path - * @param string $base + * @param string $path + * @param mixed|string $base * @return string */ public static function getRelativePath($path, $base = GRAV_ROOT) diff --git a/system/src/Grav/Common/GPM/Remote/Plugins.php b/system/src/Grav/Common/GPM/Remote/Plugins.php index ec6d64521..e40135096 100644 --- a/system/src/Grav/Common/GPM/Remote/Plugins.php +++ b/system/src/Grav/Common/GPM/Remote/Plugins.php @@ -16,6 +16,8 @@ class Plugins extends AbstractPackageCollection /** * Local Plugins Constructor + * @param bool $refresh + * @param null $callback */ public function __construct($refresh = false, $callback = null) { diff --git a/system/src/Grav/Common/GPM/Remote/Themes.php b/system/src/Grav/Common/GPM/Remote/Themes.php index 759b7e10a..e9ffacbdc 100644 --- a/system/src/Grav/Common/GPM/Remote/Themes.php +++ b/system/src/Grav/Common/GPM/Remote/Themes.php @@ -16,6 +16,8 @@ class Themes extends AbstractPackageCollection /** * Local Themes Constructor + * @param bool $refresh + * @param null $callback */ public function __construct($refresh = false, $callback = null) { diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index 4f61e2b42..0df7e3876 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -50,6 +50,7 @@ class Response /** * Sets the preferred method to use for making HTTP calls. * @param string $method Default is `auto` + * @return Response */ public static function setMethod($method = 'auto') { @@ -64,8 +65,9 @@ class Response /** * Makes a request to the URL by using the preferred method - * @param string $uri URL to call - * @param array $options An array of parameters for both `curl` and `fopen` + * @param string $uri URL to call + * @param array $options An array of parameters for both `curl` and `fopen` + * @param null $callback * @return string The response of the request */ public static function get($uri = '', $options = [], $callback = null) From 3f94a6fda97b2589f3cd3be3d6c8240f329a8079 Mon Sep 17 00:00:00 2001 From: Paul Bovis Date: Tue, 17 Nov 2015 14:25:51 -0700 Subject: [PATCH 15/18] moved $language variable inside catch statement As requested --- system/src/Grav/Common/Data/Blueprint.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 7aa51440e..9bfc32303 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -72,13 +72,11 @@ class Blueprint { // Initialize data $this->fields(); - - // Get language class - $language = self::getGrav()['language']; try { $this->validateArray($data, $this->nested); } catch (\RuntimeException $e) { + $language = self::getGrav()['language']; $message = sprintf($language->translate('FORM.VALIDATION_FAIL', null, true) . ' %s', $e->getMessage()); throw new \RuntimeException($message); } From 49a5b38589968e655d9b0762585a428f23132130 Mon Sep 17 00:00:00 2001 From: yaman-jain Date: Wed, 18 Nov 2015 16:01:19 +0530 Subject: [PATCH 16/18] PhpDoc: callback definition as per #451 --- system/src/Grav/Common/GPM/Remote/Plugins.php | 2 +- system/src/Grav/Common/GPM/Remote/Themes.php | 2 +- system/src/Grav/Common/GPM/Response.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/src/Grav/Common/GPM/Remote/Plugins.php b/system/src/Grav/Common/GPM/Remote/Plugins.php index e40135096..df8668491 100644 --- a/system/src/Grav/Common/GPM/Remote/Plugins.php +++ b/system/src/Grav/Common/GPM/Remote/Plugins.php @@ -17,7 +17,7 @@ class Plugins extends AbstractPackageCollection /** * Local Plugins Constructor * @param bool $refresh - * @param null $callback + * @param callable $callback Either a function or callback in array notation */ public function __construct($refresh = false, $callback = null) { diff --git a/system/src/Grav/Common/GPM/Remote/Themes.php b/system/src/Grav/Common/GPM/Remote/Themes.php index e9ffacbdc..0af055b03 100644 --- a/system/src/Grav/Common/GPM/Remote/Themes.php +++ b/system/src/Grav/Common/GPM/Remote/Themes.php @@ -17,7 +17,7 @@ class Themes extends AbstractPackageCollection /** * Local Themes Constructor * @param bool $refresh - * @param null $callback + * @param callable $callback Either a function or callback in array notation */ public function __construct($refresh = false, $callback = null) { diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index 0df7e3876..d2b446b3b 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -67,7 +67,7 @@ class Response * Makes a request to the URL by using the preferred method * @param string $uri URL to call * @param array $options An array of parameters for both `curl` and `fopen` - * @param null $callback + * @param callable $callback Either a function or callback in array notation * @return string The response of the request */ public static function get($uri = '', $options = [], $callback = null) From d2660e0755209523d772065ad0a2d9a2c6e4c987 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Wed, 18 Nov 2015 18:14:35 +0200 Subject: [PATCH 17/18] Fixed gzip compression making it to work correctly with all servers and browsers --- CHANGELOG.md | 1 + system/src/Grav/Common/Grav.php | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4300bc74b..60b8c6b03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 1. [](#bugfix) * Fixed a fatal error if you have a collection with missing or invalid `@page: /route` + * Fixed gzip compression making it to work correctly with all servers and browsers # v1.0.0-rc.3 ## 10/29/2015 diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index da1ea4217..1335c4480 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -201,7 +201,10 @@ class Grav extends Container // Use output buffering to prevent headers from being sent too early. ob_start(); if ($this['config']->get('system.cache.gzip')) { - ob_start('ob_gzhandler'); + // Enable zip/deflate with a fallback in case of if browser does not support compressing. + if(!ob_start("ob_gzhandler")) { + ob_start(); + } } // Initialize the timezone @@ -416,22 +419,25 @@ class Grav extends Container public function shutdown() { if ($this['config']->get('system.debugger.shutdown.close_connection')) { - //stop user abort + // Prevent user abort. if (function_exists('ignore_user_abort')) { @ignore_user_abort(true); } - // close the session + // Close the session. if (isset($this['session'])) { $this['session']->close(); } - // flush buffer if gzip buffer was started if ($this['config']->get('system.cache.gzip')) { - ob_end_flush(); // gzhandler buffer + // Flush gzhandler buffer if gzip was enabled. + ob_end_flush(); + } else { + // Otherwise prevent server from compressing the output. + header('Content-Encoding: none'); } - // get lengh and close the connection + // Get length and close the connection. header('Content-Length: ' . ob_get_length()); header("Connection: close"); @@ -440,7 +446,7 @@ class Grav extends Container @ob_flush(); flush(); - // fix for fastcgi close connection issue + // Fix for fastcgi close connection issue. if (function_exists('fastcgi_finish_request')) { @fastcgi_finish_request(); } From 5193551d04e7add4cfdcf3bc6c9596a1ffd84d22 Mon Sep 17 00:00:00 2001 From: Flavio Copes Date: Wed, 18 Nov 2015 17:15:15 +0100 Subject: [PATCH 18/18] Fix date representation in system config Fixes https://github.com/getgrav/grav-plugin-admin/issues/278 --- system/blueprints/config/system.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index e79da93e3..ce1169ea4 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -73,7 +73,7 @@ form: options: "F jS \\a\\t g:ia": Date1 "l jS \\of F g:i A": Date2 - "D, m M Y G:i:s": Date3 + "D, d M Y G:i:s": Date3 "d-m-y G:i": Date4 "jS M Y": Date5 @@ -86,7 +86,7 @@ form: options: "F jS \\a\\t g:ia": Date1 "l jS \\of F g:i A": Date2 - "D, m M Y G:i:s": Date3 + "D, d M Y G:i:s": Date3 "d-m-y G:i": Date4 "jS M Y": Date5