Shield Security for WordPress - Version 6.10.9

Version Description

  • Current Release = Released: 7th December, 2018 - Release Notes

  • (v.9) FIXED: Admin notices displaying to non-admins.

  • (v.7) ADDED: [PRO] New option to specify usernames for Security Admin role.

  • (v.7) IMPROVED: Idle user detection.

  • (v.7) IMPROVED: Support for redirect/cancel URLs in 2FA login page.

  • (v.7) CHANGED: Final release before Shield v7. Small warning shown on plugins page if PHP < 5.4

Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 6.10.9
Comparing to
See all releases

Code changes from version 6.10.4 to 6.10.9

Files changed (249) hide show
  1. icwp-plugin-controller.php +80 -73
  2. icwp-wpsf.php +1 -1
  3. init.php +8 -11
  4. plugin-spec.php +2 -2
  5. readme.txt +16 -7
  6. resources/css/plugin.css +4 -5
  7. resources/images/flags/ad.png +0 -0
  8. resources/images/flags/ae.png +0 -0
  9. resources/images/flags/af.png +0 -0
  10. resources/images/flags/ag.png +0 -0
  11. resources/images/flags/al.png +0 -0
  12. resources/images/flags/am.png +0 -0
  13. resources/images/flags/ao.png +0 -0
  14. resources/images/flags/ar.png +0 -0
  15. resources/images/flags/at.png +0 -0
  16. resources/images/flags/au.png +0 -0
  17. resources/images/flags/az.png +0 -0
  18. resources/images/flags/ba.png +0 -0
  19. resources/images/flags/bb.png +0 -0
  20. resources/images/flags/bd.png +0 -0
  21. resources/images/flags/be.png +0 -0
  22. resources/images/flags/bf.png +0 -0
  23. resources/images/flags/bg.png +0 -0
  24. resources/images/flags/bh.png +0 -0
  25. resources/images/flags/bi.png +0 -0
  26. resources/images/flags/bj.png +0 -0
  27. resources/images/flags/bn.png +0 -0
  28. resources/images/flags/bo.png +0 -0
  29. resources/images/flags/br.png +0 -0
  30. resources/images/flags/bs.png +0 -0
  31. resources/images/flags/bt.png +0 -0
  32. resources/images/flags/bw.png +0 -0
  33. resources/images/flags/by.png +0 -0
  34. resources/images/flags/bz.png +0 -0
  35. resources/images/flags/ca.png +0 -0
  36. resources/images/flags/cd.png +0 -0
  37. resources/images/flags/cf.png +0 -0
  38. resources/images/flags/cg.png +0 -0
  39. resources/images/flags/ch.png +0 -0
  40. resources/images/flags/ci.png +0 -0
  41. resources/images/flags/cl.png +0 -0
  42. resources/images/flags/cm.png +0 -0
  43. resources/images/flags/cn.png +0 -0
  44. resources/images/flags/co.png +0 -0
  45. resources/images/flags/cr.png +0 -0
  46. resources/images/flags/cu.png +0 -0
  47. resources/images/flags/cv.png +0 -0
  48. resources/images/flags/cy.png +0 -0
  49. resources/images/flags/cz.png +0 -0
  50. resources/images/flags/de.png +0 -0
  51. resources/images/flags/dj.png +0 -0
  52. resources/images/flags/dk.png +0 -0
  53. resources/images/flags/dm.png +0 -0
  54. resources/images/flags/do.png +0 -0
  55. resources/images/flags/dz.png +0 -0
  56. resources/images/flags/ec.png +0 -0
  57. resources/images/flags/ee.png +0 -0
  58. resources/images/flags/eg.png +0 -0
  59. resources/images/flags/eh.png +0 -0
  60. resources/images/flags/er.png +0 -0
  61. resources/images/flags/es.png +0 -0
  62. resources/images/flags/et.png +0 -0
  63. resources/images/flags/fi.png +0 -0
  64. resources/images/flags/fj.png +0 -0
  65. resources/images/flags/fm.png +0 -0
  66. resources/images/flags/fr.png +0 -0
  67. resources/images/flags/ga.png +0 -0
  68. resources/images/flags/gb.png +0 -0
  69. resources/images/flags/gd.png +0 -0
  70. resources/images/flags/ge.png +0 -0
  71. resources/images/flags/gh.png +0 -0
  72. resources/images/flags/gm.png +0 -0
  73. resources/images/flags/gn.png +0 -0
  74. resources/images/flags/gq.png +0 -0
  75. resources/images/flags/gr.png +0 -0
  76. resources/images/flags/gt.png +0 -0
  77. resources/images/flags/gw.png +0 -0
  78. resources/images/flags/gy.png +0 -0
  79. resources/images/flags/hn.png +0 -0
  80. resources/images/flags/hr.png +0 -0
  81. resources/images/flags/ht.png +0 -0
  82. resources/images/flags/hu.png +0 -0
  83. resources/images/flags/id.png +0 -0
  84. resources/images/flags/ie.png +0 -0
  85. resources/images/flags/il.png +0 -0
  86. resources/images/flags/in.png +0 -0
  87. resources/images/flags/iq.png +0 -0
  88. resources/images/flags/ir.png +0 -0
  89. resources/images/flags/is.png +0 -0
  90. resources/images/flags/it.png +0 -0
  91. resources/images/flags/jm.png +0 -0
  92. resources/images/flags/jo.png +0 -0
  93. resources/images/flags/jp.png +0 -0
  94. resources/images/flags/ke.png +0 -0
  95. resources/images/flags/kg.png +0 -0
  96. resources/images/flags/kh.png +0 -0
  97. resources/images/flags/ki.png +0 -0
  98. resources/images/flags/km.png +0 -0
  99. resources/images/flags/kn.png +0 -0
  100. resources/images/flags/kp.png +0 -0
  101. resources/images/flags/kr.png +0 -0
  102. resources/images/flags/ks.png +0 -0
  103. resources/images/flags/kw.png +0 -0
  104. resources/images/flags/kz.png +0 -0
  105. resources/images/flags/la.png +0 -0
  106. resources/images/flags/lb.png +0 -0
  107. resources/images/flags/lc.png +0 -0
  108. resources/images/flags/li.png +0 -0
  109. resources/images/flags/lk.png +0 -0
  110. resources/images/flags/lr.png +0 -0
  111. resources/images/flags/ls.png +0 -0
  112. resources/images/flags/lt.png +0 -0
  113. resources/images/flags/lu.png +0 -0
  114. resources/images/flags/lv.png +0 -0
  115. resources/images/flags/ly.png +0 -0
  116. resources/images/flags/ma.png +0 -0
  117. resources/images/flags/mc.png +0 -0
  118. resources/images/flags/md.png +0 -0
  119. resources/images/flags/me.png +0 -0
  120. resources/images/flags/mg.png +0 -0
  121. resources/images/flags/mh.png +0 -0
  122. resources/images/flags/mk.png +0 -0
  123. resources/images/flags/ml.png +0 -0
  124. resources/images/flags/mm.png +0 -0
  125. resources/images/flags/mn.png +0 -0
  126. resources/images/flags/mr.png +0 -0
  127. resources/images/flags/mt.png +0 -0
  128. resources/images/flags/mu.png +0 -0
  129. resources/images/flags/mv.png +0 -0
  130. resources/images/flags/mw.png +0 -0
  131. resources/images/flags/mx.png +0 -0
  132. resources/images/flags/my.png +0 -0
  133. resources/images/flags/mz.png +0 -0
  134. resources/images/flags/na.png +0 -0
  135. resources/images/flags/ne.png +0 -0
  136. resources/images/flags/ng.png +0 -0
  137. resources/images/flags/ni.png +0 -0
  138. resources/images/flags/nl.png +0 -0
  139. resources/images/flags/no.png +0 -0
  140. resources/images/flags/np.png +0 -0
  141. resources/images/flags/nr.png +0 -0
  142. resources/images/flags/nz.png +0 -0
  143. resources/images/flags/om.png +0 -0
  144. resources/images/flags/pa.png +0 -0
  145. resources/images/flags/pe.png +0 -0
  146. resources/images/flags/pg.png +0 -0
  147. resources/images/flags/ph.png +0 -0
  148. resources/images/flags/pk.png +0 -0
  149. resources/images/flags/pl.png +0 -0
  150. resources/images/flags/pt.png +0 -0
  151. resources/images/flags/pw.png +0 -0
  152. resources/images/flags/py.png +0 -0
  153. resources/images/flags/qa.png +0 -0
  154. resources/images/flags/ro.png +0 -0
  155. resources/images/flags/rs.png +0 -0
  156. resources/images/flags/ru.png +0 -0
  157. resources/images/flags/rw.png +0 -0
  158. resources/images/flags/sa.png +0 -0
  159. resources/images/flags/sb.png +0 -0
  160. resources/images/flags/sc.png +0 -0
  161. resources/images/flags/sd.png +0 -0
  162. resources/images/flags/se.png +0 -0
  163. resources/images/flags/sg.png +0 -0
  164. resources/images/flags/si.png +0 -0
  165. resources/images/flags/sk.png +0 -0
  166. resources/images/flags/sl.png +0 -0
  167. resources/images/flags/sm.png +0 -0
  168. resources/images/flags/sn.png +0 -0
  169. resources/images/flags/so.png +0 -0
  170. resources/images/flags/sr.png +0 -0
  171. resources/images/flags/st.png +0 -0
  172. resources/images/flags/sv.png +0 -0
  173. resources/images/flags/sy.png +0 -0
  174. resources/images/flags/sz.png +0 -0
  175. resources/images/flags/td.png +0 -0
  176. resources/images/flags/tg.png +0 -0
  177. resources/images/flags/th.png +0 -0
  178. resources/images/flags/tj.png +0 -0
  179. resources/images/flags/tl.png +0 -0
  180. resources/images/flags/tm.png +0 -0
  181. resources/images/flags/tn.png +0 -0
  182. resources/images/flags/to.png +0 -0
  183. resources/images/flags/tr.png +0 -0
  184. resources/images/flags/tt.png +0 -0
  185. resources/images/flags/tv.png +0 -0
  186. resources/images/flags/tw.png +0 -0
  187. resources/images/flags/tz.png +0 -0
  188. resources/images/flags/ua.png +0 -0
  189. resources/images/flags/ug.png +0 -0
  190. resources/images/flags/us.png +0 -0
  191. resources/images/flags/uy.png +0 -0
  192. resources/images/flags/uz.png +0 -0
  193. resources/images/flags/va.png +0 -0
  194. resources/images/flags/vc.png +0 -0
  195. resources/images/flags/ve.png +0 -0
  196. resources/images/flags/vn.png +0 -0
  197. resources/images/flags/vu.png +0 -0
  198. resources/images/flags/ws.png +0 -0
  199. resources/images/flags/ye.png +0 -0
  200. resources/images/flags/za.png +0 -0
  201. resources/images/flags/zm.png +0 -0
  202. resources/images/flags/zw.png +0 -0
  203. resources/js/shield-antibot.js +5 -6
  204. src/common/icwp-wpfunctions.php +2 -1
  205. src/config/feature-admin_access_restriction.php +13 -0
  206. src/config/feature-autoupdates.php +25 -0
  207. src/config/feature-firewall.php +4 -1
  208. src/config/feature-license.php +2 -37
  209. src/config/feature-login_protect.php +2 -2
  210. src/config/feature-plugin.php +9 -9
  211. src/features/admin_access_restriction.php +99 -28
  212. src/features/audit_trail.php +1 -2
  213. src/features/autoupdates.php +29 -24
  214. src/features/base.php +34 -58
  215. src/features/hack_protect.php +1 -1
  216. src/features/ips.php +3 -2
  217. src/features/license.php +85 -50
  218. src/features/login_protect.php +33 -15
  219. src/features/plugin.php +47 -12
  220. src/features/traffic.php +1 -2
  221. src/processors/admin_access_restriction.php +43 -33
  222. src/processors/adminaccess_whitelabel.php +2 -2
  223. src/processors/autoupdates.php +11 -2
  224. src/processors/base.php +10 -10
  225. src/processors/base_wpsf.php +3 -1
  226. src/processors/basedb.php +1 -1
  227. src/processors/comments_filter.php +1 -1
  228. src/processors/commentsfilter_humanspam.php +1 -1
  229. src/processors/dbhandler.php +0 -7
  230. src/processors/hackprotect_pluginvulnerabilities.php +34 -34
  231. src/processors/ips.php +0 -1
  232. src/processors/license.php +1 -1
  233. src/processors/lockdown.php +1 -0
  234. src/processors/loginprotect_backupcodes.php +0 -209
  235. src/processors/loginprotect_cooldown.php +1 -1
  236. src/processors/loginprotect_googleauthenticator.php +0 -344
  237. src/processors/loginprotect_intent.php +37 -9
  238. src/processors/loginprotect_intentprovider_backup.php +1 -1
  239. src/processors/loginprotect_intentprovider_base.php +1 -1
  240. src/processors/loginprotect_intentprovider_email.php +1 -1
  241. src/processors/loginprotect_intentprovider_ga.php +2 -2
  242. src/processors/loginprotect_intentprovider_yubikey.php +1 -1
  243. src/processors/loginprotect_track.php +0 -166
  244. src/processors/loginprotect_twofactorauth.php +0 -239
  245. src/processors/loginprotect_yubikey.php +0 -378
  246. src/processors/plugin.php +1 -21
  247. src/processors/sessions.php +6 -0
  248. src/wizards/base_wpsf.php +1 -1
  249. templates/php/page/login_intent.php +1 -0
icwp-plugin-controller.php CHANGED
@@ -91,7 +91,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
91
  /**
92
  * @var boolean
93
  */
94
- protected $bMeetsBasePermissions = false;
95
 
96
  /**
97
  * @var string
@@ -127,7 +127,6 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
127
  self::$sRootFile = $sRootFile;
128
  $this->checkMinimumRequirements();
129
  $this->doRegisterHooks();
130
- $this->loadWpTrack();
131
  $this->loadFactory(); // so we know it's loaded whenever we need it. Cuz we need it.
132
  $this->doLoadTextDomain();
133
 
@@ -260,7 +259,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
260
  /**
261
  */
262
  public function onWpDeactivatePlugin() {
263
- if ( current_user_can( $this->getBasePermissions() ) && apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
264
  do_action( $this->prefix( 'delete_plugin' ) );
265
  $this->deletePluginControllerOptions();
266
  }
@@ -277,7 +276,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
277
  protected function doRegisterHooks() {
278
  $this->registerActivationHooks();
279
 
280
- add_action( 'init', array( $this, 'onWpInit' ), 0 );
281
  add_action( 'admin_init', array( $this, 'onWpAdminInit' ) );
282
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ) );
283
 
@@ -337,8 +336,8 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
337
  /**
338
  */
339
  public function onWpInit() {
 
340
  add_action( 'wp_enqueue_scripts', array( $this, 'onWpEnqueueFrontendCss' ), 99 );
341
- $this->bMeetsBasePermissions = current_user_can( $this->getBasePermissions() );
342
  }
343
 
344
  /**
@@ -352,7 +351,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
352
  /**
353
  */
354
  public function onWpDashboardSetup() {
355
- if ( $this->isValidAdminArea() ) {
356
  wp_add_dashboard_widget(
357
  $this->prefix( 'dashboard_widget' ),
358
  apply_filters( $this->prefix( 'dashboard_widget_title' ), $this->getHumanName() ),
@@ -366,32 +365,6 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
366
  echo implode( '', $aContent );
367
  }
368
 
369
- /**
370
- * v5.4.1: Nasty looping bug in here where this function was called within the 'user_has_cap' filter
371
- * so we removed the "current_user_can()" or any such sub-call within this function
372
- * @return bool
373
- */
374
- public function getHasPermissionToManage() {
375
- if ( apply_filters( $this->prefix( 'bypass_permission_to_manage' ), false ) ) {
376
- return true;
377
- }
378
- return ( $this->getMeetsBasePermissions() && apply_filters( $this->prefix( 'has_permission_to_manage' ), true ) );
379
- }
380
-
381
- /**
382
- * Must be simple and cannot contain anything that would call filter "user_has_cap", e.g. current_user_can()
383
- * @return boolean
384
- */
385
- public function getMeetsBasePermissions() {
386
- return (bool)$this->bMeetsBasePermissions;
387
- }
388
-
389
- /**
390
- */
391
- public function getHasPermissionToView() {
392
- return $this->getHasPermissionToManage(); // TODO: separate view vs manage
393
- }
394
-
395
  /**
396
  * @uses die()
397
  */
@@ -557,6 +530,15 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
557
  $sSettingsLink = sprintf( $sLinkTemplate, $aMetaLink[ 'href' ], "_blank", $aMetaLink[ 'name' ] );;
558
  array_push( $aPluginMeta, $sSettingsLink );
559
  }
 
 
 
 
 
 
 
 
 
560
  }
561
  return $aPluginMeta;
562
  }
@@ -734,7 +716,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
734
  $sFile = $this->getPluginBaseFile();
735
  if ( !empty( $oUpdates->response ) && isset( $oUpdates->response[ $sFile ] ) ) {
736
  if ( version_compare( $oUpdates->response[ $sFile ]->new_version, '7.0.0', '>=' )
737
- && !$this->loadDP()->getPhpVersionIsAtLeast( '5.3.0' ) ) {
738
  unset( $oUpdates->response[ $sFile ] );
739
  }
740
  }
@@ -1089,9 +1071,8 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1089
  * @param bool $bCheckUserPerms - do we check the logged-in user permissions
1090
  * @return bool
1091
  */
1092
- public function isValidAdminArea( $bCheckUserPerms = true ) {
1093
- if ( $bCheckUserPerms && $this->loadWpTrack()->getWpActionHasFired( 'init' )
1094
- && !$this->getMeetsBasePermissions() ) {
1095
  return false;
1096
  }
1097
 
@@ -1105,6 +1086,29 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1105
  return false;
1106
  }
1107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1108
  /**
1109
  * @param string
1110
  * @return string
@@ -1303,7 +1307,8 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1303
  * @return string
1304
  */
1305
  public function getPath_Assets( $sAsset = '' ) {
1306
- return $this->getRootDir().$this->getPluginSpec_Path( 'assets' ).DIRECTORY_SEPARATOR.$sAsset;
 
1307
  }
1308
 
1309
  /**
@@ -1311,7 +1316,8 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1311
  * @return string
1312
  */
1313
  public function getPath_Flags( $sFlag = '' ) {
1314
- return $this->getRootDir().$this->getPluginSpec_Path( 'flags' ).DIRECTORY_SEPARATOR.$sFlag;
 
1315
  }
1316
 
1317
  /**
@@ -1319,12 +1325,14 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1319
  * @return string
1320
  */
1321
  public function getPath_Temp( $sTmpFile = '' ) {
 
1322
  $oFs = $this->loadFS();
1323
- $sTempPath = $this->getRootDir().$this->getPluginSpec_Path( 'temp' ).DIRECTORY_SEPARATOR;
1324
- if ( $oFs->mkdir( $sTempPath ) ) {
1325
- return $this->getRootDir().$this->getPluginSpec_Path( 'temp' ).DIRECTORY_SEPARATOR.$sTmpFile;
 
1326
  }
1327
- return null;
1328
  }
1329
 
1330
  /**
@@ -1351,35 +1359,28 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1351
  return $this->getPath_Assets( 'images/'.$sAsset );
1352
  }
1353
 
1354
- /**
1355
- * @return string
1356
- */
1357
- public function getPath_Config() {
1358
- return $this->getPath_Source().'config/';
1359
- }
1360
-
1361
  /**
1362
  * @param string $sSlug
1363
  * @return string
1364
  */
1365
  public function getPath_ConfigFile( $sSlug ) {
1366
- return $this->getPath_Config().sprintf( 'feature-%s.php', $sSlug );
1367
  }
1368
 
1369
  /**
1370
- * get the root directory for the plugin with the trailing slash
1371
  * @return string
1372
  */
1373
  public function getPath_Languages() {
1374
- return $this->getRootDir().$this->getPluginSpec_Path( 'languages' ).DIRECTORY_SEPARATOR;
1375
  }
1376
 
1377
  /**
1378
- * get the root directory for the plugin with the trailing slash
 
1379
  * @return string
1380
  */
1381
- public function getPath_Source() {
1382
- return $this->getRootDir().$this->getPluginSpec_Path( 'source' ).DIRECTORY_SEPARATOR;
1383
  }
1384
 
1385
  /**
@@ -1388,23 +1389,15 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1388
  * @return string
1389
  */
1390
  public function getPath_SourceFile( $sSourceFile = '' ) {
1391
- return $this->getPath_Source().$sSourceFile;
1392
- }
1393
-
1394
- /**
1395
- * Get the path to a library source file
1396
- * @param string $sLibFile
1397
- * @return string
1398
- */
1399
- public function getPath_LibFile( $sLibFile = '' ) {
1400
- return $this->getPath_Source().'lib/'.$sLibFile;
1401
  }
1402
 
1403
  /**
1404
  * @return string
1405
  */
1406
  public function getPath_Templates() {
1407
- return $this->getRootDir().$this->getPluginSpec_Path( 'templates' ).DIRECTORY_SEPARATOR;
1408
  }
1409
 
1410
  /**
@@ -1412,14 +1405,14 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1412
  * @return string
1413
  */
1414
  public function getPath_TemplatesFile( $sTemplate ) {
1415
- return $this->getPath_Templates().$sTemplate;
1416
  }
1417
 
1418
  /**
1419
  * @return string
1420
  */
1421
  private function getPathPluginSpec() {
1422
- return $this->getRootDir().'plugin-spec.php';
1423
  }
1424
 
1425
  /**
@@ -1542,9 +1535,9 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1542
  protected function saveCurrentPluginControllerOptions() {
1543
  $oOptions = $this->getPluginControllerOptions();
1544
  if ( $this->sConfigOptionsHashWhenLoaded != md5( serialize( $oOptions ) ) ) {
1545
- add_filter( $this->prefix( 'bypass_permission_to_manage' ), '__return_true' );
1546
  $this->loadWp()->updateOption( $this->getPluginControllerOptionsKey(), $oOptions );
1547
- remove_filter( $this->prefix( 'bypass_permission_to_manage' ), '__return_true' );
1548
  }
1549
  }
1550
 
@@ -1576,7 +1569,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1576
  /**
1577
  */
1578
  public function deactivateSelf() {
1579
- if ( $this->isValidAdminArea() && function_exists( 'deactivate_plugins' ) ) {
1580
  deactivate_plugins( $this->getPluginBaseFile() );
1581
  }
1582
  }
@@ -1641,7 +1634,8 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1641
  public function getUniqueRequestId( $bSetIfNeeded = true ) {
1642
  if ( !isset( self::$sRequestId ) ) {
1643
  self::$sRequestId = md5(
1644
- $this->getSessionId( $bSetIfNeeded ).$this->loadIpService()->getRequestIp().$this->loadRequest()->ts().wp_rand()
 
1645
  );
1646
  }
1647
  return self::$sRequestId;
@@ -1710,7 +1704,7 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1710
  $bSuccess = true;
1711
  }
1712
  catch ( Exception $oE ) {
1713
- if ( $this->isValidAdminArea() ) {
1714
  $this->sAdminNoticeError = $oE->getMessage();
1715
  add_action( 'admin_notices', array( $this, 'adminNoticePluginFailedToLoad' ) );
1716
  add_action( 'network_admin_notices', array( $this, 'adminNoticePluginFailedToLoad' ) );
@@ -1883,4 +1877,17 @@ class ICWP_WPSF_Plugin_Controller extends ICWP_WPSF_Foundation {
1883
  }
1884
  return $aResult;
1885
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1886
  }
91
  /**
92
  * @var boolean
93
  */
94
+ private $bMeetsBasePermissions;
95
 
96
  /**
97
  * @var string
127
  self::$sRootFile = $sRootFile;
128
  $this->checkMinimumRequirements();
129
  $this->doRegisterHooks();
 
130
  $this->loadFactory(); // so we know it's loaded whenever we need it. Cuz we need it.
131
  $this->doLoadTextDomain();
132
 
259
  /**
260
  */
261
  public function onWpDeactivatePlugin() {
262
+ if ( $this->isPluginAdmin() && apply_filters( $this->prefix( 'delete_on_deactivate' ), false ) ) {
263
  do_action( $this->prefix( 'delete_plugin' ) );
264
  $this->deletePluginControllerOptions();
265
  }
276
  protected function doRegisterHooks() {
277
  $this->registerActivationHooks();
278
 
279
+ add_action( 'init', array( $this, 'onWpInit' ), -1000 );
280
  add_action( 'admin_init', array( $this, 'onWpAdminInit' ) );
281
  add_action( 'wp_loaded', array( $this, 'onWpLoaded' ) );
282
 
336
  /**
337
  */
338
  public function onWpInit() {
339
+ $this->getMeetsBasePermissions();
340
  add_action( 'wp_enqueue_scripts', array( $this, 'onWpEnqueueFrontendCss' ), 99 );
 
341
  }
342
 
343
  /**
351
  /**
352
  */
353
  public function onWpDashboardSetup() {
354
+ if ( $this->isValidAdminArea() && $this->isPluginAdmin() ) {
355
  wp_add_dashboard_widget(
356
  $this->prefix( 'dashboard_widget' ),
357
  apply_filters( $this->prefix( 'dashboard_widget_title' ), $this->getHumanName() ),
365
  echo implode( '', $aContent );
366
  }
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  /**
369
  * @uses die()
370
  */
530
  $sSettingsLink = sprintf( $sLinkTemplate, $aMetaLink[ 'href' ], "_blank", $aMetaLink[ 'name' ] );;
531
  array_push( $aPluginMeta, $sSettingsLink );
532
  }
533
+
534
+ if ( !$this->loadDP()->getPhpVersionIsAtLeast( '5.4' ) ) {
535
+ $aPluginMeta[] = sprintf(
536
+ '<a href="%s" target="_blank" title="%s" style="color: red;">%s</a>',
537
+ 'https://icwp.io/dj',
538
+ 'Upgrades Not Available',
539
+ 'PHP Too Old!'
540
+ );
541
+ }
542
  }
543
  return $aPluginMeta;
544
  }
716
  $sFile = $this->getPluginBaseFile();
717
  if ( !empty( $oUpdates->response ) && isset( $oUpdates->response[ $sFile ] ) ) {
718
  if ( version_compare( $oUpdates->response[ $sFile ]->new_version, '7.0.0', '>=' )
719
+ && !$this->loadDP()->getPhpVersionIsAtLeast( '5.4.0' ) ) {
720
  unset( $oUpdates->response[ $sFile ] );
721
  }
722
  }
1071
  * @param bool $bCheckUserPerms - do we check the logged-in user permissions
1072
  * @return bool
1073
  */
1074
+ public function isValidAdminArea( $bCheckUserPerms = false ) {
1075
+ if ( $bCheckUserPerms && did_action( 'init' ) && !$this->isPluginAdmin() ) {
 
1076
  return false;
1077
  }
1078
 
1086
  return false;
1087
  }
1088
 
1089
+ /**
1090
+ * only ever consider after WP INIT (when a logged-in user is recognised)
1091
+ * @return bool
1092
+ */
1093
+ public function isPluginAdmin() {
1094
+ return apply_filters( $this->prefix( 'bypass_is_plugin_admin' ), false )
1095
+ || ( $this->getMeetsBasePermissions() // takes care of did_action('init)
1096
+ && apply_filters( $this->prefix( 'is_plugin_admin' ), true )
1097
+ );
1098
+ }
1099
+
1100
+ /**
1101
+ * DO NOT CHANGE THIS IMPLEMENTATION. We call this as early as possible so that the
1102
+ * current_user_can() never gets caught up in an infinite loop of permissions checking
1103
+ * @return boolean
1104
+ */
1105
+ public function getMeetsBasePermissions() {
1106
+ if ( did_action( 'init' ) && !isset( $this->bMeetsBasePermissions ) ) {
1107
+ $this->bMeetsBasePermissions = current_user_can( $this->getBasePermissions() );
1108
+ }
1109
+ return isset( $this->bMeetsBasePermissions ) ? $this->bMeetsBasePermissions : false;
1110
+ }
1111
+
1112
  /**
1113
  * @param string
1114
  * @return string
1307
  * @return string
1308
  */
1309
  public function getPath_Assets( $sAsset = '' ) {
1310
+ $sBase = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'assets' ) );
1311
+ return empty( $sAsset ) ? $sBase : path_join( $sBase, $sAsset );
1312
  }
1313
 
1314
  /**
1316
  * @return string
1317
  */
1318
  public function getPath_Flags( $sFlag = '' ) {
1319
+ $sBase = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'flags' ) );
1320
+ return empty( $sFlag ) ? $sBase : path_join( $sBase, $sFlag );
1321
  }
1322
 
1323
  /**
1325
  * @return string
1326
  */
1327
  public function getPath_Temp( $sTmpFile = '' ) {
1328
+ $sTempPath = null;
1329
  $oFs = $this->loadFS();
1330
+
1331
+ $sBase = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'temp' ) );
1332
+ if ( $oFs->mkdir( $sBase ) ) {
1333
+ $sTempPath = $sBase;
1334
  }
1335
+ return empty( $sTmpFile ) ? $sTempPath : path_join( $sTempPath, $sTmpFile );
1336
  }
1337
 
1338
  /**
1359
  return $this->getPath_Assets( 'images/'.$sAsset );
1360
  }
1361
 
 
 
 
 
 
 
 
1362
  /**
1363
  * @param string $sSlug
1364
  * @return string
1365
  */
1366
  public function getPath_ConfigFile( $sSlug ) {
1367
+ return $this->getPath_SourceFile( sprintf( 'config/feature-%s.php', $sSlug ) );
1368
  }
1369
 
1370
  /**
 
1371
  * @return string
1372
  */
1373
  public function getPath_Languages() {
1374
+ return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'languages' ) ).'/';
1375
  }
1376
 
1377
  /**
1378
+ * Get the path to a library source file
1379
+ * @param string $sLibFile
1380
  * @return string
1381
  */
1382
+ public function getPath_LibFile( $sLibFile = '' ) {
1383
+ return $this->getPath_SourceFile( 'lib/'.$sLibFile );
1384
  }
1385
 
1386
  /**
1389
  * @return string
1390
  */
