Lockdown WP Admin - Version 2.2

Version Description

  • Fixing issues with other plugins
  • Support tested for 3.9
  • Large code structure changes. If you are extending the WP_LockAuth at all, you should basically check the class anew since it was seperated into Admin and Application services.
Download this release

Release Info

Developer sean212
Plugin Icon wp plugin Lockdown WP Admin
Version 2.2
Comparing to
See all releases

Code changes from version 2.1 to 2.2

README.md CHANGED
@@ -1,6 +1,9 @@
1
  Lockdown WP Admin
2
  =============
3
 
 
 
 
4
  Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc.
5
 
6
  ### Description
@@ -99,3 +102,8 @@ A very late update, sorry! Worked to fix many issues with the admin bar and the
99
  * Unit Testing! Unit Testing ensure more reliable code going forward
100
  * Support for WordPress 3.6
101
  * General Cleaning
 
 
 
 
 
1
  Lockdown WP Admin
2
  =============
3
 
4
+ [![Build Status](https://api.travis-ci.org/srtfisher/Lockdown-WPAdmin.png?branch=master)](https://travis-ci.org/srtfisher/Lockdown-WPAdmin)
5
+
6
+
7
  Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc.
8
 
9
  ### Description
102
  * Unit Testing! Unit Testing ensure more reliable code going forward
103
  * Support for WordPress 3.6
104
  * General Cleaning
105
+
106
+ 2.2
107
+ * Fixing issues with other plugins
108
+ * Support tested for 3.9
109
+ * Large code structure changes. If you are extending the `WP_LockAuth` at all, you should basically check the class anew since it was seperated into Admin and Application services.
bin/install-wp-tests.sh ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+
14
+ WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
15
+ WP_CORE_DIR=/tmp/wordpress/
16
+
17
+ set -ex
18
+
19
+ install_wp() {
20
+ mkdir -p $WP_CORE_DIR
21
+
22
+ if [ $WP_VERSION == 'latest' ]; then
23
+ local ARCHIVE_NAME='latest'
24
+ else
25
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
26
+ fi
27
+
28
+ wget -nv -O /tmp/wordpress.tar.gz http://wordpress.org/${ARCHIVE_NAME}.tar.gz
29
+ tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
30
+
31
+ wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
32
+ }
33
+
34
+ install_test_suite() {
35
+ # portable in-place argument for both GNU sed and Mac OSX sed
36
+ if [[ $(uname -s) == 'Darwin' ]]; then
37
+ local ioption='-i .bak'
38
+ else
39
+ local ioption='-i'
40
+ fi
41
+
42
+ # set up testing suite
43
+ mkdir -p $WP_TESTS_DIR
44
+ cd $WP_TESTS_DIR
45
+ svn co --quiet http://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
46
+
47
+ wget -nv -O wp-tests-config.php http://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php
48
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
49
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
50
+ sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
51
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" wp-tests-config.php
52
+ sed $ioption "s|localhost|${DB_HOST}|" wp-tests-config.php
53
+ }
54
+
55
+ install_db() {
56
+ # parse DB_HOST for port or socket references
57
+ local PARTS=(${DB_HOST//\:/ })
58
+ local DB_HOSTNAME=${PARTS[0]};
59
+ local DB_SOCK_OR_PORT=${PARTS[1]};
60
+ local EXTRA=""
61
+
62
+ if ! [ -z $DB_HOSTNAME ] ; then
63
+ if [[ "$DB_SOCK_OR_PORT" =~ ^[0-9]+$ ]] ; then
64
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
65
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
66
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
67
+ elif ! [ -z $DB_HOSTNAME ] ; then
68
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
69
+ fi
70
+ fi
71
+
72
+ # create database
73
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
74
+ }
75
+
76
+ install_wp
77
+ install_test_suite
78
+ install_db
composer.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "srtfisher/lockdown-wp-admin",
3
+ "description": "WordPress Plugin to hide login and admin URLs when not logged into WordPress.",
4
+ "homepage": "http://wordpress.org/plugins/lockdown-wp-admin/",
5
+ "license": "GPL-2.0",
6
+ "type": "wp-cli",
7
+ "keywords": ["wordpress", "admin", "security"],
8
+
9
+ "authors": [
10
+ {
11
+ "name": "Sean Fisher",
12
+ "homepage": "http://seanfisher.co/"
13
+ }
14
+ ],
15
+ "minimum-stability": "dev",
16
+ "config": {
17
+ "preferred-install": "dist"
18
+ },
19
+ "require-dev": {
20
+ "phpunit/phpunit": "3.7.*"
21
+ }
22
+ }
lockdown-wp-admin.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Lockdown WP Admin
4
  Plugin URI: http://seanfisher.co/lockdown-wp-admin/
5
  Donate link: http://seanfisher.co/donate/
6
  Description: Securing the WordPress Administration interface by concealing the administration dashboard and changing the login page URL.
7
- Version: 2.1
8
  Author: Sean Fisher
9
  Author URI: http://seanfisher.co/
10
  License: GPL
@@ -12,6 +12,7 @@ License: GPL
12
 
13
  // This file name
14
  define('LD_FILE_NAME', __FILE__ );
 
15
 
16
  /**
17
  * This is the plugin that will add security to our site
@@ -28,14 +29,14 @@ class WP_LockAuth
28
  * @global string
29
  * @access private
30
  **/
31
- public $ld_admin_version = '2.1';
32
 
33
  /**
34
  * The HTTP Auth name for the protected area
35
  * Change this via calling the object, not by editing the file.
36
  *
37
  * @access public
38
- * @global string
39
  **/
40
  public $relm = 'Secure Area';
41
 
@@ -46,648 +47,74 @@ class WP_LockAuth
46
  **/
47
  protected $current_user = FALSE;
48
 
49
- /**
50
- * The base to get the login url
51
- *
52
- * @access private
53
- **/
54
- protected $login_base = FALSE;
55
-
56
  /**
57
  * Check if the Auth passed
58
- *
59
  * See {@link WP_LockAuth::getAuthPassed()}
60
  *
61
- * @var boolean
62
  */
63
  protected $passed = FALSE;
64
 
65
  /**
66
- * Constructor
67
  *
68
- * @return void
69
  */
70
- public function __construct()
71
- {
72
- // We don't like adding network wide WordPress plugins.
73
- if (! class_exists('Disable_WPMS_Plugin_LD'))
74
- require_once( dirname( __FILE__ ) .'/no-wpmu.php' );
75
-
76
- // Add the action to setup the menu.
77
- add_action('admin_menu', array( $this, 'add_admin_menu'));
78
-
79
- // Setup the plugin.
80
- $this->setup_hide_admin();
81
-
82
- // Hide the login form
83
- $this->redo_login_form();
84
- }
85
-
86
- /**
87
- * Get a username and password from the HTTP auth
88
- *
89
- * @return array|bool
90
- **/
91
- public function get_http_auth_creds()
92
- {
93
- // Since PHP saves the HTTP Password in a bunch of places, we have to be able to test for all of them
94
- $username = $password = NULL;
95
-
96
- // mod_php
97
- if (isset($_SERVER['PHP_AUTH_USER']))
98
- {
99
- $username = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : NULL;
100
- $password = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : NULL;
101
- }
102
 
103
- // most other servers
104
- elseif ($_SERVER['HTTP_AUTHENTICATION'])
105
- {
106
- if (strpos(strtolower($_SERVER['HTTP_AUTHENTICATION']),'basic') === 0)
107
- {
108
- list($username,$password) = explode(':',base64_decode(substr($_SERVER['HTTP_AUTHENTICATION'], 6)));
109
- }
110
- }
111
-
112
- // Check them - if they're null a/o empty, they're invalid.
113
- if ( is_null($username) OR is_null($password) OR empty($username) OR empty($password))
114
- return FALSE;
115
- else
116
- return array('username' => $username, 'password' => $password);
117
- }
118
-
119
  /**
120
- * Update the users
121
  *
122
- * @access private
123
- **/
124
- public function update_users()
125
- {
126
- if ( ! isset( $_GET['page'] ) )
127
- return;
128
-
129
- if ( $_GET['page'] !== 'lockdown-private-users' )
130
- return;
131
-
132
- // Nonce
133
- if ( ! isset( $_REQUEST['_wpnonce'] ) )
134
- return;
135
-
136
- $nonce = $_REQUEST['_wpnonce'];
137
- if ( !wp_verify_nonce( $nonce, 'lockdown-wp-admin' ) )
138
- wp_die('Security error, please try again.');
139
-
140
- // Add a user
141
- if ( isset( $_POST['private_username'] ) && isset( $_POST['private_password'] ) )
142
- {
143
- if ( $_POST['private_username'] !== '' && $_POST['private_password'] !== '' )
144
- {
145
- // Adding a user
146
- $users = $this->get_private_users();
147
- $add['user'] = sanitize_user( $_POST['private_username'] );
148
- $add['pass'] = trim( md5( $_POST['private_password'] ) );
149
-
150
- // See if it exists
151
- if ($this->user_exists($users, $add['user'])) :
152
- define('LD_ERROR', 'username-exists');
153
- return;
154
- endif;
155
 
156
- $users[] = $add;
157
-
158
- update_option('ld_private_users', $users);
159
-
160
- define('LD_WP_ADMIN', TRUE);
161
- return;
162
- }
163
- }
164
-
165
- // Deleting a user.
166
- if ( isset( $_GET['delete'] ) )
167
- {
168
- // Delete the user.
169
- unset( $users );
170
- $users = $this->get_private_users();
171
- $to_delete = (int) $_GET['delete'];
172
-
173
- if ( count( $users ) > 0 )
174
- {
175
- foreach( $users as $key => $val )
176
- {
177
- if ( $key === $to_delete ) :
178
- if( $this->current_user !== '' && $to_delete === $this->current_user )
179
- {
180
- // They can't delete themselves!
181
- define('LD_ERROR', 'delete-self');
182
- return;
183
- }
184
-
185
- unset( $users[$key] );
186
- endif;
187
- }
188
- }
189
-
190
- update_option('ld_private_users', $users);
191
-
192
- define('LD_WP_ADMIN', TRUE);
193
- return;
194
- }
195
- }
196
-
197
  /**
198
- * Update the options
199
- *
200
- * @access private
201
- **/
202
- public function update_options()
203
- {
204
- if ( !isset( $_GET['page'] ) )
205
- return;
206
-
207
- if ( $_GET['page'] !== 'lockdown-wp-admin' )
208
- return;
209
-
210
- if ( !isset( $_POST['did_update'] ) )
211
- return;
212
-
213
- // Nonce
214
- $nonce = $_POST['_wpnonce'];
215
- if ( ! wp_verify_nonce($nonce, 'lockdown-wp-admin') )
216
- wp_die('Security error, please try again.');
217
-
218
- // ---------------------------------------------------
219
- // They're updating.
220
- // ---------------------------------------------------
221
- if ( isset( $_POST['http_auth'] ) )
222
- update_option('ld_http_auth', trim( strtolower( $_POST['http_auth'] ) ) );
223
- else
224
- update_option('ld_http_auth', 'none' );
225
-
226
- if ( ! isset( $_POST['hide_wp_admin'] ) )
227
- {
228
- update_option('ld_hide_wp_admin', 'nope');
229
- }
230
- else
231
- {
232
- if ( $_POST['hide_wp_admin'] === 'yep' )
233
- update_option('ld_hide_wp_admin', 'yep');
234
- else
235
- update_option('ld_hide_wp_admin', 'nope');
236
- }
237
-
238
- if ( isset( $_POST['login_base'] ) )
239
- {
240
- $base = sanitize_title_with_dashes( $_POST['login_base']);
241
- $base = str_replace('/', '', $base);
242
-
243
- $disallowed = array(
244
- 'user', 'wp-admin', 'wp-content', 'wp-includes', 'wp-feed.php', 'index', 'feed', 'rss', 'robots', 'robots.txt', 'wp-login.php',
245
- 'wp-login', 'wp-config', 'blog', 'sitemap', 'sitemap.xml',
246
- );
247
- if ( in_array( $base, $disallowed ) )
248
- {
249
- return define('LD_DIS_BASE', TRUE);
250
- }
251
- else
252
- {
253
-
254
- update_option('ld_login_base', $base);
255
- $this->login_base = sanitize_title_with_dashes ( $base );
256
- }
257
- }
258
-
259
- // Redirect
260
- define('LD_WP_ADMIN', TRUE);
261
- return;
262
- }
263
-
264
- /**
265
- * Send headers to the browser that are going to ask for a username/pass
266
- * from the browser.
267
  *
268
- * @access private
269
  * @return void
270
- **/
271
- private function inauth_headers()
272
  {
273
- // Disable if there is a text file there.
274
- if ( file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'disable_auth.txt'))
275
- return;
276
-
277
- header('WWW-Authenticate: Basic realm="'.$this->relm.'"');
278
- header('HTTP/1.0 401 Unauthorized');
279
- echo '<h1>Authorization Required.</h1>';
280
- exit;
 
 
281
  }
282
 
283
  /**
284
  * Get the users for the private creds
285
  *
286
- * @access private
287
  **/
288
  public function get_private_users()
289
  {
290
- $opt = get_option('ld_private_users');
291
- if ( !is_array( $opt ) )
292
- return array();
293
-
294
- return $opt;
295
- }
296
-
297
- /**
298
- * Setup hiding wp-admin
299
- **/
300
- protected function setup_hide_admin()
301
- {
302
- $opt = get_option('ld_hide_wp_admin');
303
-
304
- // Nope, they didn't enable it.
305
- if ( $opt !== 'yep' ) return;
306
-
307
- // We're gonna hide it.
308
- $no_check_files = array('async-upload.php', 'admin-ajax.php', 'wp-app.php');
309
- $no_check_files = apply_filters('no_check_files', $no_check_files);
310
-
311
- $script_filename = empty($_SERVER['SCRIPT_FILENAME'])
312
- ? $_SERVER['PATH_TRANSLATED']
313
- : $_SERVER['SCRIPT_FILENAME'];
314
- $explode = explode('/', $script_filename);
315
- $file = end( $explode );
316
-
317
- // Disable for WP-CLI
318
- if ( defined('WP_CLI') AND WP_CLI )
319
- return $this->passed(true);
320
-
321
- if ( in_array( $file, $no_check_files ) )
322
- return $this->passed(true);
323
-
324
- // We only will hide it if we are in admin (/wp-admin/)
325
- if ( is_admin() )
326
- {
327
- // Non logged in users.
328
- if ( ! is_user_logged_in() )
329
- $this->throw_404();
330
-
331
- // Setup HTTP auth.
332
- $this->setup_http_area();
333
- }
334
- }
335
-
336
- /**
337
- * Get the current file name
338
- *
339
- * @return string JUST the file name
340
- **/
341
- public function get_file()
342
- {
343
- // We're gonna hide it.
344
- $no_check_files = array('async-upload.php');
345
- $no_check_files = apply_filters('no_check_files', $no_check_files);
346
-
347
- $script_filename = empty($_SERVER['SCRIPT_FILENAME'])
348
- ? $_SERVER['PATH_TRANSLATED']
349
- : $_SERVER['SCRIPT_FILENAME'];
350
- $explode = explode('/', $script_filename );
351
- return end( $explode );
352
- }
353
-
354
- /**
355
- * Setting up the HTTP Auth
356
- * Here, we only check if it's enabled
357
- *
358
- * @access protected
359
- **/
360
- protected function setup_http_area()
361
- {
362
- // We save what type of auth we're doing here.
363
- $opt = get_option('ld_http_auth');
364
-
365
- // What type of auth are we doing?
366
- switch( $opt )
367
- {
368
- // HTTP auth is going to ask for their WordPress creds.
369
- case 'wp_creds' :
370
- $creds = $this->get_http_auth_creds();
371
- if (! $creds )
372
- $this->inauth_headers(); // Invalid credentials
373
-
374
- // Are they already logged in as this?
375
- $current_uid = get_current_user_id();
376
-
377
- // We fixed this for use with non WP-MS sites
378
- $requested_user = get_user_by('login', $creds['username']);
379
-
380
- // Not a valid user.
381
- if (! $requested_user )
382
- $this->inauth_headers();
383
-
384
- // The correct User ID.
385
- $requested_uid = (int) $requested_user->ID;
386
-
387
- // Already logged in?
388
- if ( $current_uid === $requested_uid )
389
- return $this->passed(true);
390
-
391
- // Attempt to sign them in if they aren't already
392
- if (! is_user_logged_in() ) :
393
- // Try it via wp_signon
394
- $creds = array();
395
- $creds['user_login'] = $creds['username'];
396
- $creds['user_password'] = $creds['password'];
397
- $creds['remember'] = true;
398
- $user = wp_signon( $creds, false );
399
-
400
- // In error
401
- if ( is_wp_error($user) )
402
- $this->inauth_headers();
403
- endif;
404
-
405
- // They passed!
406
- $this->passed(true);
407
- break;
408
-
409
- // Private list of users to check
410
- case 'private' :
411
- $users = $this->get_private_users();
412
-
413
- // We want a user to exist.
414
- // If nobody is found, we won't lock them out!
415
- if ( ! $users || ! is_array( $users ) )
416
- return;
417
-
418
- // Let's NOT lock everybody out
419
- if ( count( $users ) < 1 )
420
- return;
421
-
422
- // Get the HTTP auth creds
423
- $creds = $this->get_http_auth_creds();
424
-
425
- // Invalid creds
426
- if (! $creds )
427
- $this->inauth_headers();
428
-
429
- // Did they enter a valid user?
430
- if ( $this->user_array_check( $users, $creds['username'], $creds['password'] ) )
431
- {
432
- $this->passed(true);
433
- $this->set_current_user( $users, $creds['username'] );
434
- return;
435
- }
436
- else
437
- {
438
- return $this->inauth_headers();
439
- }
440
-
441
- break;
442
-
443
- // Unknown type of auth
444
- default :
445
- return FALSE;
446
- }
447
-
448
- }
449
- /**
450
- * Check an internal array of users against a passed user and pass
451
- *
452
- * @access protected
453
- * @return bool
454
- *
455
- * @param array $array The array of users
456
- * @param string $user The username to check for
457
- * @param string $pass The password to check for (plain text)
458
- **/
459
- protected function user_array_check( $array, $user, $pass )
460
- {
461
- foreach( $array as $key => $val )
462
- {
463
- if ( $val['user'] === $user && md5( $pass ) === $val['pass'] )
464
- return TRUE;
465
- }
466
-
467
- return FALSE;
468
- }
469
-
470
- /**
471
- * See if a user exists in the array
472
- *
473
- * @access protected
474
- * @return boolean
475
- * @param array Array of users
476
- * @param string
477
- */
478
- protected function user_exists($array, $user)
479
- {
480
- if (count($array) == 0) return FALSE;
481
-
482
- foreach ($array as $k => $v) :
483
- if ($v['user'] == $user)
484
- return TRUE;
485
- endforeach;
486
-
487
- return FALSE;
488
  }
489
 
490
  /**
491
  * Set the current user
492
  *
493
- * @access private
494
- * @param array
495
- * @param integer
496
  **/
497
  protected function set_current_user( $array, $user )
498
  {
499
- foreach( $array as $key => $val )
500
- {
501
- if ( $val['user'] === $user )
502
- $this->current_user = $key;
503
- }
504
- }
505
-
506
- /**
507
- * Adds the admin menu
508
- *
509
- * @acces private
510
- **/
511
- public function add_admin_menu()
512
- {
513
- add_menu_page('Lockdown WP', 'Lockdown WP', 'manage_options', 'lockdown-wp-admin', array( $this, 'admin_callback'));
514
- add_submenu_page( 'lockdown-wp-admin', 'Private Users', 'Private Users', 'manage_options', 'lockdown-private-users', array( $this, 'sub_admin_callback'));
515
- }
516
-
517
- /**
518
- * The callback for the admin area
519
- *
520
- * You need the 'manage_options' capability to get here.
521
- **/
522
- public function admin_callback()
523
- {
524
- // Update the options
525
- $this->update_options();
526
-
527
- // The UI
528
- require_once( dirname( __FILE__ ) . '/admin.php' );
529
- }
530
-
531
- /**
532
- * The callback for ther private users management.
533
- *
534
- * You need the 'manage_options' capability to get here.
535
- **/
536
- public function sub_admin_callback()
537
- {
538
- // Update the users options
539
- $this->update_users();
540
-
541
- // The UI
542
- $private_users = $this->get_private_users();
543
- require_once( dirname( __FILE__ ) . '/admin-private-users.php' );
544
- }
545
-
546
- /**
547
- * Rename the login URL
548
- *
549
- * @access public
550
- **/
551
- public function redo_login_form()
552
- {
553
- $login_base = get_option('ld_login_base');
554
-
555
- // It's not enabled.
556
- if ( $login_base == NULL || ! $login_base || $login_base == '' )
557
- return;
558
-
559
- $this->login_base = $login_base;
560
- unset( $login_base );
561
-
562
- // Setup the filters for the new login form
563
- add_filter('wp_redirect', array( &$this, 'filter_wp_login'));
564
- add_filter('network_site_url', array( &$this, 'filter_wp_login'));
565
- add_filter('site_url', array( &$this, 'filter_wp_login'));
566
-
567
- // We need to get the URL
568
- // This means we need to take the current URL,
569
- // strip it of an WordPress path (if the blog is located @ /blog/)
570
- // And then remove the query string
571
- // We also need to remove the index.php from the URL if it exists
572
-
573
- // The blog's URL
574
- $blog_url = trailingslashit( get_bloginfo('url') );
575
-
576
- // The Current URL
577
- $schema = is_ssl() ? 'https://' : 'http://';
578
- $current_url = $schema . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
579
-
580
- $request_url = str_replace( $blog_url, '', $current_url );
581
- $request_url = str_replace('index.php/', '', $request_url);
582
-
583
- $url_parts = explode( '?', $request_url, 2 );
584
- $base = $url_parts[0];
585
-
586
- // Remove trailing slash
587
- $base = rtrim($base,"/");
588
- $exp = explode( '/', $base, 2 );
589
- $super_base = end( $exp );
590
-
591
- // Are they visiting wp-login.php?
592
- if ( $super_base == 'wp-login.php')
593
- $this->throw_404();
594
-
595
- // Is this the "login" url?
596
- if ( $base !== $this->login_base )
597
- return FALSE;
598
-
599
- // We dont' want a WP plugin caching this page
600
- @define('NO_CACHE', TRUE);
601
- @define('WTC_IN_MINIFY', TRUE);
602
- @define('WP_CACHE', FALSE);
603
-
604
- // Hook onto this
605
- do_action('ld_login_page');
606
-
607
- include ABSPATH . '/wp-login.php';
608
- exit;
609
- }
610
-
611
- /**
612
- * Filters out wp-login to whatever they named it
613
- *
614
- * @access public
615
- **/
616
- public function filter_wp_login( $str )
617
- {
618
- return str_replace('wp-login.php', $this->login_base, $str);
619
- }
620
-
621
- /**
622
- * Launch and display the 404 page depending upon the template
623
- *
624
- * @param void
625
- * @return void
626
- **/
627
- public function throw_404()
628
- {
629
- // Change WP Query
630
- global $wp_query;
631
- $wp_query->set_404();
632
- status_header(404);
633
-
634
- // Disable that pesky Admin Bar
635
- add_filter('show_admin_bar', '__return_false', 900);
636
- remove_action( 'admin_footer', 'wp_admin_bar_render', 10);
637
- remove_action('wp_head', 'wp_admin_bar_header', 10);
638
- remove_action('wp_head', '_admin_bar_bump_cb', 10);
639
- wp_dequeue_script( 'admin-bar' );
640
- wp_dequeue_style( 'admin-bar' );
641
-
642
- // Template
643
- $four_tpl = apply_filters('LD_404', get_404_template());
644
-
645
- // Handle the admin bar
646
- @define('APP_REQUEST', TRUE);
647
- @define('DOING_AJAX', TRUE);
648
-
649
- if ( empty($four_tpl) OR ! file_exists($four_tpl) )
650
- {
651
- // We're gonna try and get TwentyTen's one
652
- $twenty_ten_tpl = apply_filters('LD_404_FALLBACK', WP_CONTENT_DIR . '/themes/twentythirteen/404.php');
653
-
654
- if (file_exists($twenty_ten_tpl))
655
- require($twenty_ten_tpl);
656
- else
657
- wp_die('404 - File not found!', '', array('response' => 404));
658
- }
659
- else
660
- {
661
- // Their theme has a template!
662
- require( $four_tpl );
663
- }
664
-
665
- // Either way, it's gonna stop right here.
666
- exit;
667
- }
668
-
669
- /**
670
- * See if a login base is suggested against
671
- *
672
- * @return boolean
673
- */
674
- public function isSuggestedAgainst()
675
- {
676
- return (in_array($this->login_base, array(
677
- 'login',
678
- 'admin',
679
- 'user-login',
680
- )));
681
  }
682
 
683
  /**
684
  * Retrieve the Login Base
685
- *
686
  * @return string
687
  */
688
  public function getLoginBase()
689
  {
690
- return $this->login_base;
691
  }
692
 
693
  /**
@@ -695,16 +122,19 @@ class WP_LockAuth
695
  *
696
  * @return boolean
697
  */
698
- public function getAuthPassed() { return $this->passed; }
 
 
 
699
 
700
  /**
701
  * Update the Passed Auth Value
702
  * See {@link WP_LockAuth::getAuthPassed()}
703
  *
704
- * @access protected
705
  * @param boolean
706
  */
707
- protected function passed($value)
708
  {
709
  $this->passed = (bool) $value;
710
  }
@@ -728,7 +158,7 @@ function ld_setup_auth()
728
  return $auth_obj;
729
  }
730
 
731
- add_action('init', 'ld_setup_auth');
732
 
733
  /* End of file: lockdown-wp-admin.php */
734
  /* Code is poetry. */
4
  Plugin URI: http://seanfisher.co/lockdown-wp-admin/
5
  Donate link: http://seanfisher.co/donate/
6
  Description: Securing the WordPress Administration interface by concealing the administration dashboard and changing the login page URL.
7
+ Version: 2.2
8
  Author: Sean Fisher
9
  Author URI: http://seanfisher.co/
10
  License: GPL
12
 
13
  // This file name
14
  define('LD_FILE_NAME', __FILE__ );
15
+ define('LD_PLUGIN_DIR', dirname(__FILE__));
16
 
17
  /**
18
  * This is the plugin that will add security to our site
29
  * @global string
30
  * @access private
31
  **/
32
+ public static $ld_admin_version = '2.2';
33
 
34
  /**
35
  * The HTTP Auth name for the protected area
36
  * Change this via calling the object, not by editing the file.
37
  *
38
  * @access public
39
+ * @type string
40
  **/
41
  public $relm = 'Secure Area';
42
 
47
  **/
48
  protected $current_user = FALSE;
49
 
 
 
 
 
 
 
 
50
  /**
51
  * Check if the Auth passed
 
52
  * See {@link WP_LockAuth::getAuthPassed()}
53
  *
54
+ * @type boolean
55
  */
56
  protected $passed = FALSE;
57
 
58
  /**
59
+ * Admin Instance
60
  *
61
+ * @type Lockdown_Admin
62
  */
63
+ public $admin;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  /**
66
+ * Application Instance
67
  *
68
+ * @type Lockdown_Application
69
+ */
70
+ public $application;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  /**
73
+ * Constructor
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  *
 
75
  * @return void
76
+ */
77
+ public function __construct()
78
  {
79
+ // We don't like adding network wide WordPress plugins.
80
+ if (! class_exists('Disable_WPMS_Plugin_LD'))
81
+ require_once( dirname( __FILE__ ) .'/no-wpmu.php' );
82
+
83
+ require_once(LD_PLUGIN_DIR.'/src/Lockdown/Application.php');
84
+ require_once(LD_PLUGIN_DIR.'/src/Lockdown/Admin.php');
85
+
86
+ // Instantiate objects
87
+ $this->admin = new Lockdown_Admin($this);
88
+ $this->application = new Lockdown_Application($this);
89
  }
90
 
91
  /**
92
  * Get the users for the private creds
93
  *
94
+ * @deprecated Moved to `Lockdown_Application::getPrivateUsers()`
95
  **/
96
  public function get_private_users()
97
  {
98
+ return $this->application->getPrivateUsers();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
 
101
  /**
102
  * Set the current user
103
  *
104
+ * @deprecated Moved to {@see Lockdown_Application::setUser()}
 
 
105
  **/
106
  protected function set_current_user( $array, $user )
107
  {
108
+ return $this->application->setUser($array, $user);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  }
110
 
111
  /**
112
  * Retrieve the Login Base
 
113
  * @return string
114
  */
115
  public function getLoginBase()
116
  {
117
+ return $this->application->getLoginBase();
118
  }
119
 
120
  /**
122
  *
123
  * @return boolean
124
  */
125
+ public function getAuthPassed()
126
+ {
127
+ return (bool) $this->passed;
128
+ }
129
 
130
  /**
131
  * Update the Passed Auth Value
132
  * See {@link WP_LockAuth::getAuthPassed()}
133
  *
134
+ * @access public
135
  * @param boolean
136
  */
137
+ public function passed($value)
138
  {
139
  $this->passed = (bool) $value;
140
  }
158
  return $auth_obj;
159
  }
160
 
161
+ add_action('init', 'ld_setup_auth', 20);
162
 
163
  /* End of file: lockdown-wp-admin.php */
164
  /* Code is poetry. */
no-wpmu.php CHANGED
@@ -1,4 +1,4 @@
1
- <?php if (! defined('ABSPATH')) exit;
2
  /**
3
  * We don't want to allow for this plugin to be used in WP-MS or network wide.
4
  *
@@ -84,6 +84,6 @@ class Disable_WPMS_Plugin_LD
84
  }
85
 
86
  // The object.
87
- $setup_no_wpmu = new Disable_WPMS_Plugin_LD;
88
 
89
  /* End of file: no-wpmu.php */
1
+ <?php
2
  /**
3
  * We don't want to allow for this plugin to be used in WP-MS or network wide.
4
  *
84
  }
85
 
86
  // The object.
87
+ new Disable_WPMS_Plugin_LD;
88
 
89
  /* End of file: no-wpmu.php */
phpunit.xml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <phpunit
2
+ bootstrap="tests/bootstrap.php"
3
+ backupGlobals="false"
4
+ colors="true"
5
+ convertErrorsToExceptions="true"
6
+ convertNoticesToExceptions="true"
7
+ convertWarningsToExceptions="true"
8
+ >
9
+ <testsuites>
10
+ <testsuite>
11
+ <directory prefix="test-" suffix=".php">./tests/</directory>
12
+ </testsuite>
13
+ </testsuites>
14
+ </phpunit>
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: sean212
3
  Donate link: http://seanfisher.co/donate/
4
  Link: http://seanfisher.co/lockdown-wp-admin/
5
  Tags: security, secure, lockdown, vulnerability, website security, wp-admin, login, hide login, rename login, http auth, 404, lockdown, srtfisher, secure
6
- Requires at least: 3.3
7
- Tested up to: 3.6
8
- Stable tag: 2.1
9
 
10
  Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc.
11
 
@@ -102,3 +102,8 @@ A very late update, sorry! Worked to fix many issues with the admin bar and the
102
  * Unit Testing! Unit Testing ensure more reliable code going forward
103
  * Support for WordPress 3.6
104
  * General Cleaning
 
 
 
 
 
3
  Donate link: http://seanfisher.co/donate/
4
  Link: http://seanfisher.co/lockdown-wp-admin/
5
  Tags: security, secure, lockdown, vulnerability, website security, wp-admin, login, hide login, rename login, http auth, 404, lockdown, srtfisher, secure
6
+ Requires at least: 3.6
7
+ Tested up to: 3.8
8
+ Stable tag: 2.2
9
 
10
  Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc.
11
 
102
  * Unit Testing! Unit Testing ensure more reliable code going forward
103
  * Support for WordPress 3.6
104
  * General Cleaning
105
+
106
+ = 2.2 =
107
+ * Fixing issues with other plugins
108
+ * Support tested for 3.9
109
+ * Large code structure changes. If you are extending the `WP_LockAuth` at all, you should basically check the class anew since it was seperated into Admin and Application services.
src/Lockdown/Admin.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Lockdown_Admin {
3
+ /**
4
+ * Main Instance Storage
5
+ *
6
+ * @var WP_LockAuth
7
+ */
8
+ protected $instance;
9
+
10
+ /**
11
+ * Admin Constructor
12
+ *
13
+ * @param WP_LockAuth
14
+ */
15
+ public function __construct(WP_LockAuth $instance)
16
+ {
17
+ $this->instance = $instance;
18
+
19
+ // Add the action to setup the menu.
20
+ add_action('admin_menu', array( $this, 'add_admin_menu'));
21
+ }
22
+
23
+ /**
24
+ * Adds the admin menu
25
+ *
26
+ * @acces private
27
+ **/
28
+ public function add_admin_menu()
29
+ {
30
+ add_menu_page('Lockdown WP', 'Lockdown WP', 'manage_options', 'lockdown-wp-admin', array( $this, 'admin_callback'));
31
+ add_submenu_page( 'lockdown-wp-admin', 'Private Users', 'Private Users', 'manage_options', 'lockdown-private-users', array( $this, 'sub_admin_callback'));
32
+ }
33
+
34
+ /**
35
+ * The callback for the admin area
36
+ *
37
+ * You need the 'manage_options' capability to get here.
38
+ **/
39
+ public function admin_callback()
40
+ {
41
+ // Update the options
42
+ $this->updateSettings();
43
+
44
+ // The UI
45
+ require_once( LD_PLUGIN_DIR . '/views/settings.php' );
46
+ }
47
+
48
+ /**
49
+ * The callback for ther private users management.
50
+ *
51
+ * You need the 'manage_options' capability to get here.
52
+ **/
53
+ public function sub_admin_callback()
54
+ {
55
+ // Update the users options
56
+ $this->update_users();
57
+
58
+ // The UI
59
+ $private_users = $this->instance->application->getPrivateUsers();
60
+ require_once( LD_PLUGIN_DIR . '/admin-private-users.php' );
61
+ }
62
+
63
+ /**
64
+ * Update the options
65
+ *
66
+ * @access private
67
+ **/
68
+ public function updateSettings()
69
+ {
70
+ if ( !isset( $_GET['page'] ) || $_GET['page'] !== 'lockdown-wp-admin' || !isset( $_POST['did_update'] ))
71
+ return;
72
+
73
+ // Nonce
74
+ $nonce = $_POST['_wpnonce'];
75
+ if ( ! wp_verify_nonce($nonce, 'lockdown-wp-admin') )
76
+ wp_die('Security error, please try again.');
77
+
78
+ // ---------------------------------------------------
79
+ // They're updating.
80
+ // ---------------------------------------------------
81
+ if ( isset( $_POST['http_auth'] ) )
82
+ update_option('ld_http_auth', trim( strtolower( $_POST['http_auth'] ) ) );
83
+ else
84
+ update_option('ld_http_auth', 'none' );
85
+
86
+ if ( ! isset( $_POST['hide_wp_admin'] ) )
87
+ {
88
+ update_option('ld_hide_wp_admin', 'nope');
89
+ }
90
+ else
91
+ {
92
+ if ( $_POST['hide_wp_admin'] === 'yep' )
93
+ update_option('ld_hide_wp_admin', 'yep');
94
+ else
95
+ update_option('ld_hide_wp_admin', 'nope');
96
+ }
97
+
98
+ if ( isset( $_POST['login_base'] ) )
99
+ {
100
+ $base = sanitize_title_with_dashes( $_POST['login_base']);
101
+ $base = str_replace('/', '', $base);
102
+
103
+ $disallowed = array(
104
+ 'user', 'wp-admin', 'wp-content', 'wp-includes', 'wp-feed.php', 'index', 'feed', 'rss', 'robots', 'robots.txt', 'wp-login.php',
105
+ 'wp-login', 'wp-config', 'blog', 'sitemap', 'sitemap.xml',
106
+ );
107
+ if ( in_array( $base, $disallowed ) )
108
+ {
109
+ return define('LD_DIS_BASE', TRUE);
110
+ }
111
+ else
112
+ {
113
+
114
+ update_option('ld_login_base', $base);
115
+ $this->instance->application->setLoginBase(sanitize_title_with_dashes ( $base ));
116
+ }
117
+ }
118
+
119
+ // Redirect
120
+ return define('LD_WP_ADMIN', TRUE);
121
+ }
122
+
123
+ /**
124
+ * Update the users
125
+ *
126
+ * @access private
127
+ **/
128
+ public function update_users()
129
+ {
130
+ if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'lockdown-private-users')
131
+ return;
132
+
133
+ // Nonce
134
+ if ( ! isset( $_REQUEST['_wpnonce'] ) )
135
+ return;
136
+
137
+ $nonce = $_REQUEST['_wpnonce'];
138
+ if ( ! wp_verify_nonce( $nonce, 'lockdown-wp-admin' ) )
139
+ wp_die('Security error, please try again.');
140
+
141
+ // Add a user
142
+ if ( isset( $_POST['private_username'] ) && isset( $_POST['private_password'] ) )
143
+ {
144
+ if ( $_POST['private_username'] !== '' && $_POST['private_password'] !== '' )
145
+ {
146
+ // Adding a user
147
+ $users = $this->instance->application->getPrivateUsers();
148
+ $add['user'] = sanitize_user( $_POST['private_username'] );
149
+ $add['pass'] = trim( md5( $_POST['private_password'] ) );
150
+
151
+ // See if it exists
152
+ if ($this->instance->application->userExists($users, $add['user']))
153
+ return define('LD_ERROR', 'username-exists');
154
+ else
155
+ $users[] = $add;
156
+
157
+ update_option('ld_private_users', $users);
158
+
159
+ return define('LD_WP_ADMIN', TRUE);
160
+ }
161
+ }
162
+
163
+ // Deleting a user.
164
+ if ( isset( $_GET['delete'] ) )
165
+ {
166
+ // Delete the user.
167
+ unset( $users );
168
+ $users = $this->instance->application->getPrivateUsers();
169
+ $to_delete = (int) $_GET['delete'];
170
+
171
+ if ( count( $users ) > 0 )
172
+ {
173
+ foreach( $users as $key => $val )
174
+ {
175
+ if ( $key === $to_delete ) :
176
+ if( $this->current_user !== '' && $to_delete === $this->current_user )
177
+ {
178
+ // They can't delete themselves!
179
+ return define('LD_ERROR', 'delete-self');
180
+ }
181
+
182
+ unset( $users[$key] );
183
+ endif;
184
+ }
185
+ }
186
+
187
+ update_option('ld_private_users', $users);
188
+
189
+ define('LD_WP_ADMIN', TRUE);
190
+ return;
191
+ }
192
+ }
193
+ }
src/Lockdown/Application.php ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Lockdown_Application {
3
+ /**
4
+ * Main Instance Storage
5
+ *
6
+ * @var WP_LockAuth
7
+ */
8
+ protected $instance;
9
+
10
+ /**
11
+ * The base to get the login url
12
+ *
13
+ * @access private
14
+ **/
15
+ protected $login_base;
16
+
17
+ /**
18
+ * Admin Constructor
19
+ *
20
+ * @param WP_LockAuth
21
+ */
22
+ public function __construct(WP_LockAuth $instance)
23
+ {
24
+ $this->instance = $instance;
25
+
26
+ // Setup the plugin.
27
+ $this->ininitializeConceal();
28
+
29
+ // Hide the login form
30
+ $this->renameLogin();
31
+ }
32
+
33
+ /**
34
+ * Setup hiding wp-admin
35
+ */
36
+ protected function ininitializeConceal()
37
+ {
38
+ $opt = get_option('ld_hide_wp_admin');
39
+
40
+ // Nope, they didn't enable it.
41
+ if ( $opt !== 'yep' ) return;
42
+
43
+ // We're gonna hide it.
44
+ $no_check_files = array('async-upload.php', 'admin-ajax.php', 'wp-app.php');
45
+ $no_check_files = apply_filters('no_check_files', $no_check_files);
46
+
47
+ $script_filename = empty($_SERVER['SCRIPT_FILENAME'])
48
+ ? $_SERVER['PATH_TRANSLATED']
49
+ : $_SERVER['SCRIPT_FILENAME'];
50
+ $explode = explode('/', $script_filename);
51
+ $file = end( $explode );
52
+
53
+ // Disable for WP-CLI
54
+ if ( defined('WP_CLI') AND WP_CLI )
55
+ return $this->instance->passed(true);
56
+
57
+ if ( in_array( $file, $no_check_files ) )
58
+ return $this->instance->passed(true);
59
+
60
+ // We only will hide it if we are in admin (/wp-admin/)
61
+ if ( is_admin() )
62
+ {
63
+ // Non logged in users.
64
+ if ( ! is_user_logged_in() )
65
+ $this->throw404();
66
+
67
+ // Setup HTTP auth.
68
+ $this->setupHttpCheck();
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Launch and display the 404 page depending upon the template
74
+ *
75
+ * @param void
76
+ * @return void
77
+ **/
78
+ public function throw404()
79
+ {
80
+ // Change WP Query
81
+ global $wp_query;
82
+ $wp_query->set_404();
83
+ status_header(404);
84
+
85
+ // Disable that pesky Admin Bar
86
+ add_filter('show_admin_bar', '__return_false', 900);
87
+ remove_action( 'admin_footer', 'wp_admin_bar_render', 10);
88
+ remove_action('wp_head', 'wp_admin_bar_header', 10);
89
+ remove_action('wp_head', '_admin_bar_bump_cb', 10);
90
+ wp_dequeue_script( 'admin-bar' );
91
+ wp_dequeue_style( 'admin-bar' );
92
+
93
+ // Template
94
+ $four_tpl = apply_filters('LD_404', get_404_template());
95
+
96
+ // Handle the admin bar
97
+ @define('APP_REQUEST', TRUE);
98
+ @define('DOING_AJAX', TRUE);
99
+
100
+ if ( empty($four_tpl) OR ! file_exists($four_tpl) )
101
+ {
102
+ // We're gonna try and get TwentyTen's one
103
+ $twenty_ten_tpl = apply_filters('LD_404_FALLBACK', WP_CONTENT_DIR . '/themes/twentyfourteen/404.php');
104
+
105
+ if (file_exists($twenty_ten_tpl))
106
+ require($twenty_ten_tpl);
107
+ else
108
+ wp_die('404 - File not found!', '', array('response' => 404));
109
+ }
110
+ else
111
+ {
112
+ // Their theme has a template!
113
+ require( $four_tpl );
114
+ }
115
+
116
+ // Either way, it's gonna stop right here.
117
+ exit;
118
+ }
119
+
120
+
121
+
122
+ /**
123
+ * Rename the login URL
124
+ *
125
+ *
126
+ * @see do_action() Calls `ld_login_page` right before we call `wp-login.php`
127
+ * @access public
128
+ **/
129
+ public function renameLogin()
130
+ {
131
+ $this->login_base = get_option('ld_login_base');
132
+
133
+ // It's not enabled.
134
+ if ( $this->login_base == NULL || ! $this->login_base || $this->login_base == '' )
135
+ return;
136
+
137
+ // Setup the filters for the new login form
138
+ add_filter('wp_redirect', array( &$this, 'filterWpLogin'));
139
+ add_filter('network_site_url', array( &$this, 'filterWpLogin'));
140
+ add_filter('site_url', array( &$this, 'filterWpLogin'));
141
+
142
+ // We need to get the URL
143
+ // This means we need to take the current URL,
144
+ // strip it of an WordPress path (if the blog is located @ /blog/)
145
+ // And then remove the query string
146
+ // We also need to remove the index.php from the URL if it exists
147
+
148
+ // The blog's URL
149
+ $blog_url = trailingslashit( get_bloginfo('url') );
150
+
151
+ // The Current URL
152
+ $schema = is_ssl() ? 'https://' : 'http://';
153
+ $current_url = $schema . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
154
+
155
+ $request_url = str_replace( $blog_url, '', $current_url );
156
+ $request_url = str_replace('index.php/', '', $request_url);
157
+
158
+ $url_parts = explode( '?', $request_url, 2 );
159
+ $base = $url_parts[0];
160
+
161
+ // Remove trailing slash
162
+ $base = rtrim($base,"/");
163
+ $exp = explode( '/', $base, 2 );
164
+ $super_base = end( $exp );
165
+
166
+ // Are they visiting wp-login.php?
167
+ if ( $super_base == 'wp-login.php')
168
+ $this->throw404();
169
+
170
+ // Is this the "login" url?
171
+ if ( $base !== $this->getLoginBase() )
172
+ return FALSE;
173
+
174
+ // We dont' want a WP plugin caching this page
175
+ @define('NO_CACHE', TRUE);
176
+ @define('WTC_IN_MINIFY', TRUE);
177
+ @define('WP_CACHE', FALSE);
178
+
179
+ // Hook onto this
180
+ do_action('ld_login_page');
181
+
182
+ include ABSPATH . '/wp-login.php';
183
+ exit;
184
+ }
185
+
186
+ /**
187
+ * Filters out wp-login to whatever they named it
188
+ *
189
+ * @access public
190
+ **/
191
+ public function filterWpLogin( $str )
192
+ {
193
+ return str_replace('wp-login.php', $this->getLoginBase(), $str);
194
+ }
195
+
196
+ /**
197
+ * Setting up the HTTP Auth
198
+ * Here, we only check if it's enabled
199
+ *
200
+ * @access protected
201
+ **/
202
+ protected function setupHttpCheck($option = NULL)
203
+ {
204
+ // We save what type of auth we're doing here.
205
+ if (! $option)
206
+ $option = get_option('ld_http_auth');
207
+
208
+ // What type of auth are we doing?
209
+ switch( $option )
210
+ {
211
+ // HTTP auth is going to ask for their WordPress creds.
212
+ case 'wp_creds' :
213
+ $creds = $this->retrieveAuthCredentials();
214
+
215
+ if (! $creds )
216
+ $this->unauthorizedArea(); // Invalid credentials
217
+
218
+ // Are they already logged in as this?
219
+ $current_uid = get_current_user_id();
220
+
221
+ // We fixed this for use with non WP-MS sites
222
+ $requested_user = get_user_by('login', $creds['username']);
223
+
224
+ // Not a valid user.
225
+ if (! $requested_user )
226
+ $this->unauthorizedArea();
227
+
228
+ // The correct User ID.
229
+ $requested_uid = (int) $requested_user->ID;
230
+
231
+ // Already logged in?
232
+ if ( $current_uid === $requested_uid )
233
+ return $this->instance->passed(true);
234
+
235
+ // Attempt to sign them in if they aren't already
236
+ if (! is_user_logged_in() ) :
237
+ // Try it via wp_signon
238
+ $creds = array();
239
+ $creds['user_login'] = $creds['username'];
240
+ $creds['user_password'] = $creds['password'];
241
+ $creds['remember'] = true;
242
+ $user = wp_signon( $creds, false );
243
+
244
+ // In error
245
+ if ( is_wp_error($user) )
246
+ return $this->unauthorizedArea();
247
+ endif;
248
+
249
+ // They passed!
250
+ $this->passed(true);
251
+ break;
252
+
253
+ // Private list of users to check
254
+ case 'private' :
255
+ $users = $this->getPrivateUsers();
256
+
257
+ // We want a user to exist.
258
+ // If nobody is found, we won't lock them out!
259
+ if ( ! $users || ! is_array( $users ) )
260
+ return;
261
+
262
+ // Let's NOT lock everybody out
263
+ if ( count( $users ) < 1 )
264
+ return;
265
+
266
+ // Get the HTTP auth creds
267
+ $creds = $this->retrieveAuthCredentials();
268
+
269
+ // Invalid creds
270
+ if (! $creds )
271
+ $this->unauthorizedArea();
272
+
273
+ // Did they enter a valid user?
274
+ if ( $this->matchUserToArray( $users, $creds['username'], $creds['password'] ) )
275
+ {
276
+ $this->instance->passed(true);
277
+ return $this->setUser( $users, $creds['username'] );
278
+ }
279
+ else
280
+ {
281
+ return $this->unauthorizedArea();
282
+ }
283
+
284
+ break;
285
+
286
+ // Unknown type of auth
287
+ default :
288
+ $this->instance->passed(true);
289
+ return FALSE;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Send headers to the browser that are going to ask for a username/pass
295
+ * from the browser.
296
+ *
297
+ * @access private
298
+ * @return void
299
+ **/
300
+ protected function unauthorizedArea()
301
+ {
302
+ // Disable if there is a text file there.
303
+ if ( file_exists(LD_PLUGIN_DIR.'/disable_auth.txt'))
304
+ return;
305
+
306
+ header('WWW-Authenticate: Basic realm="'. $this->instance->relm.'"');
307
+ header('HTTP/1.0 401 Unauthorized');
308
+ echo '<h1>Authorization Required.</h1>';
309
+ exit;
310
+ }
311
+
312
+ /**
313
+ * Set the current user
314
+ *
315
+ * @access private
316
+ * @param array
317
+ * @param integer
318
+ **/
319
+ public function setUser( $array, $user )
320
+ {
321
+ foreach( $array as $key => $val )
322
+ {
323
+ if ( $val['user'] === $user )
324
+ $this->current_user = $key;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Get the current file name
330
+ *
331
+ * @return string JUST the file name
332
+ **/
333
+ public function retrieveFile()
334
+ {
335
+ // We're gonna hide it.
336
+ $no_check_files = array('async-upload.php');
337
+ $no_check_files = apply_filters('no_check_files', $no_check_files);
338
+
339
+ $script_filename = empty($_SERVER['SCRIPT_FILENAME'])
340
+ ? $_SERVER['PATH_TRANSLATED']
341
+ : $_SERVER['SCRIPT_FILENAME'];
342
+ $explode = explode('/', $script_filename );
343
+ return end( $explode );
344
+ }
345
+
346
+ /**
347
+ * Check an internal array of users against a passed user and pass
348
+ *
349
+ * @access protected
350
+ * @return bool
351
+ *
352
+ * @param array $array The array of users
353
+ * @param string $user The username to check for
354
+ * @param string $pass The password to check for (plain text)
355
+ **/
356
+ protected function matchUserToArray( $array, $user, $pass )
357
+ {
358
+ foreach( $array as $key => $val )
359
+ {
360
+ if (! isset($val['user']) || ! isset($val['pass']))
361
+ continue;
362
+
363
+ if ( $val['user'] === $user && md5( $pass ) === $val['pass'] )
364
+ return TRUE;
365
+ }
366
+
367
+ return FALSE;
368
+ }
369
+
370
+ /**
371
+ * See if a user exists in the array
372
+ *
373
+ * @access public
374
+ * @return boolean
375
+ * @param array Array of users
376
+ * @param string
377
+ */
378
+ public function userExists($array, $user)
379
+ {
380
+ if (count($array) == 0) return FALSE;
381
+
382
+ foreach ($array as $k => $v) :
383
+ if ($v['user'] == $user)
384
+ return TRUE;
385
+ endforeach;
386
+
387
+ return FALSE;
388
+ }
389
+
390
+ /**
391
+ * See if a login base is suggested against
392
+ *
393
+ * @return boolean
394
+ */
395
+ public function isSuggestedAgainst()
396
+ {
397
+ return (in_array($this->login_base, array(
398
+ 'login',
399
+ 'admin',
400
+ 'user-login',
401
+ )));
402
+ }
403
+
404
+ /**
405
+ * Retrieve the login base
406
+ *
407
+ * @return string
408
+ * @param string Default
409
+ */
410
+ public function getLoginBase($default = '')
411
+ {
412
+ return ($this->login_base) ? $this->login_base : $default;
413
+ }
414
+
415
+ /**
416
+ * Set the login base
417
+ *
418
+ * @param string
419
+ */
420
+ public function setLoginBase($base = '')
421
+ {
422
+ $this->login_base = $base;
423
+ return $this;
424
+ }
425
+
426
+ /**
427
+ * Get a username and password from the Basic HTTP auth
428
+ *
429
+ * @return array|bool
430
+ **/
431
+ public function retrieveAuthCredentials()
432
+ {
433
+ // Since PHP saves the HTTP Password in a bunch of places, we have to be able to test for all of them
434
+ $username = $password = NULL;
435
+
436
+ // mod_php
437
+ if (isset($_SERVER['PHP_AUTH_USER']))
438
+ {
439
+ $username = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : NULL;
440
+ $password = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : NULL;
441
+ }
442
+
443
+ // most other servers
444
+ elseif ($_SERVER['HTTP_AUTHENTICATION'])
445
+ {
446
+ if (strpos(strtolower($_SERVER['HTTP_AUTHENTICATION']),'basic') === 0)
447
+ {
448
+ list($username,$password) = explode(':',base64_decode(substr($_SERVER['HTTP_AUTHENTICATION'], 6)));
449
+ }
450
+ }
451
+
452
+ // Check them - if they're null a/o empty, they're invalid.
453
+ if ( is_null($username) OR is_null($password) OR empty($username) OR empty($password))
454
+ return FALSE;
455
+ else
456
+ return array('username' => $username, 'password' => $password);
457
+ }
458
+
459
+ /**
460
+ * Get the users for the private creds
461
+ *
462
+ * @access private
463
+ **/
464
+ public function getPrivateUsers()
465
+ {
466
+ $opt = get_option('ld_private_users');
467
+ if ( !is_array( $opt ) )
468
+ return array();
469
+
470
+ return $opt;
471
+ }
472
+ }
tests/bootstrap.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $_tests_dir = getenv('WP_TESTS_DIR');
4
+ if ( !$_tests_dir ) $_tests_dir = '/tmp/wordpress-tests-lib';
5
+
6
+ require_once $_tests_dir . '/includes/functions.php';
7
+
8
+ function _manually_load_plugin() {
9
+ require dirname( __FILE__ ) . '/../lockdown-wp-admin.php';
10
+ }
11
+ tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
12
+
13
+ require $_tests_dir . '/includes/bootstrap.php';
14
+
tests/test-application.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class LockdownApplicationTest extends PHPUnit_Framework_TestCase {
3
+ protected $object;
4
+
5
+ protected function setUp()
6
+ {
7
+ $this->object = ld_setup_auth();
8
+ }
9
+
10
+ public function testMatchUserToArray()
11
+ {
12
+ $users = array(
13
+ array(
14
+ 'user' => 'admin',
15
+ 'pass' => md5('password')
16
+ ),
17
+
18
+ array(
19
+ 'user' => 'stan',
20
+ 'pass' => md5('marsh')
21
+ )
22
+ );
23
+
24
+ // Should pass
25
+ $this->assertTrue(
26
+ $this->invokeMethod($this->object->application, 'matchUserToArray', array($users, 'admin', 'password'))
27
+ );
28
+
29
+ // Both should fail
30
+ $this->assertFalse(
31
+ $this->invokeMethod($this->object->application, 'matchUserToArray', array($users, 'admin', 'notpassword'))
32
+ );
33
+
34
+ $this->assertFalse(
35
+ $this->invokeMethod($this->object->application, 'matchUserToArray', array(array(), 'admin', 'notpassword'))
36
+ );
37
+ }
38
+
39
+ public function testNoSettingsPassed()
40
+ {
41
+ $this->invokeMethod($this->object->application, 'setupHttpCheck', array(null));
42
+
43
+ $this->assertTrue($this->object->getAuthPassed());
44
+ }
45
+
46
+ /**
47
+ * Call protected/private method of a class.
48
+ *
49
+ * @param object &$object Instantiated object that we will run method on.
50
+ * @param string $methodName Method name to call
51
+ * @param array $parameters Array of parameters to pass into method.
52
+ *
53
+ * @return mixed Method return.
54
+ */
55
+ public function invokeMethod(&$object, $methodName, array $parameters = array())
56
+ {
57
+ $reflection = new \ReflectionClass(get_class($object));
58
+ $method = $reflection->getMethod($methodName);
59
+ $method->setAccessible(true);
60
+
61
+ return $method->invokeArgs($object, $parameters);
62
+ }
63
+ }
tests/test-core.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class LockdownTest extends PHPUnit_Framework_TestCase {
3
+ protected $object;
4
+
5
+ protected function setUp()
6
+ {
7
+ $this->object = ld_setup_auth();
8
+ }
9
+
10
+ public function testApplicationSetup()
11
+ {
12
+ $this->assertInstanceOf('Lockdown_Application', $this->object->application);
13
+ }
14
+
15
+ public function testAdminSetup()
16
+ {
17
+ $this->assertInstanceOf('Lockdown_Admin', $this->object->admin);
18
+ }
19
+
20
+ public function testRelm()
21
+ {
22
+ $this->assertTrue(is_string($this->object->relm));
23
+ }
24
+
25
+ /**
26
+ * Test that the application has added an action to init
27
+ */
28
+ public function testActionAdded()
29
+ {
30
+ $this->assertTrue(has_action('init'));
31
+ }
32
+
33
+ /**
34
+ * The ability to overwrite the Lockdown WP Admin Object
35
+ */
36
+ public function testOverwriteLdObject()
37
+ {
38
+ add_filter('ld_class', function() { return 'LdProxyObject'; });
39
+ $setup = ld_setup_auth();
40
+ $this->assertEquals('LdProxyObject', get_class($setup));
41
+ $this->assertEquals('WP_LockAuth', get_class($this->object));
42
+ }
43
+
44
+ public function testFiltersWithoutBase()
45
+ {
46
+ remove_all_actions('wp_redirect');
47
+ remove_all_actions('network_site_url');
48
+ remove_all_actions('site_url');
49
+
50
+ update_option('ld_login_base', null);
51
+ $this->object->application->renameLogin();
52
+
53
+ $this->assertFalse(has_action('wp_redirect'));
54
+ $this->assertFalse(has_action('network_site_url'));
55
+ $this->assertFalse(has_action('site_url'));
56
+ }
57
+
58
+ public function testFiltersWithBase()
59
+ {
60
+ remove_all_actions('wp_redirect');
61
+ remove_all_actions('network_site_url');
62
+ remove_all_actions('site_url');
63
+
64
+ update_option('ld_login_base', 'login');
65
+ $this->object->application->renameLogin();
66
+
67
+ $this->assertTrue(has_action('wp_redirect'));
68
+ $this->assertTrue(has_action('network_site_url'));
69
+ $this->assertTrue(has_action('site_url'));
70
+ }
71
+
72
+ public function testLoginBase()
73
+ {
74
+ update_option('ld_login_base', 'login');
75
+ $this->object->application->renameLogin();
76
+
77
+ $this->assertEquals('login', $this->object->getLoginBase());
78
+ }
79
+
80
+ public function testRewriteUrl()
81
+ {
82
+ $this->assertEquals('http://localhost/login', $this->object->application->filterWpLogin('http://localhost/wp-login.php'));
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @ignore
88
+ */
89
+ class LdProxyObject extends WP_LockAuth { }
tests/test-hideadmin.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class LockdownAdminTest extends PHPUnit_Framework_TestCase {
3
+ protected $object;
4
+
5
+ protected function setUp()
6
+ {
7
+ $this->object = ld_setup_auth();
8
+ }
9
+
10
+ public function testWhitelist()
11
+ {
12
+ add_filter('no_check_files', function($a) { $a[] = 'wp-activate.php'; return $a; });
13
+ update_option('ld_hide_wp_admin', 'yep');
14
+
15
+ // Mocking a request to wp-activate.php
16
+ $_SERVER['SCRIPT_FILENAME'] = ABSPATH.'/wp-activate.php';
17
+
18
+ // Set it back up again so we can test if it passed
19
+ $this->setUp();
20
+ $this->assertTrue($this->object->getAuthPassed());
21
+ }
22
+
23
+ public function testWhitelistToBlock()
24
+ {
25
+ update_option('ld_hide_wp_admin', 'yep');
26
+
27
+ // Mocking a request to wp-activate.php
28
+ $_SERVER['SCRIPT_FILENAME'] = ABSPATH.'/wp-login.php';
29
+
30
+ // Set it back up again so we can test if it passed
31
+ $this->setUp();
32
+ $this->assertFalse($this->object->getAuthPassed());
33
+ }
34
+ }
tests/test-sample.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class SampleTest extends WP_UnitTestCase {
4
+
5
+ function testSample() {
6
+ // replace this with some actual testing code
7
+ $this->assertTrue( true );
8
+ }
9
+ }
10
+
views/settings.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if (! defined('ABSPATH')) exit; ?>
2
+ <div class="wrap">
3
+ <div id="icon-options-general" class="icon32"></div>
4
+ <h2>Lockdown WordPress Admin</h2>
5
+ <?php if ( defined('LD_WP_ADMIN') && LD_WP_ADMIN == TRUE ) : ?>
6
+ <div class="updated fade">
7
+ <p>Options updated!</p>
8
+ </div>
9
+ <?php endif;
10
+ if ( defined('LD_DIS_BASE') && LD_DIS_BASE == TRUE ) : ?>
11
+ <div class="updated error fade">
12
+ <p>You can't make that your URL Base.</p>
13
+ </div>
14
+ <?php endif; ?>
15
+
16
+ <p>
17
+ We are going to help make WordPress a bit more secure.
18
+ </p>
19
+ <p>
20
+ <a href="https://twitter.com/srtfisher" class="twitter-follow-button" data-show-count="false">Follow @srtfisher</a>
21
+ <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script><br />
22
+ I tweet a lot of things and often post whenever I update this plugin. Follow me <a href="http://twitter.com/srtfisher">@srtfisher</a>.
23
+ <br>
24
+ <em>
25
+ (Also, I am a freelancer and would love to <a href="http://seanfisher.co/contact">hear from you about your project</a>!)
26
+ </em>
27
+ </p>
28
+
29
+ <p>
30
+ Follow Lockdown WP-Admin development on <a href="https://github.com/srtfisher/Lockdown-WPAdmin" target="_blank">GitHub</a>
31
+ </p>
32
+
33
+ <form method="POST" action="<?php echo admin_url('admin.php?page=lockdown-wp-admin'); ?>">
34
+
35
+ <?php wp_nonce_field('lockdown-wp-admin'); ?>
36
+ <h3>
37
+ Hide WP Admin
38
+ </h3>
39
+ <p>
40
+ We can "hide" WordPress's administration interface from the public.
41
+ If you enable this, when you access
42
+ <code><?php echo admin_url(); ?></code> when you
43
+ <strong>aren't</strong> logged in, you will recieve a
44
+ <a href="http://en.wikipedia.org/wiki/HTTP_404">404 error page</a>
45
+ instead of redirecting to the login page.
46
+ </p>
47
+ <label>
48
+ <input type="checkbox" name="hide_wp_admin" value="yep"
49
+ <?php if ( get_option('ld_hide_wp_admin') === 'yep' ) { ?>
50
+ checked<?php
51
+ } ?>>
52
+ Yes, please hide WP Admin from the user when they aren't logged in.
53
+ </label>
54
+
55
+ <h3 style="margin-top: 30px;">
56
+ WordPress Login URL
57
+ </h3>
58
+ <label>
59
+ Change the WordPress Login URL?
60
+ <?php echo wp_guess_url().'/'; ?>
61
+ <input type="text" name="login_base" value="<?php echo $this->instance->application->getLoginBase(); ?>" />
62
+ <br>
63
+ <em>
64
+ This will change it from <?php echo wp_guess_url(); ?>/wp-login.php to whatever you put in this box. If you leave it <strong>blank</strong>, it will be disabled.<br />
65
+ Say if you put "<strong>login</strong>" into the box, your new login URL will be <?php echo home_url(); ?>/login/.
66
+ </em>
67
+ </label>
68
+ <?php
69
+ global $auth_obj;
70
+ $url = home_url() . '/'. $this->instance->application->getLoginBase();
71
+ ?>
72
+ <p>
73
+ Your current login URL is <code><a href="<?php echo $url; ?>"><?php echo $url; ?></a></code>.
74
+ </p>
75
+
76
+ <?php if ($this->instance->application->isSuggestedAgainst()) : ?>
77
+ <div class="updated error"><p>
78
+ Your login base <strong><?php echo $this->login_base; ?></strong> is highly insecure!
79
+ We strongly reccomend using another login URL to ensure maximum security.
80
+ </p>
81
+ </div>
82
+ <?php endif; ?>
83
+ <blockquote>
84
+ <h4>Please Note Something!</h4>
85
+ <p>If you are using a cache plugin (WTC, WP Super Cache, etc), you need to enable it
86
+ to not cache the above base. That means (for most caching plugins) adding
87
+ whatever you enter into the box above into your plugins Caching Whitelist, that
88
+ is the list of URLs that your plugin doesn't cache. If you have any questions, tweet
89
+ me <a href="http://twitter.com/srtfisher">@srtfisher</a>.</p>
90
+ </blockquote>
91
+ <h3>HTTP Authentication</h3>
92
+ <p>Please read about HTTP Authentication on <a href="http://en.wikipedia.org/wiki/Basic_access_authentication">http://en.wikipedia.org/wiki/Basic_access_authentication</a>.</p>
93
+ <?php $http_auth_type = get_option('ld_http_auth'); ?>
94
+ <label>
95
+ <input name="http_auth" type="radio" value="none" <?php if ( $http_auth_type === '' || $http_auth_type === 'none' || !$http_auth_type ) { ?>checked<?php } ?>>
96
+ Disable HTTP Auth.</label>
97
+ <div class="clear"></div>
98
+ <label>
99
+ <input type="radio" name="http_auth" <?php if ( $http_auth_type === 'wp_creds' ) { ?>checked<?php } ?> value="wp_creds">
100
+ WordPress Login Credentials</label>
101
+ <div class="clear"></div>
102
+ <label>
103
+ <input type="radio" name="http_auth" <?php if ( $http_auth_type === 'private' ) { ?>checked<?php } ?> value="private">
104
+ Private Usernames/Passwords</label>
105
+ <div class="clear"></div>
106
+ <br>
107
+ <input type="hidden" name="did_update" value="yes_we_did">
108
+ <input class='button-primary' type='submit' name='Save' value='<?php _e('Save Options'); ?>' id='submitbutton' />
109
+ </form>
110
+ </div>