diff --git a/README.md b/README.md
index 297b03f..08f6325 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-[![Software License](https://img.shields.io/badge/License-GPL%20v2-green.svg?style=flat-square)](LICENSE) [![PHP 7.2\+](https://img.shields.io/badge/PHP-7.2-blue?style=flat-square)](https://php.net) [![WordPress 5](https://img.shields.io/badge/WordPress-5.6-orange?style=flat-square)](https://wordpress.org)
+[![Software License](https://img.shields.io/badge/License-GPL%20v2-green.svg?style=flat-square)](LICENSE) [![PHP 7.2\+](https://img.shields.io/badge/PHP-7.2-blue?style=flat-square)](https://php.net) [![WordPress 5](https://img.shields.io/badge/WordPress-5.7-orange?style=flat-square)](https://wordpress.org)
# Fail2WP
-Security plugin for WordPress with support for Fail2ban and Cloudflare. Tested with WordPress 5.6+.
+Security plugin for WordPress with support for Fail2ban and Cloudflare. Tested with WordPress 5.5+.
## Description
@@ -17,14 +17,21 @@ Basic security functionality includes:
* Disabling login with username (require e-mail address)
* Preventing user enumeration (?author=nnn)
* Less detailed error messages on login failures
+* Minimum username length
+* Blocking specific usernames from being used to register new users
+* Requiring e-mail address matching for new user registrations
+* Warning about new user role setting
+* Blocking of portions or all of WordPress REST API
+* Disabling of RSS and Atom feeds
+* Removal of "Generator" information from HTML and feeds
* Detection of Cloudflare IP addresses for logging of actual IP addresses
The plugin also plays nicely with Fail2ban, which is an advanced way of blocking IP addresses dynamically upon suspicious behavior.
### Other notes
-* This plugin may work with earlier versions of WordPress
-* This plugin has been tested with `WordPress 5.5.3`, `5.6`, and `5.6.1` at the time of this writing
+* This plugin `may` work with earlier versions of WordPress
+* This plugin has been tested with `WordPress 5.5+` at the time of this writing
* This plugin optionally makes use of `mb_` PHP functions
* This plugin may create entries in your PHP error log (if active)
* This plugin contains no Javascript
@@ -59,11 +66,24 @@ This is a hard question to answer. There are no known incompatibilities.
## Changelog
+### 1.1.0
+* Added minimum username length
+* Added blocking of specific usernames (user registration)
+* Added requiring e-mail address matching setting
+* Added warning about new user role setting
+* Added blocking of portions or all of WordPress REST API
+* Added setting to disable RSS and Atom feeds
+* Added setting to remove "Generator" information from HTML and feeds
+* Minor corrections and general improvements
+
### 1.0.0
* Initial release
## Upgrade Notice
+### 1.1.0
+* Install the new version and walk through the settings.
+
### 1.0.0
* Initial release
@@ -101,6 +121,8 @@ If there is something you feel to be missing from this plugin, or if you have fo
This plugin can also be downloaded from [code.webbplatsen.net](https://code.webbplatsen.net/wordpress/fail2wp/) and [WordPress.org](https://wordpress.org/plugins/fail2wp/)
+More detailed documentation is available at [code.webbplatsen.net/documentation/fail2wp/](https://code.webbplatsen.net/documentation/fail2wp/)
+
Kudos to [Vincent Le Moign and Webalys](https://webalys.com) and [Thomas Lutz](https://github.com/tholu)
### External references
diff --git a/fail2wp/fail2wp.conf b/fail2wp/fail2wp.conf
index f7cf3a8..1edf6bb 100644
--- a/fail2wp/fail2wp.conf
+++ b/fail2wp/fail2wp.conf
@@ -2,23 +2,37 @@
before = common.conf
after = fail2wp.local
-# This filter is intended to be used with fail2ban and the Fail2WP plugin.
+# This filter is intended to be used with Fail2ban and the Fail2WP plugin.
#
# This file should be placed in /etc/fail2ban/filter.d as fail2wp.conf
#
# I'm by no means a fail2ban filter expert, so I'm sure this could do with some
-# improvements. It has been tested with fail2ban 0.11.1 on Ubuntu 20.04.LTS.
+# improvements. It has been tested with Fail2ban 0.11.1 on Ubuntu 20.04.LTS.
+#
+# ADVANCED: You may, of course, split these into several different jails and
+# triggers and give them different treatment in Fail2ban so that some
+# of the log messages trigger one behavior in Fail2ban, and others
+# are ignored or behave differently.
#
# Joaquim Homrighausen
#
# The intended log messages to trigger fail2ban on are:
#
+# @since 1.0.0
+#
# Authentication failure for validuser from n.n.n.n port 443
# Invalid email invalidemail from n.n.n.n port 443
# Invalid user invaliduser from n.n.n.n port 443
# User enumeration request from n.n.n.n port 443
# Invalid credentials invalidlogin from n.n.n.n port 443
#
+# @since 1.1.0
+#
+# Blocked REST API request from n.n.n.n port 443
+# Unauthenticated REST API request from n.n.n.n port 443
+#
+# Other messages:
+#
# The Fail2WP plugin can further emit these messages (no action taken):
#
# Unknown error "nnn" during login from n.n.n.n port nnn
@@ -31,7 +45,8 @@ failregex = fail2wp(.*): Authentication failure for .* from port .*$
fail2wp(.*): Invalid user .* from port .*$
fail2wp(.*): Invalid email .* from port .*$
fail2wp(.*): Invalid credentials .* from port .*$
- fail2wp(.*): User enumeration request from port .*$
+ fail2wp(.*): Blocked REST API request from from port .*$
+ fail2wp(.*): Unauthenticated REST API request from port .*$
# Your entry for Fail2WP in jail.local should look like this:
#
diff --git a/fail2wp/fail2wp.php b/fail2wp/fail2wp.php
index 49ec040..d4fdef9 100644
--- a/fail2wp/fail2wp.php
+++ b/fail2wp/fail2wp.php
@@ -11,12 +11,12 @@
* Plugin Name: Fail2WP
* Plugin URI: https://code.webbplatsen.net/wordpress/fail2wp/
* Description: Security plugin for WordPress with support for fail2ban
- * Version: 1.0.0
+ * Version: 1.1.0
* Author: WebbPlatsen, Joaquim Homrighausen
* Author URI: https://webbplatsen.se/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
- * Text Domain: playground
+ * Text Domain: fail2wp
* Domain Path: /languages
*
* fail2wp.php
@@ -42,7 +42,6 @@
*/
namespace fail2wp;
-
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
@@ -51,16 +50,28 @@
die( '-1' );
}
-define( 'FAIL2WP_VERSION', '1.0.0' );
-define( 'FAIL2WP_REV', 1 );
-define( 'FAIL2WP_PLUGINNAME_HUMAN', 'Fail2WP' );
-define( 'FAIL2WP_PLUGINNAME_SLUG', 'fail2wp' );
-define( 'FAIL2WP_DEFAULT_PREFIX', 'fail2wp' );
-define( 'FAIL2WP_ALERT_SUCCESS', 1 );
-define( 'FAIL2WP_ALERT_FAILURE', 2 );
-define( 'FAIL2WP_ALERT_USER_ENUM', 3 );
-define( 'FAIL2WP_DEFAULT_HTTP_PORT', 80 );
-define( 'FAIL2WP_DEFAULT_HTTPS_PORT', 443 );
+define( 'FAIL2WP_WORDPRESS_PLUGIN', true );
+define( 'FAIL2WP_VERSION', '1.1.0' );
+define( 'FAIL2WP_REV', 1 );
+define( 'FAIL2WP_PLUGINNAME_HUMAN', 'Fail2WP' );
+define( 'FAIL2WP_PLUGINNAME_SLUG', 'fail2wp' );
+define( 'FAIL2WP_DEFAULT_PREFIX', 'fail2wp' );
+define( 'FAIL2WP_ALERT_SUCCESS', 1 );
+define( 'FAIL2WP_ALERT_FAILURE', 2 );
+define( 'FAIL2WP_ALERT_USER_ENUM', 3 );
+define( 'FAIL2WP_ALERT_REST_NOTAUTH', 4 );
+define( 'FAIL2WP_ALERT_REST_BLOCKED', 5 );
+define( 'FAIL2WP_DEFAULT_HTTP_PORT', 80 );
+define( 'FAIL2WP_DEFAULT_HTTPS_PORT', 443 );
+define( 'FAIL2WP_DB_VERSION', 2 );
+define( 'FAIL2WP_EXPORT_HEADER', 'fail2wp_export.begin.' );
+define( 'FAIL2WP_EXPORT_FOOTER', '.fail2wp_export.end' );
+
+// define( 'FAIL2WP_DEBUG', true );
+if ( defined( 'FAIL2WP_DEBUG' ) ) {
+ define( 'FAIL2WP_REST_DEBUG', true );
+ define( 'FAIL2WP_DUMP_SETTINGS', true );
+}
require_once plugin_dir_path( __FILE__ ) . 'includes/class-fail2wp-syslog.php';
@@ -73,7 +84,10 @@
class Fail2WP {
public static $instance = null;
protected $plugin_name;
- protected $version;
+ protected $fail2wp_plugin_version;
+ protected $fail2wp_mail_headers; // @since 1.1.0
+ protected $fail2wp_have_mbstring; // @since 1.1.0
+
protected $fail2wp_wp_roles = null;
protected $fail2wp_wp_roles_enus = null;
protected $fail2wp_settings_tab = '';
@@ -81,7 +95,10 @@ class Fail2WP {
protected $fail2wp_roles_notify;
protected $fail2wp_roles_warn;
protected $fail2wp_unknown_warn;
+ protected $fail2wp_settings_dbversion; // @since 1.1.0
protected $fail2wp_settings_remove;
+ protected $fail2wp_settings_remove_generator; // @since 1.1.0
+ protected $fail2wp_settings_remove_feeds; // @since 1.1.0
protected $fail2wp_also_log_php;
protected $fail2wp_block_user_enum;
protected $fail2wp_block_username_login;
@@ -92,10 +109,29 @@ class Fail2WP {
protected $fail2wp_cloudflare_ipv4;
protected $fail2wp_cloudflare_ipv6;
+ protected $fail2wp_reguser_warn; // @since 1.1.0
+ protected $fail2wp_reguser_warn_role; // @since 1.1.0
+ protected $fail2wp_reguser_force; // @since 1.1.0
+ protected $fail2wp_reguser_force_role; // @since 1.1.0
+ protected $fail2wp_reguser_username_length; // @since 1.1.0
+ protected $fail2wp_reguser_username_ban; // @since 1.1.0
+ protected $fail2wp_reguser_useremail_require; // @since 1.1.0
+
+ protected $fail2wp_rest = null; // @since 1.1.0
+ protected $fail2wp_rest_filter_log_blocked; // @since 1.1.0
+ protected $fail2wp_rest_filter_block_all; // @since 1.1.0
+ protected $fail2wp_rest_filter_block_index; // @since 1.1.0
+ protected $fail2wp_rest_filter_block_ns; // @since 1.1.0
+ protected $fail2wp_rest_filter_block_routes; // @since 1.1.0
+ protected $fail2wp_rest_filter_route_list; // @since 1.1.0
+ protected $fail2wp_rest_filter_require_authenticated; // @since 1.1.0
+ protected $fail2wp_rest_filter_ipv4_bypass; // @since 1.1.0
+ protected $fail2wp_rest_filter_ipv6_bypass; // @since 1.1.0
+
public static function getInstance( string $version = '', string $slug = '' )
{
null === self::$instance AND self::$instance = new self( $version, $slug );
- return self::$instance;
+ return( self::$instance );
}
/**
* Start me up ...
@@ -103,53 +139,807 @@ public static function getInstance( string $version = '', string $slug = '' )
public function __construct( string $version = '', string $slug = '' ) {
if ( empty( $version ) ) {
if ( defined( 'FAIL2WP_VERSION' ) ) {
- $this->version = FAIL2WP_VERSION;
+ $this->fail2wp_plugin_version = FAIL2WP_VERSION;
} else {
- $this->version = '0.0.1';
+ $this->fail2wp_plugin_version = '1.0.0';
}
} else {
- $this->version = $version;
+ $this->fail2wp_plugin_version = $version;
}
if ( empty( $slug ) ) {
$this->plugin_name = FAIL2WP_PLUGINNAME_SLUG;
} else {
$this->plugin_name = $slug;
}
+ // Setup default mail headers
+ $this->fail2wp_mail_headers = array( 'Auto-Submitted: auto-replied',
+ 'X-Auto-Response-Suppress: All',
+ 'Precedence: auto_reply' );
+ // We only need to query this once really
+ $this->fail2wp_have_mbstring = extension_loaded( 'mbstring ');
+ // Things we may be interested in for the REST API
+ $this->fail2wp_rest_filter_route_list = array(
+ 'categories',
+ 'comments',
+ 'media',
+ 'pages',
+ 'posts',
+ 'search',
+ 'statuses',
+ 'tags',
+ 'taxonomies',
+ 'types',
+ 'users',
+ );
+
+ // Dump all of our settings, for development
+ if ( defined( 'FAIL2WP_DUMP_SETTINGS' ) && FAIL2WP_DUMP_SETTINGS ) {
+ global $wpdb;
+ $settings = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->options} WHERE option_name LIKE 'fail2wp%'" ), ARRAY_A );
+ if ( is_array( $settings ) ) {
+ error_log( var_export( $settings, true ) );
+ } else {
+ error_log ( basename( __FILE__ ) . ': Unable to fetch settings' );
+ }
+ }
// Fetch options and setup defaults
- $this->fail2wp_site_label = $this->fail2wp_get_option( 'fail2wp-site-label', true );
- $this->fail2wp_roles_notify = $this->fail2wp_get_option( 'fail2wp-roles-notify', true );
- $this->fail2wp_roles_warn = $this->fail2wp_get_option( 'fail2wp-roles-warn', true );
- $this->fail2wp_unknown_warn = $this->fail2wp_get_option( 'fail2wp-unknown-warn', true );
-
- $this->fail2wp_also_log_php = $this->fail2wp_get_option( 'fail2wp-also-log-php', false );
- $this->fail2wp_block_user_enum = $this->fail2wp_get_option( 'fail2wp-block-user-enum', false );
- $this->fail2wp_block_username_login = $this->fail2wp_get_option( 'fail2wp-block-username-login', false );
- $this->fail2wp_log_user_enum = $this->fail2wp_get_option( 'fail2wp-log-user-enum', false );
- $this->fail2wp_secure_login_message = $this->fail2wp_get_option( 'fail2wp-secure-login-message', false );
- $this->fail2wp_cloudflare_check = $this->fail2wp_get_option( 'fail2wp-cloudflare-check', false );
- $this->fail2wp_cloudflare_ipv4 = @ json_decode( $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv4', '' ), true, 2 );
+ // ..General
+ $this->fail2wp_settings_dbversion = get_option( 'fail2wp-settings-dbversion', null );
+ if ( $this->fail2wp_settings_dbversion === null ) {
+ $this->fail2wp_settings_dbversion = FAIL2WP_DB_VERSION;
+ update_option( 'fail2wp-settings-dbversion', $this->fail2wp_settings_dbversion );
+ }
+ $this->fail2wp_site_label = $this->fail2wp_get_site_label( false );
+ $this->fail2wp_roles_notify = @ json_decode( get_option( 'fail2wp-roles-notify', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_roles_notify ) ) {
+ $this->fail2wp_roles_notify = array();
+ update_option( 'fail2wp-roles-notify', json_encode( $this->fail2wp_roles_notify ) );
+ }
+ $this->fail2wp_roles_warn = @ json_decode( get_option( 'fail2wp-roles-warn', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_roles_warn ) ) {
+ $this->fail2wp_roles_warn = array( 'administrator' );
+ update_option( 'fail2wp-roles-warn', json_encode( $this->fail2wp_roles_warn ) );
+ }
+ $this->fail2wp_unknown_warn = get_option( 'fail2wp-unknown-warn', null );
+ if ( $this->fail2wp_unknown_warn || $this->fail2wp_unknown_warn === null ) {
+ $this->fail2wp_unknown_warn = true;
+ } else {
+ $this->fail2wp_unknown_warn = false;
+ }
+ // ..New users @since 1.1.0
+ $default_role = get_option( 'default_role', null );// Get WordPress' default role
+ $this->fail2wp_reguser_warn = get_option( 'fail2wp-reguser-warn', null );
+ if ( $this->fail2wp_reguser_warn === null ) {
+ $this->fail2wp_reguser_warn = true;
+ }
+ $this->fail2wp_reguser_warn_role = get_option( 'fail2wp-reguser-warn-role', null );
+ if ( $this->fail2wp_reguser_warn_role === null ) {
+ $this->fail2wp_reguser_warn_role = ( ! empty( $default_role ) ? $default_role:'subscriber' );
+ }
+ $this->fail2wp_reguser_force = get_option( 'fail2wp-reguser-force', null );
+ if ( $this->fail2wp_reguser_force === null ) {
+ $this->fail2wp_reguser_force = true;
+ }
+ $this->fail2wp_reguser_force_role = get_option( 'fail2wp-reguser-force-role', null );
+ if ( $this->fail2wp_reguser_force_role === null ) {
+ $this->fail2wp_reguser_force_role = $this->fail2wp_reguser_warn_role;
+ }
+ if ( $this->fail2wp_reguser_force && $default_role != $this->fail2wp_reguser_force_role ) {
+ // Set WordPress' default role
+ update_option( 'default_role', $this->fail2wp_reguser_force_role );
+ // Tell admin we've done this
+ add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_user_role_forced'] );
+ add_action( 'plugins_loaded', [$this, 'fail2wp_admin_alert_new_user_role_forced_email'] );
+ $default_role = $this->fail2wp_reguser_force_role;
+ }
+ $users_can_register = get_option( 'users_can_register', null );
+ if ( $this->fail2wp_reguser_warn ) {
+ if ( $users_can_register ) {
+ // Only trigger alarms if users_can_register is true
+ if ( $default_role != $this->fail2wp_reguser_warn_role ) {
+ add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_mismatch'] );
+ } elseif ( $default_role == 'administrator' ) {
+ add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_admin'] );
+ } elseif ( $default_role == null ) {
+ add_action( 'admin_notices', [$this, 'fail2wp_admin_alert_new_users_null'] );
+ }
+ }
+ }
+ $this->fail2wp_reguser_username_length = get_option( 'fail2wp-reguser-username-length', null );
+ if ( $this->fail2wp_reguser_username_length === null || $this->fail2wp_reguser_username_length > 200 ) {
+ $this->fail2wp_reguser_username_length = 0;
+ update_option( 'fail2wp-reguser-username-length', (int)$this->fail2wp_reguser_username_length );
+ }
+ $this->fail2wp_reguser_username_ban = @ json_decode( get_option ( 'fail2wp-reguser-username-ban', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_reguser_username_ban ) ) {
+ $this->fail2wp_reguser_username_ban = array( 'administrator', 'admin', 'sysadmin', 'siteadmin' );
+ update_option( 'fail2wp-reguser-username-ban', json_encode( $this->fail2wp_reguser_username_ban ) );
+ }
+ $this->fail2wp_reguser_useremail_require = @ json_decode( get_option ( 'fail2wp-reguser-useremail-require', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_reguser_useremail_require ) ) {
+ $this->fail2wp_reguser_useremail_require = array();
+ update_option( 'fail2wp-reguser-useremail-require', json_encode( $this->fail2wp_reguser_useremail_require ) );
+ }
+ // Possibly add new user registration details validation if new user
+ // registrations are active and we have something to validate against.
+ if ( $users_can_register &&
+ ( ! empty( $this->fail2wp_reguser_username_ban )
+ || ! empty( $this->fail2wp_reguser_useremail_require )
+ || $this->fail2wp_reguser_username_length > 1 ) ) {
+ add_filter( 'registration_errors', [$this, 'fail2wp_admin_check_new_user'], 10, 3 );
+ }
+ // .. REST API @since 1.1.0
+ $this->fail2wp_rest_filter_require_authenticated = get_option( 'fail2wp-rest-filter-require-authenticated', null );
+ if ( ! $this->fail2wp_rest_filter_require_authenticated || $this->fail2wp_rest_filter_require_authenticated === null ) {
+ $this->fail2wp_rest_filter_require_authenticated = false;
+ } else {
+ $this->fail2wp_rest_filter_require_authenticated = true;
+ }
+ if ( $this->fail2wp_rest_filter_require_authenticated ) {
+ add_filter( 'rest_authentication_errors', [$this, 'fail2wp_rest_filter_authenticate'], 10, 1 );
+ }
+ $this->fail2wp_rest_filter_log_blocked = get_option( 'fail2wp-rest-filter-log-blocked', null );
+ if ( ! $this->fail2wp_rest_filter_log_blocked || $this->fail2wp_rest_filter_log_blocked === null ) {
+ $this->fail2wp_rest_filter_log_blocked = false;
+ } else {
+ $this->fail2wp_rest_filter_log_blocked = true;
+ }
+ $this->fail2wp_rest_filter_block_index = get_option( 'fail2wp-rest-filter-block-index', null );
+ if ( ! $this->fail2wp_rest_filter_block_index || $this->fail2wp_rest_filter_block_index === null ) {
+ $this->fail2wp_rest_filter_block_index = false;
+ } else {
+ $this->fail2wp_rest_filter_block_index = true;
+ }
+ $this->fail2wp_rest_filter_block_all = get_option( 'fail2wp-rest-filter-block-all', null );
+ if ( ! $this->fail2wp_rest_filter_block_all || $this->fail2wp_rest_filter_block_all === null ) {
+ $this->fail2wp_rest_filter_block_all = false;
+ } else {
+ $this->fail2wp_rest_filter_block_all = true;
+ }
+ $this->fail2wp_rest_filter_block_ns = @ json_decode( get_option( 'fail2wp-rest-filter-block-ns', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_rest_filter_block_ns ) ) {
+ $this->fail2wp_rest_filter_block_ns = array();
+ update_option( 'fail2wp-rest-filter-block-ns', json_encode( $this->fail2wp_rest_filter_block_ns ) );
+ }
+ $this->fail2wp_rest_filter_block_routes = @ json_decode( get_option( 'fail2wp-rest-filter-block-routes', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_rest_filter_block_routes ) ) {
+ $this->fail2wp_rest_filter_block_routes = array();
+ update_option( 'fail2wp-rest-filter-block-routes', json_encode( $this->fail2wp_rest_filter_block_routes ) );
+ }
+ $this->fail2wp_rest_filter_ipv4_bypass = @ json_decode( get_option ( 'fail2wp-rest-filter-ipv4-bypass', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_rest_filter_ipv4_bypass ) ) {
+ $this->fail2wp_rest_filter_ipv4_bypass = array();
+ update_option( 'fail2wp-rest-filter-ipv4-bypass', json_encode( $this->fail2wp_rest_filter_ipv4_bypass ) );
+ }
+ $this->fail2wp_rest_filter_ipv6_bypass = @ json_decode( get_option ( 'fail2wp-rest-filter-ipv6-bypass', null ), true, 2 );
+ if ( ! is_array( $this->fail2wp_rest_filter_ipv6_bypass ) ) {
+ $this->fail2wp_rest_filter_ipv6_bypass = array();
+ update_option( 'fail2wp-rest-filter-ipv6-bypass', json_encode( $this->fail2wp_rest_filter_ipv6_bypass ) );
+ }
+ // ..Logging
+ $this->fail2wp_also_log_php = get_option( 'fail2wp-also-log-php', null );
+ if ( ! $this->fail2wp_also_log_php || $this->fail2wp_also_log_php === null ) {
+ $this->fail2wp_also_log_php = false;
+ } else {
+ $this->fail2wp_also_log_php = true;
+ }
+ $this->fail2wp_block_user_enum = get_option( 'fail2wp-block-user-enum', null );
+ if ( $this->fail2wp_block_user_enum || $this->fail2wp_block_user_enum === null ) {
+ $this->fail2wp_block_user_enum = true;
+ } else {
+ $this->fail2wp_block_user_enum = false;
+ }
+ $this->fail2wp_block_username_login = get_option( 'fail2wp-block-username-login', null );
+ if ( $this->fail2wp_block_username_login === null || ! $this->fail2wp_block_username_login ) {
+ $this->fail2wp_block_username_login = false;
+ } else {
+ $this->fail2wp_block_username_login = true;
+ }
+ $this->fail2wp_log_user_enum = get_option( 'fail2wp-log-user-enum', null );
+ if ( $this->fail2wp_log_user_enum || $this->fail2wp_log_user_enum === null ) {
+ $this->fail2wp_log_user_enum = true;
+ } else {
+ $this->fail2wp_log_user_enum = false;
+ }
+ // ..Failed login message
+ $this->fail2wp_secure_login_message = get_option( 'fail2wp-secure-login-message', null );
+ if ( $this->fail2wp_secure_login_message || $this->fail2wp_secure_login_message === null ) {
+ $this->fail2wp_secure_login_message = true;
+ } else {
+ $this->fail2wp_secure_login_message = false;
+ }
+ // ..Cloudflare
+ $this->fail2wp_cloudflare_check = get_option( 'fail2wp-cloudflare-check', null );
+ if ( $this->fail2wp_cloudflare_check === null || ! $this->fail2wp_cloudflare_check ) {
+ $this->fail2wp_cloudflare_check = false;
+ } else {
+ $this->fail2wp_cloudflare_check = true;
+ }
+ $this->fail2wp_cloudflare_ipv4 = @ json_decode( get_option ( 'fail2wp-cloudflare-ipv4', null ), true, 2 );
if ( ! is_array( $this->fail2wp_cloudflare_ipv4 ) ) {
$this->fail2wp_cloudflare_ipv4 = array();
+ update_option( 'fail2wp-cloudflare-ipv4', json_encode( $this->fail2wp_cloudflare_ipv4 ) );
}
- $this->fail2wp_cloudflare_ipv6 = @ json_decode( $this->fail2wp_get_option( 'fail2wp-cloudflare-ipv6', '' ), true, 2 );
+ $this->fail2wp_cloudflare_ipv6 = @ json_decode( get_option ( 'fail2wp-cloudflare-ipv6', null ), true, 2 );
if ( ! is_array( $this->fail2wp_cloudflare_ipv6 ) ) {
- $this->fail2wp_cloudflare_ipv4 = array();
+ $this->fail2wp_cloudflare_ipv6 = array();
+ update_option( 'fail2wp-cloudflare-ipv6', json_encode( $this->fail2wp_cloudflare_ipv6 ) );
+ }
+ // ..Other options
+ $this->fail2wp_settings_remove_generator = get_option( 'fail2wp-settings-remove-generator', null );
+ if ( $this->fail2wp_settings_remove_generator === null || ! $this->fail2wp_settings_remove_generator ) {
+ $this->fail2wp_settings_remove_generator = false;
+ } else {
+ $this->fail2wp_settings_remove_generator = true;
}
- $this->fail2wp_settings_remove = $this->fail2wp_get_option( 'fail2wp-settings-remove', false );
+ $this->fail2wp_settings_remove_feeds = get_option( 'fail2wp-settings-remove-feeds', null );
+ if ( $this->fail2wp_settings_remove_feeds === null || ! $this->fail2wp_settings_remove_feeds ) {
+ $this->fail2wp_settings_remove_feeds = false;
+ } else {
+ $this->fail2wp_settings_remove_feeds = true;
+ }
+ $this->fail2wp_settings_remove = get_option( 'fail2wp-settings-remove', null );
+ if ( $this->fail2wp_settings_remove === null || ! $this->fail2wp_settings_remove ) {
+ $this->fail2wp_settings_remove = false;
+ } else {
+ $this->fail2wp_settings_remove = true;
+ }
+ // .. REST filtering
+ if ( ! $this->fail2wp_rest_filter_require_authenticated ) {
+ if ( $this->fail2wp_rest_filter_block_all ) {
+ remove_action( 'wp_head', 'rest_output_link_wp_head' );
+ }
+ if ( $this->fail2wp_rest_filter_block_all
+ || ! empty( $this->fail2wp_rest_filter_block_index )
+ || ! empty( $this->fail2wp_rest_filter_block_ns )
+ || ! empty( $this->fail2wp_rest_filer_block_routes ) ) {
+ add_action( 'rest_api_init', [$this, 'fail2wp_rest_init'] );
+ }
+ }
+ // .. Generator filtering
+ if ( $this->fail2wp_settings_remove_generator ) {
+ remove_action( 'wp_head', 'wp_generator' );
+ add_filter( 'the_generator', [$this, 'fail2wp_the_generator'], 10, 2 );
+ }
+ // .. Feed filtering
+ if ( $this->fail2wp_settings_remove_feeds ) {
+ add_filter( 'feed_links_show_posts_feed', [$this, 'fail2wp_noshow_feeds'], 10, 1 );
+ add_filter( 'feed_links_show_comments_feed', [$this, 'fail2wp_noshow_feeds'], 10, 1 );
+ remove_action( 'wp_head', 'rsd_link' );
+ remove_action( 'wp_head', 'feed_links', 2 );
+ remove_action( 'wp_head', 'index_rel_link' );
+ remove_action( 'wp_head', 'wlwmanifest_link' );
+ remove_action( 'wp_head', 'feed_links_extra', 3 );
+ remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );
+ remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 );
+ remove_action( 'wp_head', 'adjacent_posts_rel_link', 10, 0 );
+ remove_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 );
+ remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );
+ remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
+ add_action( 'do_feed', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_rdf', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_rss', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_rss2', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_atom', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_rss2_comments', [$this, 'fail2wp_remove_feeds'] );
+ add_action( 'do_feed_atom_comments', [$this, 'fail2wp_remove_feeds'] );
+ }
+ // .. Various settings
$this->fail2wp_default_http_port = FAIL2WP_DEFAULT_HTTP_PORT;
$this->fail2wp_default_https_port = FAIL2WP_DEFAULT_HTTPS_PORT;
-
+ $this->fail2wp_prefix = get_option( 'fail2wp-prefix', null );
+ if ( $this->fail2wp_prefix === null ) {
+ $this->fail2wp_prefix = '';
+ }
+ // Validate selected configuration tab
$this->fail2wp_settings_tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : '' );
- if ( ! in_array( $this->fail2wp_settings_tab, ['logging', 'advanced', 'cloudflare', 'about'] ) ) {
+ if ( ! in_array( $this->fail2wp_settings_tab, ['newuser', 'logging', 'advanced', 'restapi', 'cloudflare', 'importexport', 'about'] ) ) {
$this->fail2wp_settings_tab = '';
}
+ // Add 'Settings' link in plugin list
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), [$this, 'fail2wp_settings_link']);
}
/**
- * Fetch filemtime() of filen and return it.
+ * Add link to Fail2WP settings in plugin list.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_settings_link( array $links ) {
+ $our_link = '' .
+ esc_html__( 'Settings ', $this->plugin_name ) . '';
+ array_unshift( $links, $our_link );
+ return ( $links );
+ }
+
+ /**
+ * Remove "generator" output.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_the_generator( string $generator_type, string $type ) {
+ return( '' );
+ }
+
+ /**
+ * Remove feeds.
+ *
+ * Instead of a 404, we issue a WordPress redirect to the main page.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_remove_feeds( $feed ) {
+ wp_redirect( home_url(), 302 );
+ exit();
+ // One could have something like this too of course, but WordPress
+ // doesn't seem to like this for some reason and will still display
+ // the feed content.
+ /*
+ wp_die( __( 'This feed has been disabled, please visit', $this->plugin_name ) .
+ ' ' .
+ home_url() .
+ '',
+ __( 'Feed disabled', $this->plugin_name ),
+ array( 'link_url' => home_url(),
+ 'link_text' => home_url(),
+ 'code' => 'feed_disabled',
+ 'response' => 404 ) );
+ */
+
+ }
+ public function fail2wp_noshow_feeds( $noshow ) {
+ return( false );
+ }
+
+ /**
+ * Setup various REST handlers.
+ *
+ * We only do this on 'rest_api_init' so that we don't clutter chain.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_rest_init( \WP_REST_Server $wp_rest ) {
+ if ( $this->fail2wp_rest === null ){
+ $this->fail2wp_rest = $wp_rest;
+ }
+ if ( $this->fail2wp_rest_filter_block_all ) {
+ // Block everything
+ $add_pre_dispatch_filter = true;
+ } else {
+ $add_pre_dispatch_filter = false;
+ // Block selectively
+ if ( $this->fail2wp_rest_filter_block_index ) {
+ // Block REST API index calls
+ add_filter( 'rest_index', [$this, 'fail2wp_rest_index'] );
+ $add_pre_dispatch_filter = true;
+ } elseif ( ! empty( $this->fail2wp_rest_filter_block_ns ) ) {
+ // Check namespaces on requests
+ $add_pre_dispatch_filter = true;
+ }
+ }
+ if ( $add_pre_dispatch_filter ) {
+ add_filter( 'rest_pre_dispatch', [$this, 'fail2wp_rest_pre_dispatch'], 10, 4 );
+ }
+ }
+
+ /**
+ * Require authentication for REST API calls.
+ *
+ * This effectively bypasses all other "block" settings.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_rest_filter_authenticate( $result ) {
+ // Pass along previous success or failure
+ if ( $result || is_wp_error( $result ) ) {
+ return( $result );
+ }
+ if ( ! is_user_logged_in() ) {
+ if ( $this->fail2wp_rest_filter_log_blocked ) {
+ // Possibly log this for Fail2ban
+ $alert_message = $this->fail2wp_make_alert_message( '', null, FAIL2WP_ALERT_REST_NOTAUTH );
+ if ( ! empty( $alert_message ) ) {
+ $this->fail2wp_alert_send( $alert_message );
+ }
+ }
+ // This text is taken verbatim from WordPress and will thus be
+ // translated in the same way.
+ return new \WP_Error( 'rest_not_logged_in',
+ __( 'You are not currently logged in.' ),
+ array( 'status' => 401 )
+ );
+ }
+ return( $result );
+ }
+
+ /**
+ * Block REST API index.
+ *
+ * This simply makes the REST API index request return an empty body.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_rest_index( $response ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for index' );
+ }
+ if ( $response instanceof \WP_REST_Response ) {
+ $response->data = array();
+ }
+ return( $response );
+
+ }
+
+ /**
+ * Handle majority of REST API filtering.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_rest_pre_dispatch( $result, \WP_REST_Server $rest_server, \WP_REST_Request $request ) {
+ // Figure out who we're talking to
+ $remote_ip = $_SERVER['REMOTE_ADDR'];
+ $remote_ip_cf = $this->fail2wp_do_cloudflare_lookup( $remote_ip );
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): ' .
+ 'Remote address="' . $remote_ip_cf . '" ' . ( $remote_ip !== $remote_ip_cf ? '(' . $remote_ip . ') ' : '' ) .
+ 'REST URL="' . rest_url() . '" ' .
+ 'Route="' . $request->get_route() . '"' );
+ }
+ // Check bypass lists
+ if ( ! empty( $this->fail2wp_rest_filter_ipv4_bypass ) || ! empty( $this->fail2wp_rest_filter_ipv6_bypass ) ) {
+ // Setup CIDRmatch
+ $cidrm = new \CIDRmatch\CIDRmatch();
+ $allowed_to_bypass = false;
+ if ( ! empty( $this->fail2wp_rest_filter_ipv4_bypass ) && is_array( $this->fail2wp_rest_filter_ipv4_bypass ) ) {
+ foreach( $this->fail2wp_rest_filter_ipv4_bypass as $bypass ) {
+ if ( ! empty( $bypass ) && $cidrm->match( $remote_ip_cf, $bypass ) ) {
+ $allowed_to_bypass = true;
+ break;
+ }
+ }
+ }
+ if ( ! $allowed_to_bypass && ! empty( $this->fail2wp_rest_filter_ipv6_bypass ) && is_array( $this->fail2wp_rest_filter_ipv6_bypass ) ) {
+ foreach( $this->fail2wp_rest_filter_ipv6_bypass as $bypass ) {
+ if ( ! empty( $bypass ) && $cidrm->match( $remote_ip_cf, $bypass ) ) {
+ $allowed_to_bypass = true;
+ break;
+ }
+ }
+ }
+ if ( $allowed_to_bypass ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): IP address is allowed to bypass REST API blocks' );
+ }
+ return( $result );
+ }
+ }
+ // Should we block all REST requests?
+ if ( $this->fail2wp_rest_filter_block_all ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking all REST API requests' );
+ }
+ $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true );
+ if ( ! empty( $alert_message ) ) {
+ $this->fail2wp_alert_send( $alert_message );
+ }
+ return new \WP_Error(
+ 'rest_no_route',
+ // This text is taken verbatim from WordPress and will thus be
+ // translated in the same way.
+ __( 'No route was found matching the URL and request method.' ),
+ array( 'status' => '404' )
+ );
+ }
+ // Figure out what has been requested and where we live
+ $namespaces = $this->fail2wp_get_rest_ns( $rest_server );
+ $route = $request->get_route();
+ $wp_route = '';
+ $is_wp_route = false;
+ if ( ! empty( $route ) ) {
+ // "Normalize" route, remove leading /
+ if ( $this->fail2wp_have_mbstring ) {
+ $route = mb_substr( $route, 1 );
+ $is_wp_route = ( mb_stripos( $route, 'wp/v2/' ) === 0 );
+ if ( $is_wp_route ) {
+ $wp_route = mb_substr( $route, 6 );
+ }
+ } else {
+ $route = substr( $route, 1 );
+ $is_wp_route = ( stripos( $route, 'wp/v2/' ) === 0 );
+ if ( $is_wp_route ) {
+ $wp_route = substr( $route, 6 );
+ }
+ }
+ }
+ // Figure out if we're blocking index requests and if this is one
+ if ( in_array( $route, $namespaces ) ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for index' );
+ }
+ $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true );
+ if ( ! empty( $alert_message ) ) {
+ $this->fail2wp_alert_send( $alert_message );
+ }
+ return new \WP_Error(
+ 'rest_no_route',
+ // This text is taken verbatim from WordPress and will thus be
+ // translated in the same way.
+ __( 'No route was found matching the URL and request method.' ),
+ array( 'status' => '404' )
+ );
+ }
+ // Figure out if we're blocking this namespace. Request will be /...
+ // Namespaces to block are stored without the leading slash.
+ if ( ! empty( $this->fail2wp_rest_filter_block_ns ) ) {
+ if ( in_array( $route, $this->fail2wp_rest_filter_block_ns ) ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for NS "' . $route . '"' );
+ }
+ $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true );
+ if ( ! empty( $alert_message ) ) {
+ $this->fail2wp_alert_send( $alert_message );
+ }
+ return new \WP_Error(
+ 'rest_no_route',
+ // This text is taken verbatim from WordPress and will thus be
+ // translated in the same way.
+ __( 'No route was found matching the URL and request method.' ),
+ array( 'status' => '404' )
+ );
+ }
+ }
+ // Figure out if we're blocking this WP REST API route. Request will be
+ // /... Routes to block are stored without the leading slash.
+ if ( $is_wp_route && ! empty( $this->fail2wp_rest_filter_block_routes ) ) {
+ if ( in_array( $wp_route, $this->fail2wp_rest_filter_block_routes ) ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): We are blocking REST API requests for route "' . $route . '"' );
+ }
+ $alert_message = $this->fail2wp_make_alert_message( $remote_ip_cf, null, FAIL2WP_ALERT_REST_BLOCKED, true );
+ if ( ! empty( $alert_message ) ) {
+ $this->fail2wp_alert_send( $alert_message );
+ }
+ return new \WP_Error(
+ 'rest_no_route',
+ // This text is taken verbatim from WordPress and will thus be
+ // translated in the same way.
+ __( 'No route was found matching the URL and request method.' ),
+ array( 'status' => '404' )
+ );
+ }
+ }
+ // Carry on ...
+ return( $result );
+ }
+
+ /**
+ * Checks username and e-mail address prior to registration.
+ *
+ * Hooks 'registration_errors' and validates username and e-mail address
+ * according to our settings.
+ *
+ * @since 1.1.0
+ * @param WP_Error $errors.
+ * @param string $sanitized_user_login.
+ * @param string $user_email.
+ * @return WP_Error With or without errors
+ */
+ public function fail2wp_admin_check_new_user( \WP_Error $errors, string $user_login, string $user_email ) {
+ // apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
+ if ( ! is_object( $errors ) || ! is_a( $errors, 'WP_Error' ) ) {
+ //Make sure we have what we need, otherwise just return it
+ if ( is_object( $errors ) ) {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unknown context "' . get_class( $errors ) . '"' );
+ } else {
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unknown context "' . $errors . '"' );
+ }
+ return( $errors );
+ }
+ error_log(print_r($errors, true));
+ // Check for existing error message and possibly make it less revealing
+ if ( $this->fail2wp_secure_login_message ) {
+ if ( ! empty( $errors->errors['username_exists'] ) ) {
+ $errors->remove( 'username_exists' );
+ $errors->add( 'username_exists', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) );
+ return( $errors );
+ } elseif ( ! empty( $errors->errors['email_exists'] ) ) {
+ $errors->remove( 'email_exists' );
+ $errors->add( 'email_exists', esc_html__( 'Invalid e-mail address, please try again.', $this->plugin_name ) );
+ return( $errors );
+ }
+ }
+ // Check usernames
+ $have_error = false;
+ if ( empty( $user_login ) || in_array( $user_login, $this->fail2wp_reguser_username_ban ) ) {
+ $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) );
+ $have_error = true;
+ } elseif ( $this->fail2wp_reguser_username_length > 1 ) {
+ if ( $this->fail2wp_have_mbstring ) {
+ if ( mb_strlen( $user_login ) < $this->fail2wp_reguser_username_length ) {
+ $have_error = true;
+ }
+ } elseif ( strlen( $user_login ) < $this->fail2wp_reguser_username_length ) {
+ $have_error = true;
+ }
+ if ( $have_error ) {
+ $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid username, please try again.', $this->plugin_name ) );
+ }
+ }
+ if ( ! $have_error ) {
+ $invalid_email = true;
+ if ( ! empty ( $user_email ) ) {
+ $invalid_email = true;
+ if ( $this->fail2wp_have_mbstring ) {
+ // mb_stripos
+ foreach( $this->fail2wp_reguser_useremail_require as $email_required ) {
+ if ( mb_stripos( $user_email, $email_required ) !== false ) {
+ $invalid_email = false;
+ break;
+ }
+ }// foreach
+ } else {
+ // stripos
+ foreach( $this->fail2wp_reguser_useremail_require as $email_required ) {
+ if ( stripos( $user_email, $email_required ) !== false ) {
+ $invalid_email = false;
+ break;
+ }
+ }// foreach
+ }
+ }// !empty email
+ if ( $invalid_email ) {
+ error_log( FAIL2WP_PLUGINNAME_HUMAN . ': ' .
+ '"' . $user_email . '" is not an acceptable e-mail address' );
+ $errors->add( 'fail2wp_username_ban', esc_html__( 'Invalid e-mail address, please try again.', $this->plugin_name ) );
+ }
+ }// e-mail
+
+ return( $errors );
+ }
+
+ /*
+ * Display admin alert about setting default user role.
+ *
+ * This will display an admin alert if we have overridden WordPress'
+ * default new user role.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_admin_alert_new_user_role_forced() {
+ echo '
'.
+ ' ' .
+ esc_html__( 'New user role default has been reset according to your', $this->plugin_name ) .
+ ' ' .
+ FAIL2WP_PLUGINNAME_HUMAN .
+ ' ' .
+ esc_html__( 'settings', $this->plugin_name ) .
+ '!' .
+ '
';
+ echo '
';
+ }
+ // Send the same alert via e-mail
+ public function fail2wp_admin_alert_new_user_role_forced_email() {
+ $admin_email = get_option( 'admin_email', null );
+ if ( empty( $admin_email ) || $admin_email === null ) {
+ error_log( FAIL2WP_PLUGINNAME_HUMAN . ': ' .
+ 'No admin e-mail address from WordPress!' );
+ return;
+ }
+ if ( ! empty($this->fail2wp_site_label ) ) {
+ $from_site = $this->fail2wp_site_label;
+ } else {
+ $from_site = $this->fail2wp_get_site_label( true );
+ }
+ wp_mail( $admin_email,
+ __( 'Notification about new user role from', $this->plugin_name ) . ' ' . FAIL2WP_PLUGINNAME_HUMAN,
+ "\n" .
+ __( 'This is a notification from', $this->plugin_name ) . ' ' . FAIL2WP_PLUGINNAME_HUMAN .
+ "\n\n" .
+ __( 'The role for newly registered users has been reset to your configured setting.', $this->plugin_name ) .
+ "\n\n" .
+ __( 'This notification was sent from the site', $this->plugin_name ) .
+ ' ' . $from_site . "\n\n" .
+ __( 'You may access the admin interface to the site here', $this->plugin_name ) .
+ ': ' . admin_url() .
+ "\n"
+ ,
+ $this->fail2wp_mail_headers );
+ }
+ /**
+ * Display admin alert about default user role.
+ *
+ * This will display an admin alert if the default new user role does not
+ * match that configured in Fail2WP.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_admin_alert_new_users_mismatch() {
+ echo '
'.
+ ' ' .
+ esc_html__( 'User registration is enabled, but the new user role does not match', $this->plugin_name ) .
+ ' ' .
+ FAIL2WP_PLUGINNAME_HUMAN .
+ '!' .
+ '
';
+ if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) {
+ // Include link to General settings, if we're not on that page already
+ if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) {
+ $action = admin_url( 'options-general.php' );
+ echo esc_html__( 'Go to', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'General settings', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'to check your membership and new user settings.', $this->plugin_name ).
+ '
';
+ }
+ }
+ echo '
';
+ }
+ /*
+ * Display admin alert about default user role.
+ *
+ * This will display an admin alert if the default new user role is set to
+ * 'administrator'.
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_admin_alert_new_users_admin() {
+ echo '
'.
+ ' ' .
+ esc_html__( 'User registration is enabled, and new users will be administrators', $this->plugin_name ) .
+ ' ' .
+ FAIL2WP_PLUGINNAME_HUMAN .
+ '!' .
+ '
';
+ if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) {
+ // Include link to General settings, if we're not on that page already
+ if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) {
+ $action = admin_url( 'options-general.php' );
+ echo esc_html__( 'Go to', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'General settings', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'to check your membership and new user settings.', $this->plugin_name ).
+ '
';
+ }
+ }
+ echo '
';
+ }
+ /*
+ * Display admin alert about default user role.
+ *
+ * This will display an admin alert if the default new user role is not set
+ *
+ * @since 1.1.0
+ */
+ public function fail2wp_admin_alert_new_users_null() {
+ echo '
'.
+ ' ' .
+ esc_html__( 'User registration is enabled, but no role has been configured', $this->plugin_name ) .
+ ' ' .
+ FAIL2WP_PLUGINNAME_HUMAN .
+ '!' .
+ '
';
+ if ( is_admin( ) && is_user_logged_in() && current_user_can( 'administrator' ) ) {
+ // Include link to General settings, if we're not on that page already
+ if ( empty( $_SERVER['REQUEST_URI'] ) || basename( $_SERVER['REQUEST_URI'] ) != 'options-general.php' ) {
+ $action = admin_url( 'options-general.php' );
+ echo esc_html__( 'Go to', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'General settings', $this->plugin_name ) .
+ ' ' .
+ esc_html__( 'to check your membership and new user settings.', $this->plugin_name ).
+ '
' . esc_html__( 'Provides authentication related logging and security functions for WordPress, suitable for use with Fail2ban', $this->plugin_name ) . '
';
- $html .= '
';
- $html .= ''; // tab-content
$html .= ''; // wrap
//
- echo $html;
+ echo $tab_header . $html;
}
/**
* Display about/support.
@@ -363,10 +1349,15 @@ public function fail2wp_about_page() {
' WebbPlatsen i Sverige AB '.
esc_html__('in Stockholm, Sweden. We speak Swedish and English', $this->plugin_name ) . ' :-)' .
'
' .
- esc_html__( 'The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i Sverige AB.', $this->plugin_name ) . '
' .
- '
' . esc_html__( 'If you find this plugin useful, the author is happy to receive a donation, good review, or just a kind word.', $this->plugin_name ) . '
' .
- '
' . esc_html__( 'If there is something you feel to be missing from this plugin, or if you have found a problem with the code or a feature, please do not hesitate to reach out to', $this->plugin_name ) .
- ' support@webbplatsen.se' . '
';
+ esc_html__( 'The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i Sverige AB.', $this->plugin_name ) .
+ '' .
+ '
' . esc_html__( 'If you find this plugin useful, the author is happy to receive a donation, good review, or just a kind word.', $this->plugin_name ) . ' ' .
+ esc_html__( 'If there is something you feel to be missing from this plugin, or if you have found a problem with the code or a feature, please do not hesitate to reach out to', $this->plugin_name ) .
+ ' support@webbplatsen.se' . ' '.
+ esc_html__( 'There is more documentation available at', $this->plugin_name ) . ' ' .
+ ''.
+ 'code.webbplatsen.net/documentation/fail2wp/' .
+ '
';
+ }
+ public function fail2wp_settings_reguser_username_length() {
+ echo '';
+ echo '
' . esc_html__( 'Minimum length of usernames, 2-200 characters, 0 ignores the setting', $this->plugin_name ) . '
';
+ }
+ public function fail2wp_settings_reguser_username_ban() {
+ echo '';
+ echo '
' . esc_html__( 'These usernames will be blocked for new user registrations', $this->plugin_name ) . '
';
+ }
+ public function fail2wp_settings_reguser_useremail_require() {
+ echo '';
+ echo '
' . esc_html__( 'E-mail address must match at least one of these for new users', $this->plugin_name ) . '
';
+ }
+ public function fail2wp_setting_remove_generator() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ public function fail2wp_setting_remove_feeds() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ // @since 1.0.0
public function fail2wp_setting_remove() {
- $option_val = $this->fail2wp_get_option( 'fail2wp-settings-remove', false );
echo '
';
echo ' ';
echo '
';
}
public function fail2wp_setting_block_enums() {
- $option_val = $this->fail2wp_get_option( 'fail2wp-block-user-enum', false );
echo '
';
echo ' ';
echo '
';
}
public function fail2wp_setting_block_username_login() {
- $option_val = $this->fail2wp_get_option( 'fail2wp-block-username-login', false );
echo '
';
echo ' ';
echo '
';
}
public function fail2wp_setting_secure_login_messages() {
- $option_val = $this->fail2wp_get_option( 'fail2wp-secure-login-message', false );
echo '
';
echo ' ';
echo '
';
}
+ public function fail2wp_settings_restapi_callback() {
+ if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) {
+ return;
+ }
+ echo '
' .
+ esc_html__( 'Please make sure you understand how these settings can impact the operation of WordPress and other plugins before making changes to them.', $this->plugin_name ) .
+ '
';
+ $rest_url = get_rest_url();
+ echo '
' .
+ esc_html__( 'The REST API URL of this site is', $this->plugin_name ) .
+ ': ' . esc_html( $rest_url ) . '' .
+ '
';
+ // Possibly output notice about blocking settings
+ if ( $this->fail2wp_rest_filter_require_authenticated ) {
+ echo '
' . esc_html__( 'NOTE', $this->plugin_name ) . ': ' .
+ esc_html__( 'If "Require authentication" is enabled, no REST API calls will be blocked for logged in users and/or authenticated requests!', $this->plugin_name ) .
+ '
';
+ }
+ }
public function fail2wp_settings_advanced_callback() {
if ( ! is_admin( ) || ! is_user_logged_in() || ! current_user_can( 'administrator' ) ) {
return;
}
- echo '
'.
- esc_html__( 'Please make sure you understand how these settings can impact the operation of the plugin before making changes to them.', $this->plugin_name ).
+ echo '
' .
+ esc_html__( 'Please make sure you understand how these settings can impact the operation of the plugin before making changes to them.', $this->plugin_name ) .
'
';
}
public function fail2wp_settings_cloudflare_callback() {
@@ -595,46 +1774,100 @@ public function fail2wp_settings_cloudflare_callback() {
''.
'';
}
+ // @since 1.1.0
+ public function fail2wp_setting_rest_filter_require_authenticated() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ public function fail2wp_setting_rest_filter_block_index() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ public function fail2wp_setting_rest_filter_log_blocked() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ public function fail2wp_setting_rest_filter_block_all() {
+ echo '
';
+ echo ' ';
+ echo '
';
+ }
+ public function fail2wp_setting_rest_filter_block_ns() {
+ $rest_ns = $this->fail2wp_get_rest_ns();
+ if ( ! is_array( $rest_ns ) || empty( $rest_ns ) ) {
+ echo '
';
+ echo esc_html__( 'WordPress did not return any available namespaces', $this->plugin_name );
+ echo '
' . esc_html__( 'IPs matching these addresses will be considerd to be coming from Cloudflare', $this->plugin_name ) . '
';
}
@@ -647,11 +1880,14 @@ public function fail2wp_settings_cloudflare_ipv6() {
*/
protected function fail2wp_alert_send( string $alert_message ) {
// Logging prefix (i.e. "this is us")
- $prefix = $this->fail2wp_get_option( 'fail2wp-prefix', false );
+ $prefix = $this->fail2wp_prefix;
if ( empty( $prefix ) ) {
$prefix = FAIL2WP_DEFAULT_PREFIX;
}
// Site label
+ if ( empty( $this->fail2wp_site_label ) ) {
+ $this->fail2wp_site_label = $this->fail2wp_get_site_label( true );
+ }
if ( empty( $this->fail2wp_site_label ) ) {
$prefix .= '(unknown.site)';
} else {
@@ -682,7 +1918,7 @@ protected function fail2wp_alert_send( string $alert_message ) {
$syslog_ok = true;
} else {
$syslog_ok = false;
- error_log( 'Unable to initialize syslog interface' );
+ error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Unable to initialize syslog interface' );
}
// Possibly log to PHP log as well
if ( $this->fail2wp_also_log_php ) {
@@ -712,21 +1948,20 @@ protected function fail2wp_get_message_user_display( \WP_User $user ) : string {
$name = '`' . $name . '`';
}
return( $name );
- }
+ }
/**
- * Build context based alert message.
+ * Possibly process Cloudflare address.
+ *
+ * If the passed IP address matches one of the configured Cloudflare addresses,
+ * the function attempts to fetch the actual IP address from Cloudflare
+ * headers.
*
* @since 1.0.0
- * @param string $username Username as entered when logging in.
- * @param mixed $context Either WP_User or WP_Error.
- * @param int $alert_type Type of notification.
- * @return mixed String with alert message or false on error.
+ * @param string $remote_ip Remote IP address
+ * @return string The actual IP address
*/
- protected function fail2wp_make_alert_message( string $username, $context, int $alert_type ) {
- $alert_message = '';
- // Fetch remote IP if set
- $remote_ip = $_SERVER['REMOTE_ADDR'];
+ protected function fail2wp_do_cloudflare_lookup( string $remote_ip ) {
if ( $this->fail2wp_cloudflare_check ) {
// Setup CIDRmatch
$cidrm = new \CIDRmatch\CIDRmatch();
@@ -749,10 +1984,30 @@ protected function fail2wp_make_alert_message( string $username, $context, int $
}
}
if ( $is_cloudflare && ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
- // error_log( basename(__FILE__) . ' (' . __FUNCTION__ . '): Cloudflare IP=' . $remote_ip . ', actual IP=' . $_SERVER['HTTP_CF_CONNECTING_IP'] );
$remote_ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
}
+ return( $remote_ip );
+ }
+ /**
+ * Build context based alert message.
+ *
+ * @since 1.0.0
+ * @param string $username Username as entered when logging in.
+ * @param mixed $context Either WP_User or WP_Error.
+ * @param int $alert_type Type of notification.
+ * @param bool $username_is_ip_address If $username contains already handled IP address
+ * @return mixed String with alert message or false on error.
+ */
+ protected function fail2wp_make_alert_message( string $username, $context, int $alert_type, bool $username_is_ip_address = false ) {
+ $alert_message = '';
+ if ( $username_is_ip_address ) {
+ // Username string contains IP address (some REST API requests)
+ $remote_ip = $username;
+ } else {
+ // Fetch remote IP if set
+ $remote_ip = $this->fail2wp_do_cloudflare_lookup( $_SERVER['REMOTE_ADDR'] );
+ }
if ( ! empty( $remote_ip ) ) {
$remote_ip = ' from' . ' ' . $remote_ip;
} else {
@@ -817,6 +2072,12 @@ protected function fail2wp_make_alert_message( string $username, $context, int $
case FAIL2WP_ALERT_USER_ENUM:
$alert_message = 'User enumeration request' . $remote_ip . $our_port;
break;
+ case FAIL2WP_ALERT_REST_NOTAUTH:
+ $alert_message = 'Unauthenticated REST API request' . $remote_ip . $our_port;
+ break;
+ case FAIL2WP_ALERT_REST_BLOCKED:
+ $alert_message = 'Blocked REST API request' . $remote_ip . $our_port;
+ break;
} // switch
return( $alert_message );
}
@@ -832,15 +2093,11 @@ protected function fail2wp_make_alert_message( string $username, $context, int $
* @param string $notify_rules JSON string with configured roles.
* @return boolean true=Notify, false=Don't notify
*/
- protected function fail2wp_role_is_active( array $roles, string $notify_roles ) : bool {
- $notify_array = @ json_decode( $notify_roles, true, 2 );
- if ( ! is_array( $notify_array ) || empty( $notify_array ) ) {
- return( false );
- }
+ protected function fail2wp_role_is_active( array $roles, array $notify_roles ) : bool {
// Lookup our selected notification roles. We could walk the other way
// too, but we're likely to have less configured roles/caps than what
// is available. So maybe this will save an iteration or two :-)
- foreach( $notify_array as $role ) {
+ foreach( $notify_roles as $role ) {
if ( in_array( $role, $roles ) && $roles[$role] ) {
return( true );
}
@@ -883,6 +2140,26 @@ protected function fail2wp_roles_merge( array $roles, string $notify_roles ) : s
return( ' [' . implode( ',', $new_roles ) . ']' );
}
+ /**
+ * Get REST API namespaces from WordPress.
+ *
+ * @since 1.1.0
+ * @param object $rest_server WP_REST_Server object to use or null
+ * @return array List of known namespaces
+ */
+ protected function fail2wp_get_rest_ns( $rest_server = null ) {
+ if ( $rest_server === null ) {
+ if ( $this->fail2wp_rest === null ) {
+ if ( defined( 'FAIL2WP_REST_DEBUG' ) ) {
+ error_log( basename( __FILE__ ) . ' (' . __FUNCTION__ . '): Getting WP_REST_Server instance' );
+ }
+ $this->fail2wp_rest = rest_get_server();
+ }
+ return( $this->fail2wp_rest->get_namespaces() );
+ }
+ return( $rest_server->get_namespaces() );
+ }
+
/**
* Send alert when user with configured role(s) login.
*
@@ -962,7 +2239,7 @@ public function fail2wp_auth_check( $user, string $username, string $password )
return( $user );
}
$wp_error = $user;
- if ( function_exists( 'mb_substr_count' ) && function_exists( 'mb_strpos' ) ) {
+ if ( $this->fail2wp_have_mbstring ) {
if ( mb_substr_count( $username, '@' ) !== 1 || mb_strpos( $username, '@' ) === 0) {
$wp_error = new \WP_Error( 'invalid_username', __('Please specify your e-mail address to login', $this->plugin_name) );
}
@@ -1098,7 +2375,7 @@ public function fail2wp_login_errors( $error ) {
// We're configured to notify about unknown users
if ( ! empty( $_REQUEST['user_login'] ) ) {
$username = sanitize_user( $_REQUEST['user_login'], false );
- if ( function_exists( 'mb_ereg_replace' ) ) {
+ if ( $this->fail2wp_have_mbstring ) {
$username = mb_ereg_replace( ' ', '', $username );
} else {
$username = str_replace( ' ', '', $username );
@@ -1139,7 +2416,6 @@ public function fail2wp_login_errors( $error ) {
/**
* Lost password errors
- *
* NOT HOOKED ATM
*/
/*
@@ -1181,10 +2457,12 @@ public function setup_locale() {
if ( ! load_plugin_textdomain( $this->plugin_name,
false,
dirname( plugin_basename( __FILE__ ) ) . '/languages' ) ) {
- error_log( 'Unable to load language file (' . dirname( plugin_basename( __FILE__ ) ) . '/languages' . ')' );
+ /**
+ * We don't consider this to be a "real" error since 1.1.0
+ */
+ // error_log( 'Unable to load language file (' . dirname( plugin_basename( __FILE__ ) ) . '/languages' . ')' );
}
}
-
/**
* Setup CSS (admin)
*
@@ -1207,20 +2485,21 @@ public function run() {
// Setup i18n. We use the 'init' action rather than 'plugins_loaded' as per
// https://developer.wordpress.org/reference/functions/load_plugin_textdomain/#user-contributed-notes
- add_action( 'init', [$this, 'setup_locale'] );
- // Setup CSS
+ add_action( 'init', [$this, 'setup_locale'] );
+
+ // Admin setup
if ( is_admin() ) {
- add_action( 'admin_enqueue_scripts', [$this, 'fail2wp_setup_css'] );
+ add_action( 'admin_enqueue_scripts', [$this, 'fail2wp_setup_css'] );
+ add_action( 'admin_menu', [$this, 'fail2wp_menu'] );
+ add_action( 'admin_init', [$this, 'fail2wp_settings'] );
}
- // Setup
- add_action( 'admin_menu', [$this, 'fail2wp_menu'] );
- add_action( 'admin_init', [$this, 'fail2wp_settings'] );
- add_action( 'wp_loaded', [$this, 'fail2wp_wp_loaded'] );
- add_action( 'parse_request', [$this, 'fail2wp_parse_request'] );
+ // Other setup
+ add_action( 'wp_loaded', [$this, 'fail2wp_wp_loaded'] );
+ add_action( 'parse_request', [$this, 'fail2wp_parse_request'] );
+
// add_action( 'wp', [$this, 'fail2wp_wp_main'] );
// add_action( 'pre_get_posts', [$this, 'fail2wp_pgp'] );
-
- // Plugin deactivation
+ // Plugin deactivation, not needed atm :-)
// register_deactivation_hook( __FILE__, [$this, 'fail2wp_deactivate_plugin'] );
}
diff --git a/fail2wp/includes/fail2wp_misc.inc.php b/fail2wp/includes/fail2wp_misc.inc.php
new file mode 100644
index 0000000..ee38eb1
--- /dev/null
+++ b/fail2wp/includes/fail2wp_misc.inc.php
@@ -0,0 +1,105 @@
+
+ *
+ * fail2wp_misc.inc.php
+ * Copyright (C) 2021 Joaquim Homrighausen; all rights reserved.
+ * Development sponsored by WebbPlatsen i Sverige AB, www.webbplatsen.se
+ *
+ * This file is part of Fail2WP. Fail2WP is free software.
+ *
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License version 2, as published by the Free Software
+ * Foundation.
+ *
+ * Fail2WP is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Fail2WP package. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+// If this file is called directly, abort.
+if ( ! defined( 'WPINC' ) ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: (misc) WPINC not defined ' );
+ }
+ die;
+}
+if ( ! defined( 'ABSPATH' ) ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: (misc) ABSPATH not defined ' );
+ }
+ die( '-1' );
+}
+if ( ! defined( 'FAIL2WP_WORDPRESS_PLUGIN' ) ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: (misc) FAIL2WP_WORDPRESS_PLUGIN not defined ' );
+ }
+ die( '-1' );
+}
+
+
+/**
+ * Remove all settings from WordPress database.
+ *
+ * This can, possibly, be used by more than one module.
+ *
+ * @since 1.1.0
+ */
+function fail2wp_misc_delete_all_settings() {
+
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: ' . __FUNCTION__ . ' start' );
+ }
+ delete_option( 'fail2wp-site-label' );
+ delete_option( 'fail2wp-prefix' );
+ delete_option( 'fail2wp-roles-notify' );
+ delete_option( 'fail2wp-roles-warn' );
+ delete_option( 'fail2wp-unknown-warn' );
+ delete_option( 'fail2wp-reguser-warn' );
+ delete_option( 'fail2wp-reguser-warn-role' );
+ delete_option( 'fail2wp-reguser-force' );
+ delete_option( 'fail2wp-reguser-force-role' );
+ delete_option( 'fail2wp-reguser-username-length' );
+ delete_option( 'fail2wp-reguser-username-ban' );
+ delete_option( 'fail2wp-reguser-useremail-require' );
+
+ delete_option( 'fail2wp-rest-filter-log-blocked' );
+ delete_option( 'fail2wp-rest-filter-block-all' );
+ delete_option( 'fail2wp-rest-filter-block-index' );
+ delete_option( 'fail2wp-rest-filter-block-ns' );
+ delete_option( 'fail2wp-rest-filter-block-routes' );
+ delete_option( 'fail2wp-rest-filter-require-authenticated' );
+ delete_option( 'fail2wp-rest-filter-ipv4-bypass' );
+ delete_option( 'fail2wp-rest-filter-ipv6-bypass' );
+
+ delete_option( 'fail2wp-settings-dbversion' );
+ delete_option( 'fail2wp-settings-remove' );
+ delete_option( 'fail2wp-settings-remove-generator' );
+ delete_option( 'fail2wp-settings-remove-feeds' );
+
+ delete_option( 'fail2wp-also-log-php' );
+ delete_option( 'fail2wp-block-user-enum' );
+ delete_option( 'fail2wp-log-user-enum' );
+ delete_option( 'fail2wp-block-username-login' );
+ delete_option( 'fail2wp-secure-login-message' );
+
+ delete_option( 'fail2wp-cloudflare-check' );
+ delete_option( 'fail2wp-cloudflare-ipv4' );
+ delete_option( 'fail2wp-cloudflare-ipv6' );
+
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: ' . __FUNCTION__ . ' end' );
+ }
+}
diff --git a/fail2wp/languages/fail2wp-sv_SE.mo b/fail2wp/languages/fail2wp-sv_SE.mo
index 4e040f0..7bd2925 100644
Binary files a/fail2wp/languages/fail2wp-sv_SE.mo and b/fail2wp/languages/fail2wp-sv_SE.mo differ
diff --git a/fail2wp/languages/fail2wp-sv_SE.po b/fail2wp/languages/fail2wp-sv_SE.po
index 67c5d1c..8b2ffa3 100644
--- a/fail2wp/languages/fail2wp-sv_SE.po
+++ b/fail2wp/languages/fail2wp-sv_SE.po
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: fail2wp 1.0.0\n"
+"Project-Id-Version: fail2wp 1.1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-02-03 10:24+0100\n"
+"POT-Creation-Date: 2021-03-08 15:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Joaquim Homrighausen \n"
"Language-Team: SWEDISH \n"
@@ -21,7 +21,7 @@ msgstr ""
msgid "Unknown"
msgstr "Okänd"
-#: fail2wp.php:240 fail2wp.php:245
+#: fail2wp.php:240 fail2wp.php:245 fail2wp.php:1011 fail2wp.php:1016
msgid "Unknown role"
msgstr "Okänd roll"
@@ -33,7 +33,7 @@ msgstr ""
"Tillhandahåller autentiseringsloggning och säkerhetsfunktioner för "
"WordPress, användbar tillsammans med Fail2ban"
-#: fail2wp.php:289
+#: fail2wp.php:289 fail2wp.php:1060
msgid "Basic configuration"
msgstr "Baskonfiguration"
@@ -41,47 +41,47 @@ msgstr "Baskonfiguration"
msgid "Logging"
msgstr "Loggning"
-#: fail2wp.php:295
+#: fail2wp.php:295 fail2wp.php:1072
msgid "Advanced"
msgstr "Avancerat"
-#: fail2wp.php:298
+#: fail2wp.php:298 fail2wp.php:1075
msgid "Cloudflare"
msgstr "Cloudflare"
-#: fail2wp.php:301
+#: fail2wp.php:301 fail2wp.php:1081
msgid "About"
msgstr "Om"
-#: fail2wp.php:307
+#: fail2wp.php:307 fail2wp.php:1086
msgid ""
"One or more of openlog(), closelog(), and/or syslog() seem to be missing on "
"this system"
msgstr ""
-"En eller flera av openlog(), closelog(), och/eller syslog() verkar saknas "
-"på detta system"
+"En eller flera av openlog(), closelog(), och/eller syslog() verkar saknas på "
+"detta system"
-#: fail2wp.php:355
+#: fail2wp.php:355 fail2wp.php:1343
msgid "Thank you for installing"
msgstr "Tack för att du installerat"
-#: fail2wp.php:356
+#: fail2wp.php:356 fail2wp.php:1344
msgid ""
-"This plugin provides security functions and integration between "
-"WordPress and"
+"This plugin provides security functions and integration between WordPress and"
msgstr ""
-"Detta plugin tillhandahåller säkerhetsfunktioner samt integration "
-"mellan WordPress och"
+"Detta plugin tillhandahåller säkerhetsfunktioner samt integration mellan "
+"WordPress och"
-#: fail2wp.php:360
+#: fail2wp.php:360 fail2wp.php:1348
msgid "Commercial support and customizations for this plugin is available from"
-msgstr "Kommersiell support och anpassningar av detta tillägg tillhandahålls av"
+msgstr ""
+"Kommersiell support och anpassningar av detta tillägg tillhandahålls av"
-#: fail2wp.php:362
+#: fail2wp.php:362 fail2wp.php:1350
msgid "in Stockholm, Sweden. We speak Swedish and English"
msgstr "i Stockholm. Vi pratar svenska och engelska"
-#: fail2wp.php:364
+#: fail2wp.php:364 fail2wp.php:1352
msgid ""
"The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i "
"Sverige AB."
@@ -89,7 +89,7 @@ msgstr ""
"Detta tillägg är utvecklat av Joaquim Homrighausen och sponsrat av "
"WebbPlatsen i Sverige AB."
-#: fail2wp.php:365
+#: fail2wp.php:365 fail2wp.php:1353
msgid ""
"If you find this plugin useful, the author is happy to receive a donation, "
"good review, or just a kind word."
@@ -97,7 +97,7 @@ msgstr ""
"Om du tycker detta tillägg är användbart så uppskattar utvecklaren en "
"donation, bra omdöme och/eller positiv kommentar."
-#: fail2wp.php:366
+#: fail2wp.php:366 fail2wp.php:1354
msgid ""
"If there is something you feel to be missing from this plugin, or if you "
"have found a problem with the code or a feature, please do not hesitate to "
@@ -106,90 +106,90 @@ msgstr ""
"Om du anser att något saknas i detta tillägg eller om du har hittat ett fel "
"i koden eller en funktion, tveka inte att höra av dig till"
-#: fail2wp.php:381
+#: fail2wp.php:381 fail2wp.php:1369
msgid "Site label"
msgstr "Webbplatsnamn"
-#: fail2wp.php:382
+#: fail2wp.php:382 fail2wp.php:1370
msgid "Block user enum"
msgstr "Blockera 'user enum'"
-#: fail2wp.php:383
+#: fail2wp.php:383 fail2wp.php:1371
msgid "Block username login"
msgstr "Blockera användarnamn"
-#: fail2wp.php:384
+#: fail2wp.php:384 fail2wp.php:1372
msgid "Secure login messages"
msgstr "Säkra felmeddelanden"
-#: fail2wp.php:385
+#: fail2wp.php:385 fail2wp.php:1374
msgid "Other settings"
msgstr "Andra inställningar"
-#: fail2wp.php:386
+#: fail2wp.php:386 fail2wp.php:1377
msgid "Remove settings"
msgstr "Radera inställningar"
-#: fail2wp.php:389
+#: fail2wp.php:389 fail2wp.php:1389
msgid "Successful login"
msgstr "Lyckad inloggning"
-#: fail2wp.php:390
+#: fail2wp.php:390 fail2wp.php:1390
msgid "Unsuccessful login"
msgstr "Misslyckad inloggning"
-#: fail2wp.php:395
+#: fail2wp.php:395 fail2wp.php:1405
msgid "Logging prefix"
msgstr "Loggningsprefix"
-#: fail2wp.php:396
+#: fail2wp.php:396 fail2wp.php:1406
msgid "Also log to PHP log"
msgstr "Logga även till PHP-logg"
-#: fail2wp.php:399
+#: fail2wp.php:399 fail2wp.php:1409
msgid "Check for Cloudflare IP"
msgstr "Hantera Cloudflare IP"
-#: fail2wp.php:400
+#: fail2wp.php:400 fail2wp.php:1410
msgid "Cloudflare IPv4"
msgstr "Cloudflare IPv4"
-#: fail2wp.php:401
+#: fail2wp.php:401 fail2wp.php:1411
msgid "Cloudflare IPv6"
msgstr "Cloudflare IPv6"
-#: fail2wp.php:484
+#: fail2wp.php:484 fail2wp.php:1574
msgid ""
"The site name to use for logging, defaults to your site name if left empty"
msgstr ""
"Namn för webbplatsen att använda för loggning, hämtas från WordPress om "
"fältet är tomt"
-#: fail2wp.php:529
+#: fail2wp.php:529 fail2wp.php:1600
msgid "Unknown users"
msgstr "Okända användare"
-#: fail2wp.php:537
+#: fail2wp.php:537 fail2wp.php:1607
msgid "User enumeration attempts (i.e. your.site/...?author=nnn)"
msgstr "Försök att hämta användarnamn (t ex din.sajt.../?author=nnn)"
-#: fail2wp.php:545
+#: fail2wp.php:545 fail2wp.php:1705
msgid "Remove all plugin settings and data when plugin is uninstalled"
msgstr "Ta bort alla inställningar när tillägget avinstalleras"
-#: fail2wp.php:553
+#: fail2wp.php:553 fail2wp.php:1712
msgid "Block user enumeration attempts (i.e. your.site/...?author=nnn)"
msgstr "Blockera försök att hämta användarnamn (t ex din.sajt.../?author=nnn)"
-#: fail2wp.php:561
+#: fail2wp.php:561 fail2wp.php:1719
msgid "Require users to login with their e-mail address"
msgstr "Kräv inloggning med e-postadress"
-#: fail2wp.php:569
+#: fail2wp.php:569 fail2wp.php:1726
msgid "Change login failure messages to contain less detail"
msgstr "Ändra felmeddelanden till färre detaljer vi misslyckad inloggning"
-#: fail2wp.php:577
+#: fail2wp.php:577 fail2wp.php:1753
msgid ""
"Please make sure you understand how these settings can impact the operation "
"of the plugin before making changes to them."
@@ -197,11 +197,11 @@ msgstr ""
"Var säker på att du förstår hur dessa inställningar kan förändra hur "
"tillägget fungerar innan du ändrar dem."
-#: fail2wp.php:585
+#: fail2wp.php:585 fail2wp.php:1761
msgid "These settings allows the plugin to better interact with Cloudflare."
msgstr "Dessa inställning förbättrar hur tillägget fungerar med Cloudflare."
-#: fail2wp.php:587
+#: fail2wp.php:587 fail2wp.php:1763
msgid ""
"If your site is not published via Cloudflare, you can safely ignore these "
"settings."
@@ -209,41 +209,391 @@ msgstr ""
"Om du inte använder Cloudflare för din sajt så behöver du inte ändra något "
"här."
-#: fail2wp.php:589
+#: fail2wp.php:589 fail2wp.php:1765
msgid "For an updated list of Cloudflare IPs, please use this link"
-msgstr "För en uppdaterad lista med Cloudflares IP-adresser, vänligen använd "
-"denna länk"
+msgstr ""
+"För en uppdaterad lista med Cloudflares IP-adresser, vänligen använd denna "
+"länk"
-#: fail2wp.php:599
+#: fail2wp.php:599 fail2wp.php:1840
msgid "The logging prefix, this should normally be left empty"
msgstr "Loggningsprefix, detta skall i normala fall vara tomt"
-#: fail2wp.php:606
+#: fail2wp.php:606 fail2wp.php:1846
msgid "Log the same information to PHP log using error_log()"
msgstr "Logga samma information till PHP:s logg via error_log()"
-#: fail2wp.php:614
+#: fail2wp.php:614 fail2wp.php:1854
msgid "Attempt to unmask real IP when Cloudflare IP is detected"
msgstr "Försök att avkoda den verkliga IP-adressen när Cloudflare upptäcks"
-#: fail2wp.php:626 fail2wp.php:637
+#: fail2wp.php:626 fail2wp.php:637 fail2wp.php:1861 fail2wp.php:1867
msgid ""
"IPs matching these addresses will be considerd to be coming from Cloudflare"
msgstr ""
"IP-adresser som matchar dessa kommer att hanteras som Cloudflare-adresser"
-#: fail2wp.php:962 fail2wp.php:966
+#: fail2wp.php:962 fail2wp.php:966 fail2wp.php:2239 fail2wp.php:2243
msgid "Please specify your e-mail address to login"
msgstr "Vänligen ange din e-postadress för att logga in"
-#: fail2wp.php:989
+#: fail2wp.php:989 fail2wp.php:2261
msgid "E-mail address"
msgstr "E-postadress"
-#: fail2wp.php:1094 fail2wp.php:1122
+#: fail2wp.php:1094 fail2wp.php:1122 fail2wp.php:2366 fail2wp.php:2399
msgid "Invalid login credentials, please try again."
msgstr "Ogiltig inloggningsinformation, försök igen."
-#: fail2wp.php:1126
+#: fail2wp.php:1126 fail2wp.php:2403
msgid "Lost password"
msgstr "Återställ lösenord"
+
+#: fail2wp.php:450
+msgid "Settings "
+msgstr "Inställningar "
+
+#: fail2wp.php:548
+msgid "You are not currently logged in."
+msgstr "Du är inte inloggad."
+
+#: fail2wp.php:629 fail2wp.php:667 fail2wp.php:686 fail2wp.php:706
+msgid "No route was found matching the URL and request method."
+msgstr "Ingen route hittades som motsvarade webbadressen och metod för begäran."
+
+#: fail2wp.php:743 fail2wp.php:754 fail2wp.php:765
+msgid "Invalid username, please try again."
+msgstr "Ogiltigt användarnamn, försök igen."
+
+#: fail2wp.php:747 fail2wp.php:793
+msgid "Invalid e-mail address, please try again."
+msgstr "Ogiltig e-postadress, försök igen."
+
+#: fail2wp.php:811
+msgid "New user role default has been reset according to your"
+msgstr "Förvald roll för nya användare har återställts enligt dina"
+
+#: fail2wp.php:815
+msgid "settings"
+msgstr "inställningar"
+
+#: fail2wp.php:834
+msgid "Notification about new user role from"
+msgstr "Avisering om ändring av förvald roll"
+
+#: fail2wp.php:836
+msgid "This is a notification from"
+msgstr "Detta är en avisering från"
+
+#: fail2wp.php:838
+msgid ""
+"The role for newly registered users has been reset to your configured "
+"setting."
+msgstr ""
+"Den förvalda rollen för nya användare har återställts till den som du "
+"konfigurerat."
+
+#: fail2wp.php:840
+msgid "This notification was sent from the site"
+msgstr "Denna avisering skickades från sajten"
+
+#: fail2wp.php:842
+msgid "You may access the admin interface to the site here"
+msgstr "Du når administrationsgränssnittet här"
+
+#: fail2wp.php:859
+msgid "User registration is enabled, but the new user role does not match"
+msgstr "Användarregistrering är aktiv, men den förvalda rollen matchar inte"
+
+#: fail2wp.php:868 fail2wp.php:898 fail2wp.php:927
+msgid "Go to"
+msgstr "Gå till"
+
+#: fail2wp.php:870 fail2wp.php:900 fail2wp.php:929
+msgid "General settings"
+msgstr "Allmänna inställningar"
+
+#: fail2wp.php:872 fail2wp.php:902 fail2wp.php:931
+msgid "to check your membership and new user settings."
+msgstr "för att kontrollera inställningarna för medlemsskap och nya användare."
+
+#: fail2wp.php:889
+msgid "User registration is enabled, and new users will be administrators"
+msgstr "Användarregistrering är aktiv och nya användare blir administratörer"
+
+#: fail2wp.php:918
+msgid "User registration is enabled, but no role has been configured"
+msgstr "Användarregistrering är aktiv, men ingen förvald roll har konfigurerats"
+
+#: fail2wp.php:1057
+msgid ""
+"Provides authentication security functions for WordPress, plays nicely with "
+"Fail2ban and Cloudflare"
+msgstr ""
+"Tillhandahåller autentiseringsloggning och säkerhetsfunktioner för "
+"WordPress, fungerar även bra med Fail2ban och Cloudflare"
+
+#: fail2wp.php:1063
+msgid "New users"
+msgstr "Nya användare"
+
+#: fail2wp.php:1066
+msgid "User logging"
+msgstr "Loggning"
+
+#: fail2wp.php:1069
+msgid "REST API"
+msgstr "REST API"
+
+#: fail2wp.php:1078
+msgid "Import/Export"
+msgstr "Import/Export"
+
+#: fail2wp.php:1108
+msgid ""
+"Some data was filtered, please make sure you paste the content exactly as "
+"copied"
+msgstr ""
+"Data har filtrerats, kontrollera att du klistrar in innehållet exakt som "
+"det kopierades"
+
+#: fail2wp.php:1113 fail2wp.php:1121
+msgid "Please make sure you paste the content exactly as copied"
+msgstr "Kontrollera att du klistrar in innehållet exakt som det kopierades"
+
+#: fail2wp.php:1126
+msgid ""
+"Plugin version mismatch. You can only import exported data from the same "
+"version as the one currently installed"
+msgstr ""
+"Versionerna matchar inte varandra. Du kan bara importera exporterat data "
+"från samma version som den som är installerad"
+
+#: fail2wp.php:1190
+msgid "The following settings were ignored during the import"
+msgstr "Följande inställningar ignorerades vid importen"
+
+#: fail2wp.php:1197
+msgid "Settings were successfully imported"
+msgstr "Inställningarna har importerats"
+
+#: fail2wp.php:1201
+msgid "The setting \"Site label\" may need manual correction"
+msgstr "Inställningen \"Webbplatsnamn\" kan behöva korrigeras"
+
+#: fail2wp.php:1208
+msgid "This tab can be used to import and export settings for the plugin."
+msgstr "Denna flik kan användas för att importera och exportera inställningar."
+
+#: fail2wp.php:1212 fail2wp.php:1221
+msgid "Import settings"
+msgstr "Importera inställningar"
+
+#: fail2wp.php:1217
+msgid "Paste settings in this field to import them"
+msgstr "Klistra in inställningarna i detta fält för att importera dem"
+
+#: fail2wp.php:1226
+msgid "Export settings"
+msgstr "Exportera inställningar"
+
+#: fail2wp.php:1272
+msgid "Unable to create data for export"
+msgstr "Kunde inte skapa data för export"
+
+#: fail2wp.php:1280
+msgid "Copy the text in this field to export your settings to another site"
+msgstr "Kopiera texten i detta fält för att exportera inställningar till en annan sajt"
+
+#: fail2wp.php:1304
+msgid ""
+"This is logged to the system's authentication log, which allows Fail2ban to "
+"dynamically block offending IP addresses."
+msgstr ""
+"Detta loggas till systemetes autentiseringslogg, som gör det möjligt för "
+"Fail2ban att på dynamisk väg blockera IP-adresser."
+
+#: fail2wp.php:1306
+msgid ""
+"Configuration of the Fail2ban system daemon, or similar, must be done "
+"outside of WordPress for this to have any effect."
+msgstr ""
+"Konfiguration av Fail2ban, eller liknande, måste göras utanför WordPress för "
+"att dessa inställningar skall ha någon effekt."
+
+#; fail2wp.php:1357
+msgid "There is more documentation available at"
+msgstr "Det finns mer dokumentation på"
+
+#: fail2wp.php:1375
+msgid "Remove generator info"
+msgstr "Inaktivera generatorinfo"
+
+#: fail2wp.php:1376
+msgid "Remove feeds"
+msgstr "Inaktivera flöden"
+
+#: fail2wp.php:1380
+msgid "Membership warnings"
+msgstr "Medlemsskapsvarningar"
+
+#: fail2wp.php:1381
+msgid "Check for role"
+msgstr "Jämför med roll"
+
+#: fail2wp.php:1382
+msgid "Force role"
+msgstr "Tvinga roll"
+
+#: fail2wp.php:1383
+msgid "Role to force"
+msgstr "Tvingad roll"
+
+#: fail2wp.php:1384
+msgid "Minimum username length"
+msgstr "Minsta antal tecken för användarnamn"
+
+#: fail2wp.php:1385
+msgid "Banned usernames"
+msgstr "Spärrade användarnamn"
+
+#: fail2wp.php:1386
+msgid "E-mail must match"
+msgstr "E-post måste matcha"
+
+#: fail2wp.php:1392
+msgid "Log user enum"
+msgstr "Logga 'user enum'"
+
+#: fail2wp.php:1395
+msgid "Require authentication"
+msgstr "Kräv autentisering"
+
+#: fail2wp.php:1396
+msgid "Log blocked requests"
+msgstr "Logga blockerade anrop"
+
+#: fail2wp.php:1397
+msgid "Block index requests"
+msgstr "Blockera index-anrop"
+
+#: fail2wp.php:1398
+msgid "Block all requests"
+msgstr "Blockera alla anrop"
+
+#: fail2wp.php:1399
+msgid "Block specific namespaces"
+msgstr "Blockera specifika namespaces"
+
+#: fail2wp.php:1400
+msgid "Block specific routes"
+msgstr "Blockera specifika sökvägar"
+
+#: fail2wp.php:1401
+msgid "Bypass blocks for IPv4"
+msgstr "Tillåt från IPv4"
+
+#: fail2wp.php:1402
+msgid "Bypass blocks for IPv6"
+msgstr "Tillåt från IPv6"
+
+#: fail2wp.php:1615
+msgid "Warn about odd membership/registration settings"
+msgstr "Varna för udda medlemsskap-/registreringsinställningar"
+
+#: fail2wp.php:1633
+msgid "Check WordPress setting against the value configured here"
+msgstr "Jämför WordPress inställning med det som konfigurerats här"
+
+#: fail2wp.php:1637 fail2wp.php:1666
+msgid "No available roles (?)"
+msgstr "Inga tillgängliga roller (?)"
+
+#: fail2wp.php:1645
+msgid "Force new user registration settings to a role"
+msgstr "Tvinga inställningen för nya användare till en roll"
+
+#: fail2wp.php:1663
+msgid "Force WordPress setting to the value configured here"
+msgstr "Tvinga WordPress-inställning till värdet som konfigurerats här"
+
+#: fail2wp.php:1672
+msgid "Minimum length of usernames, 2-200 characters, 0 ignores the setting"
+msgstr "Minsta antal tecken för användarnamn, 2-200 tecken, 0 avaktiverar kontrollen"
+
+#: fail2wp.php:1678
+msgid "These usernames will be blocked for new user registrations"
+msgstr "Dessa användarnamn blockeras vid registrering av nya användare"
+
+#: fail2wp.php:1684
+msgid "E-mail address must match at least one of these for new users"
+msgstr "E-postadressen för nya användare måste matcha en av dessa"
+
+#: fail2wp.php:1690
+msgid "Remove \"Generator\" output in HTML, RSS, etc."
+msgstr "Ta bort \"Generator\"-infor från HTML, RSS, etc."
+
+#: fail2wp.php:1697
+msgid "Remove RSS and Atom feeds"
+msgstr "Inaktiverar RSS- och Atom-flöden"
+
+#: fail2wp.php:1734
+msgid ""
+"Please make sure you understand how these settings can impact the operation "
+"of WordPress and other plugins before making changes to them."
+msgstr ""
+"Var säker på att du förstår hur dessa inställningar kan förändra hur "
+"WordPress och andra tillägg fungerar innan du ändrar dem."
+
+#: fail2wp.php:1738
+msgid "The REST API URL of this site is"
+msgstr "Webbplatsens REST API URL är"
+
+#: fail2wp.php:1743
+msgid "NOTE"
+msgstr "OBS!"
+
+#: fail2wp.php:1744
+msgid ""
+"If \"Require authentication\" is enabled, no REST API calls will be blocked "
+"for logged in users and/or authenticated requests!"
+msgstr ""
+"Om \"Kräv autentisering\" är aktiverat så blockeras inga REST API-anrop för "
+"inloggade och/eller autentiserade användare!"
+
+#: fail2wp.php:1777
+msgid ""
+"Require that users be logged in and/or that all REST API calls are "
+"authenticated, this is typically safe to do."
+msgstr ""
+"Kräv att användare är inloggad och/eller att alla REST API-anrop är "
+"autentiserade. Detta är säkert att göra i normala fall."
+
+#: fail2wp.php:1784
+msgid ""
+"Blocks all REST API index calls made to this site, this is typically safe to "
+"do."
+msgstr ""
+"Blockera alla REST API-anrop till index, detta är säkert att göra i "
+"normala fall."
+
+#: fail2wp.php:1791
+msgid "Log all blocked REST API calls for Fail2ban processing."
+msgstr "Logga alla blockerade REST API-anrop för behandling av Fail2ban."
+
+#: fail2wp.php:1798
+msgid ""
+"Blocks all REST API calls made to this site, this is typically not safe to "
+"do."
+msgstr ""
+"Blockera alla REST API-anrop till denna webbplats. Detta är för det mesta "
+"inte rekommenderat."
+
+#: fail2wp.php:1805
+msgid "WordPress did not return any available namespaces"
+msgstr "WordPress returnerade inga tillgängliga namespaces"
+
+#: fail2wp.php:1830 fail2wp.php:1836
+msgid "IPs matching these addresses will be allowed to make any REST API call"
+msgstr ""
+"IP-adresser som matchar dessa får göra alla REST API-anrop"
diff --git a/fail2wp/languages/fail2wp.pot b/fail2wp/languages/fail2wp.pot
index 278cffc..afc365d 100644
--- a/fail2wp/languages/fail2wp.pot
+++ b/fail2wp/languages/fail2wp.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: fail2wp 1.0.0\n"
+"Project-Id-Version: fail2wp 1.1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-02-03 10:24+0100\n"
+"POT-Creation-Date: 2021-03-08 15:28+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Joaquim Homrighausen \n"
"Language-Team: LANGUAGE \n"
@@ -17,213 +17,524 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: fail2wp.php:184
-msgid "Unknown"
+#: fail2wp.php:450
+msgid "Settings "
msgstr ""
-#: fail2wp.php:240 fail2wp.php:245
+#: fail2wp.php:548
+msgid "You are not currently logged in."
+msgstr ""
+
+#: fail2wp.php:629 fail2wp.php:667 fail2wp.php:686 fail2wp.php:706
+msgid "No route was found matching the URL and request method."
+msgstr ""
+
+#: fail2wp.php:743 fail2wp.php:754 fail2wp.php:765
+msgid "Invalid username, please try again."
+msgstr ""
+
+#: fail2wp.php:747 fail2wp.php:793
+msgid "Invalid e-mail address, please try again."
+msgstr ""
+
+#: fail2wp.php:811
+msgid "New user role default has been reset according to your"
+msgstr ""
+
+#: fail2wp.php:815
+msgid "settings"
+msgstr ""
+
+#: fail2wp.php:834
+msgid "Notification about new user role from"
+msgstr ""
+
+#: fail2wp.php:836
+msgid "This is a notification from"
+msgstr ""
+
+#: fail2wp.php:838
+msgid ""
+"The role for newly registered users has been reset to your configured "
+"setting."
+msgstr ""
+
+#: fail2wp.php:840
+msgid "This notification was sent from the site"
+msgstr ""
+
+#: fail2wp.php:842
+msgid "You may access the admin interface to the site here"
+msgstr ""
+
+#: fail2wp.php:859
+msgid "User registration is enabled, but the new user role does not match"
+msgstr ""
+
+#: fail2wp.php:868 fail2wp.php:898 fail2wp.php:927
+msgid "Go to"
+msgstr ""
+
+#: fail2wp.php:870 fail2wp.php:900 fail2wp.php:929
+msgid "General settings"
+msgstr ""
+
+#: fail2wp.php:872 fail2wp.php:902 fail2wp.php:931
+msgid "to check your membership and new user settings."
+msgstr ""
+
+#: fail2wp.php:889
+msgid "User registration is enabled, and new users will be administrators"
+msgstr ""
+
+#: fail2wp.php:918
+msgid "User registration is enabled, but no role has been configured"
+msgstr ""
+
+#: fail2wp.php:1011 fail2wp.php:1016
msgid "Unknown role"
msgstr ""
-#: fail2wp.php:286
+#: fail2wp.php:1057
msgid ""
-"Provides authentication related logging and security functions for "
-"WordPress, suitable for use with Fail2ban"
+"Provides authentication security functions for WordPress, plays nicely with "
+"Fail2ban and Cloudflare"
msgstr ""
-#: fail2wp.php:289
+#: fail2wp.php:1060
msgid "Basic configuration"
msgstr ""
-#: fail2wp.php:292
-msgid "Logging"
+#: fail2wp.php:1063
+msgid "New users"
msgstr ""
-#: fail2wp.php:295
+#: fail2wp.php:1066
+msgid "User logging"
+msgstr ""
+
+#: fail2wp.php:1069
+msgid "REST API"
+msgstr ""
+
+#: fail2wp.php:1072
msgid "Advanced"
msgstr ""
-#: fail2wp.php:298
+#: fail2wp.php:1075
msgid "Cloudflare"
msgstr ""
-#: fail2wp.php:301
+#: fail2wp.php:1078
+msgid "Import/Export"
+msgstr ""
+
+#: fail2wp.php:1081
msgid "About"
msgstr ""
-#: fail2wp.php:307
+#: fail2wp.php:1086
msgid ""
"One or more of openlog(), closelog(), and/or syslog() seem to be missing on "
"this system"
msgstr ""
-#: fail2wp.php:355
+#: fail2wp.php:1108
+msgid ""
+"Some data was filtered, please make sure you paste the content exactly as "
+"copied"
+msgstr ""
+
+#: fail2wp.php:1113 fail2wp.php:1121
+msgid "Please make sure you paste the content exactly as copied"
+msgstr ""
+
+#: fail2wp.php:1126
+msgid ""
+"Plugin version mismatch. You can only import exported data from the same "
+"version as the one currently installed"
+msgstr ""
+
+#: fail2wp.php:1190
+msgid "The following settings were ignored during the import"
+msgstr ""
+
+#: fail2wp.php:1197
+msgid "Settings were successfully imported"
+msgstr ""
+
+#: fail2wp.php:1201
+msgid "The setting \"Site label\" may need manual correction"
+msgstr ""
+
+#: fail2wp.php:1208
+msgid "This tab can be used to import and export settings for the plugin."
+msgstr ""
+
+#: fail2wp.php:1212 fail2wp.php:1221
+msgid "Import settings"
+msgstr ""
+
+#: fail2wp.php:1217
+msgid "Paste settings in this field to import them"
+msgstr ""
+
+#: fail2wp.php:1226
+msgid "Export settings"
+msgstr ""
+
+#: fail2wp.php:1272
+msgid "Unable to create data for export"
+msgstr ""
+
+#: fail2wp.php:1280
+msgid "Copy the text in this field to export your settings to another site"
+msgstr ""
+
+#: fail2wp.php:1304
+msgid ""
+"This is logged to the system's authentication log, which allows Fail2ban to "
+"dynamically block offending IP addresses."
+msgstr ""
+
+#: fail2wp.php:1306
+msgid ""
+"Configuration of the Fail2ban system daemon, or similar, must be done "
+"outside of WordPress for this to have any effect."
+msgstr ""
+
+#: fail2wp.php:1343
msgid "Thank you for installing"
msgstr ""
-#: fail2wp.php:356
+#: fail2wp.php:1344
msgid ""
-"This plugin provides security functions and integration between "
-"WordPress and"
+"This plugin provides security functions and integration between WordPress and"
msgstr ""
-#: fail2wp.php:360
+#: fail2wp.php:1348
msgid "Commercial support and customizations for this plugin is available from"
msgstr ""
-#: fail2wp.php:362
+#: fail2wp.php:1350
msgid "in Stockholm, Sweden. We speak Swedish and English"
msgstr ""
-#: fail2wp.php:364
+#: fail2wp.php:1352
msgid ""
"The plugin is written by Joaquim Homrighausen and sponsored by WebbPlatsen i "
"Sverige AB."
msgstr ""
-#: fail2wp.php:365
+#: fail2wp.php:1354
msgid ""
"If you find this plugin useful, the author is happy to receive a donation, "
"good review, or just a kind word."
msgstr ""
-#: fail2wp.php:366
+#: fail2wp.php:1355
msgid ""
"If there is something you feel to be missing from this plugin, or if you "
"have found a problem with the code or a feature, please do not hesitate to "
"reach out to"
msgstr ""
-#: fail2wp.php:381
+#: fail2wp.php:1357
+msgid "There is more documentation available at"
+msgstr ""
+
+#: fail2wp.php:1374
msgid "Site label"
msgstr ""
-#: fail2wp.php:382
+#: fail2wp.php:1375
msgid "Block user enum"
msgstr ""
-#: fail2wp.php:383
+#: fail2wp.php:1376
msgid "Block username login"
msgstr ""
-#: fail2wp.php:384
+#: fail2wp.php:1377
msgid "Secure login messages"
msgstr ""
-#: fail2wp.php:385
+#: fail2wp.php:1379
msgid "Other settings"
msgstr ""
-#: fail2wp.php:386
+#: fail2wp.php:1380
+msgid "Remove generator info"
+msgstr ""
+
+#: fail2wp.php:1381
+msgid "Remove feeds"
+msgstr ""
+
+#: fail2wp.php:1382
msgid "Remove settings"
msgstr ""
-#: fail2wp.php:389
+#: fail2wp.php:1385
+msgid "Membership warnings"
+msgstr ""
+
+#: fail2wp.php:1386
+msgid "Check for role"
+msgstr ""
+
+#: fail2wp.php:1387
+msgid "Force role"
+msgstr ""
+
+#: fail2wp.php:1388
+msgid "Role to force"
+msgstr ""
+
+#: fail2wp.php:1389
+msgid "Minimum username length"
+msgstr ""
+
+#: fail2wp.php:1390
+msgid "Banned usernames"
+msgstr ""
+
+#: fail2wp.php:1391
+msgid "E-mail must match"
+msgstr ""
+
+#: fail2wp.php:1394
msgid "Successful login"
msgstr ""
-#: fail2wp.php:390
+#: fail2wp.php:1395
msgid "Unsuccessful login"
msgstr ""
-#: fail2wp.php:395
+#: fail2wp.php:1397
+msgid "Log user enum"
+msgstr ""
+
+#: fail2wp.php:1400
+msgid "Require authentication"
+msgstr ""
+
+#: fail2wp.php:1401
+msgid "Log blocked requests"
+msgstr ""
+
+#: fail2wp.php:1402
+msgid "Block index requests"
+msgstr ""
+
+#: fail2wp.php:1403
+msgid "Block all requests"
+msgstr ""
+
+#: fail2wp.php:1404
+msgid "Block specific namespaces"
+msgstr ""
+
+#: fail2wp.php:1405
+msgid "Block specific routes"
+msgstr ""
+
+#: fail2wp.php:1406
+msgid "Bypass blocks for IPv4"
+msgstr ""
+
+#: fail2wp.php:1407
+msgid "Bypass blocks for IPv6"
+msgstr ""
+
+#: fail2wp.php:1410
msgid "Logging prefix"
msgstr ""
-#: fail2wp.php:396
+#: fail2wp.php:1411
msgid "Also log to PHP log"
msgstr ""
-#: fail2wp.php:399
+#: fail2wp.php:1414
msgid "Check for Cloudflare IP"
msgstr ""
-#: fail2wp.php:400
+#: fail2wp.php:1415
msgid "Cloudflare IPv4"
msgstr ""
-#: fail2wp.php:401
+#: fail2wp.php:1416
msgid "Cloudflare IPv6"
msgstr ""
-#: fail2wp.php:484
+#: fail2wp.php:1579
msgid ""
"The site name to use for logging, defaults to your site name if left empty"
msgstr ""
-#: fail2wp.php:529
+#: fail2wp.php:1605
msgid "Unknown users"
msgstr ""
-#: fail2wp.php:537
+#: fail2wp.php:1612
msgid "User enumeration attempts (i.e. your.site/...?author=nnn)"
msgstr ""
-#: fail2wp.php:545
+#: fail2wp.php:1620
+msgid "Warn about odd membership/registration settings"
+msgstr ""
+
+#: fail2wp.php:1638
+msgid "Check WordPress setting against the value configured here"
+msgstr ""
+
+#: fail2wp.php:1642 fail2wp.php:1671
+msgid "No available roles (?)"
+msgstr ""
+
+#: fail2wp.php:1650
+msgid "Force new user registration settings to a role"
+msgstr ""
+
+#: fail2wp.php:1668
+msgid "Force WordPress setting to the value configured here"
+msgstr ""
+
+#: fail2wp.php:1677
+msgid "Minimum length of usernames, 2-200 characters, 0 ignores the setting"
+msgstr ""
+
+#: fail2wp.php:1683
+msgid "These usernames will be blocked for new user registrations"
+msgstr ""
+
+#: fail2wp.php:1689
+msgid "E-mail address must match at least one of these for new users"
+msgstr ""
+
+#: fail2wp.php:1695
+msgid "Remove \"Generator\" output in HTML, RSS, etc."
+msgstr ""
+
+#: fail2wp.php:1702
+msgid "Remove RSS and Atom feeds"
+msgstr ""
+
+#: fail2wp.php:1710
msgid "Remove all plugin settings and data when plugin is uninstalled"
msgstr ""
-#: fail2wp.php:553
+#: fail2wp.php:1717
msgid "Block user enumeration attempts (i.e. your.site/...?author=nnn)"
msgstr ""
-#: fail2wp.php:561
+#: fail2wp.php:1724
msgid "Require users to login with their e-mail address"
msgstr ""
-#: fail2wp.php:569
+#: fail2wp.php:1731
msgid "Change login failure messages to contain less detail"
msgstr ""
-#: fail2wp.php:577
+#: fail2wp.php:1739
+msgid ""
+"Please make sure you understand how these settings can impact the operation "
+"of WordPress and other plugins before making changes to them."
+msgstr ""
+
+#: fail2wp.php:1743
+msgid "The REST API URL of this site is"
+msgstr ""
+
+#: fail2wp.php:1748
+msgid "NOTE"
+msgstr ""
+
+#: fail2wp.php:1749
+msgid ""
+"If \"Require authentication\" is enabled, no REST API calls will be blocked "
+"for logged in users and/or authenticated requests!"
+msgstr ""
+
+#: fail2wp.php:1758
msgid ""
"Please make sure you understand how these settings can impact the operation "
"of the plugin before making changes to them."
msgstr ""
-#: fail2wp.php:585
+#: fail2wp.php:1766
msgid "These settings allows the plugin to better interact with Cloudflare."
msgstr ""
-#: fail2wp.php:587
+#: fail2wp.php:1768
msgid ""
"If your site is not published via Cloudflare, you can safely ignore these "
"settings."
msgstr ""
-#: fail2wp.php:589
+#: fail2wp.php:1770
msgid "For an updated list of Cloudflare IPs, please use this link"
msgstr ""
-#: fail2wp.php:599
+#: fail2wp.php:1782
+msgid ""
+"Require that users be logged in and/or that all REST API calls are "
+"authenticated, this is typically safe to do."
+msgstr ""
+
+#: fail2wp.php:1789
+msgid ""
+"Blocks all REST API index calls made to this site, this is typically safe to "
+"do."
+msgstr ""
+
+#: fail2wp.php:1796
+msgid "Log all blocked REST API calls for Fail2ban processing."
+msgstr ""
+
+#: fail2wp.php:1803
+msgid ""
+"Blocks all REST API calls made to this site, this is typically not safe to "
+"do."
+msgstr ""
+
+#: fail2wp.php:1810
+msgid "WordPress did not return any available namespaces"
+msgstr ""
+
+#: fail2wp.php:1835 fail2wp.php:1841
+msgid "IPs matching these addresses will be allowed to make any REST API call"
+msgstr ""
+
+#: fail2wp.php:1845
msgid "The logging prefix, this should normally be left empty"
msgstr ""
-#: fail2wp.php:606
+#: fail2wp.php:1851
msgid "Log the same information to PHP log using error_log()"
msgstr ""
-#: fail2wp.php:614
+#: fail2wp.php:1859
msgid "Attempt to unmask real IP when Cloudflare IP is detected"
msgstr ""
-#: fail2wp.php:626 fail2wp.php:637
+#: fail2wp.php:1866 fail2wp.php:1872
msgid ""
"IPs matching these addresses will be considerd to be coming from Cloudflare"
msgstr ""
-#: fail2wp.php:962 fail2wp.php:966
+#: fail2wp.php:2244 fail2wp.php:2248
msgid "Please specify your e-mail address to login"
msgstr ""
-#: fail2wp.php:989
+#: fail2wp.php:2266
msgid "E-mail address"
msgstr ""
-#: fail2wp.php:1094 fail2wp.php:1122
+#: fail2wp.php:2371 fail2wp.php:2404
msgid "Invalid login credentials, please try again."
msgstr ""
-#: fail2wp.php:1126
+#: fail2wp.php:2408
msgid "Lost password"
msgstr ""
diff --git a/fail2wp/uninstall.php b/fail2wp/uninstall.php
index f7dc15e..1db7746 100644
--- a/fail2wp/uninstall.php
+++ b/fail2wp/uninstall.php
@@ -29,41 +29,53 @@
* Boston, MA 02110-1301, USA.
*/
+// define( 'FAIL2WP_UNINSTALL_TRACE', true );
+
// Don't load directly
defined( 'ABSPATH' ) || die( '-1' );
// If uninstall not called from WordPress, then exit
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: init' );
+ }
exit;
}
// If action is not to uninstall, then exit
if ( empty( $_REQUEST['action'] ) || $_REQUEST['action'] !== 'delete-plugin' ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: REQUEST["action"] is not delete-plugin' );
+ }
exit;
}
// If it's not us, then exit
if ( empty( $_REQUEST['slug'] ) || $_REQUEST['slug'] !== 'fail2wp' ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: REQUEST["slug"] is not fail2wp' );
+ }
exit;
}
// If we shouldn't do this, then exit
if ( ! current_user_can( 'manage_options' ) || ! current_user_can( 'delete_plugins' ) ) {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: User is not allowed to manage/uninstall plugins' );
+ }
exit;
}
// Figure out if an uninstall should remove plugin settings
-$remove_settings = get_option( 'fail2wp-remove-settings', '0' );
+$remove_settings = get_option( 'fail2wp-settings-remove', '0' );
if ( $remove_settings == '1' ) {
- // Remove Fail2WP settings
- delete_option( 'fail2wp-site-label' );
- delete_option( 'fail2wp-roles-notify' );
- delete_option( 'fail2wp-roles-warn' );
- delete_option( 'fail2wp-unknown-warn' );
- delete_option( 'fail2wp-settings-remove' );
- delete_option( 'fail2wp-also-log-php' );
- delete_option( 'fail2wp-block-user-enum' );
- delete_option( 'fail2wp-log-user-enum' );
- delete_option( 'fail2wp-block-username-login' );
- delete_option( 'fail2wp-secure-login-message' );
- delete_option( 'fail2wp-cloudflare-check' );
- delete_option( 'fail2wp-cloudflare-ipv4' );
- delete_option( 'fail2wp-cloudflare-ipv6' );
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: uninstalling' );
+ }
+ define( 'FAIL2WP_WORDPRESS_PLUGIN', true );
+
+ require_once dirname(__FILE__) . '/includes/fail2wp_misc.inc.php';
+
+ fail2wp_misc_delete_all_settings();
+} else {
+ if ( defined( 'FAIL2WP_UNINSTALL_TRACE' ) ) {
+ error_log( 'fail2wp-uninstall: $remove_settings = ' . var_export( $remove_settings, true ) );
+ }
}