1391
  public function getPath_SourceFile( $sSourceFile = '' ) {
1392
+ $sBase = path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'source' ) ).'/';
1393
+ return empty( $sSourceFile ) ? $sBase : path_join( $sBase, $sSourceFile );
 
 
 
 
 
 
 
 
1394
  }
1395
 
1396
  /**
1397
  * @return string
1398
  */
1399
  public function getPath_Templates() {
1400
+ return path_join( $this->getRootDir(), $this->getPluginSpec_Path( 'templates' ) ).'/';
1401
  }
1402
 
1403
  /**
1405
  * @return string
1406
  */
1407
  public function getPath_TemplatesFile( $sTemplate ) {
1408
+ return path_join( $this->getPath_Templates(), $sTemplate );
1409
  }
1410
 
1411
  /**
1412
  * @return string
1413
  */
1414
  private function getPathPluginSpec() {
1415
+ return path_join( $this->getRootDir(), 'plugin-spec.php' );
1416
  }
1417
 
1418
  /**
1535
  protected function saveCurrentPluginControllerOptions() {
1536
  $oOptions = $this->getPluginControllerOptions();
1537
  if ( $this->sConfigOptionsHashWhenLoaded != md5( serialize( $oOptions ) ) ) {
1538
+ add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1539
  $this->loadWp()->updateOption( $this->getPluginControllerOptionsKey(), $oOptions );
1540
+ remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1541
  }
1542
  }
1543
 
1569
  /**
1570
  */
1571
  public function deactivateSelf() {
1572
+ if ( $this->isPluginAdmin() && function_exists( 'deactivate_plugins' ) ) {
1573
  deactivate_plugins( $this->getPluginBaseFile() );
1574
  }
1575
  }
1634
  public function getUniqueRequestId( $bSetIfNeeded = true ) {
1635
  if ( !isset( self::$sRequestId ) ) {
1636
  self::$sRequestId = md5(
1637
+ $this->getSessionId( $bSetIfNeeded ).$this->loadIpService()->getRequestIp().$this->loadRequest()
1638
+ ->ts().wp_rand()
1639
  );
1640
  }
1641
  return self::$sRequestId;
1704
  $bSuccess = true;
1705
  }
1706
  catch ( Exception $oE ) {
1707
+ if ( $this->isValidAdminArea() && $this->isPluginAdmin() ) {
1708
  $this->sAdminNoticeError = $oE->getMessage();
1709
  add_action( 'admin_notices', array( $this, 'adminNoticePluginFailedToLoad' ) );
1710
  add_action( 'network_admin_notices', array( $this, 'adminNoticePluginFailedToLoad' ) );
1877
  }
1878
  return $aResult;
1879
  }
1880
+
1881
+ /**
1882
+ * v5.4.1: Nasty looping bug in here where this function was called within the 'user_has_cap' filter
1883
+ * so we removed the "current_user_can()" or any such sub-call within this function
1884
+ * @deprecated v6.10.7
1885
+ * @return bool
1886
+ */
1887
+ public function getHasPermissionToManage() {
1888
+ if ( apply_filters( $this->prefix( 'bypass_permission_to_manage' ), false ) ) {
1889
+ return true;
1890
+ }
1891
+ return ( $this->isPluginAdmin() && apply_filters( $this->prefix( 'is_plugin_admin' ), true ) );
1892
+ }
1893
  }
icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 6.10.4
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages/
9
  * Author: One Dollar Plugin
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://icwp.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
+ * Version: 6.10.9
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages/
9
  * Author: One Dollar Plugin
init.php CHANGED
@@ -56,21 +56,18 @@ class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Foundation {
56
  */
57
  public function onWpPluginActionLinks( $aActionLinks, $sPluginFile ) {
58
  $oCon = $this->getController();
59
- if ( !$oCon->isValidAdminArea() ) {
60
- return $aActionLinks;
61
- }
62
 
63
- if ( $sPluginFile == $oCon->getPluginBaseFile() ) {
64
- if ( !$oCon->getHasPermissionToManage() ) {
65
 
66
- if ( array_key_exists( 'edit', $aActionLinks ) ) {
67
- unset( $aActionLinks[ 'edit' ] );
68
- }
69
- if ( array_key_exists( 'deactivate', $aActionLinks ) ) {
70
- unset( $aActionLinks[ 'deactivate' ] );
71
- }
72
  }
73
  }
 
74
  return $aActionLinks;
75
  }
76
  }
56
  */
57
  public function onWpPluginActionLinks( $aActionLinks, $sPluginFile ) {
58
  $oCon = $this->getController();
 
 
 
59
 
60
+ if ( $oCon->isValidAdminArea() && !$oCon->isPluginAdmin()
61
+ && $sPluginFile == $oCon->getPluginBaseFile() ) {
62
 
63
+ if ( array_key_exists( 'edit', $aActionLinks ) ) {
64
+ unset( $aActionLinks[ 'edit' ] );
65
+ }
66
+ if ( array_key_exists( 'deactivate', $aActionLinks ) ) {
67
+ unset( $aActionLinks[ 'deactivate' ] );
 
68
  }
69
  }
70
+
71
  return $aActionLinks;
72
  }
73
  }
