PluginProbe
Easy Basic Authentication – Add basic auth to site or admin area / 4.2.0
Easy Basic Authentication – Add basic auth to site or admin area v4.2.0
4.2.0 4.1.0 trunk 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.7 1.7.1 1.8 All 52 releases
← All changes | class/easy-basic-authentication-class.php +364 -211 1.3.4 → 4.2.0 View file →
@@ -1,269 +1,422 @@
1 -<?php
1 +<?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + exit;
4 +}
2 5
6 +
7 +require_once (dirname(__FILE__).'/easy-basic-authentication-log-class.php');
8 +require_once (dirname(__FILE__).'/easy-basic-authentication-emailalert-class.php');
9 +require_once (dirname(__FILE__).'/easy-basic-authentication-form-class.php');
10 +require_once (dirname(__FILE__).'/easy-basic-authentication-notice-class.php');
11 +require_once (dirname(__FILE__).'/easy-basic-authentication-compatcheck-class.php');
12 +
3 13 class easy_basic_authentication_class {
4 14
15 + private $log;
16 + private $email;
17 + private $form;
18 + private $compatcheck;
5 19
6 20 public function __construct()
7 21 {
8 - if(get_option( 'basic_auth_plugin_admin_enable' )) {
9 - if (in_array($GLOBALS['pagenow'], array('wp-login.php', 'wp-register.php'))) {
10 - add_action( 'init', array($this,'basic_auth_admin') );
22 + $this->log = new easy_basic_authentication_log_class();
23 + $this->email = new easy_basic_authentication_emailalert_class();
24 + $this->form = new easy_basic_authentication_form_class();
25 + $notice = new easy_basic_authentication_notice_class();
26 + $this->compatcheck = new easy_basic_authentication_compatcheck_class();
27 + $this->compatcheck->register_hooks();
28 +
29 + // Fuori da una richiesta HTTP la sfida 401 non ha alcun senso: da riga di comando
30 + // diventa un exit() silenzioso che blocca WP-CLI, il cron di sistema e qualunque
31 + // script che faccia require di wp-load.php.
32 + if ( ! self::is_cli() ) {
33 + $pagenow = isset( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : '';
34 +
35 + if(get_option( 'basic_auth_plugin_admin_enable' )) {
36 + if (in_array($pagenow, array('wp-login.php', 'wp-register.php'))) {
37 + add_action( 'init', array($this,'basic_auth_root') );
38 + }
11 39 }
12 - }
13 40
14 - if(get_option( 'basic_auth_plugin_enable' ) && get_option( 'basic_auth_plugin_admin_enable' )){
15 - add_action( 'init', array($this,'basic_auth_root') );
41 + if(get_option( 'basic_auth_plugin_enable' ) && get_option( 'basic_auth_plugin_admin_enable' )){
42 + add_action( 'init', array($this,'basic_auth_root') );
43 + }
16 44 }
17 45
46 + add_action( 'init', array($this, 'maybe_upgrade'), 1 );
47 +
18 48 add_action( 'admin_menu', array($this,'basic_auth_plugin_menu' ));
19 - add_action( 'admin_init', array($this,'basic_auth_plugin_settings_init' ));
49 + add_action( 'admin_init', array($this->form,'basic_auth_plugin_settings_init' ));
20 50
21 - add_action( 'admin_init', array($this,'basic_auth_plugin_save_settings') );
51 + add_action('admin_init', function () {
52 + if ( ! current_user_can( 'manage_options' ) ) {
53 + return;
54 + }
55 +
56 + // Il nonce e verificato dentro basic_auth_plugin_save_settings(), che riceve
57 + // i dati grezzi perche deve distinguere un campo assente da uno vuoto (le
58 + // checkbox non spuntate non vengono inviate).
59 + // phpcs:ignore WordPress.Security.NonceVerification.Missing
60 + $post_data = $_POST;
61 + $this->form->basic_auth_plugin_save_settings($post_data);
62 + });
22 63
23 64 }
24 65
25 - public function basic_auth_root() {
26 - $user = get_option( 'basic_auth_plugin_username' );
27 - $pass = get_option( 'basic_auth_plugin_password' );
28 -
29 - if ( !isset( $_SERVER['PHP_AUTH_USER'] ) || !isset( $_SERVER['PHP_AUTH_PW'] ) ||
30 - $_SERVER['PHP_AUTH_USER'] != $user || !wp_check_password( $_SERVER['PHP_AUTH_PW'], $pass ) ) {
31 -
32 - $this->basic_auth_log_failed_access();
66 + /**
67 + * Se stiamo girando fuori da una richiesta HTTP (WP-CLI, cron di sistema, script).
68 + *
69 + * @return bool
70 + */
71 + public static function is_cli() {
72 + if ( defined( 'WP_CLI' ) && WP_CLI ) {
73 + return true;
74 + }
33 75
34 - do_action('basic_auth_before_401');
35 -
36 - header( 'WWW-Authenticate: Basic realm="My Website"' );
37 - header( 'HTTP/1.0 401 Unauthorized' );
38 - echo 'Autenticazione richiesta';
39 - exit;
40 - }
76 + return 'cli' === PHP_SAPI || 'phpdbg' === PHP_SAPI;
41 77 }
42 -
43 - public function basic_auth_admin() {
44 - $user = get_option( 'basic_auth_plugin_username' );
45 - $pass = get_option( 'basic_auth_plugin_password' );
46 -
47 - if ( !isset( $_SERVER['PHP_AUTH_USER'] ) || !isset( $_SERVER['PHP_AUTH_PW'] ) ||
48 - $_SERVER['PHP_AUTH_USER'] != $user || !wp_check_password( $_SERVER['PHP_AUTH_PW'], $pass ) ) {
49 -
50 - $this->basic_auth_log_failed_access();
51 78
52 - do_action('basic_auth_before_401');
79 + public function basic_auth_root()
80 + {
81 + if ( self::is_cli() ) {
82 + return;
83 + }
53 84
54 - header( 'HTTP/1.1 401 Unauthorized' );
55 - header( 'WWW-Authenticate: Basic realm="Admin Area"' );
56 - exit;
57 - }
58 - }
59 -
60 -
61 - public function basic_auth_plugin_menu() {
62 - $access_count = $this->basic_auth_count_not_viewed_log_failed_access();
63 - add_menu_page(
64 - __('Configurations for Easy Basic Authentication', 'easy-basic-authentication'),
65 - __('Easy Basic A.', 'easy-basic-authentication'),
66 - 'manage_options',
67 - 'basic-auth-plugin',
68 - array($this, 'basic_auth_plugin_settings_page'),
69 - 'dashicons-lock'
70 - );
71 - if($this->log_is_enabled()) {
72 - add_submenu_page(
73 - 'basic-auth-plugin',
74 - __('Log Page', 'easy-basic-authentication'),
75 - __('Log Page', 'easy-basic-authentication'). '<span class="update-plugins count-' . $access_count . '"><span class="plugin-count">' . $access_count . '</span></span>',
76 - 'manage_options',
77 - 'basic-auth-login',
78 - array($this, 'basic_auth_login_page')
79 - );
85 + // wp-cron.php chiude la risposta con fastcgi_finish_request() prima ancora di
86 + // caricare WordPress: quando arriviamo qui gli header sono gia partiti. La sfida
87 + // 401 non raggiunge nessuno, lascia un warning "headers already sent" nel log a
88 + // ogni giro, e l'exit che la segue ferma tutte le azioni pianificate del sito.
89 + if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
90 + return;
80 91 }
92 +
93 + $user = get_option('basic_auth_plugin_username');
94 + $pass = get_option('basic_auth_plugin_password');
81 95
96 + if ($this->whiteListChecker()) {
97 + return;
98 + }
99 +
100 + if ($this->urlWhiteListChecker()) {
101 + return;
102 + }
103 +
104 + if ($this->tokenChecker()) {
105 + return;
106 + }
107 +
108 + // Alcuni server (nginx + FastCGI su tutti) non popolano PHP_AUTH_USER da soli:
109 + // le credenziali arrivano solo nell'header Authorization e vanno estratte a mano.
110 + if (!isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['HTTP_AUTHORIZATION'])) {
111 + $authorization = sanitize_text_field( wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ) );
112 +
113 + if ( preg_match( '#^Basic\s+([A-Za-z0-9+/=]+)$#i', $authorization, $matches ) ) {
114 + $decoded = base64_decode( $matches[1], true );
115 +
116 + // Senza i due punti non ci sono due campi: la list() originale
117 + // generava un warning "Undefined array key 1" su PHP 8.
118 + if ( false !== $decoded && false !== strpos( $decoded, ':' ) ) {
119 + list( $sent_user, $sent_pass ) = explode( ':', $decoded, 2 );
120 + $_SERVER['PHP_AUTH_USER'] = $sent_user;
121 + $_SERVER['PHP_AUTH_PW'] = $sent_pass;
122 + }
123 + }
124 + }
125 +
126 + // Basic Auth prevede sempre un primo giro senza credenziali: il browser chiede,
127 + // riceve 401, e solo allora rimanda con utente e password. Quel 401 non e un
128 + // tentativo fallito, e registrarlo significherebbe una voce di log (e una mail)
129 + // per ogni visitatore e ogni bot che passa.
130 + $credentials_sent = isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']);
131 +
132 + if (!$credentials_sent) {
133 + $this->do_exit(true, false);
134 + return;
135 + }
136 +
137 + // L'utente e salvato passando per sanitize_text_field(), quindi quello in arrivo
138 + // va normalizzato allo stesso modo perche il confronto sia sensato.
139 + $sent_user = sanitize_text_field( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) );
140 +
141 + // La password invece NON va sanitizzata: alterarla farebbe fallire l'accesso a
142 + // chiunque ne usi una con caratteri speciali. Non viene mai stampata, solo
143 + // confrontata con l'hash.
144 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
145 + $sent_pass = wp_unslash( $_SERVER['PHP_AUTH_PW'] );
146 +
147 + $credentials_valid = hash_equals( (string) $user, $sent_user )
148 + && wp_check_password( $sent_pass, $pass );
149 +
150 + if (!$credentials_valid) {
151 + // Credenziali inviate e sbagliate: questo si che va segnalato.
152 + $this->do_exit(true, true);
153 + }
82 154 }
83 -
84 - public function basic_auth_login_page() {
85 - if(array_key_exists('clear_mode',$_POST)) {
86 - update_option('basic_auth_failure_logs', array());
155 +
156 + public function urlWhiteListChecker() {
157 + if (empty($_SERVER['HTTP_HOST']) || empty($_SERVER['REQUEST_URI'])) {
158 + return false;
87 159 }
88 - $user_log_data = array_reverse($this->basic_auth_get_log_failed_access());
89 - include plugin_dir_path( __FILE__ ) . '../template/log_page.php';
90 - $this->update_view();
160 +
161 + $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
162 +
163 + $host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
164 +
165 + // L'URI serve solo al confronto con la whitelist, non viene mai stampato.
166 + // sanitize_text_field() qui sarebbe dannoso: rimuove le sequenze %xx e
167 + // cambierebbe il confronto su qualsiasi percorso con caratteri codificati.
168 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
169 + $request_uri = wp_unslash( $_SERVER['REQUEST_URI'] );
170 +
171 + $currentUrl = $scheme . '://' . $host . $request_uri;
172 +
173 + $whitelist = $this->getUrlWhiteList();
174 +
175 + foreach ($whitelist as $entry) {
176 + if ($this->isUrlAllowed($currentUrl, $entry)) {
177 + return true;
178 + }
179 + }
180 +
181 + return false;
91 182 }
92 -
93 183
94 - public function basic_auth_plugin_settings_page() {
95 - include plugin_dir_path( __FILE__ ) . '../template/settings_page.php';
184 + private function isUrlAllowed($currentUrl, $entry) {
185 + $currentUrl = rtrim($currentUrl, '/');
186 + $entry = rtrim($entry, '/');
187 +
188 + if (strpos($entry, '/') === 0) {
189 + $path = wp_parse_url($currentUrl, PHP_URL_PATH);
190 + return stripos($path, $entry) === 0;
191 + }
192 +
193 + if (!preg_match('#^https?://#i', $entry)) {
194 + $scheme = wp_parse_url($currentUrl, PHP_URL_SCHEME) ?: 'https';
195 + $entry = $scheme . '://' . $entry;
196 + }
197 +
198 + return stripos($currentUrl, $entry) === 0;
96 199 }
200 +
201 + public function whiteListChecker() {
202 + if (!isset($_SERVER['REMOTE_ADDR'])) {
203 + return false;
204 + }
97 205
98 - public function basic_auth_plugin_settings_init() {
99 - register_setting( 'basic-auth-plugin-settings', 'basic_auth_plugin_admin_enable' );
100 - register_setting( 'basic-auth-plugin-settings', 'basic_auth_plugin_enable' );
101 - register_setting( 'basic-auth-plugin-settings', 'basic_auth_plugin_username' );
102 - register_setting( 'basic-auth-plugin-settings', 'basic_auth_plugin_admin_log_enable' );
103 -
104 - add_settings_section(
105 - 'basic-auth-plugin-section',
106 - __('Configurations for Easy Basic Authentication', 'easy-basic-authentication'),
107 - array($this,'basic_auth_plugin_section_cb'),
108 - 'basic-auth-plugin-settings'
109 - );
110 -
111 - add_settings_field(
112 - 'basic-auth-plugin-admin-enable',
113 - __('Enable for wp-admin', 'easy-basic-authentication'),
114 - array($this,'basic_auth_plugin_admin_enable_cb'),
115 - 'basic-auth-plugin-settings',
116 - 'basic-auth-plugin-section'
117 - );
118 -
119 - add_settings_field(
120 - 'basic-auth-plugin-enable',
121 - __('Enable for the entire site (only if wp-admin is enabled)', 'easy-basic-authentication'),
122 - array($this,'basic_auth_plugin_enable_cb'),
123 - 'basic-auth-plugin-settings',
124 - 'basic-auth-plugin-section'
125 - );
126 -
127 - add_settings_field(
128 - 'basic-auth-plugin-username',
129 - __('Username', 'easy-basic-authentication'),
130 - array($this,'basic_auth_plugin_username_cb'),
131 - 'basic-auth-plugin-settings',
132 - 'basic-auth-plugin-section'
133 - );
134 -
135 - add_settings_field(
136 - 'basic-auth-plugin-password',
137 - __('Password', 'easy-basic-authentication'),
138 - array($this,'basic_auth_plugin_password_cb'),
139 - 'basic-auth-plugin-settings',
140 - 'basic-auth-plugin-section'
141 - );
142 -
143 - add_settings_field(
144 - 'basic-auth-plugin-admin-log-enable',
145 - __('Enable access logs', 'easy-basic-authentication'),
146 - array($this,'basic_auth_plugin_admin_log_enable_cb'),
147 - 'basic-auth-plugin-settings',
148 - 'basic-auth-plugin-section'
149 - );
150 -
206 + // Un REMOTE_ADDR malformato non deve arrivare ai confronti della whitelist:
207 + // ip2long() restituirebbe false e il risultato sarebbe imprevedibile.
208 + $ip = filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ), FILTER_VALIDATE_IP );
209 +
210 + if ( false === $ip ) {
211 + return false;
212 + }
213 +
214 + $whitelist = $this->getWhiteList();
215 +
216 + foreach ($whitelist as $entry) {
217 + if ($this->isIpAllowed($ip, $entry)) {
218 + return true;
219 + }
220 + }
221 + return false;
151 222 }
152 223
153 - public function basic_auth_plugin_section_cb() {
154 - echo __('Configure basic authentication', 'easy-basic-authentication');
224 + private function isIpAllowed($ip, $entry) {
225 + if (filter_var($entry, FILTER_VALIDATE_IP)) {
226 + return $ip === $entry;
227 + } elseif (strpos($entry, '/') !== false) {
228 + return $this->isIpInCidr($ip, $entry);
229 + } elseif (strpos($entry, '-') !== false) {
230 + return $this->isIpInRange($ip, $entry);
231 + }
232 + return false;
155 233 }
156 234
157 - public function basic_auth_plugin_enable_cb() {
158 - $admin_enable = get_option( 'basic_auth_plugin_admin_enable' );
159 - $enable = get_option( 'basic_auth_plugin_enable' );
160 - ?>
161 - <input type="checkbox" name="basic_auth_plugin_enable" value="1" <?php checked( $enable, 1 ); ?><?php disabled( !$admin_enable ); ?>>
162 - <?php
235 + private function isIpInCidr($ip, $cidr) {
236 + list($subnet, $mask) = explode('/', $cidr);
237 + $ipLong = ip2long($ip);
238 + $subnetLong = ip2long($subnet);
239 + $maskLong = -1 << (32 - $mask);
240 + return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
163 241 }
164 242
165 - public function basic_auth_plugin_admin_enable_cb() {
166 - $enable = get_option( 'basic_auth_plugin_admin_enable' );
167 - ?>
168 - <input type="checkbox" name="basic_auth_plugin_admin_enable" value="1" <?php checked( $enable, 1 ); ?>>
169 - <?php
243 + private function isIpInRange($ip, $range) {
244 + list($start, $end) = array_map('trim', explode('-', $range));
245 + $ipLong = ip2long($ip);
246 + $startLong = ip2long($start);
247 + $endLong = ip2long($end);
248 + return ($ipLong >= $startLong && $ipLong <= $endLong);
170 249 }
171 -
172 - public function basic_auth_plugin_username_cb() {
173 - $username = get_option( 'basic_auth_plugin_username' );
174 - ?>
175 - <input type="text" name="basic_auth_plugin_username" value="<?php echo esc_attr( $username ); ?>">
176 - <?php
250 +
251 + /**
252 + * Un crawler o un servizio esterno sta passando col token d'accesso?
253 + *
254 + * Serve a far entrare chi non puo usare l'autenticazione HTTP: un crawler, un
255 + * controllo di raggiungibilita, un front-end headless. Il token vale solo per
256 + * il sito: l'amministrazione e la pagina di accesso restano protette, perche
257 + * un crawler non ha motivo di entrarci e aprirgliele sarebbe rischio gratuito.
258 + *
259 + * @return bool
260 + */
261 + public function tokenChecker() {
262 + $token = (string) get_option('basic_auth_plugin_access_token', '');
263 +
264 + // Funzione spenta finche non si genera un token.
265 + if ('' === $token) {
266 + return false;
267 + }
268 +
269 + // Mai per l'amministrazione o la pagina di accesso.
270 + if (is_admin() || $this->isLoginRequest()) {
271 + return false;
272 + }
273 +
274 + $sent = $this->getRequestToken();
275 +
276 + if ('' === $sent) {
277 + return false;
278 + }
279 +
280 + // hash_equals: il confronto non deve rivelare quanti caratteri erano giusti.
281 + if (!hash_equals($token, $sent)) {
282 + // Un token sbagliato e un tentativo deliberato, non il primo giro a vuoto
283 + // del protocollo: va segnalato come una password sbagliata.
284 + $this->basic_auth_action_failed_access();
285 +
286 + return false;
287 + }
288 +
289 + /**
290 + * Scatta quando una richiesta entra col token invece che con le credenziali.
291 + */
292 + do_action('basic_auth_token_access_granted');
293 +
294 + return true;
177 295 }
178 -
179 - public function basic_auth_plugin_password_cb() {
180 - $password = get_option( 'basic_auth_plugin_password' )
181 - ? __('Password entered', 'easy-basic-authentication')
182 - : __('Enter the password', 'easy-basic-authentication');
183 - ?>
184 - <input type="password" name="basic_auth_plugin_password" value="" placeholder="<?php echo $password; ?>">
185 - <?php
186 - }
187 -
188 - public function basic_auth_plugin_admin_log_enable_cb() {
189 - $enable = get_option( 'basic_auth_plugin_admin_log_enable' );
190 - ?>
191 - <input type="checkbox" name="basic_auth_plugin_admin_log_enable" value="1" <?php checked( $enable, 1 ); ?>>
192 - <?php
193 - }
194 -
195 -
196 - public function basic_auth_plugin_save_settings() {
197 - if ( isset( $_POST['eba_submit'] ) ) {
198 - $admin_enable = isset( $_POST['basic_auth_plugin_admin_enable'] ) ? 1 : 0;
199 - $enable = isset( $_POST['basic_auth_plugin_enable'] ) ? 1 : 0;
200 - $username = sanitize_text_field( $_POST['basic_auth_plugin_username'] );
201 - $password = sanitize_text_field( $_POST['basic_auth_plugin_password'] );
202 - $log_enable = isset( $_POST['basic_auth_plugin_admin_log_enable'] ) ? 1 : 0;
203 -
204 - update_option( 'basic_auth_plugin_admin_enable', $admin_enable );
205 - update_option( 'basic_auth_plugin_enable', $enable );
206 - update_option( 'basic_auth_plugin_username', $username );
207 - update_option( 'basic_auth_plugin_admin_log_enable', $log_enable );
208 -
209 - if ( ! empty( $password ) ) {
210 - $hashed_password = wp_hash_password( $password );
211 - update_option( 'basic_auth_plugin_password', $hashed_password );
296 +
297 + /**
298 + * Il token inviato con la richiesta, da uno dei due header accettati.
299 + *
300 + * @return string
301 + */
302 + private function getRequestToken() {
303 + // Header dedicato: la strada piu pulita.
304 + if (!empty($_SERVER['HTTP_X_BASIC_AUTH_TOKEN'])) {
305 + return trim(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_BASIC_AUTH_TOKEN'])));
306 + }
307 +
308 + // Authorization: Bearer, per i servizi che sanno mandare solo questo.
309 + if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
310 + $header = sanitize_text_field(wp_unslash($_SERVER['HTTP_AUTHORIZATION']));
311 +
312 + if (preg_match('#^Bearer\s+(\S+)$#i', $header, $matches)) {
313 + return $matches[1];
212 314 }
213 315 }
316 +
317 + return '';
214 318 }
215 319
216 - public function basic_auth_log_failed_access() {
217 - if($this->log_is_enabled()) {
218 - $logs = get_option('basic_auth_failure_logs', array());
320 + /**
321 + * Se la richiesta corrente e la pagina di accesso.
322 + *
323 + * @return bool
324 + */
325 + private function isLoginRequest() {
326 + $pagenow = isset($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : '';
219 327
220 - $log_entry = array(
221 - 'id' => uniqid(),
222 - 'ip' => $_SERVER['REMOTE_ADDR'],
223 - 'data' => current_time('mysql'),
224 - 'browser' => $_SERVER['HTTP_USER_AGENT'],
225 - 'viewed' => 0
226 - );
328 + if (in_array($pagenow, array('wp-login.php', 'wp-register.php'), true)) {
329 + return true;
330 + }
227 331
228 - $log_entry = apply_filters('basic_auth_single_log_entry', $log_entry);
332 + if (empty($_SERVER['REQUEST_URI'])) {
333 + return false;
334 + }
229 335
230 - $logs[] = $log_entry;
336 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- solo confronto, mai stampato.
337 + $uri = wp_unslash($_SERVER['REQUEST_URI']);
231 338
232 - $logs = apply_filters('basic_auth_logs_entry', $logs);
339 + return false !== stripos($uri, 'wp-login.php') || false !== stripos($uri, 'wp-register.php');
340 + }
233 341
234 - update_option('basic_auth_failure_logs', $logs);
342 + /**
343 + * Manda la sfida 401.
344 + *
345 + * @param bool $admin_area Se la richiesta riguarda l'area di amministrazione.
346 + * @param bool $log_attempt Se registrare l'accesso fra i tentativi falliti.
347 + * False per il 401 iniziale, che fa parte del protocollo.
348 + */
349 + public function do_exit($admin_area = false, $log_attempt = true) {
350 +
351 + if ($log_attempt) {
352 + $this->basic_auth_action_failed_access();
235 353 }
354 +
355 + do_action('basic_auth_before_401');
356 +
357 + if ($admin_area) {
358 + do_action('basic_auth_before_401_admin_area');
359 + }
360 +
361 + // I browser memorizzano le credenziali per realm: cambiare questa stringa fa
362 + // ricomparire la richiesta di accesso a chi era gia autenticato. Il valore
363 + // predefinito resta quello storico; chi vuole cambiarlo usa il filtro.
364 + $realm = apply_filters('basic_auth_realm', 'My Website', $admin_area);
365 + $realm = str_replace(array('"', "\r", "\n"), '', (string) $realm);
366 +
367 + header('WWW-Authenticate: Basic realm="' . $realm . '"');
368 + status_header(401);
369 + exit;
236 370 }
237 371
238 - public function basic_auth_get_log_failed_access() {
239 - return get_option('basic_auth_failure_logs', array());
372 + public function getWhiteList() {
373 + return get_option( 'basic_auth_plugin_whitelist' )?explode(',',get_option( 'basic_auth_plugin_whitelist' )):[];
240 374 }
241 375
242 - public function basic_auth_count_log_failed_access() {
243 - return count($this->basic_auth_get_log_failed_access());
376 + public function getUrlWhiteList() {
377 + return get_option( 'basic_auth_plugin_urlwhitelist' )?explode(',',get_option( 'basic_auth_plugin_urlwhitelist' )):[];
244 378 }
245 379
246 - public function basic_auth_not_viewed_log_failed_access() {
247 - $return_not_viewed = array_filter($this->basic_auth_get_log_failed_access(), function ($entry) {
248 - return isset($entry['viewed']) && $entry['viewed'] === 0;
249 - });
250 - return $return_not_viewed;
251 - }
380 + public function basic_auth_plugin_menu() {
381 + add_menu_page(
382 + __('Configurations for Easy Basic Authentication', 'easy-basic-authentication'),
383 + __('Easy Basic A.', 'easy-basic-authentication'),
384 + 'manage_options',
385 + 'basic-auth-plugin',
386 + array($this->form, 'basic_auth_plugin_settings_page'),
387 + 'dashicons-lock'
388 + );
389 + if($this->log->is_enabled()) {
390 + $this->log->getMenu();
391 + }
392 + }
393 +
394 + /**
395 + * Migrazioni una tantum, eseguite quando cambia la versione del plugin.
396 + *
397 + * Fino alla 4.0.0 il log degli accessi stava in un'opzione in autoload: fino a
398 + * 500 voci, circa 144 KB, deserializzate a ogni richiesta del sito.
399 + */
400 + public function maybe_upgrade() {
401 + if ( get_option( 'basic_auth_plugin_db_version' ) === EASY_BASIC_AUTHENTICATION_VERSION ) {
402 + return;
403 + }
252 404
253 - public function basic_auth_count_not_viewed_log_failed_access() {
254 - return count($this->basic_auth_not_viewed_log_failed_access());
405 + $logs = get_option( 'basic_auth_failure_logs', null );
406 + if ( null !== $logs ) {
407 + update_option( 'basic_auth_failure_logs', $logs, false );
408 + }
409 +
410 + update_option( 'basic_auth_plugin_db_version', EASY_BASIC_AUTHENTICATION_VERSION, false );
255 411 }
256 412
257 - public function update_view() {
258 - $logs = get_option('basic_auth_failure_logs', array());
259 - foreach ($logs as &$entry) {
260 - $entry['viewed'] = 1;
413 + public function basic_auth_action_failed_access() {
414 + if($this->log->is_enabled()) {
415 + $this->log->update_status($_SERVER);
261 416 }
262 - update_option('basic_auth_failure_logs', $logs);
263 - }
264 -
265 - public function log_is_enabled() {
266 - return get_option( 'basic_auth_plugin_admin_log_enable' );
417 + if($this->email->is_enabled()) {
418 + $this->email->sendAlert($_SERVER);
419 + }
267 420 }
268 421
269 422 }