PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.0.1
Defender Security – Malware Scanner, Login Security & Firewall v6.0.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / controller / class-setup-wizard.php

class-setup-wizard.php in Defender Security – Malware Scanner, Login Security & Firewall 6.0.1, at src/controller/class-setup-wizard.php

332 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles setup wizard.
4 *
5 * @package WP_Defender\Controller
6 */
7
8 namespace WP_Defender\Controller;
9
10 use WP_Defender\Event;
11 use Calotes\Component\Request;
12 use Calotes\Component\Response;
13 use WP_Defender\Component\Feature_Modal;
14 use WP_Defender\Controller\Data_Tracking;
15 use WP_Defender\Controller\Security_Tweaks;
16 use WP_Defender\Component\Config\Config_Hub_Helper;
17 use WP_Defender\Component\Security_Tweaks\Security_Key;
18
19 /**
20 * Handles setup wizard.
21 */
22 class Setup_Wizard extends Event {
23 /**
24 * Legacy site option key for skipped setup wizard bubble state.
25 *
26 * @var string
27 */
28 public const RESUME_NOTICE_OPTION = 'wd_setup_wizard_resume_notice';
29
30 /**
31 * The slug identifier for this controller.
32 *
33 * @var string
34 */
35 public $slug = 'wdf-setup-wizard';
36
37 /**
38 * Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
39 */
40 public function __construct() {
41 $this->register_routes();
42 }
43
44 /**
45 * Provides data for the frontend.
46 *
47 * @return array An array of data for the frontend.
48 */
49 public function data_frontend(): array {
50 return array_merge(
51 array(
52 'custom_nonce' => wp_create_nonce( 'wpdef_setup_wizard' ),
53 // Saved wizard selections, so the wizard can resume from server state after a refresh.
54 'onboardingData' => get_site_option( 'wd_onboarding_data', array() ),
55 ),
56 $this->dump_routes_and_nonces()
57 );
58 }
59
60 /**
61 * Deletes any legacy resume setup wizard bubble state.
62 *
63 * @return void
64 */
65 private static function clear_resume_notice_option(): void {
66 delete_site_option( self::RESUME_NOTICE_OPTION );
67 }
68
69 /**
70 * Persist a single wizard step's selection to the database.
71 *
72 * Each step is merged into the canonical `wd_onboarding_data` site option so the
73 * wizard can resume from server state after a refresh or on another device. The
74 * actual module side effects are still applied atomically in finish_setup_wizard().
75 *
76 * @param Request $request The request object containing the step data.
77 *
78 * @return Response
79 * @defender_route
80 */
81 public function save_step( Request $request ): Response {
82 if ( ! $this->check_permission() ) {
83 return new Response(
84 false,
85 array(
86 'message' => esc_html__( 'Invalid', 'defender-security' ),
87 )
88 );
89 }
90
91 $payload = $request->get_data();
92 $step = isset( $payload['step'] ) ? sanitize_text_field( $payload['step'] ) : '';
93 $data = isset( $payload['data'] ) && is_array( $payload['data'] ) ? $payload['data'] : array();
94
95 $allowed_steps = array( 'local-firewall', 'hardening', 'audit-logging' );
96 if ( ! in_array( $step, $allowed_steps, true ) ) {
97 return new Response(
98 false,
99 array(
100 'message' => esc_html__( 'Invalid step.', 'defender-security' ),
101 )
102 );
103 }
104
105 $saved = get_site_option( 'wd_onboarding_data', array() );
106 if ( ! is_array( $saved ) ) {
107 $saved = array();
108 }
109
110 switch ( $step ) {
111 case 'local-firewall':
112 $saved['local_firewall'] = ! empty( $data['local_firewall'] );
113 break;
114 case 'hardening':
115 $hardening = isset( $data['hardening'] ) && is_array( $data['hardening'] )
116 ? $data['hardening']
117 : array();
118 $saved['hardening'] = array_map(
119 static function ( $value ) {
120 return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
121 },
122 $hardening
123 );
124 break;
125 }
126
127 $saved['current_screen'] = $step;
128 update_site_option( 'wd_onboarding_data', $saved );
129
130 return new Response(
131 true,
132 array(
133 'message' => esc_html__( 'Step saved.', 'defender-security' ),
134 )
135 );
136 }
137
138 /**
139 * Finish the setup wizard.
140 *
141 * @param Request $request The request object containing the data.
142 *
143 * @return Response
144 * @defender_route
145 */
146 public function finish_setup_wizard( Request $request ): Response {
147 if ( ! $this->check_permission() ) {
148 return new Response(
149 false,
150 array(
151 'message' => esc_html__( 'Invalid', 'defender-security' ),
152 )
153 );
154 }
155
156 // Merge any per-step selections already saved with the final payload (request wins).
157 $saved = get_site_option( 'wd_onboarding_data', array() );
158 $saved = is_array( $saved ) ? $saved : array();
159 $settings = array_merge( $saved, $request->get_data() );
160 $settings['last_scan'] = time();
161 update_site_option( 'wd_onboarding_data', $settings );
162
163 $firewall_state = (bool) $settings['local_firewall'];
164 if ( $firewall_state ) {
165 wd_di()->get( \WP_Defender\Controller\Firewall::class )->preset_firewall( true );
166 }
167
168 if ( isset( $settings['hardening'] ) && is_array( $settings['hardening'] ) ) {
169 $toggle_to_slug = array(
170 'disable_xmlrpc' => 'disable-xml-rpc',
171 'hide_error_reporting' => 'hide-error',
172 'update_security_keys' => 'security-key',
173 'prevent_enum_users' => 'prevent-enum-users',
174 'disable_trackbacks_pingbacks' => 'disable-trackback',
175 'disable_file_editor' => 'disable-file-editor',
176 );
177 $hardening_state = $settings['hardening'];
178 $resolved_slugs = array();
179
180 foreach ( $toggle_to_slug as $toggle_key => $slug ) {
181 if ( ! array_key_exists( $toggle_key, $hardening_state ) ) {
182 continue;
183 }
184
185 if ( filter_var( $hardening_state[ $toggle_key ], FILTER_VALIDATE_BOOLEAN ) ) {
186 $resolved_slugs[] = $slug;
187 }
188 }
189
190 $security_key_slug = 'security-key';
191 $auto_regen_enabled = in_array( $security_key_slug, $resolved_slugs, true );
192
193 // Only save the autogenerate flag — don't resolve the tweak (that would regenerate keys immediately).
194 $resolved_slugs = array_values( array_filter( $resolved_slugs, fn( $s ) => $s !== $security_key_slug ) );
195
196 $security_key = wd_di()->get( Security_Key::class );
197 $security_key->set_is_autogenrate_keys( $auto_regen_enabled );
198 if ( $auto_regen_enabled ) {
199 $security_key->cron_schedule();
200 } else {
201 $security_key->cron_unschedule();
202 }
203
204 // Activate all other selected hardening tweaks.
205 if ( ! empty( $resolved_slugs ) ) {
206 $security_tweaks = wd_di()->get( Security_Tweaks::class );
207 $security_tweaks->security_tweaks_auto_action( $resolved_slugs, 'resolve' );
208 }
209 }
210
211 update_site_option( 'wp_defender_shown_activator', true );
212 delete_site_option( 'wp_defender_is_free_activated' );
213 self::clear_resume_notice_option();
214 // Skip Welcome modal.
215 Feature_Modal::delete_modal_key();
216
217 return new Response(
218 true,
219 array(
220 'message' => esc_html__( 'Setup wizard completed successfully.', 'defender-security' ),
221 )
222 );
223 }
224
225
226 /**
227 * Close setup wizard session.
228 *
229 * @return Response
230 * @defender_route
231 */
232 public function skip(): Response {
233 if ( ! $this->check_permission() ) {
234 return new Response(
235 false,
236 array(
237 'message' => esc_html__( 'Invalid', 'defender-security' ),
238 )
239 );
240 }
241
242 update_site_option( 'wp_defender_shown_activator', true );
243 delete_site_option( 'wp_defender_is_free_activated' );
244 delete_site_option( 'wp_defender_onboarding_step' );
245 self::clear_resume_notice_option();
246 Data_Tracking::delete_modal_key();
247 // Skip Welcome modal.
248 Feature_Modal::delete_modal_key();
249
250 return new Response(
251 true,
252 array(
253 'message' => esc_html__( 'Setup wizard skipped successfully.', 'defender-security' ),
254 )
255 );
256 }
257
258 /**
259 * Clears skip state so the setup wizard can be opened again.
260 *
261 * @return Response
262 * @defender_route
263 */
264 public function resume_setup_wizard(): Response {
265 if ( ! $this->check_permission() ) {
266 return new Response(
267 false,
268 array(
269 'message' => esc_html__( 'Invalid', 'defender-security' ),
270 )
271 );
272 }
273
274 self::clear_resume_notice_option();
275 delete_site_option( 'wp_defender_shown_activator' );
276 delete_site_option( 'wp_defender_onboarding_step' );
277
278 return new Response(
279 true,
280 array(
281 'message' => esc_html__( 'Setup wizard resume state cleared successfully.', 'defender-security' ),
282 )
283 );
284 }
285
286 /**
287 * Converts the current object state to an array.
288 *
289 * @return array The array representation of the object.
290 */
291 public function to_array(): array {
292 return array();
293 }
294
295 /**
296 * Import the data of other source into this, it can be when HUB trigger the import, or user apply a preset.
297 *
298 * @param array $data Data from other source.
299 *
300 * @return null|void
301 */
302 public function import_data( array $data ) {
303 }
304
305 /**
306 * Remove all settings, configs generated in this container runtime.
307 *
308 * @return void
309 */
310 public function remove_settings() {
311 delete_site_option( 'wd_onboarding_data' );
312 self::clear_resume_notice_option();
313 }
314
315 /**
316 * Remove all data.
317 *
318 * @return void
319 */
320 public function remove_data() {
321 }
322
323 /**
324 * Export strings.
325 *
326 * @return array
327 */
328 public function export_strings(): array {
329 return array();
330 }
331 }
332