plugin-spec.php CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "properties": {
3
- "version": "6.10.4",
4
- "release_timestamp": 1541528200,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
1
  {
2
  "properties": {
3
+ "version": "6.10.9",
4
+ "release_timestamp": 1544177090,
5
  "slug_parent": "icwp",
6
  "slug_plugin": "wpsf",
7
  "human_name": "Shield",
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.5.0
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 5.0
11
- Stable tag: 6.10.4
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
@@ -354,9 +354,22 @@ You will always be able to use Shield Security and its free features in-full.
354
 
355
  [Go Pro for just $1/month](https://icwp.io/aa).
356
 
357
- = 6.10.4 - Current Release =
358
- *Released: 5th November, 2018* - [Release Notes](https://icwp.io/dg)
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  * **(v.4)** FIXED: Couldn't deactivate plugin.
361
  * **(v.3)** ADDED: Support for Ultimate Member forms
362
  * **(v.3)** ADDED: Support for LearnPress login/registration forms
@@ -364,10 +377,6 @@ You will always be able to use Shield Security and its free features in-full.
364
  * **(v.3)** IMPROVED: Distinguish which sub-site (sub-domain) for WPMS installations on [Traffic Watcher](https://icwp.io/c1).
365
  * **(v.3)** IMPROVED: Server's own IP lookup is only attempted once.
366
  * **(v.3)** ADDED: Experimental feature to help with some custom 3rd party login/registration forms
367
-
368
- = 6.10 - Series =
369
- *Released: 15th October, 2018* - [Release Notes](https://icwp.io/dg)
370
-
371
  * **(v.2)** IMPROVED: Visitor IP address detection
372
  * **(v.2)** IMPROVED: Automatic whitelisting of Manage WP IP addresses
373
  * **(v.2)** IMPROVED: SPAM Comments code enhanced and optimised
8
  Requires PHP: 5.2.4
9
  Recommended PHP: 5.4
10
  Tested up to: 5.0
11
+ Stable tag: 6.10.9
12
 
13
  Complete All-In-One Protection for your WordPress sites, that makes Security Easy for Everyone - it doesn't have to be hard anymore.
14
 
354
 
355
  [Go Pro for just $1/month](https://icwp.io/aa).
356
 
357
+ = 6.10.9 - Current Release =
358
+ *Released: 7th December, 2018* - [Release Notes](https://icwp.io/dg)
359
 
360
+ * **(v.9)** FIXED: Admin notices displaying to non-admins.
361
+ * **(v.7)** ADDED: [**PRO**] New option to specify usernames for Security Admin role.
362
+ * **(v.7)** IMPROVED: Idle user detection.
363
+ * **(v.7)** IMPROVED: Support for redirect/cancel URLs in 2FA login page.
364
+ * **(v.7)** CHANGED: Final release before Shield v7. Small warning shown on plugins page if PHP < 5.4
365
+
366
+ = 6.10 - Series =
367
+ *Released: 15th October, 2018* - [Release Notes](https://icwp.io/dg)
368
+
369
+ * **(v.6)** ADDED: New option to control plugin automatic updates.
370
+ * **(v.6)** IMPROVED: Enhancements to the experimental bot JS.
371
+ * **(v.6)** IMPROVED: Support for Easy Digital Downloads forms.
372
+ * **(v.5)** Release skipped.
373
  * **(v.4)** FIXED: Couldn't deactivate plugin.
374
  * **(v.3)** ADDED: Support for Ultimate Member forms
375
  * **(v.3)** ADDED: Support for LearnPress login/registration forms
377
  * **(v.3)** IMPROVED: Distinguish which sub-site (sub-domain) for WPMS installations on [Traffic Watcher](https://icwp.io/c1).
378
  * **(v.3)** IMPROVED: Server's own IP lookup is only attempted once.
379
  * **(v.3)** ADDED: Experimental feature to help with some custom 3rd party login/registration forms
 
 
 
 
380
  * **(v.2)** IMPROVED: Visitor IP address detection
381
  * **(v.2)** IMPROVED: Automatic whitelisting of Manage WP IP addresses
382
  * **(v.2)** IMPROVED: SPAM Comments code enhanced and optimised
resources/css/plugin.css CHANGED
@@ -802,7 +802,9 @@ input:checked + .icwp-slider:before {
802
  #IcwpWpsfSecurityAdmin {
803
  background-color: white;
804
  padding: 40px;
805
- margin: 40px 0;
 
 
806
  }
807
  .card.gopro-card {
808
  padding: 0; /* fix to override for bootstrap4 cuz WP has a .card style*/
@@ -857,10 +859,7 @@ input:checked + .icwp-slider:before {
857
  }
858
 
859
  .icon-flag {
860
- width: 16px;
861
- height: 12px;
862
- border: 1px solid black;
863
- vertical-align: unset;
864
  }
865
 
866
  /** TABLE: Audit trail **/
802
  #IcwpWpsfSecurityAdmin {
803
  background-color: white;
804
  padding: 40px;
805
+ margin: 120px 0 40px;
806
+ box-shadow: 6px 6px 0 rgba(0,0,0,0.2);
807
+ border: 1px solid rgba(0,0,0,0.2);
808
  }
809
  .card.gopro-card {
810
  padding: 0; /* fix to override for bootstrap4 cuz WP has a .card style*/
859
  }
860
 
861
  .icon-flag {
862
+ vertical-align: bottom;
 
 
 
863
  }
864
 
865
  /** TABLE: Audit trail **/
resources/images/flags/ad.png DELETED
Binary file
resources/images/flags/ae.png DELETED
Binary file
resources/images/flags/af.png DELETED
Binary file
resources/images/flags/ag.png DELETED
Binary file
resources/images/flags/al.png DELETED
Binary file
resources/images/flags/am.png DELETED
Binary file
resources/images/flags/ao.png DELETED
Binary file
resources/images/flags/ar.png DELETED
Binary file
resources/images/flags/at.png DELETED
Binary file
resources/images/flags/au.png DELETED
Binary file
resources/images/flags/az.png DELETED
Binary file
resources/images/flags/ba.png DELETED
Binary file
resources/images/flags/bb.png DELETED
Binary file
resources/images/flags/bd.png DELETED
Binary file
resources/images/flags/be.png DELETED
Binary file
resources/images/flags/bf.png DELETED
Binary file
resources/images/flags/bg.png DELETED
Binary file
resources/images/flags/bh.png DELETED
Binary file
resources/images/flags/bi.png DELETED
Binary file
resources/images/flags/bj.png DELETED
Binary file
resources/images/flags/bn.png DELETED
Binary file
resources/images/flags/bo.png DELETED
Binary file
resources/images/flags/br.png DELETED
Binary file
resources/images/flags/bs.png DELETED
Binary file
resources/images/flags/bt.png DELETED
Binary file
resources/images/flags/bw.png DELETED
Binary file
resources/images/flags/by.png DELETED
Binary file
resources/images/flags/bz.png DELETED
Binary file
resources/images/flags/ca.png DELETED
Binary file
resources/images/flags/cd.png DELETED
Binary file
resources/images/flags/cf.png DELETED
Binary file
resources/images/flags/cg.png DELETED
Binary file
resources/images/flags/ch.png DELETED
Binary file
resources/images/flags/ci.png DELETED
Binary file
resources/images/flags/cl.png DELETED
Binary file
resources/images/flags/cm.png DELETED
Binary file
resources/images/flags/cn.png DELETED
Binary file
resources/images/flags/co.png DELETED
Binary file
resources/images/flags/cr.png DELETED
Binary file
resources/images/flags/cu.png DELETED
Binary file
resources/images/flags/cv.png DELETED
Binary file
resources/images/flags/cy.png DELETED
Binary file
resources/images/flags/cz.png DELETED
Binary file
resources/images/flags/de.png DELETED
Binary file
resources/images/flags/dj.png DELETED
Binary file
resources/images/flags/dk.png DELETED
Binary file
resources/images/flags/dm.png DELETED
Binary file
resources/images/flags/do.png DELETED
Binary file
resources/images/flags/dz.png DELETED
Binary file
resources/images/flags/ec.png DELETED
Binary file
resources/images/flags/ee.png DELETED
Binary file
resources/images/flags/eg.png DELETED
Binary file
resources/images/flags/eh.png DELETED
Binary file
resources/images/flags/er.png DELETED
Binary file
resources/images/flags/es.png DELETED
Binary file
resources/images/flags/et.png DELETED
Binary file
resources/images/flags/fi.png DELETED
Binary file
resources/images/flags/fj.png DELETED
Binary file
resources/images/flags/fm.png DELETED
Binary file
resources/images/flags/fr.png DELETED
Binary file
resources/images/flags/ga.png DELETED
Binary file
resources/images/flags/gb.png DELETED
Binary file
resources/images/flags/gd.png DELETED
Binary file
resources/images/flags/ge.png DELETED
Binary file
resources/images/flags/gh.png DELETED
Binary file
resources/images/flags/gm.png DELETED
Binary file
resources/images/flags/gn.png DELETED
Binary file
resources/images/flags/gq.png DELETED
Binary file
resources/images/flags/gr.png DELETED
Binary file
resources/images/flags/gt.png DELETED
Binary file
resources/images/flags/gw.png DELETED
Binary file
resources/images/flags/gy.png DELETED
Binary file
resources/images/flags/hn.png DELETED
Binary file
resources/images/flags/hr.png DELETED
Binary file
resources/images/flags/ht.png DELETED
Binary file
resources/images/flags/hu.png DELETED
Binary file
resources/images/flags/id.png DELETED
Binary file
resources/images/flags/ie.png DELETED
Binary file
resources/images/flags/il.png DELETED
Binary file
resources/images/flags/in.png DELETED
Binary file
resources/images/flags/iq.png DELETED
Binary file
resources/images/flags/ir.png DELETED
Binary file
resources/images/flags/is.png DELETED
Binary file
resources/images/flags/it.png DELETED
Binary file
resources/images/flags/jm.png DELETED
Binary file
resources/images/flags/jo.png DELETED
Binary file
resources/images/flags/jp.png DELETED
Binary file
resources/images/flags/ke.png DELETED
Binary file
resources/images/flags/kg.png DELETED
Binary file
resources/images/flags/kh.png DELETED
Binary file
resources/images/flags/ki.png DELETED
Binary file
resources/images/flags/km.png DELETED
Binary file
resources/images/flags/kn.png DELETED
Binary file
resources/images/flags/kp.png DELETED
Binary file
resources/images/flags/kr.png DELETED
Binary file
resources/images/flags/ks.png DELETED
Binary file
resources/images/flags/kw.png DELETED
Binary file
resources/images/flags/kz.png DELETED
Binary file
resources/images/flags/la.png DELETED
Binary file
resources/images/flags/lb.png DELETED
Binary file
resources/images/flags/lc.png DELETED
Binary file
resources/images/flags/li.png DELETED
Binary file
resources/images/flags/lk.png DELETED
Binary file
resources/images/flags/lr.png DELETED
Binary file
resources/images/flags/ls.png DELETED
Binary file
resources/images/flags/lt.png DELETED
Binary file
resources/images/flags/lu.png DELETED
Binary file
resources/images/flags/lv.png DELETED
Binary file
resources/images/flags/ly.png DELETED
Binary file
resources/images/flags/ma.png DELETED
Binary file
resources/images/flags/mc.png DELETED
Binary file
resources/images/flags/md.png DELETED
Binary file
resources/images/flags/me.png DELETED
Binary file
resources/images/flags/mg.png DELETED
Binary file
resources/images/flags/mh.png DELETED
Binary file
resources/images/flags/mk.png DELETED
Binary file
resources/images/flags/ml.png DELETED
Binary file
resources/images/flags/mm.png DELETED
Binary file
resources/images/flags/mn.png DELETED
Binary file
resources/images/flags/mr.png DELETED
Binary file
resources/images/flags/mt.png DELETED
Binary file
resources/images/flags/mu.png DELETED
Binary file
resources/images/flags/mv.png DELETED
Binary file
resources/images/flags/mw.png DELETED
Binary file
resources/images/flags/mx.png DELETED
Binary file
resources/images/flags/my.png DELETED
Binary file
resources/images/flags/mz.png DELETED
Binary file
resources/images/flags/na.png DELETED
Binary file
resources/images/flags/ne.png DELETED
Binary file
resources/images/flags/ng.png DELETED
Binary file
resources/images/flags/ni.png DELETED
Binary file
resources/images/flags/nl.png DELETED
Binary file
resources/images/flags/no.png DELETED
Binary file
resources/images/flags/np.png DELETED
Binary file
resources/images/flags/nr.png DELETED
Binary file
resources/images/flags/nz.png DELETED
Binary file
resources/images/flags/om.png DELETED
Binary file
resources/images/flags/pa.png DELETED
Binary file
resources/images/flags/pe.png DELETED
Binary file
resources/images/flags/pg.png DELETED
Binary file
resources/images/flags/ph.png DELETED
Binary file
resources/images/flags/pk.png DELETED
Binary file
resources/images/flags/pl.png DELETED
Binary file
resources/images/flags/pt.png DELETED
Binary file
resources/images/flags/pw.png DELETED
Binary file
resources/images/flags/py.png DELETED
Binary file
resources/images/flags/qa.png DELETED
Binary file
resources/images/flags/ro.png DELETED
Binary file
resources/images/flags/rs.png DELETED
Binary file
resources/images/flags/ru.png DELETED
Binary file
resources/images/flags/rw.png DELETED
Binary file
resources/images/flags/sa.png DELETED
Binary file
resources/images/flags/sb.png DELETED
Binary file
resources/images/flags/sc.png DELETED
Binary file
resources/images/flags/sd.png DELETED
Binary file
resources/images/flags/se.png DELETED
Binary file
resources/images/flags/sg.png DELETED
Binary file
resources/images/flags/si.png DELETED
Binary file
resources/images/flags/sk.png DELETED
Binary file
resources/images/flags/sl.png DELETED
Binary file
resources/images/flags/sm.png DELETED
Binary file
resources/images/flags/sn.png DELETED
Binary file
resources/images/flags/so.png DELETED
Binary file
resources/images/flags/sr.png DELETED
Binary file
resources/images/flags/st.png DELETED
Binary file
resources/images/flags/sv.png DELETED
Binary file
resources/images/flags/sy.png DELETED
Binary file
resources/images/flags/sz.png DELETED
Binary file
resources/images/flags/td.png DELETED
Binary file
resources/images/flags/tg.png DELETED
Binary file
resources/images/flags/th.png DELETED
Binary file
resources/images/flags/tj.png DELETED
Binary file
resources/images/flags/tl.png DELETED
Binary file
resources/images/flags/tm.png DELETED
Binary file
resources/images/flags/tn.png DELETED
Binary file
resources/images/flags/to.png DELETED
Binary file
resources/images/flags/tr.png DELETED
Binary file
resources/images/flags/tt.png DELETED
Binary file
resources/images/flags/tv.png DELETED
Binary file
resources/images/flags/tw.png DELETED
Binary file
resources/images/flags/tz.png DELETED
Binary file
resources/images/flags/ua.png DELETED
Binary file
resources/images/flags/ug.png DELETED
Binary file
resources/images/flags/us.png DELETED
Binary file
resources/images/flags/uy.png DELETED
Binary file
resources/images/flags/uz.png DELETED
Binary file
resources/images/flags/va.png DELETED
Binary file
resources/images/flags/vc.png DELETED
Binary file
resources/images/flags/ve.png DELETED
Binary file
resources/images/flags/vn.png DELETED
Binary file
resources/images/flags/vu.png DELETED
Binary file
resources/images/flags/ws.png DELETED
Binary file
resources/images/flags/ye.png DELETED
Binary file
resources/images/flags/za.png DELETED
Binary file
resources/images/flags/zm.png DELETED
Binary file
resources/images/flags/zw.png DELETED
Binary file
resources/js/shield-antibot.js CHANGED
@@ -3,15 +3,14 @@ if ( typeof icwp_wpsf_vars_lpantibot !== 'undefined' ) {
3
 
4
  this.initialise = function () {
5
  jQuery( document ).ready( function () {
6
- jQuery.each( icwp_wpsf_vars_lpantibot.form_ids,
7
- function ( _, form_id ) {
8
- var form = document.getElementById( form_id );
9
- if ( form !== null ) {
10
  if ( icwp_wpsf_vars_lpantibot.flags.recap ) {
11
- insertPlaceHolder_Recap( form );
12
  }
13
  if ( icwp_wpsf_vars_lpantibot.flags.gasp ) {
14
- insertPlaceHolder_Gasp( form );
15
  }
16
  }
17
  }
3
 
4
  this.initialise = function () {
5
  jQuery( document ).ready( function () {
6
+ jQuery( icwp_wpsf_vars_lpantibot.form_selectors ).each(
7
+ function ( _ ) {
8
+ if ( this !== null ) {
 
9
  if ( icwp_wpsf_vars_lpantibot.flags.recap ) {
10
+ insertPlaceHolder_Recap( this );
11
  }
12
  if ( icwp_wpsf_vars_lpantibot.flags.gasp ) {
13
+ insertPlaceHolder_Gasp( this );
14
  }
15
  }
16
  }
src/common/icwp-wpfunctions.php CHANGED
@@ -728,7 +728,8 @@ class ICWP_WPSF_WpFunctions extends ICWP_WPSF_Foundation {
728
  public function isRest() {
729
  $bIsRest = ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || !empty( $_REQUEST[ 'rest_route' ] );
730
 
731
- if ( !$bIsRest && function_exists( 'rest_url' ) ) {
 
732
  $sRestUrlBase = get_rest_url( get_current_blog_id(), '/' );
733
  $sRestPath = trim( parse_url( $sRestUrlBase, PHP_URL_PATH ), '/' );
734
  $sRequestPath = trim( $this->loadRequest()->getPath(), '/' );
728
  public function isRest() {
729
  $bIsRest = ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || !empty( $_REQUEST[ 'rest_route' ] );
730
 
731
+ global $wp_rewrite;
732
+ if ( !$bIsRest && function_exists( 'rest_url' ) && is_object( $wp_rewrite ) ) {
733
  $sRestUrlBase = get_rest_url( get_current_blog_id(), '/' );
734
  $sRestPath = trim( parse_url( $sRestUrlBase, PHP_URL_PATH ), '/' );
735
  $sRequestPath = trim( $this->loadRequest()->getPath(), '/' );
src/config/feature-admin_access_restriction.php CHANGED
@@ -96,6 +96,19 @@
96
  "summary": "Provide/Update Security Admin Access Key",
97
  "description": "Careful: If you forget this, you could potentially lock yourself out from using this plugin."
98
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  {
100
  "key": "admin_access_timeout",
101
  "section": "section_admin_access_restriction_settings",
96
  "summary": "Provide/Update Security Admin Access Key",
97
  "description": "Careful: If you forget this, you could potentially lock yourself out from using this plugin."
98
  },
99
+ {
100
+ "key": "sec_admin_users",
101
+ "section": "section_admin_access_restriction_settings",
102
+ "sensitive": true,
103
+ "premium": true,
104
+ "default": "",
105
+ "type": "array",
106
+ "link_info": "https://icwp.io/dk",
107
+ "link_blog": "",
108
+ "name": "Security Admins",
109
+ "summary": "Persistent Security Admins",
110
+ "description": "All emails, usernames, or user IDs entered here will always be Security Admins."
111
+ },
112
  {
113
  "key": "admin_access_timeout",
114
  "section": "section_admin_access_restriction_settings",
src/config/feature-autoupdates.php CHANGED
@@ -160,6 +160,31 @@
160
  "summary": "Delay Automatic Updates For Period Of Stability",
161
  "description": "Shield will delay upgrades until the new update has been available for the set number of days."
162
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  {
164
  "key": "enable_upgrade_notification_email",
165
  "section": "section_options",
160
  "summary": "Delay Automatic Updates For Period Of Stability",
161
  "description": "Shield will delay upgrades until the new update has been available for the set number of days."
162
  },
163
+ {
164
+ "key": "autoupdate_plugin_self",
165
+ "section": "section_options",
166
+ "default": "auto",
167
+ "type": "select",
168
+ "value_options": [
169
+ {
170
+ "value_key": "auto",
171
+ "text": "Let Shield Decide"
172
+ },
173
+ {
174
+ "value_key": "disabled",
175
+ "text": "Disabled"
176
+ },
177
+ {
178
+ "value_key": "immediate",
179
+ "text": "As Soon As Possible"
180
+ }
181
+ ],
182
+ "link_info": "https://icwp.io/3x",
183
+ "link_blog": "",
184
+ "name": "WordPress Core Updates",
185
+ "summary": "Decide how the WordPress Core will automatically update, if at all",
186
+ "description": "At least automatically upgrading minor versions is recommended (and is the WordPress default)."
187
+ },
188
  {
189
  "key": "enable_upgrade_notification_email",
190
  "section": "section_options",
src/config/feature-firewall.php CHANGED
@@ -253,6 +253,7 @@
253
  {
254
  "key": "text_firewalldie",
255
  "section": "section_user_messages",
 
256
  "premium": true,
257
  "default": "default",
258
  "type": "text",
@@ -306,7 +307,9 @@
306
  "redirect_to",
307
  "jetpack_sso_original_request",
308
  "jetpack_sso_redirect_to",
309
- "/^wordpress_logged_in_[0-9a-f]+$/"
 
 
310
  ]
311
  },
312
  "firewall_patterns": {
253
  {
254
  "key": "text_firewalldie",
255
  "section": "section_user_messages",
256
+ "sensitive": true,
257
  "premium": true,
258
  "default": "default",
259
  "type": "text",
307
  "redirect_to",
308
  "jetpack_sso_original_request",
309
  "jetpack_sso_redirect_to",
310
+ "/^wordpress_logged_in_[0-9a-f]+$/",
311
+ "edd_action",
312
+ "edd_redirect"
313
  ]
314
  },
315
  "firewall_patterns": {
src/config/feature-license.php CHANGED
@@ -51,46 +51,12 @@
51
  "default": 0,
52
  "section": "section_non_ui"
53
  },
54
- {
55
- "key": "license_last_request_at",
56
- "transferable": false,
57
- "default": 0,
58
- "section": "section_non_ui"
59
- },
60
- {
61
- "key": "license_verified_at",
62
- "sensitive": true,
63
- "transferable": false,
64
- "default": 0,
65
- "section": "section_non_ui"
66
- },
67
- {
68
- "key": "license_expires_at",
69
- "sensitive": true,
70
- "transferable": false,
71
- "default": 0,
72
- "section": "section_non_ui"
73
- },
74
- {
75
- "key": "license_official_status",
76
- "sensitive": true,
77
- "transferable": false,
78
- "default": "",
79
- "section": "section_non_ui"
80
- },
81
  {
82
  "key": "license_deactivated_reason",
83
  "transferable": false,
84
  "default": "",
85
  "section": "section_non_ui"
86
  },
87
- {
88
- "key": "license_registered_email",
89
- "sensitive": true,
90
- "transferable": false,
91
- "default": "",
92
- "section": "section_non_ui"
93
- },
94
  {
95
  "key": "last_warning_email_sent_at",
96
  "transferable": false,
@@ -98,10 +64,9 @@
98
  "section": "section_non_ui"
99
  },
100
  {
101
- "key": "is_shield_central",
102
- "sensitive": true,
103
  "transferable": false,
104
- "default": false,
105
  "section": "section_non_ui"
106
  },
107
  {
51
  "default": 0,
52
  "section": "section_non_ui"
53
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  {
55
  "key": "license_deactivated_reason",
56
  "transferable": false,
57
  "default": "",
58
  "section": "section_non_ui"
59
  },
 
 
 
 
 
 
 
60
  {
61
  "key": "last_warning_email_sent_at",
62
  "transferable": false,
64
  "section": "section_non_ui"
65
  },
66
  {
67
+ "key": "last_deactivated_email_sent_at",
 
68
  "transferable": false,
69
+ "default": 0,
70
  "section": "section_non_ui"
71
  },
72
  {
src/config/feature-login_protect.php CHANGED
@@ -354,8 +354,8 @@
354
  "section": "section_brute_force_login_protection",
355
  "type": "array",
356
  "default": [
357
- "ihc_login_form",
358
- "createuser"
359
  ],
360
  "link_info": "",
361
  "link_blog": "",
354
  "section": "section_brute_force_login_protection",
355
  "type": "array",
356
  "default": [
357
+ "form#ihc_login_form",
358
+ "form#createuser"
359
  ],
360
  "link_info": "",
361
  "link_blog": "",
src/config/feature-plugin.php CHANGED
@@ -319,9 +319,9 @@
319
  "type": "text",
320
  "link_info": "https://icwp.io/shld5",
321
  "link_blog": "",
322
- "name": "reCAPTCHA Secret",
323
- "summary": "Google reCAPTCHA Secret Key",
324
- "description": "Enter your Google reCAPTCHA secret key for use throughout the plugin."
325
  },
326
  {
327
  "key": "google_recaptcha_secret_key",
@@ -331,9 +331,9 @@
331
  "type": "text",
332
  "link_info": "https://icwp.io/shld5",
333
  "link_blog": "",
334
- "name": "reCAPTCHA Site Key",
335
- "summary": "Google reCAPTCHA Site Key",
336
- "description": "Enter your Google reCAPTCHA site key for use throughout the plugin."
337
  },
338
  {
339
  "key": "tracking_last_sent_at",
@@ -377,19 +377,19 @@
377
  "transferable": false,
378
  "sensitive": true,
379
  "section": "section_non_ui",
380
- "value": ""
381
  },
382
  {
383
  "key": "this_server_ip_last_check_at",
384
  "transferable": false,
385
  "section": "section_non_ui",
386
- "value": 0
387
  },
388
  {
389
  "key": "insights_test_cron_last_run_at",
390
  "transferable": false,
391
  "section": "section_non_ui",
392
- "value": 0
393
  }
394
  ],
395
  "definitions": {
319
  "type": "text",
320
  "link_info": "https://icwp.io/shld5",
321
  "link_blog": "",
322
+ "name": "reCAPTCHA Site Key",
323
+ "summary": "Google reCAPTCHA Site Key",
324
+ "description": "Enter your Google reCAPTCHA site key for use throughout the plugin."
325
  },
326
  {
327
  "key": "google_recaptcha_secret_key",
331
  "type": "text",
332
  "link_info": "https://icwp.io/shld5",
333
  "link_blog": "",
334
+ "name": "reCAPTCHA Secret",
335
+ "summary": "Google reCAPTCHA Secret Key",
336
+ "description": "Enter your Google reCAPTCHA secret key for use throughout the plugin."
337
  },
338
  {
339
  "key": "tracking_last_sent_at",
377
  "transferable": false,
378
  "sensitive": true,
379
  "section": "section_non_ui",
380
+ "default": ""
381
  },
382
  {
383
  "key": "this_server_ip_last_check_at",
384
  "transferable": false,
385
  "section": "section_non_ui",
386
+ "default": 0
387
  },
388
  {
389
  "key": "insights_test_cron_last_run_at",
390
  "transferable": false,
391
  "section": "section_non_ui",
392
+ "default": 0
393
  }
394
  ],
395
  "definitions": {
src/features/admin_access_restriction.php CHANGED
@@ -10,13 +10,11 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
10
 
11
  const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
12
 
13
- private $bHasPermissionToSubmit;
14
-
15
  /**
16
  * @return bool
17
  */
18
  protected function isReadyToExecute() {
19
- return $this->hasAccessKey() && parent::isReadyToExecute();
20
  }
21
 
22
  /**
@@ -31,6 +29,7 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
31
  case 'sec_admin_check':
32
  $aAjaxResponse = $this->ajaxExec_SecAdminCheck();
33
  break;
 
34
  case 'sec_admin_login':
35
  case 'restricted_access':
36
  $aAjaxResponse = $this->ajaxExec_SecAdminLogin();
@@ -110,22 +109,6 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
110
  return $this->renderTemplate( 'snippets/admin_access_login', $aData );
111
  }
112
 
113
- /**
114
- * @param bool $bHasPermission
115
- * @return bool
116
- */
117
- public function doCheckHasPermissionToSubmit( $bHasPermission = true ) {
118
-
119
- $this->bHasPermissionToSubmit = $bHasPermission;
120
- if ( $this->isModuleEnabled() ) {
121
- $sAccessKey = $this->getAccessKeyHash();
122
- if ( !empty( $sAccessKey ) ) {
123
- $this->bHasPermissionToSubmit = $this->isSecAdminSessionValid() || $this->checkAdminAccessKeySubmission();
124
- }
125
- }
126
- return $this->bHasPermissionToSubmit;
127
- }
128
-
129
  /**
130
  * @return string
131
  */
@@ -178,6 +161,14 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
178
  return is_array( $aOptions ) ? $aOptions : array();
179
  }
180
 
 
 
 
 
 
 
 
 
181
  /**
182
  * TODO: Bug where if $sType is defined, it'll be set to 'wp' anyway
183
  * @param string $sType - wp or wpms
@@ -207,6 +198,23 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
207
  return !empty( $sKey ) && strlen( $sKey ) == 32;
208
  }
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  /**
211
  * @return bool
212
  */
@@ -236,11 +244,50 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
236
  }
237
  }
238
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
240
 
241
  protected function setSaveUserResponse() {
242
  if ( $this->isAccessKeyRequest() ) {
243
- $bSuccess = $this->doCheckHasPermissionToSubmit();
244
 
245
  if ( $bSuccess ) {
246
  $sMessage = _wpsf__( 'Security Admin key accepted.' );
@@ -268,8 +315,15 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
268
  */
269
  public function getSecAdminTimeLeft() {
270
  $nLeft = 0;
271
- if ( $this->isReadyToExecute() && $this->hasSession() ) {
272
- $nLeft = $this->getSecAdminTimeout() - ( $this->loadRequest()->ts() - $this->getSession()->getSecAdminAt() );
 
 
 
 
 
 
 
273
  }
274
  return max( 0, $nLeft );
275
  }
@@ -295,7 +349,7 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
295
  /**
296
  * @return bool
297
  */
298
- protected function checkAdminAccessKeySubmission() {
299
  $sAccessKeyRequest = $this->loadRequest()->post( 'admin_access_key_request', '' );
300
  $bSuccess = $this->verifyAccessKey( $sAccessKeyRequest );
301
  if ( !$bSuccess && !empty( $sAccessKeyRequest ) ) {
@@ -389,12 +443,12 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
389
  * @throws Exception
390
  */
391
  public function setNewAccessKeyManually( $sKey ) {
392
- if ( !$this->doCheckHasPermissionToSubmit() ) {
393
- throw new Exception( 'User does not have permission to update the Security Admin Access Key.' );
394
- }
395
  if ( empty( $sKey ) ) {
396
  throw new Exception( 'Attempting to set an empty Security Admin Access Key.' );
397
  }
 
 
 
398
 
399
  $this->setIsMainFeatureEnabled( true )
400
  ->setOpt( 'admin_access_key', md5( $sKey ) )
@@ -421,7 +475,7 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
421
  );
422
 
423
  {//sec admin
424
- if ( !( $this->isModuleEnabled() && $this->hasAccessKey() ) ) {
425
  $aNotices[ 'messages' ][ 'sec_admin' ] = array(
426
  'title' => 'Security Plugin Unprotected',
427
  'message' => sprintf(
@@ -445,7 +499,8 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
445
  * @return bool
446
  */
447
  protected function isEnabledForUiSummary() {
448
- return parent::isEnabledForUiSummary() && $this->hasAccessKey() && $this->getSecAdminTimeout() > 0;
 
449
  }
450
 
451
  /**
@@ -538,6 +593,14 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
538
  .( $this->hasAccessKey() ? '<br/>'.sprintf( _wpsf__( 'To delete the current security key, type exactly "%s" and save.' ), '<strong>DELETE</strong>' ) : '' );
539
  break;
540
 
 
 
 
 
 
 
 
 
541
  case 'admin_access_timeout' :
542
  $sName = _wpsf__( 'Security Admin Timeout' );
543
  $sSummary = _wpsf__( 'Specify An Automatic Timeout Interval For Security Admin Access' );
@@ -702,4 +765,12 @@ class ICWP_WPSF_FeatureHandler_AdminAccessRestriction extends ICWP_WPSF_FeatureH
702
  );
703
  }
704
  }
 
 
 
 
 
 
 
 
705
  }
10
 
11
  const HASH_DELETE = '32f68a60cef40faedbc6af20298c1a1e';
12
 
 
 
13
  /**
14
  * @return bool
15
  */
16
  protected function isReadyToExecute() {
17
+ return ( $this->hasAccessKey() || $this->hasSecAdminUsers() ) && parent::isReadyToExecute();
18
  }
19
 
20
  /**
29
  case 'sec_admin_check':
30
  $aAjaxResponse = $this->ajaxExec_SecAdminCheck();
31
  break;
32
+
33
  case 'sec_admin_login':
34
  case 'restricted_access':
35
  $aAjaxResponse = $this->ajaxExec_SecAdminLogin();
109
  return $this->renderTemplate( 'snippets/admin_access_login', $aData );
110
  }
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  /**
113
  * @return string
114
  */
161
  return is_array( $aOptions ) ? $aOptions : array();
162
  }
163
 
164
+ /**
165
+ * @return array
166
+ */
167
+ public function getSecurityAdminUsers() {
168
+ $aU = $this->getOpt( 'sec_admin_users', array() );
169
+ return ( is_array( $aU ) && $this->isPremium() ) ? $aU : array();
170
+ }
171
+
172
  /**
173
  * TODO: Bug where if $sType is defined, it'll be set to 'wp' anyway
174
  * @param string $sType - wp or wpms
198
  return !empty( $sKey ) && strlen( $sKey ) == 32;
199
  }
200
 
201
+ /**
202
+ * @return bool
203
+ */
204
+ public function hasSecAdminUsers() {
205
+ $aUsers = $this->getSecurityAdminUsers();
206
+ return !empty( $aUsers );
207
+ }
208
+
209
+ /**
210
+ * No checking of admin capabilities in-case of infinite loop with admin access caps check
211
+ * @return bool
212
+ */
213
+ public function isRegisteredSecAdminUser() {
214
+ $sUser = $this->loadWpUsers()->getCurrentWpUsername();
215
+ return !empty( $sUser ) && in_array( $sUser, $this->getSecurityAdminUsers() );
216
+ }
217
+
218
  /**
219
  * @return bool
220
  */
244
  }
245
  }
246
  }
247
+
248
+ $this->setOpt( 'sec_admin_users', $this->verifySecAdminUsers( $this->getSecurityAdminUsers() ) );
249
+ }
250
+
251
+ /**
252
+ * Ensures that all entries are valid users.
253
+ * @param string[] $aSecUsers
254
+ * @return string[]
255
+ */
256
+ private function verifySecAdminUsers( $aSecUsers ) {
257
+ $oDP = $this->loadDP();
258
+ $oWpUsers = $this->loadWpUsers();
259
+
260
+ $aFiltered = array();
261
+ foreach ( $aSecUsers as $nCurrentKey => $sUsernameOrEmail ) {
262
+ if ( $oDP->validEmail( $sUsernameOrEmail ) ) {
263
+ $oUser = $oWpUsers->getUserByEmail( $sUsernameOrEmail );
264
+ }
265
+ else {
266
+ $oUser = $oWpUsers->getUserByUsername( $sUsernameOrEmail );
267
+ if ( is_null( $oUser ) && is_numeric( $sUsernameOrEmail ) ) {
268
+ $oUser = $oWpUsers->getUserById( $sUsernameOrEmail );
269
+ }
270
+ }
271
+
272
+ if ( $oUser instanceof WP_User && $oUser->ID > 0 && $oWpUsers->isUserAdmin( $oUser ) ) {
273
+ $aFiltered[] = $oUser->user_login;
274
+ }
275
+ }
276
+
277
+ // We now run a bit of a sanity check to ensure that the current user is
278
+ // not adding users here that aren't themselves without a key to still gain access
279
+ $oCurrent = $oWpUsers->getCurrentWpUser();
280
+ if ( !empty( $aFiltered ) && !$this->hasAccessKey() && !in_array( $oCurrent->user_login, $aFiltered ) ) {
281
+ $aFiltered[] = $oCurrent->user_login;
282
+ }
283
+
284
+ natsort( $aFiltered );
285
+ return array_unique( $aFiltered );
286
  }
287
 
288
  protected function setSaveUserResponse() {
289
  if ( $this->isAccessKeyRequest() ) {
290
+ $bSuccess = $this->checkAdminAccessKeySubmission();
291
 
292
  if ( $bSuccess ) {
293
  $sMessage = _wpsf__( 'Security Admin key accepted.' );
315
  */
316
  public function getSecAdminTimeLeft() {
317
  $nLeft = 0;
318
+ if ( $this->hasSession() ) {
319
+
320
+ $nSecAdminAt = $this->getSession()->getSecAdminAt();
321
+ if ( $this->isRegisteredSecAdminUser() ) {
322
+ $nLeft = 0;
323
+ }
324
+ else if ( $nSecAdminAt > 0 ) {
325
+ $nLeft = $this->getSecAdminTimeout() - ( $this->loadRequest()->ts() - $nSecAdminAt );
326
+ }
327
  }
328
  return max( 0, $nLeft );
329
  }
349
  /**
350
  * @return bool
351
  */
352
+ public function checkAdminAccessKeySubmission() {
353
  $sAccessKeyRequest = $this->loadRequest()->post( 'admin_access_key_request', '' );
354
  $bSuccess = $this->verifyAccessKey( $sAccessKeyRequest );
355
  if ( !$bSuccess && !empty( $sAccessKeyRequest ) ) {
443
  * @throws Exception
444
  */
445
  public function setNewAccessKeyManually( $sKey ) {
 
 
 
446
  if ( empty( $sKey ) ) {
447
  throw new Exception( 'Attempting to set an empty Security Admin Access Key.' );
448
  }
449
+ if ( !$this->getConn()->isPluginAdmin() ) {
450
+ throw new Exception( 'User does not have permission to update the Security Admin Access Key.' );
451
+ }
452
 
453
  $this->setIsMainFeatureEnabled( true )
454
  ->setOpt( 'admin_access_key', md5( $sKey ) )
475
  );
476
 
477
  {//sec admin
478
+ if ( !( $this->isModuleEnabled() && ( $this->hasAccessKey() || $this->hasSecAdminUsers() ) ) ) {
479
  $aNotices[ 'messages' ][ 'sec_admin' ] = array(
480
  'title' => 'Security Plugin Unprotected',
481
  'message' => sprintf(
499
  * @return bool
500
  */
501
  protected function isEnabledForUiSummary() {
502
+ return parent::isEnabledForUiSummary() && ( $this->getSecAdminTimeout() > 0 )
503
+ && ( $this->hasAccessKey() || $this->hasSecAdminUsers() );
504
  }
505
 
506
  /**
593
  .( $this->hasAccessKey() ? '<br/>'.sprintf( _wpsf__( 'To delete the current security key, type exactly "%s" and save.' ), '<strong>DELETE</strong>' ) : '' );
594
  break;
595
 
596
+ case 'sec_admin_users' :
597
+ $sName = _wpsf__( 'Security Admins' );
598
+ $sSummary = _wpsf__( 'Persistent Security Admins' );
599
+ $sDescription = _wpsf__( "Users provided will be security admins automatically, without needing the security key." )
600
+ .'<br/>'._wpsf__( 'Enter admin username, email or ID.' ).' '._wpsf__( '1 entry per-line.' )
601
+ .'<br/>'.sprintf( '%s: %s', _wpsf__( 'Note' ), _wpsf__( 'Verified users will be converted to usernames.' ) );
602
+ break;
603
+
604
  case 'admin_access_timeout' :
605
  $sName = _wpsf__( 'Security Admin Timeout' );
606
  $sSummary = _wpsf__( 'Specify An Automatic Timeout Interval For Security Admin Access' );
765
  );
766
  }
767
  }
768
+
769
+ /**
770
+ * @deprecated v6.10.7
771
+ * @return bool
772
+ */
773
+ public function doCheckHasPermissionToSubmit() {
774
+ return $this->getConn()->isPluginAdmin();
775
+ }
776
  }
src/features/audit_trail.php CHANGED
@@ -331,8 +331,7 @@ class ICWP_WPSF_FeatureHandler_AuditTrail extends ICWP_WPSF_FeatureHandler_BaseW
331
  * @throws Exception
332
  */
333
  protected function loadStrings_Options( $aOptionsParams ) {
334
-
335
- $oCon = self::getConn();
336
 
337
  $sKey = $aOptionsParams[ 'key' ];
338
  switch ( $sKey ) {
331
  * @throws Exception
332
  */
333
  protected function loadStrings_Options( $aOptionsParams ) {
334
+ $oCon = $this->getConn();
 
335
 
336
  $sKey = $aOptionsParams[ 'key' ];
337
  switch ( $sKey ) {
src/features/autoupdates.php CHANGED
@@ -112,9 +112,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
112
  switch ( $this->loadRequest()->request( 'exec' ) ) {
113
 
114
  case 'toggle_plugin_autoupdate':
115
- if ( $this->isAutoupdateIndividualPlugins() && $this->getConn()->isValidAdminArea() ) {
116
- $aAjaxResponse = $this->ajaxExec_TogglePluginAutoupdate();
117
- }
118
  break;
119
 
120
  default:
@@ -128,24 +126,26 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
128
  * @return array
129
  */
130
  public function ajaxExec_TogglePluginAutoupdate() {
131
-
132
  $bSuccess = false;
133
-
134
- $oWpPlugins = $this->loadWpPlugins();
135
- $sFile = $this->loadRequest()->post( 'pluginfile' );
136
- if ( $oWpPlugins->isInstalled( $sFile ) ) {
137
- $this->setPluginToAutoUpdate( $sFile );
138
-
139
- $aPlugin = $oWpPlugins->getPlugin( $sFile );
140
- $sMessage = sprintf( _wpsf__( 'Plugin "%s" will %s.' ),
141
- $aPlugin[ 'Name' ],
142
- $this->loadWp()
143
- ->getIsPluginAutomaticallyUpdated( $sFile ) ? _wpsf__( 'update automatically' ) : _wpsf__( 'not update automatically' )
144
- );
145
- $bSuccess = true;
146
- }
147
- else {
148
- $sMessage = _wpsf__( 'Failed to change the update status of the plugin.' );
 
 
 
149
  }
150
 
151
  return array(
@@ -180,6 +180,7 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
180
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
181
 
182
  $sSectionSlug = $aOptionsParams[ 'slug' ];
 
183
  switch ( $sSectionSlug ) {
184
 
185
  case 'section_enable_plugin_feature_automatic_updates_control' :
@@ -203,8 +204,10 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
203
  case 'section_automatic_plugin_self_update' :
204
  $sTitle = _wpsf__( 'Automatic Plugin Self-Update' );
205
  $aSummary = array(
206
- sprintf( '%s - %s', _wpsf__( 'Purpose' ), sprintf( _wpsf__( 'Allows the %s plugin to automatically update itself when an update is available.' ), self::getConn()
207
- ->getHumanName() ) ),
 
 
208
  sprintf( '%s - %s', _wpsf__( 'Recommendation' ), _wpsf__( 'Keep this option turned on.' ) )
209
  );
210
  $sTitleShort = _wpsf__( 'Self-Update' );
@@ -262,8 +265,10 @@ class ICWP_WPSF_FeatureHandler_Autoupdates extends ICWP_WPSF_FeatureHandler_Base
262
  case 'autoupdate_plugin_self' :
263
  $sName = _wpsf__( 'Auto Update Plugin' );
264
  $sSummary = _wpsf__( 'Always Automatically Update This Plugin' );
265
- $sDescription = sprintf( _wpsf__( 'Regardless of any component settings below, automatically update the "%s" plugin.' ), self::getConn()
266
- ->getHumanName() );
 
 
267
  break;
268
 
269
  case 'autoupdate_core' :
112
  switch ( $this->loadRequest()->request( 'exec' ) ) {
113
 
114
  case 'toggle_plugin_autoupdate':
115
+ $aAjaxResponse = $this->ajaxExec_TogglePluginAutoupdate();
 
 
116
  break;
117
 
118
  default:
126
  * @return array
127
  */
128
  public function ajaxExec_TogglePluginAutoupdate() {
 
129
  $bSuccess = false;
130
+ $sMessage = _wpsf__( 'You do not have permissions to perform this action.' );
131
+
132
+ if ( $this->isAutoupdateIndividualPlugins() && $this->getConn()->isPluginAdmin() ) {
133
+ $oWpPlugins = $this->loadWpPlugins();
134
+ $sFile = $this->loadRequest()->post( 'pluginfile' );
135
+ if ( $oWpPlugins->isInstalled( $sFile ) ) {
136
+ $this->setPluginToAutoUpdate( $sFile );
137
+
138
+ $aPlugin = $oWpPlugins->getPlugin( $sFile );
139
+ $sMessage = sprintf( _wpsf__( 'Plugin "%s" will %s.' ),
140
+ $aPlugin[ 'Name' ],
141
+ $this->loadWp()
142
+ ->getIsPluginAutomaticallyUpdated( $sFile ) ? _wpsf__( 'update automatically' ) : _wpsf__( 'not update automatically' )
143
+ );
144
+ $bSuccess = true;
145
+ }
146
+ else {
147
+ $sMessage = _wpsf__( 'Failed to change the update status of the plugin.' );
148
+ }
149
  }
150
 
151
  return array(
180
  protected function loadStrings_SectionTitles( $aOptionsParams ) {
181
 
182
  $sSectionSlug = $aOptionsParams[ 'slug' ];
183
+ $sPlugName = $this->getConn()->getHumanName();
184
  switch ( $sSectionSlug ) {
185
 
186
  case 'section_enable_plugin_feature_automatic_updates_control' :
204
  case 'section_automatic_plugin_self_update' :
205
  $sTitle = _wpsf__( 'Automatic Plugin Self-Update' );
206
  $aSummary = array(
207
+ sprintf( '%s - %s',
208
+ _wpsf__( 'Purpose' ),
209
+ sprintf( _wpsf__( 'Allows the %s plugin to automatically update itself when an update is available.' ), $sPlugName )
210
+ ),
211
  sprintf( '%s - %s', _wpsf__( 'Recommendation' ), _wpsf__( 'Keep this option turned on.' ) )
212
  );
213
  $sTitleShort = _wpsf__( 'Self-Update' );
265
  case 'autoupdate_plugin_self' :
266
  $sName = _wpsf__( 'Auto Update Plugin' );
267
  $sSummary = _wpsf__( 'Always Automatically Update This Plugin' );
268
+ $sDescription = sprintf(
269
+ _wpsf__( 'Regardless of any other settings, automatically update the "%s" plugin.' ),
270
+ $sPlugName
271
+ );
272
  break;
273
 
274
  case 'autoupdate_core' :
src/features/base.php CHANGED
@@ -274,7 +274,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
274
  if ( $this->getOptionsVo()->getFeatureProperty( 'auto_load_processor' ) ) {
275
  $this->loadProcessor();
276
  }
277
- if ( $this->isModuleEnabled() && $this->isReadyToExecute() ) {
278
  $this->doExecuteProcessor();
279
  }
280
  }
@@ -297,7 +297,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
297
  protected function importOptions() {
298
  // So we don't poll for the file every page load.
299
  if ( $this->loadRequest()->query( 'icwp_shield_import' ) == 1 ) {
300
- $aOptions = self::getConn()->getOptionsImportFromFile();
301
  if ( !empty( $aOptions ) && is_array( $aOptions ) && array_key_exists( $this->getOptionsStorageKey(), $aOptions ) ) {
302
  $this->getOptionsVo()->setMultipleOptions( $aOptions[ $this->getOptionsStorageKey() ] );
303
  $this
@@ -356,8 +356,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
356
  */
357
  protected function loadProcessor() {
358
  if ( !isset( $this->oProcessor ) ) {
359
- include_once( self::getConn()
360
- ->getPath_SourceFile( sprintf( 'processors/%s.php', $this->getSlug() ) ) );
361
  $sClassName = $this->getProcessorClassName();
362
  if ( !class_exists( $sClassName, false ) ) {
363
  return null;
@@ -372,7 +372,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
372
  * @return string
373
  */
374
  protected function getProcessorClassName() {
375
- return ucwords( self::getConn()->getOptionStoragePrefix() ).'Processor_'.
376
  str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) );
377
  }
378
 
@@ -381,7 +381,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
381
  * @return string
382
  */
383
  protected function getWizardClassName() {
384
- return ucwords( self::getConn()->getOptionStoragePrefix() ).'Wizard_'.
385
  str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) );
386
  }
387
 
@@ -390,7 +390,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
390
  */
391
  protected function getOptionsVo() {
392
  if ( !isset( $this->oOptions ) ) {
393
- $oCon = self::getConn();
394
  $this->oOptions = ICWP_WPSF_Factory::OptionsVo();
395
  $this->oOptions
396
  ->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
@@ -414,8 +414,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
414
  * @return bool
415
  */
416
  public function isUpgrading() {
417
- // return $this->getVersion() != self::getController()->getVersion();
418
- return self::getConn()->getIsRebuildOptionsFromFile() || $this->getOptionsVo()->getRebuildFromFile();
419
  }
420
 
421
  /**
@@ -460,7 +460,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
460
  return $this->loadWp()
461
  ->getUrl_AdminPage(
462
  $this->getModSlug(),
463
- self::getConn()->getIsWpmsNetworkAdminOnly()
464
  );
465
  }
466
 
@@ -470,7 +470,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
470
  */
471
  public function getEmailHandler() {
472
  if ( is_null( self::$oEmailHandler ) ) {
473
- self::$oEmailHandler = self::getConn()->loadFeatureHandler( array( 'slug' => 'email' ) );
474
  }
475
  return self::$oEmailHandler;
476
  }
@@ -505,7 +505,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
505
  else if ( apply_filters( $this->prefix( 'globally_disabled' ), false ) ) {
506
  $bEnabled = false;
507
  }
508
- else if ( self::getConn()->getIfForceOffActive() ) {
509
  $bEnabled = false;
510
  }
511
  else if ( $oOpts->getFeatureProperty( 'premium' ) === true && !$this->isPremium() ) {
@@ -550,23 +550,6 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
550
  return $this->sModSlug;
551
  }
552
 
553
- /**
554
- * @return int
555
- */
556
- public function getPluginInstallationTime() {
557
- return $this->getOpt( 'installation_time', 0 );
558
- }
559
-
560
- /**
561
- * With trailing slash
562
- * @param string $sSourceFile
563
- * @return string
564
- */
565
- public function getResourcesDir( $sSourceFile = '' ) {
566
- return self::getConn()
567
- ->getRootDir().'resources/'.ltrim( $sSourceFile, '/' );
568
- }
569
-
570
  /**
571
  * @param array $aItems
572
  * @return array
@@ -578,7 +561,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
578
  }
579
  if ( !empty( $sMenuTitleName ) ) {
580
 
581
- $sHumanName = self::getConn()->getHumanName();
582
 
583
  $bMenuHighlighted = $this->getOptionsVo()->getFeatureProperty( 'highlight_menu_item' );
584
  if ( $bMenuHighlighted ) {
@@ -887,7 +870,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
887
  */
888
  protected function getWizardHandler() {
889
  if ( !isset( $this->oWizard ) ) {
890
- include_once( self::getConn()->getPath_SourceFile( sprintf( 'wizards/%s.php', $this->getSlug() ) ) );
891
  $sClassName = $this->getWizardClassName();
892
  if ( !class_exists( $sClassName, false ) ) {
893
  return null;
@@ -916,9 +899,9 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
916
  }
917
 
918
  private function store() {
919
- add_filter( $this->prefix( 'bypass_permission_to_manage' ), '__return_true', 1000 );
920
- $this->getOptionsVo()->doOptionsSave( self::getConn()->getIsResetPlugin() );
921
- remove_filter( $this->prefix( 'bypass_permission_to_manage' ), '__return_true', 1000 );
922
  }
923
 
924
  /**
@@ -937,7 +920,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
937
  */
938
  public function buildOptions() {
939
 
940
- $bPremiumEnabled = self::getConn()->isPremiumExtensionsEnabled();
941
 
942
  $oOptsVo = $this->getOptionsVo();
943
  $aOptions = $oOptsVo->getOptionsForPluginUse();
@@ -1087,10 +1070,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1087
  * Deletes all the options including direct save.
1088
  */
1089
  public function deletePluginOptions() {
1090
- if ( self::getConn()->getHasPermissionToManage() ) {
1091
- $this->getOptionsVo()->doOptionsDelete();
1092
- $this->bPluginDeleting = true;
1093
- }
1094
  }
1095
 
1096
  /**
@@ -1115,7 +1096,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1115
  */
1116
  protected function ajaxExec_ModOptions() {
1117
 
1118
- $sName = self::getConn()->getHumanName();
1119
 
1120
  try {
1121
  $this->saveOptionsSubmit();
@@ -1163,7 +1144,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1163
  * @throws Exception
1164
  */
1165
  protected function saveOptionsSubmit() {
1166
- if ( !self::getConn()->getHasPermissionToManage() ) {
1167
  throw new Exception( _wpsf__( "You don't currently have permission to save settings." ) );
1168
  }
1169
  $this->doSaveStandardOptions();
@@ -1171,13 +1152,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1171
  }
1172
 
1173
  protected function verifyFormSubmit() {
1174
- if ( !self::getConn()->getHasPermissionToManage() ) {
1175
- // TODO: manage how we react to prohibited submissions
1176
- return false;
1177
- }
1178
-
1179
- // Now verify this is really a valid submission.
1180
- return check_admin_referer( self::getConn()->getPluginPrefix() );
1181
  }
1182
 
1183
  /**
@@ -1203,7 +1179,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1203
  */
1204
  public function setFlashAdminNotice( $sMsg, $bError = false ) {
1205
  $this->loadWpNotices()
1206
- ->addFlashUserMessage( sprintf( '[%s] %s', self::getConn()->getHumanName(), $sMsg ), $bError );
1207
  return $this;
1208
  }
1209
 
@@ -1385,7 +1361,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1385
  * @return string
1386
  */
1387
  public function prefix( $sSuffix = '', $sGlue = '-' ) {
1388
- return self::getConn()->prefix( $sSuffix, $sGlue );
1389
  }
1390
 
1391
  /**
@@ -1393,7 +1369,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1393
  * @return string
1394
  */
1395
  public function getOptionStoragePrefix() {
1396
- return self::getConn()->getOptionStoragePrefix();
1397
  }
1398
 
1399
  /**
@@ -1437,7 +1413,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1437
  * @return array
1438
  */
1439
  protected function getBaseDisplayData( $bRenderEmbeddedContent = false ) {
1440
- $oCon = self::getConn();
1441
  self::$sActivelyDisplayedModuleOptions = $this->getSlug();
1442
 
1443
  $aData = array(
@@ -1693,7 +1669,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1693
 
1694
  // Get the same Base Data as normal display
1695
  try {
1696
- return $this->loadRenderer( self::getConn()->getPath_Templates() )
1697
  ->setTemplate( $sTemplate )
1698
  ->setRenderVars( $this->getBaseDisplayData( true ) )
1699
  ->render();
@@ -1707,8 +1683,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1707
  * @return bool
1708
  */
1709
  protected function canDisplayOptionsForm() {
1710
- return $this->getOptionsVo()->isAccessRestricted() ? self::getConn()
1711
- ->getHasPermissionToView() : true;
1712
  }
1713
 
1714
  /**
@@ -1779,7 +1754,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1779
  $aData[ 'unique_render_id' ] = substr( md5( mt_rand() ), 0, 5 );
1780
  }
1781
  try {
1782
- $oRndr = $this->loadRenderer( self::getConn()->getPath_Templates() );
1783
  if ( $bUseTwig ) {
1784
  $oRndr->setTemplateEngineTwig();
1785
  }
@@ -1990,7 +1965,8 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
1990
  * @return $this
1991
  */
1992
  protected function setOptAt( $sOpt, $nAt = null ) {
1993
- return $this->setOpt( $sOpt, is_null( $nAt ) ? $this->loadRequest()->ts() : max( 0, (int)$nAt ) );
 
1994
  }
1995
 
1996
  /**
@@ -2023,7 +1999,7 @@ abstract class ICWP_WPSF_FeatureHandler_Base extends ICWP_WPSF_Foundation {
2023
  * @return string
2024
  */
2025
  public function getVersion() {
2026
- return self::getConn()->getVersion();
2027
  }
2028
 
2029
  /**
274
  if ( $this->getOptionsVo()->getFeatureProperty( 'auto_load_processor' ) ) {
275
  $this->loadProcessor();
276
  }
277
+ if ( !$this->isUpgrading() && $this->isModuleEnabled() && $this->isReadyToExecute() ) {
278
  $this->doExecuteProcessor();
279
  }
280
  }
297
  protected function importOptions() {
298
  // So we don't poll for the file every page load.
299
  if ( $this->loadRequest()->query( 'icwp_shield_import' ) == 1 ) {
300
+ $aOptions = $this->getConn()->getOptionsImportFromFile();
301
  if ( !empty( $aOptions ) && is_array( $aOptions ) && array_key_exists( $this->getOptionsStorageKey(), $aOptions ) ) {
302
  $this->getOptionsVo()->setMultipleOptions( $aOptions[ $this->getOptionsStorageKey() ] );
303
  $this
356
  */
357
  protected function loadProcessor() {
358
  if ( !isset( $this->oProcessor ) ) {
359
+ include_once( $this->getConn()
360
+ ->getPath_SourceFile( sprintf( 'processors/%s.php', $this->getSlug() ) ) );
361
  $sClassName = $this->getProcessorClassName();
362
  if ( !class_exists( $sClassName, false ) ) {
363
  return null;
372
  * @return string
373
  */
374
  protected function getProcessorClassName() {
375
+ return ucwords( $this->getConn()->getOptionStoragePrefix() ).'Processor_'.
376
  str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) );
377
  }
378
 
381
  * @return string
382
  */
383
  protected function getWizardClassName() {
384
+ return ucwords( $this->getConn()->getOptionStoragePrefix() ).'Wizard_'.
385
  str_replace( ' ', '', ucwords( str_replace( '_', ' ', $this->getSlug() ) ) );
386
  }
387
 
390
  */
391
  protected function getOptionsVo() {
392
  if ( !isset( $this->oOptions ) ) {
393
+ $oCon = $this->getConn();
394
  $this->oOptions = ICWP_WPSF_Factory::OptionsVo();
395
  $this->oOptions
396
  ->setPathToConfig( $oCon->getPath_ConfigFile( $this->getSlug() ) )
414
  * @return bool
415
  */
416
  public function isUpgrading() {
417
+ // return $this->getVersion() != $this->getController()->getVersion();
418
+ return $this->getConn()->getIsRebuildOptionsFromFile() || $this->getOptionsVo()->getRebuildFromFile();
419
  }
420
 
421
  /**
460
  return $this->loadWp()
461
  ->getUrl_AdminPage(
462
  $this->getModSlug(),
463
+ $this->getConn()->getIsWpmsNetworkAdminOnly()
464
  );
465
  }
466
 
470
  */
471
  public function getEmailHandler() {
472
  if ( is_null( self::$oEmailHandler ) ) {
473
+ self::$oEmailHandler = $this->getConn()->loadFeatureHandler( array( 'slug' => 'email' ) );
474
  }
475
  return self::$oEmailHandler;
476
  }
505
  else if ( apply_filters( $this->prefix( 'globally_disabled' ), false ) ) {
506
  $bEnabled = false;
507
  }
508
+ else if ( $this->getConn()->getIfForceOffActive() ) {
509
  $bEnabled = false;
510
  }
511
  else if ( $oOpts->getFeatureProperty( 'premium' ) === true && !$this->isPremium() ) {
550
  return $this->sModSlug;
551
  }
552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
553
  /**
554
  * @param array $aItems
555
  * @return array
561
  }
562
  if ( !empty( $sMenuTitleName ) ) {
563
 
564
+ $sHumanName = $this->getConn()->getHumanName();
565
 
566
  $bMenuHighlighted = $this->getOptionsVo()->getFeatureProperty( 'highlight_menu_item' );
567
  if ( $bMenuHighlighted ) {
870
  */
871
  protected function getWizardHandler() {
872
  if ( !isset( $this->oWizard ) ) {
873
+ include_once( $this->getConn()->getPath_SourceFile( sprintf( 'wizards/%s.php', $this->getSlug() ) ) );
874
  $sClassName = $this->getWizardClassName();
875
  if ( !class_exists( $sClassName, false ) ) {
876
  return null;
899
  }
900
 
901
  private function store() {
902
+ add_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
903
+ $bSuccess = $this->getOptionsVo()->doOptionsSave( $this->getConn()->getIsResetPlugin() );
904
+ remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true', 1000 );
905
  }
906
 
907
  /**
920
  */
921
  public function buildOptions() {
922
 
923
+ $bPremiumEnabled = $this->getConn()->isPremiumExtensionsEnabled();
924
 
925
  $oOptsVo = $this->getOptionsVo();
926
  $aOptions = $oOptsVo->getOptionsForPluginUse();
1070
  * Deletes all the options including direct save.
1071
  */
1072
  public function deletePluginOptions() {
1073
+ $this->getOptionsVo()->doOptionsDelete();
1074
+ $this->bPluginDeleting = true;
 
 
1075
  }
1076
 
1077
  /**
1096
  */
1097
  protected function ajaxExec_ModOptions() {
1098
 
1099
+ $sName = $this->getConn()->getHumanName();
1100
 
1101
  try {
1102
  $this->saveOptionsSubmit();
1144
  * @throws Exception
1145
  */
1146
  protected function saveOptionsSubmit() {
1147
+ if ( !$this->getConn()->isPluginAdmin() ) {
1148
  throw new Exception( _wpsf__( "You don't currently have permission to save settings." ) );
1149
  }
1150
  $this->doSaveStandardOptions();
1152
  }
1153
 
1154
  protected function verifyFormSubmit() {
1155
+ return $this->getConn()->isPluginAdmin()
1156
+ && check_admin_referer( $this->getConn()->getPluginPrefix() );
 
 
 
 
 
1157
  }
1158
 
1159
  /**
1179
  */
1180
  public function setFlashAdminNotice( $sMsg, $bError = false ) {
1181
  $this->loadWpNotices()
1182
+ ->addFlashUserMessage( sprintf( '[%s] %s', $this->getConn()->getHumanName(), $sMsg ), $bError );
1183
  return $this;
1184
  }
1185
 
1361
  * @return string
1362
  */
1363
  public function prefix( $sSuffix = '', $sGlue = '-' ) {
1364
+ return $this->getConn()->prefix( $sSuffix, $sGlue );
1365
  }
1366
 
1367
  /**
1369
  * @return string
1370
  */
1371
  public function getOptionStoragePrefix() {
1372
+ return $this->getConn()->getOptionStoragePrefix();
1373
  }
1374
 
1375
  /**
1413
  * @return array
1414
  */
1415
  protected function getBaseDisplayData( $bRenderEmbeddedContent = false ) {
1416
+ $oCon = $this->getConn();
1417
  self::$sActivelyDisplayedModuleOptions = $this->getSlug();
1418
 
1419
  $aData = array(
1669
 
1670
  // Get the same Base Data as normal display
1671
  try {
1672
+ return $this->loadRenderer( $this->getConn()->getPath_Templates() )
1673
  ->setTemplate( $sTemplate )
1674
  ->setRenderVars( $this->getBaseDisplayData( true ) )
1675
  ->render();
1683
  * @return bool
1684
  */
1685
  protected function canDisplayOptionsForm() {
1686
+ return $this->getOptionsVo()->isAccessRestricted() ? $this->getConn()->isPluginAdmin() : true;
 
1687
  }
1688
 
1689
  /**
1754
  $aData[ 'unique_render_id' ] = substr( md5( mt_rand() ), 0, 5 );
1755
  }
1756
  try {
1757
+ $oRndr = $this->loadRenderer( $this->getConn()->getPath_Templates() );
1758
  if ( $bUseTwig ) {
1759
  $oRndr->setTemplateEngineTwig();
1760
  }
1965
  * @return $this
1966
  */
1967
  protected function setOptAt( $sOpt, $nAt = null ) {
1968
+ $nAt = is_null( $nAt ) ? $this->loadRequest()->ts() : max( 0, (int)$nAt );
1969
+ return $this->setOpt( $sOpt, $nAt );
1970
  }
1971
 
1972
  /**
1999
  * @return string
2000
  */
2001
  public function getVersion() {
2002
+ return $this->getConn()->getVersion();
2003
  }
2004
 
2005
  /**
src/features/hack_protect.php CHANGED
@@ -384,7 +384,7 @@ class ICWP_WPSF_FeatureHandler_HackProtect extends ICWP_WPSF_FeatureHandler_Base
384
  public function isWpvulnPluginsHighlightEnabled() {
385
  $sOpt = $this->getWpvulnPluginsHighlightOption();
386
  return ( $sOpt != 'disabled' ) && $this->loadWpUsers()->isUserAdmin()
387
- && ( ( $sOpt != 'enabled_securityadmin' ) || $this->getConn()->getHasPermissionToManage() );
388
  }
389
 
390
  /**
384
  public function isWpvulnPluginsHighlightEnabled() {
385
  $sOpt = $this->getWpvulnPluginsHighlightOption();
386
  return ( $sOpt != 'disabled' ) && $this->loadWpUsers()->isUserAdmin()
387
+ && ( ( $sOpt != 'enabled_securityadmin' ) || $this->getConn()->isPluginAdmin() );
388
  }
389
 
390
  /**
src/features/ips.php CHANGED
@@ -347,6 +347,8 @@ class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
347
  * @throws Exception
348
  */
349
  protected function loadStrings_Options( $aOptionsParams ) {
 
 
350
  switch ( $aOptionsParams[ 'key' ] ) {
351
 
352
  case 'enable_ips' :
@@ -358,8 +360,7 @@ class ICWP_WPSF_FeatureHandler_Ips extends ICWP_WPSF_FeatureHandler_BaseWpsf {
358
  case 'transgression_limit' :
359
  $sName = _wpsf__( 'Transgression Limit' );
360
  $sSummary = _wpsf__( 'Visitor IP address will be Black Listed after X bad actions on your site' );
361
- $sDescription = sprintf( _wpsf__( 'A black mark is set against an IP address each time a visitor trips the defenses of the %s plugin.' ), self::getConn()
362
- ->getHumanName() )
363
  .'<br />'._wpsf__( 'When the number of these transgressions exceeds specified limit, they are automatically blocked from accessing the site.' )
364
  .'<br />'.sprintf( _wpsf__( 'Set this to "0" to turn off the %s feature.' ), _wpsf__( 'Automatic IP Black List' ) );
365
  break;
347
  * @throws Exception
348
  */
349
  protected function loadStrings_Options( $aOptionsParams ) {
350
+
351
+ $sPlugName = $this->getConn()->getHumanName();
352
  switch ( $aOptionsParams[ 'key' ] ) {
353
 
354
  case 'enable_ips' :
360
  case 'transgression_limit' :
361
  $sName = _wpsf__( 'Transgression Limit' );
362
  $sSummary = _wpsf__( 'Visitor IP address will be Black Listed after X bad actions on your site' );
363
+ $sDescription = sprintf( _wpsf__( 'A black mark is set against an IP address each time a visitor trips the defenses of the %s plugin.' ), $sPlugName )
 
364
  .'<br />'._wpsf__( 'When the number of these transgressions exceeds specified limit, they are automatically blocked from accessing the site.' )
365
  .'<br />'.sprintf( _wpsf__( 'Set this to "0" to turn off the %s feature.' ), _wpsf__( 'Automatic IP Black List' ) );
366
  break;
src/features/license.php CHANGED
@@ -9,7 +9,10 @@ require_once( dirname( __FILE__ ).'/base_wpsf.php' );
9
  class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
 
11
  protected function doPostConstruction() {
12
- add_filter( $this->getConn()->getPremiumLicenseFilterName(), array( $this, 'hasValidWorkingLicense' ), PHP_INT_MAX );
 
 
 
13
  }
14
 
15
  public function action_doFeatureShutdown() {
@@ -266,19 +269,16 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
266
  * @return $this
267
  */
268
  public function verifyLicense( $bForceCheck = true ) {
269
- $nNow = $this->loadRequest()->ts();
270
- $oCurrent = $this->loadLicense();
271
-
272
- // If your last license verification has expired and it's been 4hrs since your last check.
273
- $bCheck = $bForceCheck
274
- || ( $this->isLicenseActive() && !$oCurrent->isReady() && $this->getIsLicenseNotCheckedFor( HOUR_IN_SECONDS ) )
275
- || ( $this->hasValidWorkingLicense() && $this->isLastVerifiedExpired()
276
- && $this->getIsLicenseNotCheckedFor( HOUR_IN_SECONDS*4 ) );
277
 
278
  // 1 check in 20 seconds
279
- if ( $bCheck && $this->getIsLicenseNotCheckedFor( 20 ) ) {
 
 
280
 
281
- $this->setLicenseLastCheckedAt()
 
282
  ->savePluginOptions();
283
 
284
  /** @var ICWP_WPSF_Processor_License $oPro */
@@ -293,16 +293,15 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
293
  $oPro->addToAuditEntry( 'Pro License check succeeded.', 1, 'license_check_success' );
294
  }
295
  else {
296
- $oCurrent->setLastRequestAt( $nNow );
297
  if ( $oCurrent->isValid() ) { // we have something valid previously stored
298
 
299
  if ( !$bForceCheck && $this->isWithinVerifiedGraceExpired() ) {
300
  $this->sendLicenseWarningEmail();
301
  $oPro->addToAuditEntry( 'License check failed. Sending Warning Email.', 2, 'license_check_failed' );
302
  }
303
- else if ( $bForceCheck || $this->isLastVerifiedGraceExpired() ) {
304
  $oCurrent = $oLookupLicense;
305
- $this->deactivate( sprintf( _wpsf__( 'Automatic license verification failed after %s days.' ), 6 ) );
306
  $this->sendLicenseDeactivatedEmail();
307
  $oPro->addToAuditEntry( 'License check failed. Deactivating Pro.', 3, 'license_check_failed' );
308
  }
@@ -316,19 +315,64 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
316
  }
317
  }
318
 
 
319
  $this->setLicenseData( $oCurrent )
320
  ->savePluginOptions();
321
-
322
- try {
323
- }
324
- catch ( Exception $oE ) {
325
- // $oCurrent->setLastErrors( 'Could not find a valid license' );
326
- }
327
  }
328
 
329
  return $this;
330
  }
331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  /**
333
  * @return $this
334
  */
@@ -347,6 +391,7 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
347
  $bCanSend = $nNow - $this->getOpt( 'last_warning_email_sent_at' ) > DAY_IN_SECONDS;
348
 
349
  if ( $bCanSend ) {
 
350
  $aMessage = array(
351
  _wpsf__( 'Attempts to verify Shield Pro license has just failed.' ),
352
  sprintf( _wpsf__( 'Please check your license on-site: %s' ), $this->getUrl_AdminPage() ),
@@ -364,17 +409,22 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
364
  /**
365
  */
366
  private function sendLicenseDeactivatedEmail() {
367
- $aMessage = array(
368
- _wpsf__( 'All attempts to verify Shield Pro license have failed.' ),
369
- sprintf( _wpsf__( 'Please check your license on-site: %s' ), $this->getUrl_AdminPage() ),
370
- sprintf( _wpsf__( 'If this problem persists, please contact support: %s' ), 'https://support.onedollarplugin.com/' )
371
- );
372
- $this->getEmailProcessor()
373
- ->sendEmailWithWrap(
374
- $this->getPluginDefaultRecipientAddress(),
375
- '[Action May Be Required] Pro License Has Been Deactivated',
376
- $aMessage
377
- );
 
 
 
 
 
378
  }
379
 
380
  /**
@@ -541,15 +591,7 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
541
  * @return bool
542
  */
543
  protected function isWithinVerifiedGraceExpired() {
544
- return false;//$this->isLastVerifiedExpired() && !$this->isLastVerifiedGraceExpired();
545
- }
546
-
547
- /**
548
- * @param string $sEmail
549
- * @return string
550
- */
551
- protected function setOfficialLicenseRegisteredEmail( $sEmail ) {
552
- return $this->setOpt( 'license_registered_email', $sEmail );
553
  }
554
 
555
  /**
@@ -560,14 +602,6 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
560
  return $this->setOptAt( 'license_last_checked_at', $nAt );
561
  }
562
 
563
- /**
564
- * @param int $nAt
565
- * @return $this
566
- */
567
- protected function setLicenseLastRequestedAt( $nAt = null ) {
568
- return $this->setOptAt( 'license_last_request_at', $nAt );
569
- }
570
-
571
  /**
572
  * @param string $sKey
573
  * @return string|null
@@ -609,8 +643,9 @@ class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf
609
  * @return boolean
610
  */
611
  public function getIfShowModuleMenuItem() {
612
- return parent::getIfShowModuleMenuItem() && self::getConn()->isPremiumExtensionsEnabled()
613
- && $this->getConn()->getHasPermissionToManage();
 
614
  }
615
 
616
  /**
9
  class ICWP_WPSF_FeatureHandler_License extends ICWP_WPSF_FeatureHandler_BaseWpsf {
10
 
11
  protected function doPostConstruction() {
12
+ add_filter( $this->getConn()->getPremiumLicenseFilterName(), array(
13
+ $this,
14
+ 'hasValidWorkingLicense'
15
+ ), PHP_INT_MAX );
16
  }
17
 
18
  public function action_doFeatureShutdown() {
269
  * @return $this
270
  */
271
  public function verifyLicense( $bForceCheck = true ) {
272
+ // Is a check actually required and permitted
273
+ $bCheckReq = $this->isLicenseCheckRequired() && $this->canLicenseCheck();
 
 
 
 
 
 
274
 
275
  // 1 check in 20 seconds
276
+ if ( ( $bForceCheck || $bCheckReq ) && $this->getIsLicenseNotCheckedFor( 20 ) ) {
277
+
278
+ $oCurrent = $this->loadLicense();
279
 
280
+ $this->touchLicenseCheckFileFlag()
281
+ ->setLicenseLastCheckedAt()
282
  ->savePluginOptions();
283
 
284
  /** @var ICWP_WPSF_Processor_License $oPro */
293
  $oPro->addToAuditEntry( 'Pro License check succeeded.', 1, 'license_check_success' );
294
  }
295
  else {
 
296
  if ( $oCurrent->isValid() ) { // we have something valid previously stored
297
 
298
  if ( !$bForceCheck && $this->isWithinVerifiedGraceExpired() ) {
299
  $this->sendLicenseWarningEmail();
300
  $oPro->addToAuditEntry( 'License check failed. Sending Warning Email.', 2, 'license_check_failed' );
301
  }
302
+ else if ( $bForceCheck || $oCurrent->isExpired() || $this->isLastVerifiedGraceExpired() ) {
303
  $oCurrent = $oLookupLicense;
304
+ $this->deactivate( _wpsf__( 'Automatic license verification failed.' ) );
305
  $this->sendLicenseDeactivatedEmail();
306
  $oPro->addToAuditEntry( 'License check failed. Deactivating Pro.', 3, 'license_check_failed' );
307
  }
315
  }
316
  }
317
 
318
+ $oCurrent->setLastRequestAt( $this->loadRequest()->ts() );
319
  $this->setLicenseData( $oCurrent )
320
  ->savePluginOptions();
 
 
 
 
 
 
321
  }
322
 
323
  return $this;
324
  }
325
 
326
+ /**
327
+ * @return bool
328
+ */
329
+ private function isLicenseCheckRequired() {
330
+ return ( $this->isLicenseMaybeExpiring() && $this->getIsLicenseNotCheckedFor( HOUR_IN_SECONDS*4 ) )
331
+ || ( $this->isLicenseActive()
332
+ && !$this->loadLicense()->isReady() && $this->getIsLicenseNotCheckedFor( HOUR_IN_SECONDS ) )
333
+ || ( $this->hasValidWorkingLicense() && $this->isLastVerifiedExpired()
334
+ && $this->getIsLicenseNotCheckedFor( HOUR_IN_SECONDS*4 ) );
335
+ }
336
+
337
+ /**
338
+ * @return bool
339
+ */
340
+ private function canLicenseCheck() {
341
+ $sShieldAction = $this->loadRequest()->query( 'shield_action' );
342
+ return !in_array( $sShieldAction, array( 'keyless_handshake', 'license_check' ) )
343
+ && $this->canLicenseCheck_FileFlag();
344
+ }
345
+
346
+ /**
347
+ * @return bool
348
+ */
349
+ private function canLicenseCheck_FileFlag() {
350
+ $oFs = $this->loadFS();
351
+ $sFileFlag = $this->getConn()->getPath_Flags( 'license_check' );
352
+ $nMtime = $oFs->exists( $sFileFlag ) ? $oFs->getModifiedTime( $sFileFlag ) : 0;
353
+ return ( $this->loadRequest()->ts() - $nMtime ) > MINUTE_IN_SECONDS;
354
+ }
355
+
356
+ /**
357
+ * @return $this
358
+ */
359
+ private function touchLicenseCheckFileFlag() {
360
+ $this->loadFS()->touch( $this->getConn()->getPath_Flags( 'license_check' ) );
361
+ return $this;
362
+ }
363
+
364
+ /**
365
+ * @return bool
366
+ */
367
+ protected function isLicenseMaybeExpiring() {
368
+ $bNearly = $this->isLicenseActive() &&
369
+ (
370
+ abs( $this->loadRequest()->ts() - $this->loadLicense()->getExpiresAt() )
371
+ < ( DAY_IN_SECONDS/2 )
372
+ );
373
+ return $bNearly;
374
+ }
375
+
376
  /**
377
  * @return $this
378
  */
391
  $bCanSend = $nNow - $this->getOpt( 'last_warning_email_sent_at' ) > DAY_IN_SECONDS;
392
 
393
  if ( $bCanSend ) {
394
+ $this->setOptAt( 'last_warning_email_sent_at' )->savePluginOptions();
395
  $aMessage = array(
396
  _wpsf__( 'Attempts to verify Shield Pro license has just failed.' ),
397
  sprintf( _wpsf__( 'Please check your license on-site: %s' ), $this->getUrl_AdminPage() ),
409
  /**
410
  */
411
  private function sendLicenseDeactivatedEmail() {
412
+ $nNow = $this->loadRequest()->ts();
413
+
414
+ if ( ( $nNow - $this->getOpt( 'last_deactivated_email_sent_at' ) ) > DAY_IN_SECONDS ) {
415
+ $this->setOptAt( 'last_deactivated_email_sent_at' )->savePluginOptions();
416
+ $aMessage = array(
417
+ _wpsf__( 'All attempts to verify Shield Pro license have failed.' ),
418
+ sprintf( _wpsf__( 'Please check your license on-site: %s' ), $this->getUrl_AdminPage() ),
419
+ sprintf( _wpsf__( 'If this problem persists, please contact support: %s' ), 'https://support.onedollarplugin.com/' )
420
+ );
421
+ $this->getEmailProcessor()
422
+ ->sendEmailWithWrap(
423
+ $this->getPluginDefaultRecipientAddress(),
424
+ '[Action May Be Required] Pro License Has Been Deactivated',
425
+ $aMessage
426
+ );
427
+ }
428
  }
429
 
430
  /**
591
  * @return bool
592
  */
593
  protected function isWithinVerifiedGraceExpired() {
594
+ return $this->isLastVerifiedExpired() && !$this->isLastVerifiedGraceExpired();
 
 
 
 
 
 
 
 
595
  }
596
 
597
  /**
602
  return $this->setOptAt( 'license_last_checked_at', $nAt );
603
  }
604
 
 
 
 
 
 
 
 
 
605
  /**
606
  * @param string $sKey
607
  * @return string|null
643
  * @return boolean
644
  */
645
  public function getIfShowModuleMenuItem() {
646
+ $oCon = $this->getConn();
647
+ return parent::getIfShowModuleMenuItem() && $oCon->isPremiumExtensionsEnabled()
648
+ && $oCon->isPluginAdmin();
649
  }
650
 
651
  /**
src/features/login_protect.php CHANGED
@@ -57,9 +57,9 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
57
  $this->getOptionsVo()->resetOptToDefault( 'login_limit_interval' );
58
  }
59
 
60
- $aIds = $this->getAntiBotFormIds();
61
  foreach ( $aIds as $nKey => $sId ) {
62
- $sId = preg_replace( '/\s/', '', strip_tags( trim( $sId ) ) );
63
  if ( empty( $sId ) ) {
64
  unset( $aIds[ $nKey ] );
65
  }
@@ -160,7 +160,10 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
160
  $aRoles = $this->getOptEmailTwoFactorRolesDefaults();
161
  $this->setOpt( 'two_factor_auth_user_roles', $aRoles );
162
  }
163
- return $aRoles;
 
 
 
164
  }
165
 
166
  /**
@@ -256,9 +259,21 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
256
  }
257
  else if ( $this->getIfSupport3rdParty() && class_exists( 'WC_Social_Login' ) ) {
258
  // custom support for WooCommerce Social login
259
- $oMeta = $this->getController()->getUserMeta( $oUser );
260
  $bCanSkip = isset( $oMeta->wc_social_login_valid ) ? $oMeta->wc_social_login_valid : false;
261
  }
 
 
 
 
 
 
 
 
 
 
 
 
262
  return $bCanSkip;
263
  }
264
 
@@ -279,7 +294,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
279
  * @return array
280
  */
281
  public function getMfaLoginHashes( $oUser ) {
282
- $oMeta = $this->getController()->getUserMeta( $oUser );
283
  $aHashes = $oMeta->hash_loginmfa;
284
  if ( !is_array( $aHashes ) ) {
285
  $aHashes = array();
@@ -499,7 +514,7 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
499
  $aWarnings[] =
500
  _wpsf__( '2FA by email demands that your WP site is properly configured to send email.' )
501
  .'<br/>'._wpsf__( 'This is a common problem and you may get locked out in the future if you ignore this.' )
502
- .' '.sprintf( '<a href="%s" target="_blank" style="font-weight: bolder">%s</a>', 'https://icwp.io/dd', _wpsf__( 'Learn More.' ) );
503
  }
504
 
505
  return $aWarnings;
@@ -590,14 +605,14 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
590
  */
591
  public function isEnabledBotJs() {
592
  return $this->isPremium() && $this->isOpt( 'enable_antibot_js', 'Y' )
593
- && count( $this->getAntiBotFormIds() ) > 0
594
  && ( $this->isEnabledGaspCheck() || $this->isGoogleRecaptchaEnabled() );
595
  }
596
 
597
  /**
598
  * @return array
599
  */
600
- public function getAntiBotFormIds() {
601
  $aIds = $this->getOpt( 'antibot_form_ids', array() );
602
  return is_array( $aIds ) ? $aIds : array();
603
  }
@@ -623,14 +638,14 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
623
  $sUnique,
624
  'icwp_wpsf_vars_lpantibot',
625
  array(
626
- 'form_ids' => $this->getAntiBotFormIds(),
627
- 'uniq' => preg_replace( '#[^a-zA-Z0-9]#', '', apply_filters( 'icwp_shield_lp_gasp_uniqid', uniqid() ) ),
628
- 'cbname' => $this->getGaspKey(),
629
- 'strings' => array(
630
  'label' => $this->getTextImAHuman(),
631
  'alert' => $this->getTextPleaseCheckBox(),
632
  ),
633
- 'flags' => array(
634
  'gasp' => $this->isEnabledGaspCheck(),
635
  'recap' => $this->isGoogleRecaptchaEnabled(),
636
  )
@@ -869,8 +884,11 @@ class ICWP_WPSF_FeatureHandler_LoginProtect extends ICWP_WPSF_FeatureHandler_Bas
869
 
870
  case 'antibot_form_ids' :
871
  $sName = _wpsf__( 'AntiBot Forms' );
872
- $sSummary = _wpsf__( 'Enter The IDs Of The 3rd Party Login Forms For Use With AntiBot JS' );
873
- $sDescription = _wpsf__( 'For use with the AntiBot JS option.' );
 
 
 
874
  break;
875
 
876
  case 'login_limit_interval' :
57
  $this->getOptionsVo()->resetOptToDefault( 'login_limit_interval' );
58
  }
59
 
60
+ $aIds = $this->getAntiBotFormSelectors();
61
  foreach ( $aIds as $nKey => $sId ) {
62
+ $sId = trim( strip_tags( $sId ) );
63
  if ( empty( $sId ) ) {
64
  unset( $aIds[ $nKey ] );
65
  }
160
  $aRoles = $this->getOptEmailTwoFactorRolesDefaults();
161
  $this->setOpt( 'two_factor_auth_user_roles', $aRoles );
162
  }
163
+ if ( $this->isPremium() ) {
164
+ $aRoles = apply_filters( 'odp-shield-2fa_email_user_roles', $aRoles );
165
+ }
166
+ return is_array( $aRoles ) ? $aRoles : $this->getOptEmailTwoFactorRolesDefaults();
167
  }
168
 
169
  /**
259
  }
260
  else if ( $this->getIfSupport3rdParty() && class_exists( 'WC_Social_Login' ) ) {
261
  // custom support for WooCommerce Social login
262
+ $oMeta = $this->getConn()->getUserMeta( $oUser );
263
  $bCanSkip = isset( $oMeta->wc_social_login_valid ) ? $oMeta->wc_social_login_valid : false;
264
  }
265
+ else {
266
+ /**
267
+ * TODO: remove the HTTP_REFERER bit once iCWP plugin is updated.
268
+ * We want logins from iCWP to skip 2FA. To achieve this, iCWP plugin needs
269
+ * to add a TRUE filter on 'odp-shield-2fa_skip' at the point of login.
270
+ * Until then, we'll use the HTTP referrer as an indicator
271
+ */
272
+ $bCanSkip = apply_filters(
273
+ 'odp-shield-2fa_skip',
274
+ strpos( $this->loadRequest()->server( 'HTTP_REFERER' ), 'https://app.icontrolwp.com/' ) === 0
275
+ );
276
+ }
277
  return $bCanSkip;
278
  }
279
 
294
  * @return array
295
  */
296
  public function getMfaLoginHashes( $oUser ) {
297
+ $oMeta = $this->getConn()->getUserMeta( $oUser );
298
  $aHashes = $oMeta->hash_loginmfa;
299
  if ( !is_array( $aHashes ) ) {
300
  $aHashes = array();
514
  $aWarnings[] =
515
  _wpsf__( '2FA by email demands that your WP site is properly configured to send email.' )
516
  .'<br/>'._wpsf__( 'This is a common problem and you may get locked out in the future if you ignore this.' )
517
+ .' '.sprintf( '<a href="%s" target="_blank" class="alert-link">%s</a>', 'https://icwp.io/dd', _wpsf__( 'Learn More.' ) );
518
  }
519
 
520
  return $aWarnings;
605
  */
606
  public function isEnabledBotJs() {
607
  return $this->isPremium() && $this->isOpt( 'enable_antibot_js', 'Y' )
608
+ && count( $this->getAntiBotFormSelectors() ) > 0
609
  && ( $this->isEnabledGaspCheck() || $this->isGoogleRecaptchaEnabled() );
610
  }
611
 
612
  /**
613
  * @return array
614
  */
615
+ public function getAntiBotFormSelectors() {
616
  $aIds = $this->getOpt( 'antibot_form_ids', array() );
617
  return is_array( $aIds ) ? $aIds : array();
618
  }
638
  $sUnique,
639
  'icwp_wpsf_vars_lpantibot',
640
  array(
641
+ 'form_selectors' => implode( ',', $this->getAntiBotFormSelectors() ),
642
+ 'uniq' => preg_replace( '#[^a-zA-Z0-9]#', '', apply_filters( 'icwp_shield_lp_gasp_uniqid', uniqid() ) ),
643
+ 'cbname' => $this->getGaspKey(),
644
+ 'strings' => array(
645
  'label' => $this->getTextImAHuman(),
646
  'alert' => $this->getTextPleaseCheckBox(),
647
  ),
648
+ 'flags' => array(
649
  'gasp' => $this->isEnabledGaspCheck(),
650
  'recap' => $this->isGoogleRecaptchaEnabled(),
651
  )
884
 
885
  case 'antibot_form_ids' :
886
  $sName = _wpsf__( 'AntiBot Forms' );
887
+ $sSummary = _wpsf__( 'Enter The Selectors Of The 3rd Party Login Forms For Use With AntiBot JS' );
888
+ $sDescription = _wpsf__( 'For use with the AntiBot JS option.' )
889
+ .' '._wpsf__( 'IDs are prefixed with "#".' )
890
+ .' '._wpsf__( 'Classes are prefixed with ".".' )
891
+ .'<br />'._wpsf__( 'IDs are preferred over classes.' );
892
  break;
893
 
894
  case 'login_limit_interval' :
src/features/plugin.php CHANGED
@@ -316,9 +316,9 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
316
  * @param string $sPlugin
317
  */
318
  public function onWpHookDeactivatePlugin( $sPlugin ) {
319
- $oCon = self::getConn();
320
  if ( strpos( $oCon->getRootFile(), $sPlugin ) !== false ) {
321
- if ( !$oCon->getHasPermissionToManage() ) {
322
  $this->loadWp()->wpDie(
323
  _wpsf__( 'Sorry, you do not have permission to disable this plugin.' )
324
  .' '._wpsf__( 'You need to authenticate first.' )
@@ -385,10 +385,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
385
  */
386
  protected function doPrePluginOptionsSave() {
387
 
388
- $nInstalledAt = $this->getPluginInstallationTime();
389
- if ( empty( $nInstalledAt ) || $nInstalledAt <= 0 ) {
390
- $this->setOpt( 'installation_time', $this->loadRequest()->ts() );
391
- }
392
 
393
  if ( $this->isTrackingEnabled() && !$this->isTrackingPermissionSet() ) {
394
  $this->setOpt( 'tracking_permission_set_at', $this->loadRequest()->ts() );
@@ -403,6 +400,46 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
403
  $this->setPluginInstallationId();
404
  }
405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  /**
407
  * @param string $sOptionKey
408
  */
@@ -451,7 +488,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
451
  */
452
  protected function genInstallId() {
453
  return sha1(
454
- $this->getPluginInstallationTime()
455
  .$this->loadWp()->getWpUrl()
456
  .$this->loadDbProcessor()->getPrefix()
457
  );
@@ -720,9 +757,7 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
720
  * @return $this
721
  */
722
  public function updateTestCronLastRunAt() {
723
- $this->setOptInsightsAt( 'test_cron_last_run_at' )
724
- ->savePluginOptions();
725
- return $this;
726
  }
727
 
728
  /**
@@ -837,13 +872,13 @@ class ICWP_WPSF_FeatureHandler_Plugin extends ICWP_WPSF_FeatureHandler_BaseWpsf
837
  protected function loadStrings_Options( $aOptionsParams ) {
838
 
839
  $sKey = $aOptionsParams[ 'key' ];
 
840
  switch ( $sKey ) {
841
 
842
  case 'global_enable_plugin_features' :
843
  $sName = _wpsf__( 'Enable/Disable Plugin Modules' );
844
  $sSummary = _wpsf__( 'Enable/Disable All Plugin Modules' );
845
- $sDescription = sprintf( _wpsf__( 'Uncheck this option to disable all %s features.' ), self::getConn()
846
- ->getHumanName() );
847
  break;
848
 
849
  case 'enable_notes' :
316
  * @param string $sPlugin
317
  */
318
  public function onWpHookDeactivatePlugin( $sPlugin ) {
319
+ $oCon = $this->getConn();
320
  if ( strpos( $oCon->getRootFile(), $sPlugin ) !== false ) {
321
+ if ( !$oCon->isPluginAdmin() ) {
322
  $this->loadWp()->wpDie(
323
  _wpsf__( 'Sorry, you do not have permission to disable this plugin.' )
324
  .' '._wpsf__( 'You need to authenticate first.' )
385
  */
386
  protected function doPrePluginOptionsSave() {
387
 
388
+ $this->storeRealInstallDate();
 
 
 
389
 
390
  if ( $this->isTrackingEnabled() && !$this->isTrackingPermissionSet() ) {
391
  $this->setOpt( 'tracking_permission_set_at', $this->loadRequest()->ts() );
400
  $this->setPluginInstallationId();
401
  }
402
 
403
+ /**
404
+ * @return int
405
+ */
406
+ public function getFirstInstallDate() {
407
+ return $this->loadWp()->getOption( $this->prefixOptionKey( 'install_date' ) );
408
+ }
409
+
410
+ /**
411
+ * @return int
412
+ */
413
+ public function getInstallDate() {
414
+ return $this->getOpt( 'installation_time', 0 );
415
+ }
416
+
417
+ /**
418
+ * @return int - the real install timestamp
419
+ */
420
+ public function storeRealInstallDate() {
421
+ $oWP = $this->loadWp();
422
+ $nNow = $this->loadRequest()->ts();
423
+
424
+ $sOptKey = $this->prefixOptionKey( 'install_date' );
425
+
426
+ $nWpDate = $oWP->getOption( $sOptKey );
427
+ if ( empty( $nWpDate ) ) {
428
+ $nWpDate = $nNow;
429
+ }
430
+
431
+ $nPluginDate = $this->getInstallDate();
432
+ if ( $nPluginDate == 0 ) {
433
+ $nPluginDate = $nNow;
434
+ }
435
+
436
+ $nFinal = min( $nPluginDate, $nWpDate );
437
+ $oWP->updateOption( $sOptKey, $nFinal );
438
+ $this->setOpt( 'installation_time', $nPluginDate );
439
+
440
+ return $nFinal;
441
+ }
442
+
443
  /**
444
  * @param string $sOptionKey
445
  */
488
  */
489
  protected function genInstallId() {
490
  return sha1(
491
+ $this->getInstallDate()
492
  .$this->loadWp()->getWpUrl()
493
  .$this->loadDbProcessor()->getPrefix()
494
  );
757
  * @return $this
758
  */
759
  public function updateTestCronLastRunAt() {
760
+ return $this->setOptInsightsAt( 'test_cron_last_run_at' );
 
 
761
  }
762
 
763
  /**
872
  protected function loadStrings_Options( $aOptionsParams ) {
873
 
874
  $sKey = $aOptionsParams[ 'key' ];
875
+ $sPlugName = $this->getConn()->getHumanName();
876
  switch ( $sKey ) {
877
 
878
  case 'global_enable_plugin_features' :
879
  $sName = _wpsf__( 'Enable/Disable Plugin Modules' );
880
  $sSummary = _wpsf__( 'Enable/Disable All Plugin Modules' );
881
+ $sDescription = sprintf( _wpsf__( 'Uncheck this option to disable all %s features.' ), $sPlugName );
 
882
  break;
883
 
884
  case 'enable_notes' :
src/features/traffic.php CHANGED
@@ -322,7 +322,6 @@ class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf
322
  public function formatEntriesForDisplay( $aEntries ) {
323
 
324
  if ( is_array( $aEntries ) ) {
325
- $oCon = $this->getController();
326
  $oWpUsers = $this->loadWpUsers();
327
  $oGeo = $this->loadGeoIp2();
328
  $sYou = $this->loadIpService()->getRequestIp();
@@ -371,7 +370,7 @@ class ICWP_WPSF_FeatureHandler_Traffic extends ICWP_WPSF_FeatureHandler_BaseWpsf
371
  $sCountry = _wpsf__( 'Unknown' );
372
  }
373
  else {
374
- $sFlag = $oCon->getPluginUrl_Image( 'flags/'.strtolower( $oGeo->countryIso( $sIp ) ).'.png' );
375
  $sCountry = sprintf( '<img class="icon-flag" src="%s"/> %s', $sFlag, $sCountry );
376
  }
377
 
322
  public function formatEntriesForDisplay( $aEntries ) {
323
 
324
  if ( is_array( $aEntries ) ) {
 
325
  $oWpUsers = $this->loadWpUsers();
326
  $oGeo = $this->loadGeoIp2();
327
  $sYou = $this->loadIpService()->getRequestIp();
370
  $sCountry = _wpsf__( 'Unknown' );
371
  }
372
  else {
373
+ $sFlag = sprintf( 'https://www.countryflags.io/%s/flat/16.png', strtolower( $oGeo->countryIso( $sIp ) ) );
374
  $sCountry = sprintf( '<img class="icon-flag" src="%s"/> %s', $sFlag, $sCountry );
375
  }
376
 
src/processors/admin_access_restriction.php CHANGED
@@ -17,25 +17,37 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
17
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
18
  $oFO = $this->getMod();
19
 
20
- add_filter( $oFO->prefix( 'has_permission_to_manage' ), array( $oFO, 'doCheckHasPermissionToSubmit' ) );
21
- add_filter( $oFO->prefix( 'has_permission_to_view' ), array( $oFO, 'doCheckHasPermissionToSubmit' ) );
22
-
23
- if ( !$oFO->isUpgrading() && !$this->loadWp()->isRequestUserLogin() ) {
24
- add_filter( 'pre_update_option', array( $this, 'blockOptionsSaves' ), 1, 3 );
25
- }
26
 
27
  if ( $oFO->isWlEnabled() ) {
28
  $this->runWhiteLabel();
29
  }
30
  }
31
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  public function onWpInit() {
33
  parent::onWpInit();
34
 
35
- if ( $this->loadWpUsers()->isUserLoggedIn() && !$this->isSecurityAdmin() ) {
 
36
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
37
  $oFO = $this->getMod();
38
 
 
 
 
 
39
  if ( $oFO->isAdminAccessAdminUsersEnabled() ) {
40
  add_filter( 'editable_roles', array( $this, 'restrictEditableRoles' ), 100, 1 );
41
  add_filter( 'user_has_cap', array( $this, 'restrictAdminUserChanges' ), 100, 3 );
@@ -107,13 +119,6 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
107
  return $aData;
108
  }
109
 
110
- /**
111
- * @return bool
112
- */
113
- protected function isSecurityAdmin() {
114
- return self::getController()->getHasPermissionToManage();
115
- }
116
-
117
  /**
118
  * @param int $nUserId
119
  * @param string $sRole
@@ -276,7 +281,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
276
  public function addNotice_certain_options_restricted( $aNoticeAttributes ) {
277
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
278
  $oFO = $this->getMod();
279
- if ( $oFO->doCheckHasPermissionToSubmit() ) {
280
  return;
281
  }
282
 
@@ -312,7 +317,8 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
312
  * @throws Exception
313
  */
314
  public function addNotice_admin_users_restricted( $aNoticeAttributes ) {
315
- if ( $this->isSecurityAdmin() ) {
 
316
  return;
317
  }
318
 
@@ -324,7 +330,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
324
  return;
325
  }
326
 
327
- $sName = $this->getController()->getHumanName();
328
  $aRenderData = array(
329
  'notice_attributes' => $aNoticeAttributes,
330
  'strings' => array(
@@ -357,8 +363,11 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
357
  }
358
 
359
  /**
360
- * Right before a plugin option is due to update it will check that we have permissions to do so and if not, will
361
- * revert the option to save to the previous one.
 
 
 
362
  * @param mixed $mNewOptionValue
363
  * @param string $sOptionKey
364
  * @param mixed $mOldValue
@@ -366,17 +375,13 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
366
  */
367
  public function blockOptionsSaves( $mNewOptionValue, $sOptionKey, $mOldValue ) {
368
 
369
- $bSavingIsPermitted = true;
370
-
371
- if ( $this->isOptionForThisPlugin( $sOptionKey ) || $this->isOptionRestricted( $sOptionKey ) ) {
372
- $bSavingIsPermitted = $this->isSecurityAdmin();
373
-
374
- if ( !$bSavingIsPermitted ) {
375
- $this->doStatIncrement( 'option.save.blocked' );
376
- }
377
  }
378
 
379
- return $bSavingIsPermitted ? $mNewOptionValue : $mOldValue;
380
  }
381
 
382
  /**
@@ -447,8 +452,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
447
  */
448
  public function disableThemeManipulation( $aAllCaps, $cap, $aArgs ) {
449
  // If we're registered with Admin Access we don't modify anything
450
- $bHasAdminAccess = self::getController()->getHasPermissionToManage();
451
- if ( $bHasAdminAccess ) {
452
  return $aAllCaps;
453
  }
454
 
@@ -482,9 +486,7 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
482
  * @return array
483
  */
484
  public function disablePostsManipulation( $aAllCaps, $cap, $aArgs ) {
485
- // If we're registered with Admin Access we don't modify anything
486
- $bHasAdminAccess = self::getController()->getHasPermissionToManage();
487
- if ( $bHasAdminAccess ) {
488
  return $aAllCaps;
489
  }
490
 
@@ -572,4 +574,12 @@ class ICWP_WPSF_Processor_AdminAccessRestriction extends ICWP_WPSF_Processor_Bas
572
  $sLinkText
573
  );
574
  }
 
 
 
 
 
 
 
 
575
  }
17
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
18
  $oFO = $this->getMod();
19
 
20
+ add_filter( $oFO->prefix( 'is_plugin_admin' ), array( $this, 'adjustUserAdminPermissions' ) );
 
 
 
 
 
21
 
22
  if ( $oFO->isWlEnabled() ) {
23
  $this->runWhiteLabel();
24
  }
25
  }
26
 
27
+ /**
28
+ * @param bool $bHasPermission
29
+ * @return bool
30
+ */
31
+ public function adjustUserAdminPermissions( $bHasPermission = true ) {
32
+ /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
33
+ $oFO = $this->getMod();
34
+ return $bHasPermission &&
35
+ ( $oFO->isRegisteredSecAdminUser() || $oFO->isSecAdminSessionValid()
36
+ || $oFO->checkAdminAccessKeySubmission() );
37
+ }
38
+
39
  public function onWpInit() {
40
  parent::onWpInit();
41
 
42
+ $oCon = $this->getController();
43
+ if ( !$oCon->isPluginAdmin() ) {
44
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
45
  $oFO = $this->getMod();
46
 
47
+ if ( !$oFO->isUpgrading() && !$this->loadWp()->isRequestUserLogin() ) {
48
+ add_filter( 'pre_update_option', array( $this, 'blockOptionsSaves' ), 1, 3 );
49
+ }
50
+
51
  if ( $oFO->isAdminAccessAdminUsersEnabled() ) {
52
  add_filter( 'editable_roles', array( $this, 'restrictEditableRoles' ), 100, 1 );
53
  add_filter( 'user_has_cap', array( $this, 'restrictAdminUserChanges' ), 100, 3 );
119
  return $aData;
120
  }
121
 
 
 
 
 
 
 
 
122
  /**
123
  * @param int $nUserId
124
  * @param string $sRole
281
  public function addNotice_certain_options_restricted( $aNoticeAttributes ) {
282
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
283
  $oFO = $this->getMod();
284
+ if ( $this->getController()->isPluginAdmin() ) {
285
  return;
286
  }
287
 
317
  * @throws Exception
318
  */
319
  public function addNotice_admin_users_restricted( $aNoticeAttributes ) {
320
+ $oCon = $this->getController();
321
+ if ( $oCon->isPluginAdmin() ) {
322
  return;
323
  }
324
 
330
  return;
331
  }
332
 
333
+ $sName = $oCon->getHumanName();
334
  $aRenderData = array(
335
  'notice_attributes' => $aNoticeAttributes,
336
  'strings' => array(
363
  }
364
 
365
  /**
366
+ * Need to always re-test isPluginAdmin() because there's a dynamic filter in there to
367
+ * permit saving by the plugin itself.
368
+ *
369
+ * Right before a plugin option is due to update it will check that we have permissions to do so
370
+ * and if not, will * revert the option to save to the previous one.
371
  * @param mixed $mNewOptionValue
372
  * @param string $sOptionKey
373
  * @param mixed $mOldValue
375
  */
376
  public function blockOptionsSaves( $mNewOptionValue, $sOptionKey, $mOldValue ) {
377
 
378
+ if ( !$this->getController()->isPluginAdmin()
379
+ && ( $this->isOptionForThisPlugin( $sOptionKey ) || $this->isOptionRestricted( $sOptionKey ) ) ) {
380
+ $this->doStatIncrement( 'option.save.blocked' );
381
+ $mNewOptionValue = $mOldValue;
 
 
 
 
382
  }
383
 
384
+ return $mNewOptionValue;
385
  }
386
 
387
  /**
452
  */
453
  public function disableThemeManipulation( $aAllCaps, $cap, $aArgs ) {
454
  // If we're registered with Admin Access we don't modify anything
455
+ if ( $this->getController()->isPluginAdmin() ) {
 
456
  return $aAllCaps;
457
  }
458
 
486
  * @return array
487
  */
488
  public function disablePostsManipulation( $aAllCaps, $cap, $aArgs ) {
489
+ if ( $this->getController()->isPluginAdmin() ) {
 
 
490
  return $aAllCaps;
491
  }
492
 
574
  $sLinkText
575
  );
576
  }
577
+
578
+ /**
579
+ * @deprecated
580
+ * @return bool
581
+ */
582
+ protected function isSecurityAdmin() {
583
+ return $this->getController()->isPluginAdmin();
584
+ }
585
  }
src/processors/adminaccess_whitelabel.php CHANGED
@@ -21,12 +21,12 @@ class ICWP_WPSF_Processor_AdminAccess_Whitelabel extends ICWP_WPSF_Processor_Bas
21
 
22
  public function onWpInit() {
23
  parent::onWpInit();
 
24
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
25
  $oFO = $this->getMod();
26
  $oCon = $this->getController();
27
 
28
- if ( $oFO->isWlHideUpdates() && $this->isNeedToHideUpdates()
29
- && !$oCon->getHasPermissionToManage() ) {
30
  $this->hideUpdates();
31
  }
32
  }
21
 
22
  public function onWpInit() {
23
  parent::onWpInit();
24
+
25
  /** @var ICWP_WPSF_FeatureHandler_AdminAccessRestriction $oFO */
26
  $oFO = $this->getMod();
27
  $oCon = $this->getController();
28
 
29
+ if ( $oFO->isWlHideUpdates() && $this->isNeedToHideUpdates() && !$oCon->isPluginAdmin() ) {
 
30
  $this->hideUpdates();
31
  }
32
  }
src/processors/autoupdates.php CHANGED
@@ -70,7 +70,7 @@ class ICWP_WPSF_Processor_Autoupdates extends ICWP_WPSF_Processor_BaseWpsf {
70
  $this->loadWp()->doForceRunAutomaticUpdates();
71
  }
72
 
73
- if ( $oFO->isAutoupdateIndividualPlugins() && $oFO->getConn()->isValidAdminArea() ) {
74
  // Adds automatic update indicator column to all plugins in plugin listing.
75
  add_filter( 'manage_plugins_columns', array( $this, 'fAddPluginsListAutoUpdateColumn' ) );
76
  }
@@ -275,6 +275,15 @@ class ICWP_WPSF_Processor_Autoupdates extends ICWP_WPSF_Processor_BaseWpsf {
275
  else if ( $oFO->isPluginSetToAutoupdate( $sFile ) ) {
276
  $bDoAutoUpdate = true;
277
  }
 
 
 
 
 
 
 
 
 
278
 
279
  return $bDoAutoUpdate;
280
  }
@@ -406,7 +415,7 @@ class ICWP_WPSF_Processor_Autoupdates extends ICWP_WPSF_Processor_BaseWpsf {
406
  * @return array
407
  */
408
  public function fAddPluginsListAutoUpdateColumn( $aColumns ) {
409
- if ( !isset( $aColumns[ 'icwp_autoupdate' ] ) ) {
410
  $aColumns[ 'icwp_autoupdate' ] = 'Auto Update';
411
  add_action( 'manage_plugins_custom_column',
412
  array( $this, 'aPrintPluginsListAutoUpdateColumnContent' ),
70
  $this->loadWp()->doForceRunAutomaticUpdates();
71
  }
72
 
73
+ if ( $oFO->isAutoupdateIndividualPlugins() ) {
74
  // Adds automatic update indicator column to all plugins in plugin listing.
75
  add_filter( 'manage_plugins_columns', array( $this, 'fAddPluginsListAutoUpdateColumn' ) );
76
  }
275
  else if ( $oFO->isPluginSetToAutoupdate( $sFile ) ) {
276
  $bDoAutoUpdate = true;
277
  }
278
+ else if ( $sFile === $this->getController()->getPluginBaseFile() ) {
279
+ $sAuto = $oFO->getOpt( 'autoupdate_plugin_self' );
280
+ if ( $sAuto === 'immediate' ) {
281
+ $bDoAutoUpdate = true;
282
+ }
283
+ else if ( $sAuto === 'disabled' ) {
284
+ $bDoAutoUpdate = false;
285
+ }
286
+ }
287
 
288
  return $bDoAutoUpdate;
289
  }
415
  * @return array
416
  */
417
  public function fAddPluginsListAutoUpdateColumn( $aColumns ) {
418
+ if ( $this->getController()->isPluginAdmin() && !isset( $aColumns[ 'icwp_autoupdate' ] ) ) {
419
  $aColumns[ 'icwp_autoupdate' ] = 'Auto Update';
420
  add_action( 'manage_plugins_custom_column',
421
  array( $this, 'aPrintPluginsListAutoUpdateColumnContent' ),
src/processors/base.php CHANGED
@@ -31,20 +31,24 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
31
  */
32
  public function __construct( $oModCon ) {
33
  $this->oModCon = $oModCon;
34
- add_action( $oModCon->prefix( 'plugin_shutdown' ), array( $this, 'onModuleShutdown' ) );
35
- add_action( $oModCon->prefix( 'generate_admin_notices' ), array( $this, 'autoAddToAdminNotices' ) );
36
- if ( method_exists( $this, 'addToAdminNotices' ) ) {
37
- add_action( $oModCon->prefix( 'generate_admin_notices' ), array( $this, 'addToAdminNotices' ) );
38
- }
39
 
40
  add_action( 'init', array( $this, 'onWpInit' ) );
41
  add_action( 'wp_login', array( $this, 'onWpLogin' ), 10, 2 );
42
  add_action( 'set_logged_in_cookie', array( $this, 'onWpSetLoggedInCookie' ), 5, 4 );
 
43
 
44
  $this->init();
45
  }
46
 
47
  public function onWpInit() {
 
 
 
 
 
 
 
 
48
  }
49
 
50
  /**
@@ -106,8 +110,6 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
106
  }
107
 
108
  public function autoAddToAdminNotices() {
109
- $oCon = $this->getController();
110
-
111
  foreach ( $this->getMod()->getAdminNotices() as $sNoticeId => $aAttrs ) {
112
 
113
  if ( !$this->getIfDisplayAdminNotice( $aAttrs ) ) {
@@ -115,9 +117,7 @@ abstract class ICWP_WPSF_Processor_Base extends ICWP_WPSF_Foundation {
115
  }
116
 
117
  $sMethodName = 'addNotice_'.str_replace( '-', '_', $sNoticeId );
118
- if ( method_exists( $this, $sMethodName ) && isset( $aAttrs[ 'valid_admin' ] )
119
- && $aAttrs[ 'valid_admin' ] && $oCon->isValidAdminArea() ) {
120
-
121
  $aAttrs[ 'id' ] = $sNoticeId;
122
  $aAttrs[ 'notice_id' ] = $sNoticeId;
123
  call_user_func( array( $this, $sMethodName ), $aAttrs );
31
  */
32
  public function __construct( $oModCon ) {
33
  $this->oModCon = $oModCon;
 
 
 
 
 
34
 
35
  add_action( 'init', array( $this, 'onWpInit' ) );
36
  add_action( 'wp_login', array( $this, 'onWpLogin' ), 10, 2 );
37
  add_action( 'set_logged_in_cookie', array( $this, 'onWpSetLoggedInCookie' ), 5, 4 );
38
+ add_action( $oModCon->prefix( 'plugin_shutdown' ), array( $this, 'onModuleShutdown' ) );
39
 
40
  $this->init();
41
  }
42
 
43
  public function onWpInit() {
44
+ $oMod = $this->getMod();
45
+ $oCon = $oMod->getConn();
46
+ if ( $oCon->isValidAdminArea() && $oCon->isPluginAdmin() ) {
47
+ add_action( $oMod->prefix( 'generate_admin_notices' ), array( $this, 'autoAddToAdminNotices' ) );
48
+ if ( method_exists( $this, 'addToAdminNotices' ) ) {
49
+ add_action( $oMod->prefix( 'generate_admin_notices' ), array( $this, 'addToAdminNotices' ) );
50
+ }
51
+ }
52
  }
53
 
54
  /**
110
  }
111
 
112
  public function autoAddToAdminNotices() {
 
 
113
  foreach ( $this->getMod()->getAdminNotices() as $sNoticeId => $aAttrs ) {
114
 
115
  if ( !$this->getIfDisplayAdminNotice( $aAttrs ) ) {
117
  }
118
 
119
  $sMethodName = 'addNotice_'.str_replace( '-', '_', $sNoticeId );
120
+ if ( method_exists( $this, $sMethodName ) ) {
 
 
121
  $aAttrs[ 'id' ] = $sNoticeId;
122
  $aAttrs[ 'notice_id' ] = $sNoticeId;
123
  call_user_func( array( $this, $sMethodName ), $aAttrs );
src/processors/base_wpsf.php CHANGED
@@ -45,7 +45,9 @@ abstract class ICWP_WPSF_Processor_BaseWpsf extends ICWP_WPSF_Processor_Base {
45
  * @return int
46
  */
47
  protected function getInstallationDays() {
48
- $nTimeInstalled = $this->getMod()->getPluginInstallationTime();
 
 
49
  if ( empty( $nTimeInstalled ) ) {
50
  return 0;
51
  }
45
  * @return int
46
  */
47
  protected function getInstallationDays() {
48
+ $nTimeInstalled = $this->getController()
49
+ ->loadCorePluginFeatureHandler()
50
+ ->getInstallDate();
51
  if ( empty( $nTimeInstalled ) ) {
52
  return 0;
53
  }
src/processors/basedb.php CHANGED
@@ -51,7 +51,7 @@ abstract class ICWP_WPSF_BaseDbProcessor extends ICWP_WPSF_Processor_BaseWpsf {
51
  /**
52
  */
53
  public function deleteTable() {
54
- if ( self::getController()->getHasPermissionToManage() && $this->getTableExists() ) {
55
  $this->deleteCleanupCron();
56
  $this->loadDbProcessor()->doDropTable( $this->getTableName() );
57
  }
51
  /**
52
  */
53
  public function deleteTable() {
54
+ if ( $this->getTableExists() ) {
55
  $this->deleteCleanupCron();
56
  $this->loadDbProcessor()->doDropTable( $this->getTableName() );
57
  }
src/processors/comments_filter.php CHANGED
@@ -54,7 +54,7 @@ class ICWP_WPSF_Processor_CommentsFilter extends ICWP_WPSF_Processor_BaseWpsf {
54
  $oFO = $this->getMod();
55
 
56
  // We only warn when the human spam filter is running
57
- if ( $oFO->isOpt( 'enable_comments_human_spam_filter', 'Y' ) && $oFO->getConn()->isValidAdminArea() ) {
58
 
59
  $oWpPlugins = $this->loadWpPlugins();
60
  $sPluginFile = $oWpPlugins->findPluginBy( 'Akismet', 'Name' );
54
  $oFO = $this->getMod();
55
 
56
  // We only warn when the human spam filter is running
57
+ if ( $oFO->isOpt( 'enable_comments_human_spam_filter', 'Y' ) ) {
58
 
59
  $oWpPlugins = $this->loadWpPlugins();
60
  $sPluginFile = $oWpPlugins->findPluginBy( 'Akismet', 'Name' );
src/processors/commentsfilter_humanspam.php CHANGED
@@ -210,6 +210,6 @@ class ICWP_WPSF_Processor_CommentsFilter_HumanSpam extends ICWP_WPSF_Processor_C
210
  * @return string
211
  */
212
  protected function getSpamBlacklistFile() {
213
- return $this->getMod()->getResourcesDir().'spamblacklist.txt';
214
  }
215
  }
210
  * @return string
211
  */
212
  protected function getSpamBlacklistFile() {
213
+ return $this->getController()->getPath_Assets( 'spamblacklist.txt' );
214
  }
215
  }
src/processors/dbhandler.php DELETED
@@ -1,7 +0,0 @@
1
- <?php
2
- /**
3
- * Created by PhpStorm.
4
- * User: paulg
5
- * Date: 19/02/2016
6
- * Time: 13:48
7
- */
 
 
 
 
 
 
 
src/processors/hackprotect_pluginvulnerabilities.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', false ) ):
4
 
5
- require_once( dirname(__FILE__ ).'/base_wpsf.php' );
6
 
7
  class ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities extends ICWP_WPSF_Processor_BaseWpsf {
8
 
@@ -32,18 +32,16 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
32
  // For display on the Plugins page
33
  add_filter( 'manage_plugins_columns', array( $this, 'fCountColumns' ), 1000 );
34
  add_action( 'admin_init', array( $this, 'addPluginVulnerabilityRows' ), 10, 2 );
35
-
36
  }
37
 
38
  protected function setupNotificationsCron() {
39
- $oWpCron = $this->loadWpCronProcessor();
40
- $oWpCron
41
- ->setRecurrence( 'daily' )
42
- ->createCronJob(
43
- $this->getCronName(),
44
- array( $this, 'cron_dailyPluginVulnerabilitiesScan' )
45
- );
46
- add_action( $this->getMod()->prefix( 'delete_plugin' ), array( $this, 'deleteCron' ) );
47
  }
48
 
49
  /**
@@ -56,7 +54,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
56
  /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
57
  $oFO = $this->getMod();
58
 
59
- foreach( $this->loadWpPlugins()->getPlugins() as $sPluginFile => $aPluginData ) {
60
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
61
  if ( is_array( $aPluginVulnerabilityData ) ) {
62
  $this->addPluginVulnerabilityToEmail( $aPluginData, $aPluginVulnerabilityData );
@@ -77,10 +75,10 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
77
  $this->aEmailContents = array_merge(
78
  $this->aEmailContents,
79
  array(
80
- '- ' . sprintf( _wpsf__( 'Plugin Name: %s' ), $aPluginData[ 'Name' ] ),
81
- '- ' . sprintf( _wpsf__( 'Vulnerability Type: %s' ), $aVulnerabilityData[ 'TypeOfVulnerability' ] ),
82
- '- ' . sprintf( _wpsf__( 'Vulnerable Plugin Version Range: %s' ), $aVulnerabilityData[ 'FirstVersion' ] . ' - ' . $aVulnerabilityData[ 'LastVersion' ] ),
83
- '- ' . sprintf( _wpsf__( 'Further Information: %s' ), $aVulnerabilityData[ 'URL' ] ),
84
  '',
85
  )
86
  );
@@ -97,13 +95,14 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
97
  }
98
 
99
  $aPreamble = array(
100
- sprintf( _wpsf__( '%s has detected a plugin with a known security vulnerability on your site.' ), $this->getController()->getHumanName() ),
 
101
  _wpsf__( 'Details for the plugin(s) are below:' ),
102
  '',
103
  );
104
 
105
  $this->aEmailContents = array_merge( $aPreamble, $this->aEmailContents );
106
- $this->aEmailContents[ ] = _wpsf__( 'You should update or remove these plugins at your earliest convenience.' );
107
 
108
  $sEmailSubject = sprintf( '%s - %s', _wpsf__( 'Warning' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
109
 
@@ -120,7 +119,7 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
120
  }
121
 
122
  public function addPluginVulnerabilityRows() {
123
- foreach( $this->loadWpPlugins()->getInstalledBaseFiles() as $sPluginFile ) {
124
  add_action( "after_plugin_row_$sPluginFile", array( $this, 'attachVulnerabilityWarning' ), 100, 2 );
125
  }
126
  }
@@ -138,23 +137,24 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
138
 
139
  /**
140
  * @param string $sPluginFile
141
- * @param array $aPluginData
142
  */
143
  public function attachVulnerabilityWarning( $sPluginFile, $aPluginData ) {
144
 
145
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
146
  if ( is_array( $aPluginVulnerabilityData ) ) {
147
  $aRenderData = array(
148
- 'strings' => array (
149
- 'known_vuln' => sprintf( _wpsf__( '%s has discovered that the currently installed version of the "%s" plugin has a known security vulnerability.'), $this->getController()->getHumanName(), $aPluginData['Name'] ),
150
- 'vuln_type' => _wpsf__( 'Vulnerability Type' ),
151
- 'vuln_type_explanation' => ucfirst( $aPluginVulnerabilityData['TypeOfVulnerability'] ),
152
- 'vuln_versions' => _wpsf__( 'Vulnerable Versions' ),
153
- 'more_info' => _wpsf__( 'More Info' ),
154
- 'first_version' => $aPluginVulnerabilityData['FirstVersion'],
155
- 'last_version' => $aPluginVulnerabilityData['LastVersion'],
 
156
  ),
157
- 'hrefs' => array(
158
  'more_info' => $aPluginVulnerabilityData[ 'URL' ]
159
  ),
160
  'nColspan' => $this->nColumnsCount
@@ -165,8 +165,8 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
165
 
166
  /**
167
  * @param string $sPluginFile
168
- * @param array $aPluginData
169
- * @return false|array - array if a vulnerability exists
170
  */
171
  protected function getPluginVulnerabilityData( $sPluginFile, $aPluginData ) {
172
 
@@ -175,12 +175,12 @@ if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', fal
175
  return false;
176
  }
177
 
178
- $sSlug = !empty( $aPluginData['slug'] ) ? $aPluginData['slug'] : substr( $sPluginFile, 0, strpos( $sPluginFile, DIRECTORY_SEPARATOR ) );
179
  if ( array_key_exists( $sSlug, $aPV ) ) {
180
- foreach( $aPV[$sSlug] as $aVulnerabilityItem ) {
181
 
182
- if ( version_compare( $aPluginData['Version'], $aVulnerabilityItem['FirstVersion'], '>=' )
183
- && version_compare( $aPluginData['Version'], $aVulnerabilityItem['LastVersion'], '<=' ) ) {
184
 
185
  return $aVulnerabilityItem;
186
  }
2
 
3
  if ( !class_exists( 'ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities', false ) ):
4
 
5
+ require_once( dirname( __FILE__ ).'/base_wpsf.php' );
6
 
7
  class ICWP_WPSF_Processor_HackProtect_PluginVulnerabilities extends ICWP_WPSF_Processor_BaseWpsf {
8
 
32
  // For display on the Plugins page
33
  add_filter( 'manage_plugins_columns', array( $this, 'fCountColumns' ), 1000 );
34
  add_action( 'admin_init', array( $this, 'addPluginVulnerabilityRows' ), 10, 2 );
 
35
  }
36
 
37
  protected function setupNotificationsCron() {
38
+ $this->loadWpCronProcessor()
39
+ ->setRecurrence( 'daily' )
40
+ ->createCronJob(
41
+ $this->getCronName(),
42
+ array( $this, 'cron_dailyPluginVulnerabilitiesScan' )
43
+ );
44
+ add_action( $this->getMod()->prefix( 'delete_plugin' ), array( $this, 'deleteCron' ) );
 
45
  }
46
 
47
  /**
54
  /** @var ICWP_WPSF_FeatureHandler_HackProtect $oFO */
55
  $oFO = $this->getMod();
56
 
57
+ foreach ( $this->loadWpPlugins()->getPlugins() as $sPluginFile => $aPluginData ) {
58
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
59
  if ( is_array( $aPluginVulnerabilityData ) ) {
60
  $this->addPluginVulnerabilityToEmail( $aPluginData, $aPluginVulnerabilityData );
75
  $this->aEmailContents = array_merge(
76
  $this->aEmailContents,
77
  array(
78
+ '- '.sprintf( _wpsf__( 'Plugin Name: %s' ), $aPluginData[ 'Name' ] ),
79
+ '- '.sprintf( _wpsf__( 'Vulnerability Type: %s' ), $aVulnerabilityData[ 'TypeOfVulnerability' ] ),
80
+ '- '.sprintf( _wpsf__( 'Vulnerable Plugin Version Range: %s' ), $aVulnerabilityData[ 'FirstVersion' ].' - '.$aVulnerabilityData[ 'LastVersion' ] ),
81
+ '- '.sprintf( _wpsf__( 'Further Information: %s' ), $aVulnerabilityData[ 'URL' ] ),
82
  '',
83
  )
84
  );
95
  }
96
 
97
  $aPreamble = array(
98
+ sprintf( _wpsf__( '%s has detected a plugin with a known security vulnerability on your site.' ), $this->getController()
99
+ ->getHumanName() ),
100
  _wpsf__( 'Details for the plugin(s) are below:' ),
101
  '',
102
  );
103
 
104
  $this->aEmailContents = array_merge( $aPreamble, $this->aEmailContents );
105
+ $this->aEmailContents[] = _wpsf__( 'You should update or remove these plugins at your earliest convenience.' );
106
 
107
  $sEmailSubject = sprintf( '%s - %s', _wpsf__( 'Warning' ), _wpsf__( 'Plugin(s) Discovered With Known Security Vulnerabilities.' ) );
108
 
119
  }
120
 
121
  public function addPluginVulnerabilityRows() {
122
+ foreach ( $this->loadWpPlugins()->getInstalledBaseFiles() as $sPluginFile ) {
123
  add_action( "after_plugin_row_$sPluginFile", array( $this, 'attachVulnerabilityWarning' ), 100, 2 );
124
  }
125
  }
137
 
138
  /**
139
  * @param string $sPluginFile
140
+ * @param array $aPluginData
141
  */
142
  public function attachVulnerabilityWarning( $sPluginFile, $aPluginData ) {
143
 
144
  $aPluginVulnerabilityData = $this->getPluginVulnerabilityData( $sPluginFile, $aPluginData );
145
  if ( is_array( $aPluginVulnerabilityData ) ) {
146
  $aRenderData = array(
147
+ 'strings' => array(
148
+ 'known_vuln' => sprintf( _wpsf__( '%s has discovered that the currently installed version of the "%s" plugin has a known security vulnerability.' ), $this->getController()
149
+ ->getHumanName(), $aPluginData[ 'Name' ] ),
150
+ 'vuln_type' => _wpsf__( 'Vulnerability Type' ),
151
+ 'vuln_type_explanation' => ucfirst( $aPluginVulnerabilityData[ 'TypeOfVulnerability' ] ),
152
+ 'vuln_versions' => _wpsf__( 'Vulnerable Versions' ),
153
+ 'more_info' => _wpsf__( 'More Info' ),
154
+ 'first_version' => $aPluginVulnerabilityData[ 'FirstVersion' ],
155
+ 'last_version' => $aPluginVulnerabilityData[ 'LastVersion' ],
156
  ),
157
+ 'hrefs' => array(
158
  'more_info' => $aPluginVulnerabilityData[ 'URL' ]
159
  ),
160
  'nColspan' => $this->nColumnsCount
165
 
166
  /**
167
  * @param string $sPluginFile
168
+ * @param array $aPluginData
169
+ * @return false|array - array if a vulnerability exists
170
  */
171
  protected function getPluginVulnerabilityData( $sPluginFile, $aPluginData ) {
172
 
175
  return false;
176
  }
177
 
178
+ $sSlug = !empty( $aPluginData[ 'slug' ] ) ? $aPluginData[ 'slug' ] : substr( $sPluginFile, 0, strpos( $sPluginFile, DIRECTORY_SEPARATOR ) );
179
  if ( array_key_exists( $sSlug, $aPV ) ) {
180
+ foreach ( $aPV[ $sSlug ] as $aVulnerabilityItem ) {
181
 
182
+ if ( version_compare( $aPluginData[ 'Version' ], $aVulnerabilityItem[ 'FirstVersion' ], '>=' )
183
+ && version_compare( $aPluginData[ 'Version' ], $aVulnerabilityItem[ 'LastVersion' ], '<=' ) ) {
184
 
185
  return $aVulnerabilityItem;
186
  }
src/processors/ips.php CHANGED
@@ -43,7 +43,6 @@ class ICWP_WPSF_Processor_Ips extends ICWP_WPSF_BaseDbProcessor {
43
  }
44
 
45
  add_filter( 'authenticate', array( $this, 'addLoginFailedWarningMessage' ), 10000, 1 );
46
- // add_filter( $oFO->prefix( 'has_permission_to_manage' ), array( $this, 'isCurrentIpWhitelisted' ), 30, 0 );
47
  add_action( 'template_redirect', array( $this, 'doTrack404' ) );
48
  }
49
 
43
  }
44
 
45
  add_filter( 'authenticate', array( $this, 'addLoginFailedWarningMessage' ), 10000, 1 );
 
46
  add_action( 'template_redirect', array( $this, 'doTrack404' ) );
47
  }
48
 
src/processors/license.php CHANGED
@@ -33,7 +33,7 @@ class ICWP_WPSF_Processor_License extends ICWP_WPSF_Processor_BaseWpsf {
33
 
34
  case 'license_check':
35
  if ( !wp_next_scheduled( $oFO->prefix( 'adhoc_cron_license_check' ) ) ) {
36
- wp_schedule_single_event( $oReq->ts() + 12, $oFO->prefix( 'adhoc_cron_license_check' ), array( true ) );
37
  }
38
  break;
39
  }
33
 
34
  case 'license_check':
35
  if ( !wp_next_scheduled( $oFO->prefix( 'adhoc_cron_license_check' ) ) ) {
36
+ wp_schedule_single_event( $oReq->ts() + 20, $oFO->prefix( 'adhoc_cron_license_check' ), array( true ) );
37
  }
38
  break;
39
  }
src/processors/lockdown.php CHANGED
@@ -68,6 +68,7 @@ class ICWP_WPSF_Processor_Lockdown extends ICWP_WPSF_Processor_BaseWpsf {
68
 
69
  public function onWpInit() {
70
  parent::onWpInit();
 
71
  if ( $this->loadWp()->isRest() ) {
72
  $this->processRestApi();
73
  }
68
 
69
  public function onWpInit() {
70
  parent::onWpInit();
71
+
72
  if ( $this->loadWp()->isRest() ) {
73
  $this->processRestApi();
74
  }
src/processors/loginprotect_backupcodes.php DELETED
@@ -1,209 +0,0 @@
1
- <?php
2
-
3
- if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_BackupCodes', false ) ) {
4
- return;
5
- }
6
-
7
- require_once( dirname( __FILE__ ).'/loginprotect_intentprovider_base.php' );
8
-
9
- class ICWP_WPSF_Processor_LoginProtect_BackupCodes extends ICWP_WPSF_Processor_LoginProtect_IntentProviderBase {
10
-
11
- /**
12
- * This MUST only ever be hooked into when the User is looking at their OWN profile, so we can use "current user"
13
- * functions. Otherwise we need to be careful of mixing up users.
14
- * @param WP_User $oUser
15
- */
16
- public function addOptionsToUserProfile( $oUser ) {
17
- $oCon = $this->getController();
18
-
19
- $bValidatedProfile = $this->hasValidatedProfile( $oUser );
20
- $aData = array(
21
- 'has_mfa' => $this->isUserSubjectToLoginIntent( $oUser ),
22
- 'has_validated_profile' => $bValidatedProfile,
23
- 'user_google_authenticator_secret' => $this->getSecret( $oUser ),
24
- 'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
25
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
26
- 'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
27
- 'strings' => array(
28
- 'button_gen_code' => _wpsf__( 'Generate ONE-Time Backup 2FA Login Code' ),
29
- 'button_del_code' => _wpsf__( 'Delete Login Backup Code' ),
30
- 'not_available' => _wpsf__( 'Backup login codes are not available if you do not have any other two-factor authentication modes active.' ),
31
- 'description_code' => _wpsf__( 'Click to generate a backup login code for your two-factor authentication.' ),
32
- 'description_code_ext1' => sprintf( '%s: %s',
33
- _wpsf__( 'Important' ),
34
- _wpsf__( 'This code will be displayed only once and you may use it to verify your login only once.' )
35
- .' '._wpsf__( 'Store it somewhere safe.' ) ),
36
- 'description_code_ext2' => _wpsf__( 'Generating a new code will replace your existing code.' ),
37
- 'description_chart_url' => _wpsf__( 'Use your Google Authenticator app to scan this QR code and enter the one time password below.' ),
38
- 'description_ga_secret' => _wpsf__( 'If you have a problem with scanning the QR code enter this code manually into the app.' ),
39
- 'desc_remove' => _wpsf__( 'Check the box to remove Google Authenticator login authentication.' ),
40
- 'label_check_to_remove' => sprintf( _wpsf__( 'Remove %s' ), _wpsf__( 'Google Authenticator' ) ),
41
- 'label_enter_code' => _wpsf__( 'Create Backup 2FA Login Code' ),
42
- 'label_ga_secret' => _wpsf__( 'Manual Code' ),
43
- 'label_scan_qr_code' => _wpsf__( 'Scan This QR Code' ),
44
- 'title' => _wpsf__( 'Backup Login Code' ),
45
- 'cant_add_other_user' => sprintf( _wpsf__( "Sorry, %s may not be added to another user's account." ), 'Backup Codes' ),
46
- 'cant_remove_admins' => sprintf( _wpsf__( "Sorry, %s may only be removed from another user's account by a Security Administrator." ), _wpsf__( 'Backup Codes' ) ),
47
- 'provided_by' => sprintf( _wpsf__( 'Provided by %s' ), $oCon->getHumanName() ),
48
- 'remove_more_info' => sprintf( _wpsf__( 'Understand how to remove Google Authenticator' ) )
49
- ),
50
- 'data' => array(
51
- 'otp_field_name' => $this->getLoginFormParameter()
52
- )
53
- );
54
-
55
- echo $this->getMod()->renderTemplate( 'snippets/user_profile_backupcode.php', $aData );
56
- }
57
-
58
- /**
59
- * @param WP_User $oUser
60
- */
61
- public function addOptionsToUserEditProfile( $oUser ) {
62
- // Allow no actions to be taken on other user profiles
63
- }
64
-
65
- /**
66
- * @param array $aFields
67
- * @return array
68
- */
69
- public function addLoginIntentField( $aFields ) {
70
- if ( $this->getCurrentUserHasValidatedProfile() ) {
71
- $aFields[] = array(
72
- 'name' => $this->getLoginFormParameter(),
73
- 'type' => 'text',
74
- 'value' => '',
75
- 'placeholder' => _wpsf__( 'Please use your Backup Code to login.' ),
76
- 'text' => _wpsf__( 'Login Backup Code' ),
77
- 'help_link' => '',
78
- );
79
- }
80
- return $aFields;
81
- }
82
-
83
- /**
84
- * Backup codes shouldn't make a user subject to login intent, but only be presented as required
85
- * - i.e. they have other MFA options but they can't be used at the moment. So no MFA options =
86
- * no need for backup codes
87
- * @param bool $bIsSubjectTo
88
- * @param WP_User $oUser
89
- * @return bool
90
- */
91
- public function filterUserSubjectToIntent( $bIsSubjectTo, $oUser ) {
92
- return $bIsSubjectTo;
93
- }
94
-
95
- /**
96
- * @param WP_User $oUser
97
- * @return bool
98
- */
99
- protected function hasValidatedProfile( $oUser ) {
100
- return $this->hasValidSecret( $oUser );
101
- }
102
-
103
- /**
104
- * Backup Code are 1-time only and if you have MFA, then we need to remove all the other tracking factors
105
- * @param WP_User $oUser
106
- * @param string $sOtpCode
107
- * @return bool
108
- */
109
- protected function processOtp( $oUser, $sOtpCode ) {
110
- $bValid = $this->validateBackupCode( $oUser, $sOtpCode );
111
- if ( $bValid ) {
112
- $this->deleteSecret( $oUser );
113
- }
114
- return $bValid;
115
- }
116
-
117
- /**
118
- * @param WP_User $oUser
119
- * @param string $sOtpCode
120
- * @return bool
121
- */
122
- private function validateBackupCode( $oUser, $sOtpCode ) {
123
- return wp_check_password( str_replace( '-', '', $sOtpCode ), $this->getSecret( $oUser ) );
124
- }
125
-
126
- /**
127
- * @param WP_User $oUser
128
- * @param bool $bIsSuccess
129
- */
130
- protected function auditLogin( $oUser, $bIsSuccess ) {
131
- if ( $bIsSuccess ) {
132
- $this->addToAuditEntry(
133
- sprintf( _wpsf__( 'User "%s" verified their identity using %s method.' ),
134
- $oUser->user_login, _wpsf__( 'Backup Code' )
135
- ), 2, 'login_protect_bc_verified'
136
- );
137
- $this->doStatIncrement( 'login.backupcode.verified' );
138
- }
139
- else {
140
- $this->addToAuditEntry(
141
- sprintf( _wpsf__( 'User "%s" failed to verify their identity using %s method.' ),
142
- $oUser->user_login, _wpsf__( 'Backup Code' )
143
- ), 2, 'login_protect_bc_failed'
144
- );
145
- $this->doStatIncrement( 'login.backupcode.fail' );
146
- }
147
- }
148
-
149
- /**
150
- * @param WP_User $oUser
151
- * @param bool $bIsOtpSuccess
152
- * @param bool $bOtpProvided - whether a OTP was actually provided
153
- * @return $this
154
- */
155
- protected function postOtpProcessAction( $oUser, $bIsOtpSuccess, $bOtpProvided ) {
156
- parent::postOtpProcessAction( $oUser, $bIsOtpSuccess, $bOtpProvided );
157
-
158
- if ( $bOtpProvided && $bIsOtpSuccess ) {
159
- $this->sendBackupCodeUsedEmail( $oUser );
160
- }
161
- return $this;
162
- }
163
-
164
- /**
165
- * @param WP_User $oUser
166
- */
167
- private function sendBackupCodeUsedEmail( $oUser ) {
168
- $aEmailContent = array(
169
- _wpsf__( 'This is a quick notice to inform you that your Backup Login code was just used.' ),
170
- _wpsf__( "Your WordPress account had only 1 backup login code." )
171
- .' '._wpsf__( "You must go to your profile and regenerate a new code if you want to use this method again." ),
172
- '',
173
- sprintf( '<strong>%s</strong>', _wpsf__( 'Login Details' ) ),
174
- sprintf( '%s: %s', _wpsf__( 'URL' ), $this->loadWp()->getHomeUrl() ),
175
- sprintf( '%s: %s', _wpsf__( 'Username' ), $oUser->user_login ),
176
- sprintf( '%s: %s', _wpsf__( 'IP Address' ), $this->ip() ),
177
- '',
178
- _wpsf__( 'Thank You.' ),
179
- );
180
-
181
- $sTitle = sprintf( _wpsf__( "Notice: %s" ), _wpsf__( "Backup Login Code Just Used" ) );
182
- $this->getEmailProcessor()
183
- ->sendEmailWithWrap( $oUser->user_email, $sTitle, $aEmailContent );
184
- }
185
-
186
- /**
187
- * @return string
188
- */
189
- protected function genNewSecret() {
190
- return wp_generate_password( 25, false );
191
- }
192
-
193
- /**
194
- * @param WP_User $oUser
195
- * @param string $sNewSecret
196
- * @return $this
197
- */
198
- protected function setSecret( $oUser, $sNewSecret ) {
199
- parent::setSecret( $oUser, wp_hash_password( $sNewSecret ) );
200
- return $this;
201
- }
202
-
203
- /**
204
- * @return string
205
- */
206
- protected function getStub() {
207
- return ICWP_WPSF_Processor_LoginProtect_Track::Factor_BackupCode;
208
- }
209
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/loginprotect_cooldown.php CHANGED
@@ -58,7 +58,7 @@ class ICWP_WPSF_Processor_LoginProtect_Cooldown extends ICWP_WPSF_Processor_Logi
58
  * @return string
59
  */
60
  protected function getLastLoginTimeFilePath() {
61
- return self::getController()->getRootDir().'mode.login_throttled';
62
  }
63
 
64
  /**
58
  * @return string
59
  */
60
  protected function getLastLoginTimeFilePath() {
61
+ return path_join( $this->getController()->getRootDir(), 'mode.login_throttled' );
62
  }
63
 
64
  /**
src/processors/loginprotect_googleauthenticator.php DELETED
@@ -1,344 +0,0 @@
1
- <?php
2
-
3
- if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator', false ) ) {
4
- return;
5
- }
6
-
7
- require_once( dirname( __FILE__ ).'/loginprotect_intentprovider_base.php' );
8
-
9
- class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Processor_LoginProtect_IntentProviderBase {
10
-
11
- /**
12
- */
13
- public function run() {
14
- parent::run();
15
- if ( $this->loadRequest()->query( 'shield_action' ) == 'garemovalconfirm' ) {
16
- add_action( 'wp_loaded', array( $this, 'validateUserGaRemovalLink' ), 10 );
17
- }
18
- }
19
-
20
- /**
21
- * This MUST only ever be hooked into when the User is looking at their OWN profile, so we can use "current user"
22
- * functions. Otherwise we need to be careful of mixing up users.
23
- * @param WP_User $oUser
24
- */
25
- public function addOptionsToUserProfile( $oUser ) {
26
- $oCon = $this->getController();
27
-
28
- $bValidatedProfile = $this->hasValidatedProfile( $oUser );
29
- $aData = array(
30
- 'has_validated_profile' => $bValidatedProfile,
31
- 'user_google_authenticator_secret' => $this->getSecret( $oUser ),
32
- 'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
33
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
34
- 'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
35
- 'strings' => array(
36
- 'description_otp_code' => _wpsf__( 'Provide the current code generated by your Google Authenticator app.' ),
37
- 'description_otp_code_ext' => _wpsf__( 'To reset this QR Code enter fake data here.' ),
38
- 'description_chart_url' => _wpsf__( 'Use your Google Authenticator app to scan this QR code and enter the one time password below.' ),
39
- 'description_ga_secret' => _wpsf__( 'If you have a problem with scanning the QR code enter this code manually into the app.' ),
40
- 'desc_remove' => _wpsf__( 'Check the box to remove Google Authenticator login authentication.' ),
41
- 'label_check_to_remove' => sprintf( _wpsf__( 'Remove %s' ), _wpsf__( 'Google Authenticator' ) ),
42
- 'label_enter_code' => _wpsf__( 'Google Authenticator Code' ),
43
- 'label_ga_secret' => _wpsf__( 'Manual Code' ),
44
- 'label_scan_qr_code' => _wpsf__( 'Scan This QR Code' ),
45
- 'title' => _wpsf__( 'Google Authenticator' ),
46
- 'cant_add_other_user' => sprintf( _wpsf__( "Sorry, %s may not be added to another user's account." ), 'Google Authenticator' ),
47
- 'cant_remove_admins' => sprintf( _wpsf__( "Sorry, %s may only be removed from another user's account by a Security Administrator." ), _wpsf__( 'Google Authenticator' ) ),
48
- 'provided_by' => sprintf( _wpsf__( 'Provided by %s' ), $oCon->getHumanName() ),
49
- 'remove_more_info' => sprintf( _wpsf__( 'Understand how to remove Google Authenticator' ) )
50
- ),
51
- 'data' => array(
52
- 'otp_field_name' => $this->getLoginFormParameter()
53
- )
54
- );
55
-
56
- if ( !$bValidatedProfile ) {
57
- $aData[ 'chart_url' ] = $this->getGaRegisterChartUrl( $oUser );
58
- }
59
-
60
- echo $this->getMod()->renderTemplate( 'snippets/user_profile_googleauthenticator.php', $aData );
61
- }
62
-
63
- /**
64
- * @param WP_User $oUser
65
- * @return string
66
- */
67
- public function getGaRegisterChartUrl( $oUser ) {
68
- if ( empty( $oUser ) ) {
69
- $sUrl = '';
70
- }
71
- else {
72
- $sUrl = $this->loadGoogleAuthenticatorProcessor()
73
- ->getGoogleQrChartUrl(
74
- $this->getSecret( $oUser ),
75
- preg_replace( '#[^0-9a-z]#i', '', $oUser->user_login )
76
- .'@'.preg_replace( '#[^0-9a-z]#i', '', $this->loadWp()->getSiteName() )
77
- );
78
- }
79
- return $sUrl;
80
- }
81
-
82
- /**
83
- * The only thing we can do is REMOVE Google Authenticator from an account that is not our own
84
- * But, only admins can do this. If Security Admin feature is enabled, then only they can do it.
85
- * @param int $nSavingUserId
86
- */
87
- public function handleEditOtherUserProfileSubmit( $nSavingUserId ) {
88
-
89
- // Can only edit other users if you're admin/security-admin
90
- if ( $this->getController()->getHasPermissionToManage() ) {
91
- $oWpUsers = $this->loadWpUsers();
92
- $oSavingUser = $oWpUsers->getUserById( $nSavingUserId );
93
-
94
- $sShieldTurnOff = $this->loadRequest()->post( 'shield_turn_off_google_authenticator' );
95
- if ( !empty( $sShieldTurnOff ) && $sShieldTurnOff == 'Y' ) {
96
-
97
- $bPermissionToRemoveGa = true;
98
- // if the current user has Google Authenticator on THEIR account, process their OTP.
99
- $oCurrentUser = $oWpUsers->getCurrentWpUser();
100
- if ( $this->hasValidatedProfile( $oCurrentUser ) ) {
101
- $bPermissionToRemoveGa = $this->processOtp( $oCurrentUser, $this->fetchCodeFromRequest() );
102
- }
103
-
104
- if ( $bPermissionToRemoveGa ) {
105
- $this->processRemovalFromAccount( $oSavingUser );
106
- $sMsg = _wpsf__( 'Google Authenticator was successfully removed from the account.' );
107
- }
108
- else {
109
- $sMsg = _wpsf__( 'Google Authenticator could not be removed from the account - ensure your code is correct.' );
110
- }
111
- $this->getMod()->setFlashAdminNotice( $sMsg, $bPermissionToRemoveGa );
112
- }
113
- }
114
- else {
115
- // DO NOTHING EVER
116
- }
117
- }
118
-
119
- /**
120
- * @param WP_User $oUser
121
- * @return $this
122
- */
123
- protected function processRemovalFromAccount( $oUser ) {
124
- $this->setProfileValidated( $oUser, false )
125
- ->resetSecret( $oUser );
126
- return $this;
127
- }
128
-
129
- /**
130
- * This MUST only ever be hooked into when the User is looking at their OWN profile,
131
- * so we can use "current user" functions. Otherwise we need to be careful of mixing up users.
132
- * @param int $nSavingUserId
133
- */
134
- public function handleUserProfileSubmit( $nSavingUserId ) {
135
- $oWpUsers = $this->loadWpUsers();
136
-
137
- $oSavingUser = $oWpUsers->getUserById( $nSavingUserId );
138
-
139
- // If it's your own account, you CANT do anything without your OTP (except turn off via email).
140
- $sOtp = $this->fetchCodeFromRequest();
141
- $bValidOtp = $this->processOtp( $oSavingUser, $sOtp );
142
-
143
- $sMessageOtpInvalid = _wpsf__( 'One Time Password (OTP) was not valid.' ).' '._wpsf__( 'Please try again.' );
144
-
145
- $sShieldTurnOff = $this->loadRequest()->post( 'shield_turn_off_google_authenticator' );
146
- if ( !empty( $sShieldTurnOff ) && $sShieldTurnOff == 'Y' ) {
147
-
148
- $bError = false;
149
- if ( $bValidOtp ) {
150
- $this->processRemovalFromAccount( $oSavingUser );
151
- $sFlash = _wpsf__( 'Google Authenticator was successfully removed from the account.' );
152
- }
153
- else if ( empty( $sOtp ) ) {
154
-
155
- if ( $this->sendEmailConfirmationGaRemoval( $oSavingUser ) ) {
156
- $sFlash = _wpsf__( 'An email has been sent to you in order to confirm Google Authenticator removal' );
157
- }
158
- else {
159
- $bError = true;
160
- $sFlash = _wpsf__( 'We tried to send an email for you to confirm Google Authenticator removal but it failed.' );
161
- }
162
- }
163
- else {
164
- $bError = true;
165
- $sFlash = $sMessageOtpInvalid;
166
- }
167
- $this->getMod()->setFlashAdminNotice( $sFlash, $bError );
168
- return;
169
- }
170
-
171
- // At this stage, if the OTP was empty, then we have no further processing to do.
172
- if ( empty( $sOtp ) ) {
173
- return;
174
- }
175
-
176
- // We're trying to validate our OTP to activate our GA
177
- if ( !$this->hasValidatedProfile( $oSavingUser ) ) {
178
-
179
- if ( $bValidOtp ) {
180
- $this->setProfileValidated( $oSavingUser );
181
- $sFlash = sprintf(
182
- _wpsf__( '%s was successfully added to your account.' ),
183
- _wpsf__( 'Google Authenticator' )
184
- );
185
- }
186
- else {
187
- $this->resetSecret( $oSavingUser );
188
- $sFlash = $sMessageOtpInvalid;
189
- }
190
- $this->getMod()->setFlashAdminNotice( $sFlash, !$bValidOtp );
191
- }
192
- }
193
-
194
- /**
195
- * @param array $aFields
196
- * @return array
197
- */
198
- public function addLoginIntentField( $aFields ) {
199
- if ( $this->getCurrentUserHasValidatedProfile() ) {
200
- $aFields[] = array(
201
- 'name' => $this->getLoginFormParameter(),
202
- 'type' => 'text',
203
- 'value' => '',
204
- 'placeholder' => _wpsf__( 'Please use your Google Authenticator App to retrieve your code.' ),
205
- 'text' => _wpsf__( 'Google Authenticator Code' ),
206
- 'help_link' => 'https://icwp.io/wpsf42',
207
- 'extras' => array(
208
- 'onkeyup' => "this.value=this.value.replace(/[^\d]/g,'')"
209
- )
210
- );
211
- }
212
- return $aFields;
213
- }
214
-
215
- /**
216
- * @param WP_User $oUser
217
- * @return bool
218
- */
219
- protected function sendEmailConfirmationGaRemoval( $oUser ) {
220
- $bSendSuccess = false;
221
-
222
- $aEmailContent = array();
223
- $aEmailContent[] = _wpsf__( 'You have requested the removal of Google Authenticator from your WordPress account.' )
224
- ._wpsf__( 'Please click the link below to confirm.' );
225
- $aEmailContent[] = $this->generateGaRemovalConfirmationLink();
226
-
227
- $sRecipient = $oUser->get( 'user_email' );
228
- if ( $this->loadDP()->validEmail( $sRecipient ) ) {
229
- $sEmailSubject = _wpsf__( 'Google Authenticator Removal Confirmation' );
230
- $bSendSuccess = $this->getEmailProcessor()
231
- ->sendEmailWithWrap( $sRecipient, $sEmailSubject, $aEmailContent );
232
- }
233
- return $bSendSuccess;
234
- }
235
-
236
- /**
237
- */
238
- public function validateUserGaRemovalLink() {
239
- // Must be already logged in for this link to work.
240
- $oWpCurrentUser = $this->loadWpUsers()->getCurrentWpUser();
241
- if ( empty( $oWpCurrentUser ) ) {
242
- return;
243
- }
244
-
245
- // Session IDs must be the same
246
- $sSessionId = $this->loadRequest()->query( 'sessionid' );
247
- if ( empty( $sSessionId ) || ( $sSessionId !== $this->getController()->getSessionId() ) ) {
248
- return;
249
- }
250
-
251
- $this->processRemovalFromAccount( $oWpCurrentUser );
252
- $this->getMod()
253
- ->setFlashAdminNotice( _wpsf__( 'Google Authenticator was successfully removed from this account.' ) );
254
- $this->loadWp()->redirectToAdmin();
255
- }
256
-
257
- /**
258
- * @param WP_User $oUser
259
- * @param string $sOtpCode
260
- * @return bool
261
- */
262
- protected function processOtp( $oUser, $sOtpCode ) {
263
- return $this->validateGaCode( $oUser, $sOtpCode );
264
- }
265
-
266
- /**
267
- * @param WP_User $oUser
268
- * @param string $sOtpCode
269
- * @return bool
270
- */
271
- public function validateGaCode( $oUser, $sOtpCode ) {
272
- $bValidOtp = false;
273
- if ( !empty( $sOtpCode ) && preg_match( '#^[0-9]{6}$#', $sOtpCode ) ) {
274
- $bValidOtp = $this->loadGoogleAuthenticatorProcessor()
275
- ->verifyOtp( $this->getSecret( $oUser ), $sOtpCode );
276
- }
277
- return $bValidOtp;
278
- }
279
-
280
- /**
281
- * @param WP_User $oUser
282
- * @param bool $bIsSuccess
283
- */
284
- protected function auditLogin( $oUser, $bIsSuccess ) {
285
- if ( $bIsSuccess ) {
286
- $this->addToAuditEntry(
287
- sprintf( _wpsf__( 'User "%s" verified their identity using %s method.' ),
288
- $oUser->user_login, _wpsf__( 'Google Authenticator' )
289
- ), 2, 'login_protect_ga_verified'
290
- );
291
- $this->doStatIncrement( 'login.googleauthenticator.verified' );
292
- }
293
- else {
294
- $this->addToAuditEntry(
295
- sprintf( _wpsf__( 'User "%s" failed to verify their identity using %s method.' ),
296
- $oUser->user_login, _wpsf__( 'Google Authenticator' )
297
- ), 2, 'login_protect_ga_failed'
298
- );
299
- $this->doStatIncrement( 'login.googleauthenticator.fail' );
300
- }
301
- }
302
-
303
- /**
304
- * @return string
305
- */
306
- protected function generateGaRemovalConfirmationLink() {
307
- $aQueryArgs = array(
308
- 'shield_action' => 'garemovalconfirm',
309
- 'sessionid' => $this->getController()->getSessionId()
310
- );
311
- return add_query_arg( $aQueryArgs, $this->loadWp()->getUrl_WpAdmin() );
312
- }
313
-
314
- /**
315
- * @return string
316
- */
317
- protected function genNewSecret() {
318
- return $this->loadGoogleAuthenticatorProcessor()->generateNewSecret();
319
- }
320
-
321
- /**
322
- * @param WP_User $oUser
323
- * @return string
324
- */
325
- protected function getSecret( WP_User $oUser ) {
326
- $sSec = parent::getSecret( $oUser );
327
- return empty( $sSec ) ? $this->resetSecret( $oUser ) : $sSec;
328
- }
329
-
330
- /**
331
- * @return string
332
- */
333
- protected function getStub() {
334
- return ICWP_WPSF_Processor_LoginProtect_Track::Factor_Google_Authenticator;
335
- }
336
-
337
- /**
338
- * @param string $sSecret
339
- * @return bool
340
- */
341
- protected function isSecretValid( $sSecret ) {
342
- return parent::isSecretValid( $sSecret ) && ( strlen( $sSecret ) == 16 );
343
- }
344
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/loginprotect_intent.php CHANGED
@@ -45,6 +45,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
45
 
46
  public function onWpInit() {
47
  parent::onWpInit();
 
48
  $this->setupLoginIntent();
49
  }
50
 
@@ -110,6 +111,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
110
  * @param WP_User|WP_Error $oUser
111
  */
112
  protected function initLoginIntent( $oUser ) {
 
113
  if ( !$this->isLoginCaptured() && $oUser instanceof WP_User
114
  && $this->getLoginTrack()->hasFactorsRemainingToTrack() ) {
115
 
@@ -129,6 +131,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
129
  * hooked to 'init' and only run if a user is logged-in (not on the login request)
130
  */
131
  private function processLoginIntent() {
 
132
  $oWpUsers = $this->loadWpUsers();
133
 
134
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
@@ -137,16 +140,16 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
137
  if ( $this->hasValidLoginIntent() ) { // ie. valid login intent present
138
  $oReq = $this->loadRequest();
139
 
140
- $bIsLoginIntentSubmission = $oReq->request( $oFO->getLoginIntentRequestFlag() ) == 1;
141
- if ( $bIsLoginIntentSubmission ) {
142
 
143
  if ( $oReq->post( 'cancel' ) == 1 ) {
144
  $oWpUsers->logoutUser(); // clears the login and login intent
145
- $this->loadWp()->redirectToLogin();
146
- return;
147
  }
 
148
 
149
- if ( $this->isLoginIntentValid() ) {
150
  if ( $oReq->post( 'skip_mfa' ) === 'Y' ) { // store the browser hash
151
  $oFO->addMfaLoginHash( $oWpUsers->getCurrentWpUser() );
152
  }
@@ -160,11 +163,15 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
160
 
161
  $oFO->setFlashAdminNotice( $sFlash )
162
  ->setOptInsightsAt( 'last_2fa_login_at' );
 
 
 
163
  }
164
  else {
165
  $oFO->setFlashAdminNotice( _wpsf__( 'One or more of your authentication codes failed or was missing' ), true );
 
166
  }
167
- $this->loadWp()->redirectHere();
168
  }
169
  if ( $this->printLoginIntentForm() ) {
170
  die();
@@ -185,7 +192,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
185
  * Use this ONLY when the login intent has been successfully verified.
186
  * @return $this
187
  */
188
- protected function removeLoginIntent() {
189
  return $this->setLoginIntentExpiresAt( 0 );
190
  }
191
 
@@ -248,6 +255,7 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
248
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
249
  $oFO = $this->getMod();
250
  $oCon = $this->getController();
 
251
  $aLoginIntentFields = apply_filters( $oFO->prefix( 'login-intent-form-fields' ), array() );
252
 
253
  if ( empty( $aLoginIntentFields ) ) {
@@ -271,7 +279,26 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
271
  $sMessageType = 'warning';
272
  }
273
 
274
- $sRedirectTo = rawurlencode( $this->loadRequest()->getUri() ); // not actually used
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
 
276
  $aLabels = $oCon->getPluginLabels();
277
  $sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $oCon->getPluginUrl_Image( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
@@ -300,12 +327,13 @@ class ICWP_WPSF_Processor_LoginProtect_Intent extends ICWP_WPSF_Processor_BaseWp
300
  'login_intent_flag' => $oFO->getLoginIntentRequestFlag()
301
  ),
302
  'hrefs' => array(
303
- 'form_action' => $this->loadRequest()->getUri(),
304
  'css_bootstrap' => $oCon->getPluginUrl_Css( 'bootstrap4.min.css' ),
305
  'js_bootstrap' => $oCon->getPluginUrl_Js( 'bootstrap4.min.js' ),
306
  'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
307
  'redirect_to' => $sRedirectTo,
308
  'what_is_this' => 'https://icontrolwp.freshdesk.com/support/solutions/articles/3000064840',
 
309
  ),
310
  'imgs' => array(
311
  'banner' => $sBannerUrl,
45
 
46
  public function onWpInit() {
47
  parent::onWpInit();
48
+
49
  $this->setupLoginIntent();
50
  }
51
 
111
  * @param WP_User|WP_Error $oUser
112
  */
113
  protected function initLoginIntent( $oUser ) {
114
+
115
  if ( !$this->isLoginCaptured() && $oUser instanceof WP_User
116
  && $this->getLoginTrack()->hasFactorsRemainingToTrack() ) {
117
 
131
  * hooked to 'init' and only run if a user is logged-in (not on the login request)
132
  */
133
  private function processLoginIntent() {
134
+ $oWp = $this->loadWp();
135
  $oWpUsers = $this->loadWpUsers();
136
 
137
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
140
  if ( $this->hasValidLoginIntent() ) { // ie. valid login intent present
141
  $oReq = $this->loadRequest();
142
 
143
+ // Is 2FA/login-intent submit
144
+ if ( $oReq->request( $oFO->getLoginIntentRequestFlag() ) == 1 ) {
145
 
146
  if ( $oReq->post( 'cancel' ) == 1 ) {
147
  $oWpUsers->logoutUser(); // clears the login and login intent
148
+ $sRedirectHref = $oReq->post( 'cancel_href' );
149
+ empty( $sRedirectHref ) ? $oWp->redirectToLogin() : $oWp->doRedirect( rawurldecode( $sRedirectHref ) );
150
  }
151
+ else if ( $this->isLoginIntentValid() ) {
152
 
 
153
  if ( $oReq->post( 'skip_mfa' ) === 'Y' ) { // store the browser hash
154
  $oFO->addMfaLoginHash( $oWpUsers->getCurrentWpUser() );
155
  }
163
 
164
  $oFO->setFlashAdminNotice( $sFlash )
165
  ->setOptInsightsAt( 'last_2fa_login_at' );
166
+
167
+ $sRedirectHref = $oReq->post( 'redirect_to' );
168
+ empty( $sRedirectHref ) ? $oWp->redirectHere() : $oWp->doRedirect( rawurldecode( $sRedirectHref ) );
169
  }
170
  else {
171
  $oFO->setFlashAdminNotice( _wpsf__( 'One or more of your authentication codes failed or was missing' ), true );
172
+ $this->loadWp()->redirectHere();
173
  }
174
+ return; // we've redirected anyway.
175
  }
176
  if ( $this->printLoginIntentForm() ) {
177
  die();
192
  * Use this ONLY when the login intent has been successfully verified.
193
  * @return $this
194
  */
195
+ private function removeLoginIntent() {
196
  return $this->setLoginIntentExpiresAt( 0 );
197
  }
198
 
255
  /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
256
  $oFO = $this->getMod();
257
  $oCon = $this->getController();
258
+ $oReq = $this->loadRequest();
259
  $aLoginIntentFields = apply_filters( $oFO->prefix( 'login-intent-form-fields' ), array() );
260
 
261
  if ( empty( $aLoginIntentFields ) ) {
279
  $sMessageType = 'warning';
280
  }
281
 
282
+ $sReferUrl = $oReq->server( 'HTTP_REFERER', '' );
283
+ if ( strpos( $sReferUrl, '?' ) ) {
284
+ list( $sReferUrl, $sReferQuery ) = explode( '?', $sReferUrl, 2 );
285
+ }
286
+ else {
287
+ $sReferQuery = '';
288
+ }
289
+
290
+ $sRedirectTo = $oReq->post( 'redirect_to', '' );
291
+ if ( !empty( $sReferQuery ) ) {
292
+ parse_str( $sReferQuery, $aReferQueryItems );
293
+ if ( !empty( $aReferQueryItems[ 'redirect_to' ] ) ) {
294
+ $sRedirectTo = rawurlencode( $aReferQueryItems[ 'redirect_to' ] );
295
+ }
296
+ }
297
+
298
+ $sCancelHref = $oReq->post( 'cancel_href', '' );
299
+ if ( empty( $sCancelHref ) && $this->loadDP()->isValidUrl( $sReferUrl ) ) {
300
+ $sCancelHref = rawurlencode( parse_url( $sReferUrl, PHP_URL_PATH ) );
301
+ }
302
 
303
  $aLabels = $oCon->getPluginLabels();
304
  $sBannerUrl = empty( $aLabels[ 'url_login2fa_logourl' ] ) ? $oCon->getPluginUrl_Image( 'pluginlogo_banner-772x250.png' ) : $aLabels[ 'url_login2fa_logourl' ];
327
  'login_intent_flag' => $oFO->getLoginIntentRequestFlag()
328
  ),
329
  'hrefs' => array(
330
+ 'form_action' => $oReq->getUri(),
331
  'css_bootstrap' => $oCon->getPluginUrl_Css( 'bootstrap4.min.css' ),
332
  'js_bootstrap' => $oCon->getPluginUrl_Js( 'bootstrap4.min.js' ),
333
  'shield_logo' => 'https://ps.w.org/wp-simple-firewall/assets/banner-772x250.png',
334
  'redirect_to' => $sRedirectTo,
335
  'what_is_this' => 'https://icontrolwp.freshdesk.com/support/solutions/articles/3000064840',
336
+ 'cancel_href' => $sCancelHref
337
  ),
338
  'imgs' => array(
339
  'banner' => $sBannerUrl,
src/processors/loginprotect_intentprovider_backup.php CHANGED
@@ -22,7 +22,7 @@ class ICWP_WPSF_Processor_LoginProtect_BackupCodes extends ICWP_WPSF_Processor_L
22
  'has_validated_profile' => $bValidatedProfile,
23
  'user_google_authenticator_secret' => $this->getSecret( $oUser ),
24
  'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
25
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
26
  'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
27
  'strings' => array(
28
  'button_gen_code' => _wpsf__( 'Generate ONE-Time Backup 2FA Login Code' ),
22
  'has_validated_profile' => $bValidatedProfile,
23
  'user_google_authenticator_secret' => $this->getSecret( $oUser ),
24
  'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
25
+ 'i_am_valid_admin' => $oCon->isPluginAdmin(),
26
  'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
27
  'strings' => array(
28
  'button_gen_code' => _wpsf__( 'Generate ONE-Time Backup 2FA Login Code' ),
src/processors/loginprotect_intentprovider_base.php CHANGED
@@ -36,7 +36,7 @@ abstract class ICWP_WPSF_Processor_LoginProtect_IntentProviderBase extends ICWP_
36
  add_action( 'show_user_profile', array( $this, 'addOptionsToUserProfile' ) );
37
  add_action( 'personal_options_update', array( $this, 'handleUserProfileSubmit' ) );
38
 
39
- if ( $this->getController()->isValidAdminArea( true ) ) {
40
  add_action( 'edit_user_profile', array( $this, 'addOptionsToUserEditProfile' ) );
41
  add_action( 'edit_user_profile_update', array( $this, 'handleEditOtherUserProfileSubmit' ) );
42
  }
36
  add_action( 'show_user_profile', array( $this, 'addOptionsToUserProfile' ) );
37
  add_action( 'personal_options_update', array( $this, 'handleUserProfileSubmit' ) );
38
 
39
+ if ( $this->getController()->isPluginAdmin() ) {
40
  add_action( 'edit_user_profile', array( $this, 'addOptionsToUserEditProfile' ) );
41
  add_action( 'edit_user_profile_update', array( $this, 'handleEditOtherUserProfileSubmit' ) );
42
  }
src/processors/loginprotect_intentprovider_email.php CHANGED
@@ -203,7 +203,7 @@ class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor
203
  'user_has_email_authentication_active' => $bValidatedProfile,
204
  'user_has_email_authentication_enforced' => $this->isSubjectToEmailAuthentication( $oUser ),
205
  'is_my_user_profile' => ( $oUser->ID == $oWp->getCurrentWpUserId() ),
206
- 'i_am_valid_admin' => $this->getController()->isValidAdminArea( true ),
207
  'user_to_edit_is_admin' => $oWp->isUserAdmin( $oUser ),
208
  'strings' => array(
209
  'label_email_authentication' => _wpsf__( 'Email Authentication' ),
203
  'user_has_email_authentication_active' => $bValidatedProfile,
204
  'user_has_email_authentication_enforced' => $this->isSubjectToEmailAuthentication( $oUser ),
205
  'is_my_user_profile' => ( $oUser->ID == $oWp->getCurrentWpUserId() ),
206
+ 'i_am_valid_admin' => $this->getController()->isPluginAdmin(),
207
  'user_to_edit_is_admin' => $oWp->isUserAdmin( $oUser ),
208
  'strings' => array(
209
  'label_email_authentication' => _wpsf__( 'Email Authentication' ),
src/processors/loginprotect_intentprovider_ga.php CHANGED
@@ -30,7 +30,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
30
  'has_validated_profile' => $bValidatedProfile,
31
  'user_google_authenticator_secret' => $this->getSecret( $oUser ),
32
  'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
33
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
34
  'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
35
  'strings' => array(
36
  'description_otp_code' => _wpsf__( 'Provide the current code generated by your Google Authenticator app.' ),
@@ -87,7 +87,7 @@ class ICWP_WPSF_Processor_LoginProtect_GoogleAuthenticator extends ICWP_WPSF_Pro
87
  public function handleEditOtherUserProfileSubmit( $nSavingUserId ) {
88
 
89
  // Can only edit other users if you're admin/security-admin
90
- if ( $this->getController()->getHasPermissionToManage() ) {
91
  $oWpUsers = $this->loadWpUsers();
92
  $oSavingUser = $oWpUsers->getUserById( $nSavingUserId );
93
 
30
  'has_validated_profile' => $bValidatedProfile,
31
  'user_google_authenticator_secret' => $this->getSecret( $oUser ),
32
  'is_my_user_profile' => ( $oUser->ID == $this->loadWpUsers()->getCurrentWpUserId() ),
33
+ 'i_am_valid_admin' => $oCon->isPluginAdmin(),
34
  'user_to_edit_is_admin' => $this->loadWpUsers()->isUserAdmin( $oUser ),
35
  'strings' => array(
36
  'description_otp_code' => _wpsf__( 'Provide the current code generated by your Google Authenticator app.' ),
87
  public function handleEditOtherUserProfileSubmit( $nSavingUserId ) {
88
 
89
  // Can only edit other users if you're admin/security-admin
90
+ if ( $this->getController()->isPluginAdmin() ) {
91
  $oWpUsers = $this->loadWpUsers();
92
  $oSavingUser = $oWpUsers->getUserById( $nSavingUserId );
93
 
src/processors/loginprotect_intentprovider_yubikey.php CHANGED
@@ -27,7 +27,7 @@ class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_Login
27
  $aData = array(
28
  'has_validated_profile' => $bValidatedProfile,
29
  'is_my_user_profile' => ( $oUser->ID == $oWpUsers->getCurrentWpUserId() ),
30
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
31
  'user_to_edit_is_admin' => $oWpUsers->isUserAdmin( $oUser ),
32
  'strings' => array(
33
  'description_otp_code' => _wpsf__( 'This is your unique Yubikey Device ID.' ),
27
  $aData = array(
28
  'has_validated_profile' => $bValidatedProfile,
29
  'is_my_user_profile' => ( $oUser->ID == $oWpUsers->getCurrentWpUserId() ),
30
+ 'i_am_valid_admin' => $oCon->isPluginAdmin(),
31
  'user_to_edit_is_admin' => $oWpUsers->isUserAdmin( $oUser ),
32
  'strings' => array(
33
  'description_otp_code' => _wpsf__( 'This is your unique Yubikey Device ID.' ),
src/processors/loginprotect_track.php DELETED
@@ -1,166 +0,0 @@
1
- <?php
2
-
3
- if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_Track', false ) ) {
4
- return;
5
- }
6
-
7
- class ICWP_WPSF_Processor_LoginProtect_Track {
8
-
9
- const Factor_Google_Authenticator = 'ga';
10
- const Factor_Yubikey = 'yubi';
11
- const Factor_Email = 'email';
12
- const Factor_BackupCode = 'backupcode';
13
-
14
- /**
15
- * @var array
16
- */
17
- private $aFactorsTracked;
18
-
19
- /**
20
- * @var array
21
- */
22
- private $aFactorsToTrack;
23
-
24
- /**
25
- * @param string $sFactor
26
- * @return $this
27
- */
28
- public function addFactorToTrack( $sFactor ) {
29
- $aFactorsToTrack = $this->getAuthFactorsToTrack();
30
- $aFactorsToTrack[ $sFactor ] = true;
31
- $this->aFactorsToTrack = $aFactorsToTrack;
32
- return $this;
33
- }
34
-
35
- /**
36
- * @param string $sFactor
37
- * @return $this
38
- */
39
- public function addSuccessfulFactor( $sFactor ) {
40
- return $this->setFactorState( $sFactor, true );
41
- }
42
-
43
- /**
44
- * @param string $sFactor
45
- * @return $this
46
- */
47
- public function addUnSuccessfulFactor( $sFactor ) {
48
- return $this->setFactorState( $sFactor, false );
49
- }
50
-
51
- /**
52
- * @return array
53
- */
54
- public function getAuthFactorsTracked() {
55
- if ( !isset( $this->aFactorsTracked ) ) {
56
- $this->aFactorsTracked = array();
57
- }
58
- return $this->aFactorsTracked;
59
- }
60
-
61
- /**
62
- * @return array
63
- */
64
- public function getAuthFactorsToTrack() {
65
- if ( !is_array( $this->aFactorsToTrack ) ) {
66
- $this->aFactorsToTrack = array();
67
- }
68
- return array_unique( $this->aFactorsToTrack );
69
- }
70
-
71
- /**
72
- * @return int
73
- */
74
- public function getCountAuthFactorsTrackedTotal() {
75
- return count( $this->getAuthFactorsTracked() );
76
- }
77
-
78
- /**
79
- * Works by using array_filter() with no callback, so only those values in the
80
- * array that don't evaluate as false are returned. #SuperOmgElegant :)
81
- * @return int
82
- */
83
- public function getCountFactorsSuccessful() {
84
- return count( $this->getFactorsSuccessful() );
85
- }
86
-
87
- /**
88
- * @return array
89
- */
90
- public function getFactorsSuccessful() {
91
- return array_keys( array_filter( $this->getAuthFactorsTracked() ) ); // filter out the 'falses'
92
- }
93
-
94
- /**
95
- * @return array
96
- */
97
- public function getFactorsUnsuccessful() {
98
- return array_diff( array_keys( $this->getAuthFactorsTracked() ), $this->getFactorsSuccessful() );
99
- }
100
-
101
- /**
102
- * @return int
103
- */
104
- public function getCountFactorsUnsuccessful() {
105
- return ( $this->getCountAuthFactorsTrackedTotal() - $this->getCountFactorsSuccessful() );
106
- }
107
-
108
- /**
109
- * @return int
110
- */
111
- public function getCountFactorsRemainingToTrack() {
112
- return count( $this->getAuthFactorsToTrack() );
113
- }
114
-
115
- /**
116
- * @return bool
117
- */
118
- public function hasFactorsRemainingToTrack() {
119
- return ( $this->getCountFactorsRemainingToTrack() > 0 );
120
- }
121
-
122
- /**
123
- * @return bool
124
- */
125
- public function hasSuccessfulFactor() {
126
- return ( $this->getCountFactorsSuccessful() > 0 );
127
- }
128
-
129
- /**
130
- * @return bool
131
- */
132
- public function hasUnSuccessfulFactor() {
133
- return ( $this->getCountFactorsUnsuccessful() > 0 );
134
- }
135
-
136
- /**
137
- * @return bool
138
- */
139
- public function isFinalFactorRemainingToTrack() {
140
- return ( $this->getCountFactorsRemainingToTrack() === 1 );
141
- }
142
-
143
- /**
144
- * @param string $sFactor
145
- * @return $this
146
- */
147
- public function removeFactorToTrack( $sFactor ) {
148
- $aFactorsToTrack = $this->getAuthFactorsToTrack();
149
- unset( $aFactorsToTrack[ $sFactor ] );
150
- $this->aFactorsToTrack = $aFactorsToTrack;
151
- return $this;
152
- }
153
-
154
- /**
155
- * Also remove remaining factors to track
156
- * @param string $sFactor
157
- * @param bool $bState
158
- * @return $this
159
- */
160
- protected function setFactorState( $sFactor, $bState ) {
161
- $aFactors = $this->getAuthFactorsTracked();
162
- $aFactors[ $sFactor ] = $bState;
163
- $this->aFactorsTracked = $aFactors;
164
- return $this->removeFactorToTrack( $sFactor );
165
- }
166
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/loginprotect_twofactorauth.php DELETED
@@ -1,239 +0,0 @@
1
- <?php
2
-
3
- if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth', false ) ) {
4
- return;
5
- }
6
-
7
- require_once( dirname( __FILE__ ).'/loginprotect_intentprovider_base.php' );
8
-
9
- class ICWP_WPSF_Processor_LoginProtect_TwoFactorAuth extends ICWP_WPSF_Processor_LoginProtect_IntentProviderBase {
10
-
11
- /**
12
- * @param WP_User|WP_Error|null $oUser
13
- * @return WP_Error|WP_User|null - WP_User when the login success AND the IP is authenticated. null when login
14
- * not successful but IP is valid. WP_Error otherwise.
15
- */
16
- public function processLoginAttempt_Filter( $oUser ) {
17
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
18
- $oFO = $this->getMod();
19
-
20
- if ( !$this->isLoginCaptured() && $oUser instanceof WP_User
21
- && $this->hasValidatedProfile( $oUser ) && !$oFO->canUserMfaSkip( $oUser ) ) {
22
-
23
- $oFO->getSessionsProcessor()
24
- ->getQueryUpdater()
25
- ->setLoginIntentCodeEmail( $oFO->getSession(), $this->getSecret( $oUser ) );
26
-
27
- // Now send email with authentication link for user.
28
- $this->doStatIncrement( 'login.twofactor.started' )
29
- ->sendEmailTwoFactorVerify( $oUser )
30
- ->setLoginCaptured();
31
- }
32
- return $oUser;
33
- }
34
-
35
- /**
36
- * @param WP_User $oUser
37
- * @param bool $bIsSuccess
38
- */
39
- protected function auditLogin( $oUser, $bIsSuccess ) {
40
- if ( $bIsSuccess ) {
41
- $this->addToAuditEntry(
42
- sprintf( _wpsf__( 'User "%s" verified their identity using %s method.' ),
43
- $oUser->user_login, _wpsf__( 'Email Auth' )
44
- ), 2, 'login_protect_emailauth_verified'
45
- );
46
- $this->doStatIncrement( 'login.emailauth.verified' );
47
- }
48
- else {
49
- $this->addToAuditEntry(
50
- sprintf( _wpsf__( 'User "%s" failed to verify their identity using %s method.' ),
51
- $oUser->user_login, _wpsf__( 'Email Auth' )
52
- ), 2, 'login_protect_emailauth_failed'
53
- );
54
- $this->doStatIncrement( 'login.emailauth.failed' );
55
- }
56
- }
57
-
58
- /**
59
- * @param WP_User $oUser
60
- * @param string $sOtpCode
61
- * @return bool
62
- */
63
- protected function processOtp( $oUser, $sOtpCode ) {
64
- $bValid = !empty( $sOtpCode ) && ( $sOtpCode == $this->getStoredSessionHashCode() );
65
- if ( $bValid ) {
66
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
67
- $oFO = $this->getMod();
68
- $oFO->getSessionsProcessor()
69
- ->getQueryUpdater()
70
- ->clearLoginIntentCodeEmail( $oFO->getSession() );
71
- }
72
- return $bValid;
73
- }
74
-
75
- /**
76
- * @param array $aFields
77
- * @return array
78
- */
79
- public function addLoginIntentField( $aFields ) {
80
- if ( $this->getCurrentUserHasValidatedProfile() ) {
81
- $aFields[] = array(
82
- 'name' => $this->getLoginFormParameter(),
83
- 'type' => 'text',
84
- 'value' => $this->fetchCodeFromRequest(),
85
- 'placeholder' => _wpsf__( 'This code was just sent to your registered Email address.' ),
86
- 'text' => _wpsf__( 'Email OTP' ),
87
- 'help_link' => 'https://icwp.io/3t'
88
- );
89
- }
90
- return $aFields;
91
- }
92
-
93
- /**
94
- * @param WP_User $oUser
95
- * @return bool
96
- */
97
- protected function hasValidatedProfile( $oUser ) {
98
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
99
- $oFO = $this->getMod();
100
- // Currently it's a global setting but this will evolve to be like Google Authenticator so that it's a user meta
101
- return ( $oFO->isEmailAuthenticationActive() && $this->isSubjectToEmailAuthentication( $oUser ) );
102
- }
103
-
104
- /**
105
- * @param WP_User $oUser
106
- * @return bool
107
- */
108
- private function isSubjectToEmailAuthentication( $oUser ) {
109
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
110
- $oFO = $this->getMod();
111
- return count( array_intersect( $oFO->getEmail2FaRoles(), $oUser->roles ) ) > 0;
112
- }
113
-
114
- /**
115
- * @return string
116
- */
117
- protected function genSessionHash() {
118
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
119
- $oFO = $this->getMod();
120
- return hash_hmac(
121
- 'sha1',
122
- $this->getController()->getUniqueRequestId(),
123
- $oFO->getTwoAuthSecretKey()
124
- );
125
- }
126
-
127
- /**
128
- * We don't use user meta as it's dependent on the particular user sessions in-use
129
- * @param WP_User $oUser
130
- * @return string
131
- */
132
- protected function getSecret( WP_User $oUser ) {
133
- return strtoupper( substr( $this->genSessionHash(), 0, 6 ) );
134
- }
135
-
136
- /**
137
- * @return string The unique 2FA 6-digit code
138
- */
139
- protected function getStoredSessionHashCode() {
140
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
141
- $oFO = $this->getMod();
142
- return $oFO->hasSession() ? $oFO->getSession()->getLoginIntentCodeEmail() : '';
143
- }
144
-
145
- /**
146
- * @param string $sSecret
147
- * @return bool
148
- */
149
- protected function isSecretValid( $sSecret ) {
150
- $sHash = $this->getStoredSessionHashCode();
151
- return !empty( $sHash );
152
- }
153
-
154
- /**
155
- * @param WP_User $oUser
156
- * @return $this
157
- */
158
- protected function sendEmailTwoFactorVerify( WP_User $oUser ) {
159
- $sIpAddress = $this->ip();
160
-
161
- $aMessage = array(
162
- _wpsf__( 'Someone attempted to login into this WordPress site using your account.' ),
163
- _wpsf__( 'Login requires verification with the following code.' ),
164
- '',
165
- sprintf( _wpsf__( 'Verification Code: %s' ), sprintf( '<strong>%s</strong>', $this->getSecret( $oUser ) ) ),
166
- '',
167
- sprintf( '<strong>%s</strong>', _wpsf__( 'Login Details' ) ),
168
- sprintf( '%s: %s', _wpsf__( 'URL' ), $this->loadWp()->getHomeUrl() ),
169
- sprintf( '%s: %s', _wpsf__( 'Username' ), $oUser->user_login ),
170
- sprintf( '%s: %s', _wpsf__( 'IP Address' ), $sIpAddress ),
171
- '',
172
- );
173
-
174
- if ( !$this->getController()->isRelabelled() ) {
175
- $aMessage[] = sprintf( '- <a href="%s" target="_blank">%s</a>', 'https://icwp.io/96', _wpsf__( 'Why no login link?' ) );
176
- $aContent[] = '';
177
- }
178
-
179
- $sEmailSubject = _wpsf__( 'Two-Factor Login Verification' );
180
-
181
- $bResult = $this->getEmailProcessor()
182
- ->sendEmailWithWrap( $oUser->user_email, $sEmailSubject, $aMessage );
183
- if ( $bResult ) {
184
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" was sent an email to verify their Identity using Two-Factor Login Auth for IP address "%s".' ), $oUser->user_login, $sIpAddress );
185
- $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_two_factor_email_send' );
186
- }
187
- else {
188
- $sAuditMessage = sprintf( _wpsf__( 'Tried to send email to User "%s" to verify their identity using Two-Factor Login Auth for IP address "%s", but email sending failed.' ), $oUser->user_login, $sIpAddress );
189
- $this->addToAuditEntry( $sAuditMessage, 3, 'login_protect_two_factor_email_send_fail' );
190
- }
191
- return $this;
192
- }
193
-
194
- /**
195
- * This MUST only ever be hooked into when the User is looking at their OWN profile, so we can use "current user"
196
- * functions. Otherwise we need to be careful of mixing up users.
197
- * @param WP_User $oUser
198
- */
199
- public function addOptionsToUserProfile( $oUser ) {
200
- $oWp = $this->loadWpUsers();
201
- $bValidatedProfile = $this->hasValidatedProfile( $oUser );
202
- $aData = array(
203
- 'user_has_email_authentication_active' => $bValidatedProfile,
204
- 'user_has_email_authentication_enforced' => $this->isSubjectToEmailAuthentication( $oUser ),
205
- 'is_my_user_profile' => ( $oUser->ID == $oWp->getCurrentWpUserId() ),
206
- 'i_am_valid_admin' => $this->getController()->isValidAdminArea( true ),
207
- 'user_to_edit_is_admin' => $oWp->isUserAdmin( $oUser ),
208
- 'strings' => array(
209
- 'label_email_authentication' => _wpsf__( 'Email Authentication' ),
210
- 'title' => _wpsf__( 'Email Authentication' ),
211
- 'description_email_authentication_checkbox' => _wpsf__( 'Check the box to enable email-based login authentication.' ),
212
- 'provided_by' => sprintf( _wpsf__( 'Provided by %s' ), $this->getController()
213
- ->getHumanName() )
214
- )
215
- );
216
-
217
- $aData[ 'bools' ] = array(
218
- 'checked' => $bValidatedProfile || $aData[ 'user_has_email_authentication_enforced' ],
219
- 'disabled' => true || $aData[ 'user_has_email_authentication_enforced' ]
220
- //TODO: Make email authentication a per-user setting
221
- );
222
-
223
- echo $this->getMod()->renderTemplate( 'snippets/user_profile_emailauthentication.php', $aData );
224
- }
225
-
226
- /**
227
- * @return string
228
- */
229
- protected function getStub() {
230
- return ICWP_WPSF_Processor_LoginProtect_Track::Factor_Email;
231
- }
232
-
233
- /**
234
- * @return string
235
- */
236
- protected function get2FaCodeUserMetaKey() {
237
- return $this->getMod()->prefix( 'tfaemail_reqid' );
238
- }
239
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/loginprotect_yubikey.php DELETED
@@ -1,378 +0,0 @@
1
- <?php
2
-
3
- if ( class_exists( 'ICWP_WPSF_Processor_LoginProtect_Yubikey', false ) ) {
4
- return;
5
- }
6
-
7
- require_once( dirname( __FILE__ ).'/loginprotect_intentprovider_base.php' );
8
-
9
- class ICWP_WPSF_Processor_LoginProtect_Yubikey extends ICWP_WPSF_Processor_LoginProtect_IntentProviderBase {
10
-
11
- const OTP_LENGTH = 12;
12
- /**
13
- * @const string
14
- */
15
- const URL_YUBIKEY_VERIFY = 'https://api.yubico.com/wsapi/2.0/verify';
16
-
17
- /**
18
- * This MUST only ever be hooked into when the User is looking at their OWN profile, so we can use "current user"
19
- * functions. Otherwise we need to be careful of mixing up users.
20
- * @param WP_User $oUser
21
- */
22
- public function addOptionsToUserProfile( $oUser ) {
23
- $oCon = $this->getController();
24
- $oWpUsers = $this->loadWpUsers();
25
-
26
- $bValidatedProfile = $this->hasValidatedProfile( $oUser );
27
- $aData = array(
28
- 'has_validated_profile' => $bValidatedProfile,
29
- 'is_my_user_profile' => ( $oUser->ID == $oWpUsers->getCurrentWpUserId() ),
30
- 'i_am_valid_admin' => $oCon->getHasPermissionToManage(),
31
- 'user_to_edit_is_admin' => $oWpUsers->isUserAdmin( $oUser ),
32
- 'strings' => array(
33
- 'description_otp_code' => _wpsf__( 'This is your unique Yubikey Device ID.' ),
34
- 'description_otp_code_ext' => '['._wpsf__( 'Pro Only' ).'] '
35
- ._wpsf__( 'Multiple Yubikey Device IDs are separated by a comma.' ),
36
- 'description_otp' => _wpsf__( 'Provide a One Time Password from your Yubikey.' ),
37
- 'description_otp_ext' => $bValidatedProfile ?
38
- _wpsf__( 'This will remove the Yubikey Device ID from your profile.' )
39
- : _wpsf__( 'This will add the Yubikey Device ID to your profile.' ),
40
- 'description_otp_ext_2' => $bValidatedProfile ?
41
- '['._wpsf__( 'Pro Only' ).'] '._wpsf__( 'If you provide a OTP from an alternative Yubikey device, it will also be added to your profile.' )
42
- : '',
43
- 'label_enter_code' => _wpsf__( 'Yubikey ID' ),
44
- 'label_enter_otp' => _wpsf__( 'Yubikey OTP' ),
45
- 'title' => _wpsf__( 'Yubikey Authentication' ),
46
- 'cant_add_other_user' => sprintf( _wpsf__( "Sorry, %s may not be added to another user's account." ), 'Yubikey' ),
47
- 'cant_remove_admins' => sprintf( _wpsf__( "Sorry, %s may only be removed from another user's account by a Security Administrator." ), _wpsf__( 'Yubikey' ) ),
48
- 'provided_by' => sprintf( _wpsf__( 'Provided by %s' ), $oCon->getHumanName() ),
49
- 'remove_more_info' => sprintf( _wpsf__( 'Understand how to remove Google Authenticator' ) )
50
- ),
51
- 'data' => array(
52
- 'otp_field_name' => $this->getLoginFormParameter(),
53
- 'secret' => str_replace( ',', ', ', $this->getSecret( $oUser ) ),
54
- )
55
- );
56
-
57
- echo $this->getMod()->renderTemplate( 'snippets/user_profile_yubikey.php', $aData );
58
- }
59
-
60
- /**
61
- * This MUST only ever be hooked into when the User is looking at their OWN profile,
62
- * so we can use "current user" functions. Otherwise we need to be careful of mixing up users.
63
- * @param int $nSavingUserId
64
- */
65
- public function handleUserProfileSubmit( $nSavingUserId ) {
66
-
67
- // If it's your own account, you CANT do anything without your OTP (except turn off via email).
68
- $sOtp = $this->fetchCodeFromRequest();
69
-
70
- // At this stage, if the OTP was empty, then we have no further processing to do.
71
- if ( empty( $sOtp ) ) {
72
- return;
73
- }
74
-
75
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
76
- $oFO = $this->getMod();
77
-
78
- if ( !$this->sendYubiOtpRequest( $sOtp ) ) {
79
- $oFO->setFlashAdminNotice(
80
- _wpsf__( 'One Time Password (OTP) was not valid.' ).' '._wpsf__( 'Please try again.' ),
81
- true
82
- );
83
- return;
84
- }
85
-
86
- /*
87
- * How we proceed depends on :
88
- * 1) Is the OTP for a registered ID - if so, remove it; If not, add it;
89
- * 2) Is this a premium Shield installation - if so, multiple yubikeys are permitted
90
- */
91
-
92
- $oSavingUser = $this->loadWpUsers()->getUserById( $nSavingUserId );
93
- $sYubiId = $this->getYubiIdFromOtp( $sOtp );
94
-
95
- $bError = false;
96
- if ( $this->hasYubiIdInProfile( $oSavingUser, $sYubiId ) ) {
97
- $this->removeYubiIdFromProfile( $oSavingUser, $sYubiId );
98
- $sMsg = sprintf(
99
- _wpsf__( '%s was removed from your profile.' ),
100
- _wpsf__( 'Yubikey Device' ).sprintf( ' "%s"', $sYubiId )
101
- );
102
- }
103
- else if ( count( $this->getYubiIds( $oSavingUser ) ) == 0 || $oFO->isPremium() ) {
104
- $this->addYubiIdToProfile( $oSavingUser, $sYubiId );
105
- $sMsg = sprintf(
106
- _wpsf__( '%s was added to your profile.' ),
107
- _wpsf__( 'Yubikey Device' ).sprintf( ' (%s)', $sYubiId )
108
- );
109
- }
110
- else {
111
- $bError = true;
112
- $sMsg = _wpsf__( 'No changes were made to your Yubikey configuration' );
113
- }
114
-
115
- $this->setProfileValidated( $oSavingUser, $this->hasValidSecret( $oSavingUser ) );
116
- $oFO->setFlashAdminNotice( $sMsg, $bError );
117
- }
118
-
119
- /**
120
- * @param WP_User $oUser
121
- * @return array
122
- */
123
- protected function getYubiIds( WP_User $oUser ) {
124
- return explode( ',', parent::getSecret( $oUser ) );
125
- }
126
-
127
- /**
128
- * @param string $sOTP
129
- * @return string
130
- */
131
- protected function getYubiIdFromOtp( $sOTP ) {
132
- return substr( $sOTP, 0, $this->getYubiOtpLength() );
133
- }
134
-
135
- /**
136
- * @param WP_User $oUser
137
- * @param string $sKey
138
- * @return bool
139
- */
140
- protected function hasYubiIdInProfile( WP_User $oUser, $sKey ) {
141
- return in_array( $sKey, $this->getYubiIds( $oUser ) );
142
- }
143
-
144
- /**
145
- * @param WP_User $oUser
146
- * @param string $sOneTimePassword
147
- * @return bool
148
- */
149
- protected function processOtp( $oUser, $sOneTimePassword ) {
150
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
151
- $oFO = $this->getMod();
152
- $bSuccess = false;
153
-
154
- $aYubiKeys = $this->getYubiIds( $oUser );
155
-
156
- // Only process the 1st secret if premium
157
- if ( !$oFO->isPremium() ) {
158
- $aYubiKeys = array_slice( $aYubiKeys, 0, 1 );
159
- }
160
-
161
- foreach ( $aYubiKeys as $sKey ) {
162
- $bSuccess = strpos( $sOneTimePassword, $sKey ) === 0
163
- && $this->sendYubiOtpRequest( $sOneTimePassword );
164
- if ( $bSuccess ) {
165
- break;
166
- }
167
- }
168
-
169
- return $bSuccess;
170
- }
171
-
172
- /**
173
- * @param string $sOTP
174
- * @return bool
175
- */
176
- private function sendYubiOtpRequest( $sOTP ) {
177
- $sOTP = trim( $sOTP );
178
- $bSuccess = preg_match( '#^[a-z]{44}$#', $sOTP );
179
-
180
- if ( $bSuccess ) {
181
- $aParts = array(
182
- 'otp' => $sOTP,
183
- 'nonce' => md5( uniqid( rand() ) ),
184
- 'id' => $this->getOption( 'yubikey_app_id' )
185
- );
186
- $sYubiResponse = trim( $this->loadFS()
187
- ->getUrlContent( add_query_arg( $aParts, self::URL_YUBIKEY_VERIFY ) ) );
188
-
189
- unset( $aParts[ 'id' ] );
190
- $aParts[ 'status' ] = 'OK';
191
-
192
- $bSuccess = true;
193
- foreach ( $aParts as $sKey => $mVal ) {
194
- $bSuccess = $bSuccess && preg_match( sprintf( '#%s=%s#', $sKey, $mVal ), $sYubiResponse );
195
- }
196
- }
197
-
198
- return $bSuccess;
199
- }
200
-
201
- /**
202
- * @param WP_User $oUser
203
- * @param string $sNewKey
204
- * @return $this
205
- */
206
- protected function addYubiIdToProfile( $oUser, $sNewKey ) {
207
- $aKeys = $this->getYubiIds( $oUser );
208
- $aKeys[] = $sNewKey;
209
- return $this->storeYubiIdInProfile( $oUser, $aKeys );
210
- }
211
-
212
- /**
213
- * @param WP_User $oUser
214
- * @param string $sKey
215
- * @return $this
216
- */
217
- protected function removeYubiIdFromProfile( $oUser, $sKey ) {
218
- $aKeys = $this->loadDP()->removeFromArrayByValue( $this->getYubiIds( $oUser ), $sKey );
219
- return $this->storeYubiIdInProfile( $oUser, $aKeys );
220
- }
221
-
222
- /**
223
- * @param WP_User $oUser
224
- * @param array $aKeys
225
- * @return $this
226
- */
227
- private function storeYubiIdInProfile( $oUser, $aKeys ) {
228
- parent::setSecret( $oUser, implode( ',', array_unique( array_filter( $aKeys ) ) ) );
229
- return $this;
230
- }
231
-
232
- /**
233
- * @param WP_User $oUser
234
- * @param bool $bIsSuccess
235
- */
236
- protected function auditLogin( $oUser, $bIsSuccess ) {
237
- if ( $bIsSuccess ) {
238
- $this->addToAuditEntry(
239
- sprintf( _wpsf__( 'User "%s" verified their identity using %s method.' ),
240
- $oUser->user_login, _wpsf__( 'Yubikey OTP' )
241
- ), 2, 'login_protect_yubikey_login_success'
242
- );
243
- $this->doStatIncrement( 'login.yubikey.verified' );
244
- }
245
- else {
246
- $this->addToAuditEntry(
247
- sprintf( _wpsf__( 'User "%s" failed to verify their identity using %s method.' ),
248
- $oUser->user_login, _wpsf__( 'Yubikey OTP' )
249
- ),2, 'login_protect_yubikey_failed'
250
- );
251
- $this->doStatIncrement( 'login.yubikey.failed' );
252
- }
253
- }
254
-
255
- /**
256
- * @param array $aFields
257
- * @return array
258
- */
259
- public function addLoginIntentField( $aFields ) {
260
- if ( $this->getCurrentUserHasValidatedProfile() ) {
261
- $aFields[] = array(
262
- 'name' => $this->getLoginFormParameter(),
263
- 'type' => 'text',
264
- 'placeholder' => _wpsf__( 'Use your Yubikey to generate a new code.' ),
265
- 'value' => '',
266
- 'text' => _wpsf__( 'Yubikey OTP' ),
267
- 'help_link' => 'https://icwp.io/4i'
268
- );
269
- }
270
- return $aFields;
271
- }
272
-
273
- /**
274
- * @return string
275
- */
276
- protected function getStub() {
277
- return ICWP_WPSF_Processor_LoginProtect_Track::Factor_Yubikey;
278
- }
279
-
280
- /**
281
- * @param string $sSecret
282
- * @return bool
283
- */
284
- protected function isSecretValid( $sSecret ) {
285
- $bValid = parent::isSecretValid( $sSecret );
286
- if ( $bValid ) {
287
- foreach ( explode( ',', $sSecret ) as $sId ) {
288
- $bValid = $bValid && preg_match( sprintf( '#^[a-z]{%s}$#', $this->getYubiOtpLength() ), $sId );
289
- }
290
- }
291
- return $bValid;
292
- }
293
-
294
- /**
295
- * @return int
296
- */
297
- protected function getYubiOtpLength() {
298
- return self::OTP_LENGTH;
299
- }
300
-
301
- /**
302
- * @param WP_User $oUser
303
- * @return WP_User|WP_Error
304
- */
305
- public function processLoginAttempt_Filter( $oUser ) {
306
- return $oUser;
307
- /** @var ICWP_WPSF_FeatureHandler_LoginProtect $oFO */
308
- $oFO = $this->getMod();
309
- $oLoginTrack = $this->getLoginTrack();
310
-
311
- $bNeedToCheckThisFactor = $oFO->isChainedAuth() || !$oLoginTrack->hasSuccessfulFactor();
312
- $bErrorOnFailure = $bNeedToCheckThisFactor && $oLoginTrack->isFinalFactorRemainingToTrack();
313
- $oLoginTrack->addUnSuccessfulFactor( ICWP_WPSF_Processor_LoginProtect_Track::Factor_Yubikey );
314
-
315
- if ( !$bNeedToCheckThisFactor || empty( $oUser ) || is_wp_error( $oUser ) ) {
316
- return $oUser;
317
- }
318
-
319
- $oError = new WP_Error();
320
- $sUsername = $oUser->user_login;
321
-
322
- $sOneTimePassword = $this->fetchCodeFromRequest();
323
- // $sApiKey = $this->getOption('yubikey_api_key');
324
-
325
- // check that if we have a list of permitted keys, that the one used is on that list connected with the username.
326
- $sYubikey12 = $this->getYubiIdFromOtp( $sOneTimePassword );
327
- $fUsernameFound = false; // if username is never found, it means there's no yubikey specified which means we can bypass this authentication method.
328
- $fFoundMatch = false;
329
- foreach ( $this->getOption( 'yubikey_unique_keys' ) as $aUsernameYubikeyPair ) {
330
- if ( isset( $aUsernameYubikeyPair[ $sUsername ] ) ) {
331
- $fUsernameFound = true;
332
- if ( $aUsernameYubikeyPair[ $sUsername ] == $sYubikey12 ) {
333
- $fFoundMatch = true;
334
- break;
335
- }
336
- }
337
- }
338
-
339
- // If no yubikey-username pair found for given username, we by-pass Yubikey auth.
340
- if ( !$fUsernameFound ) {
341
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" logged in without a Yubikey One Time Password because no username-yubikey pair was found for this user.' ), $sUsername );
342
- $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_yubikey_bypass' );
343
- return $oUser;
344
- }
345
-
346
- // Username was found in the list of key pairs, but the yubikey provided didn't match that username.
347
- if ( !$fFoundMatch ) {
348
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" attempted to login but Yubikey ID "%s" used was not in list of authorised keys.' ), $sUsername, $sYubikey12 );
349
- $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_yubikey_fail_permitted_id' );
350
-
351
- if ( $bErrorOnFailure ) {
352
- $oError->add(
353
- 'yubikey_not_allowed',
354
- sprintf( _wpsf__( 'ERROR: %s' ), _wpsf__( 'The Yubikey provided is not on the list of permitted keys for this user.' ) )
355
- );
356
- return $oError;
357
- }
358
- }
359
-
360
- if ( $this->processOtp( null, $sOneTimePassword ) ) {
361
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" successfully logged in using a validated Yubikey One Time Password.' ), $sUsername );
362
- $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_yubikey_login_success' );
363
- $this->getLoginTrack()->addSuccessfulFactor( ICWP_WPSF_Processor_LoginProtect_Track::Factor_Yubikey );
364
- }
365
- else {
366
-
367
- $sAuditMessage = sprintf( _wpsf__( 'User "%s" attempted to login but Yubikey One Time Password failed to validate due to invalid Yubi API response.".' ), $sUsername );
368
- $this->addToAuditEntry( $sAuditMessage, 2, 'login_protect_yubikey_fail_invalid_api_response' );
369
-
370
- $oError->add(
371
- 'yubikey_validate_fail',
372
- sprintf( _wpsf__( 'ERROR: %s' ), _wpsf__( 'The Yubikey authentication was not validated successfully.' ) )
373
- );
374
- }
375
-
376
- return $bErrorOnFailure ? $oError : $oUser;
377
- }
378
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/processors/plugin.php CHANGED
@@ -147,33 +147,13 @@ class ICWP_WPSF_Processor_Plugin extends ICWP_WPSF_Processor_BasePlugin {
147
  /**
148
  */
149
  public function dumpTrackingData() {
150
- if ( $this->getController()->isValidAdminArea() ) {
151
  echo sprintf( '<pre><code>%s</code></pre>', print_r( $this->getTrackingProcessor()
152
  ->collectTrackingData(), true ) );
153
  die();
154
  }
155
  }
156
 
157
- /**
158
- */
159
- public function printTrackingDataBox() {
160
- /** @var ICWP_WPSF_FeatureHandler_Plugin $oFO */
161
- $oFO = $this->getMod();
162
-
163
- if ( !$this->getController()->isValidAdminArea() ) {
164
- return;
165
- }
166
-
167
- $aRenderData = array(
168
- 'strings' => array(
169
- 'tracking_data' => print_r( $this->getTrackingProcessor()->collectTrackingData(), true ),
170
- ),
171
- 'js_snippets' => array()
172
- );
173
- add_thickbox();
174
- echo $oFO->renderTemplate( 'snippets/plugin_tracking_data_dump.php', $aRenderData );
175
- }
176
-
177
  protected function setupTestCron() {
178
  try {
179
  $this->loadWpCronProcessor()
147
  /**
148
  */
149
  public function dumpTrackingData() {
150
+ if ( $this->getController()->isPluginAdmin() ) {
151
  echo sprintf( '<pre><code>%s</code></pre>', print_r( $this->getTrackingProcessor()
152
  ->collectTrackingData(), true ) );
153
  die();
154
  }
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  protected function setupTestCron() {
158
  try {
159
  $this->loadWpCronProcessor()
src/processors/sessions.php CHANGED
@@ -65,7 +65,11 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
65
  public function onWpLoaded() {
66
  if ( $this->loadWpUsers()->isUserLoggedIn() && !$this->loadWp()->isRest() ) {
67
  $this->autoAddSession();
 
 
68
 
 
 
69
  /** @var ICWP_WPSF_FeatureHandler_Sessions $oFO */
70
  $oFO = $this->getMod();
71
  if ( $oFO->hasSession() ) {
@@ -73,6 +77,8 @@ class ICWP_WPSF_Processor_Sessions extends ICWP_WPSF_BaseDbProcessor {
73
  ->updateLastActivity( $this->getCurrentSession() );
74
  }
75
  }
 
 
76
  }
77
 
78
  private function autoAddSession() {
65
  public function onWpLoaded() {
66
  if ( $this->loadWpUsers()->isUserLoggedIn() && !$this->loadWp()->isRest() ) {
67
  $this->autoAddSession();
68
+ }
69
+ }
70
 
71
+ public function onModuleShutdown() {
72
+ if ( !$this->loadWp()->isRest() ) {
73
  /** @var ICWP_WPSF_FeatureHandler_Sessions $oFO */
74
  $oFO = $this->getMod();
75
  if ( $oFO->hasSession() ) {
77
  ->updateLastActivity( $this->getCurrentSession() );
78
  }
79
  }
80
+
81
+ parent::onModuleShutdown();
82
  }
83
 
84
  private function autoAddSession() {
src/wizards/base_wpsf.php CHANGED
@@ -18,7 +18,7 @@ abstract class ICWP_WPSF_Wizard_BaseWpsf extends ICWP_WPSF_Wizard_Base {
18
  protected function getUserCanSlide( $sSlide ) {
19
  $aSlide = $this->getStepsDefinition()[ $sSlide ];
20
  $bRestricted = !isset( $aSlide[ 'security_admin' ] ) || $aSlide[ 'security_admin' ];
21
- return !$bRestricted || $this->getPluginCon()->getHasPermissionToManage();
22
  }
23
 
24
  /**
18
  protected function getUserCanSlide( $sSlide ) {
19
  $aSlide = $this->getStepsDefinition()[ $sSlide ];
20
  $bRestricted = !isset( $aSlide[ 'security_admin' ] ) || $aSlide[ 'security_admin' ];
21
+ return !$bRestricted || $this->getPluginCon()->isPluginAdmin();
22
  }
23
 
24
  /**
templates/php/page/login_intent.php CHANGED
@@ -93,6 +93,7 @@
93
  <form action="<?php echo $hrefs[ 'form_action' ]; ?>" method="post" class="form-horizontal">
94
  <input type="hidden" name="<?php echo $data[ 'login_intent_flag' ]; ?>" value="1" />
95
  <input type="hidden" name="redirect_to" value="<?php echo $hrefs[ 'redirect_to' ]; ?>" />
 
96
 
97
  <?php foreach ( $data[ 'login_fields' ] as $aField ) : ?>
98
  <div class="form-row">
93
  <form action="<?php echo $hrefs[ 'form_action' ]; ?>" method="post" class="form-horizontal">
94
  <input type="hidden" name="<?php echo $data[ 'login_intent_flag' ]; ?>" value="1" />
95
  <input type="hidden" name="redirect_to" value="<?php echo $hrefs[ 'redirect_to' ]; ?>" />
96
+ <input type="hidden" name="cancel_href" value="<?php echo $hrefs[ 'cancel_href' ]; ?>" />
97
 
98
  <?php foreach ( $data[ 'login_fields' ] as $aField ) : ?>
99
  <div class="form-row">