diff --git a/plugins/bcc-login/bcc-login.php b/plugins/bcc-login/bcc-login.php index 5be994c..528f135 100644 --- a/plugins/bcc-login/bcc-login.php +++ b/plugins/bcc-login/bcc-login.php @@ -28,6 +28,7 @@ require_once( 'includes/class-bcc-storage.php' ); require_once( 'includes/class-bcc-notifications.php' ); require_once( 'includes/class-exclusive-lock.php' ); +require_once( 'includes/class-bcc-login-cleanup.php' ); class BCC_Login { /** @@ -51,7 +52,8 @@ class BCC_Login { private BCC_Coreapi_Client $_coreapi; private BCC_Notifications $_notifications; private BCC_Storage $_storage; - + private BCC_Login_Cleanup $_cleanup; + /** * Initialize the plugin. */ @@ -73,6 +75,7 @@ private function __construct(){ $this->_feed = new BCC_Login_Feed( $this->_settings, $this->_client ); $this->_updater = new BCC_Login_Updater( $this->plugin, $this->plugin_slug, $this->plugin_version, $this->plugin_name ); $this->_notifications = new BCC_Notifications( $this->_settings, $this->_coreapi ); + $this->_cleanup = new BCC_Login_Cleanup(); if (!empty($this->_settings->site_groups) || !empty($this->_settings->full_content_access_groups)) { $this->_coreapi->ensure_subscription_to_person_updates(); @@ -265,6 +268,7 @@ static function activate_plugin() { * Called when plugin is deactivated */ static function deactivate_plugin() { + BCC_Login_Cleanup::unschedule(); flush_rewrite_rules(); } diff --git a/plugins/bcc-login/includes/class-bcc-login-cleanup.php b/plugins/bcc-login/includes/class-bcc-login-cleanup.php new file mode 100644 index 0000000..54dfe18 --- /dev/null +++ b/plugins/bcc-login/includes/class-bcc-login-cleanup.php @@ -0,0 +1,69 @@ +get_results( + "SELECT meta_key, COUNT(*) AS count + FROM {$wpdb->usermeta} + WHERE meta_key LIKE 'openid-connect-generic%' + GROUP BY meta_key" + ); + + $deleted = 0; + foreach ( (array) $legacy_meta_keys as $legacy_meta_key ) { + // Unlike a plain DELETE query, this also clears the cached meta of the users. + delete_metadata( 'user', 0, $legacy_meta_key->meta_key, '', true ); + $deleted += (int) $legacy_meta_key->count; + } + + $remaining = $wpdb->get_var( + "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key LIKE 'openid-connect-generic%'" + ); + + // Null means the query failed. + if ( null === $remaining || (int) $remaining > 0 ) { + return false; + } + + return $deleted; + } +} diff --git a/plugins/bcc-login/includes/class-bcc-login-settings.php b/plugins/bcc-login/includes/class-bcc-login-settings.php index f4149a8..7baf398 100644 --- a/plugins/bcc-login/includes/class-bcc-login-settings.php +++ b/plugins/bcc-login/includes/class-bcc-login-settings.php @@ -653,8 +653,9 @@ function render_delete_subscribers_button () {

+

- + 'return confirm("Are you sure you want to delete all the subscribers?");' )); ?> @@ -667,7 +668,13 @@ function render_delete_subscribers_button () { * Handles deletion of subscribers */ function delete_subscribers_handler() { - if ( strpos(wp_get_referer(), 'delete_subscribers=true') !== false) { + // Only run for the form above, submitted by an administrator. The nonce stops other sites + // from submitting it on behalf of a logged-in administrator. + if ( + isset( $_POST['delete_subscribers'], $_POST['delete_subscribers_nonce'] ) && + wp_verify_nonce( $_POST['delete_subscribers_nonce'], 'delete_subscribers' ) && + current_user_can( 'administrator' ) + ) { global $wpdb; $sql = "DELETE {$wpdb->prefix}users, {$wpdb->prefix}usermeta @@ -677,12 +684,71 @@ function delete_subscribers_handler() { meta_key = '{$wpdb->prefix}capabilities' AND meta_value LIKE 'a:1:{s:10:\"subscriber\";b:1;}'"; - $result = $wpdb->get_results($sql); + $wpdb->query($sql); + + // The join above only removes the capabilities meta row of each subscriber, + // so remove all remaining meta (e.g. openid-connect-generic-*) of users that no longer exist. + // The users tables are shared by all sites of a multisite network, so these aren't prefixed per site. + $sql = "DELETE {$wpdb->usermeta} + FROM {$wpdb->usermeta} + LEFT JOIN {$wpdb->users} ON {$wpdb->usermeta}.user_id = {$wpdb->users}.ID + WHERE {$wpdb->users}.ID IS NULL"; + + $orphaned_meta_deleted = $wpdb->query($sql); + + if ( false === $orphaned_meta_deleted ) { + add_settings_error( + 'subscribers_deleted', + 'orphaned_user_meta_not_deleted', + __( 'The user meta of deleted users could not be deleted.', 'bcc-login' ), + 'error' + ); + } + + // Remove tokens and claims stored by the old OpenID Connect Generic plugin for the remaining users (e.g. admins and editors). + $legacy_meta_deleted = BCC_Login_Cleanup::delete_legacy_user_meta(); + + if ( false === $legacy_meta_deleted ) { + add_settings_error( + 'subscribers_deleted', + 'legacy_user_meta_not_deleted', + __( 'Not all OpenID Connect Generic user meta could be deleted.', 'bcc-login' ), + 'error' + ); + } + + // Remove options and transients left behind by the old OpenID Connect Generic plugin (states, logs, version). + // 'openid_connect_generic_settings' is kept, as it's still used as fallback for the client id and secret. + $legacy_options = $wpdb->get_col( + "SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE '%openid-connect-generic%'" + ); + + foreach ( $legacy_options as $legacy_option ) { + delete_option( $legacy_option ); + } + + // Remove expired transients, e.g. the oidc_* tokens of ended sessions. WP does this daily via WP-Cron, + // but skips the database when an object cache is in use, so force it here. + delete_expired_transients( true ); + + // Only report the counts of the steps that succeeded, the others have an error notice. + $messages = array( __( 'All subscribers were successfully deleted.' ) ); + + if ( false !== $orphaned_meta_deleted ) { + $messages[] = sprintf( __( 'Removed %d user meta rows of deleted users.', 'bcc-login' ), $orphaned_meta_deleted ); + } + + if ( false !== $legacy_meta_deleted ) { + $messages[] = sprintf( __( 'Removed %d OpenID Connect Generic user meta rows.', 'bcc-login' ), $legacy_meta_deleted ); + } + + $messages[] = sprintf( __( 'Removed %d OpenID Connect Generic options.', 'bcc-login' ), count( $legacy_options ) ); + $messages[] = __( 'Removed expired transients.', 'bcc-login' ); add_settings_error( 'subscribers_deleted', 'subscribers_deleted', - __( 'All subscribers were successfully deleted.' ) . ' – ' . implode(', ', $result), + implode( ' ', $messages ), 'success' ); }