PluginProbe
DoLogin Security / trunk
DoLogin Security vtrunk
5.0.10 4.8.3 trunk 1.0 1.1 1.1.1 1.2 1.2.1 1.2.2 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 1.6 All 64 releases
dologin / src / data.upgrade.func.php

data.upgrade.func.php in DoLogin Security trunk, at src/data.upgrade.func.php

121 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'WPINC' ) || exit;
3
4
5 function dologin_update_1_4_1() {
6 global $wpdb;
7 // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-time schema migration on the plugin's own custom table; table name is a hardcoded internal identifier.
8 $wpdb->query( 'ALTER TABLE `' . $wpdb->prefix . "dologin_pswdless` ADD COLUMN `src` varchar(255) NOT NULL DEFAULT '' AFTER `hash`" );
9 }
10
11 function dologin_update_4_0_0() {
12 \dologin\Data::cls()->tb_create( 'site' );
13 }
14
15 function dologin_update_4_5_0() {
16 global $wpdb;
17
18 // SMS login is legacy and no longer participates in DoLogin authentication.
19 // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- one-time schema migration on the plugin's own legacy table.
20 $wpdb->query( 'DROP TABLE IF EXISTS `' . $wpdb->prefix . 'dologin_sms`' );
21
22 delete_option( 'dologin.sms' );
23 delete_option( 'dologin.sms_force' );
24 }
25
26 function dologin_update_4_6_0() {
27 delete_option( 'dologin.kl_sso_app_tag' );
28 delete_option( 'dologin.kl_sso_app_hash' );
29 delete_option( 'dologin.kl_sso_api_base' );
30 delete_option( 'dologin.kl_sso_ws_url' );
31 }
32
33 /**
34 * Protect legacy bearer tokens and recoverable authentication secrets at rest.
35 */
36 function dologin_update_4_7_4() {
37 $data = \dologin\Data::cls();
38 $data->tables_create();
39
40 dologin_migrate_token_hashes( $data->tb( 'pswdless' ), 'passwordless-login' );
41 dologin_migrate_token_hashes( $data->tb( 'site' ), 'site-connection' );
42 dologin_migrate_twofa_secrets();
43
44 $site_sk = (string) \dologin\Conf::val( '_sk' );
45 if ( $site_sk && ! \dologin\Secret::is_sealed( $site_sk ) ) {
46 $sealed = \dologin\Secret::seal( 'site-easy-login-signing-key', $site_sk );
47 if ( $sealed ) {
48 \dologin\Conf::update( '_sk', $sealed );
49 }
50 }
51 }
52
53 /**
54 * Remove the obsolete force-mode activation-owner marker.
55 */
56 function dologin_update_4_7_5() {
57 \dologin\Conf::delete( '_kl_sso_force_uid' );
58 }
59
60 /**
61 * Replace raw token values with purpose-bound HMAC verifiers.
62 */
63 function dologin_migrate_token_hashes( $table, $purpose ) {
64 global $wpdb;
65
66 $last_id = 0;
67 do {
68 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- one-time migration of a hardcoded internal table; the cursor is prepared.
69 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id, hash FROM `$table` WHERE id > %d AND hash <> '' ORDER BY id ASC LIMIT 500", $last_id ) );
70 if ( ! is_array( $rows ) ) {
71 return false;
72 }
73 foreach ( $rows as $row ) {
74 $last_id = (int) $row->id;
75 $stored = (string) $row->hash;
76 if ( \dologin\Secret::is_token_hash( $stored ) ) {
77 continue;
78 }
79 $hash = \dologin\Secret::token_hash( $purpose, $stored );
80 if ( ! $hash ) {
81 continue;
82 }
83 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- one-time migration of a hardcoded internal table; all values are prepared and the old value is compared atomically.
84 $wpdb->query( $wpdb->prepare( "UPDATE `$table` SET hash = %s WHERE id = %d AND hash = %s", $hash, $last_id, $stored ) );
85 }
86 } while ( 500 === count( $rows ) );
87
88 return true;
89 }
90
91 /**
92 * Encrypt existing TOTP shared secrets in bounded batches.
93 */
94 function dologin_migrate_twofa_secrets() {
95 global $wpdb;
96
97 $last_id = 0;
98 do {
99 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- one-time migration of WordPress user metadata; the key and cursor are prepared.
100 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, meta_value FROM `$wpdb->usermeta` WHERE umeta_id > %d AND meta_key = %s ORDER BY umeta_id ASC LIMIT 500", $last_id, '2fa' ) );
101 if ( ! is_array( $rows ) ) {
102 return false;
103 }
104 foreach ( $rows as $row ) {
105 $last_id = (int) $row->umeta_id;
106 $stored = (string) $row->meta_value;
107 if ( ! $stored || \dologin\Secret::is_sealed( $stored ) ) {
108 continue;
109 }
110 $sealed = \dologin\Secret::seal( 'totp-user-secret', $stored );
111 if ( ! $sealed ) {
112 continue;
113 }
114 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- one-time usermeta migration; all values are prepared and the old value is compared atomically.
115 $wpdb->query( $wpdb->prepare( "UPDATE `$wpdb->usermeta` SET meta_value = %s WHERE umeta_id = %d AND meta_value = %s", $sealed, $last_id, $stored ) );
116 }
117 } while ( 500 === count( $rows ) );
118
119 return true;
120 }
121