feat(2meet-data-optimizer): set an entity bridge mode from the CLI under the Promotion Guard
- New `wp tmdo bridge-mode <entity> <mode>` goes through TMDO_Mode_Manager::set(): unsafe jumps and aeav_only promotions refused by the Promotion Guard exit with the reason and leave the mode unchanged. Named apart from the Feature Flags `mode-set`. - member-sso-sync and member-force-logout warn that they are deprecated: the user sso group they work on is no longer registered. - Verify: CliBridgeModeTest (RED: method missing); dev36 bridge-mode user aeav_only refused (unsafe jump), term dual_write → shadow_read → aeav_only refused by the HivePress guard → back to disabled, wpdo_bridge_modes identical to before. - PR: PR-069
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
* wp wpdo member-shadow-report — Recent entity_type=user diff records.
|
||||
* wp wpdo member-cutover — Switch user mode to aeav_only.
|
||||
* wp wpdo member-points-check — Show balance + recent ledger for a user.
|
||||
* wp wpdo member-sso-sync — Sync Hub membership claims to flat table.
|
||||
* wp wpdo member-force-logout — Set token_expires_at to past → re-auth.
|
||||
* wp wpdo member-sso-sync — (deprecated) Sync Hub membership claims to flat table.
|
||||
* wp wpdo member-force-logout — (deprecated) Set token_expires_at to past → re-auth.
|
||||
*
|
||||
* @package WP_Data_Optimizer
|
||||
* @since 2.5.5
|
||||
@@ -358,6 +358,9 @@ class TMDO_CLI_Member {
|
||||
/**
|
||||
* Sync Hub membership claims into the user flat table.
|
||||
*
|
||||
* Deprecated since 1.2.0: the user `sso` group is no longer registered, so
|
||||
* its flat table is never created on new installs. Will be removed.
|
||||
*
|
||||
* Reads _tmso_refresh_token / _tmso_picture_url / _tmso_last_id_token
|
||||
* from usermeta and writes them into the sso group flat table.
|
||||
* Intended as a one-shot sync until 2meet-spoke-sso v1.14+ starts writing
|
||||
@@ -383,6 +386,7 @@ class TMDO_CLI_Member {
|
||||
* @param array $assoc_args Named arguments.
|
||||
*/
|
||||
public function member_sso_sync( $args, $assoc_args ): void {
|
||||
WP_CLI::warning( 'member-sso-sync is deprecated since 1.2.0 (the user sso group is no longer registered) and will be removed.' );
|
||||
global $wpdb;
|
||||
|
||||
$user_id = isset( $assoc_args['user-id'] ) ? (int) $assoc_args['user-id'] : 0;
|
||||
@@ -468,6 +472,9 @@ class TMDO_CLI_Member {
|
||||
/**
|
||||
* Force re-authentication by setting token_expires_at to a past datetime.
|
||||
*
|
||||
* Deprecated since 1.2.0: works on the user `sso` flat table, which is no
|
||||
* longer registered. Will be removed.
|
||||
*
|
||||
* The Spoke silent-refresh logic detects an expired token_expires_at and
|
||||
* redirects the user to re-authenticate against the Hub. If the Hub has
|
||||
* the account blocked, the user is logged out from all Spokes.
|
||||
@@ -492,6 +499,7 @@ class TMDO_CLI_Member {
|
||||
* @param array $assoc_args Named arguments.
|
||||
*/
|
||||
public function member_force_logout( $args, $assoc_args ): void {
|
||||
WP_CLI::warning( 'member-force-logout is deprecated since 1.2.0 (the user sso group is no longer registered) and will be removed.' );
|
||||
if ( ! isset( $assoc_args['confirm'] ) ) {
|
||||
WP_CLI::error( 'This invalidates live SSO tokens. Add --confirm to proceed.' );
|
||||
}
|
||||
|
||||
@@ -152,6 +152,40 @@ class TMDO_CLI_V2 {
|
||||
WP_CLI::success( "Hook Bus: {$state}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an entity's bridge mode. Goes through TMDO_Mode_Manager::set(): unsafe
|
||||
* jumps and promotions refused by the Promotion Guard (aeav_only on
|
||||
* HivePress / WooCommerce sites) fail and leave the mode unchanged.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* <entity>
|
||||
* : post | user | term | comment
|
||||
*
|
||||
* <mode>
|
||||
* : disabled | dual_write | shadow_read | aeav_only
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp tmdo bridge-mode user dual_write
|
||||
* wp tmdo bridge-mode post disabled
|
||||
*
|
||||
* @param array<int, string> $args Entity type and mode.
|
||||
* @param array<string, string> $assoc_args Unused.
|
||||
*/
|
||||
public function bridge_mode( $args, $assoc_args ): void {
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
$entity = (string) ( $args[0] ?? '' );
|
||||
$mode = (string) ( $args[1] ?? '' );
|
||||
$from = TMDO_Mode_Manager::get( $entity );
|
||||
|
||||
$result = TMDO_Mode_Manager::set( $entity, $mode );
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WP_CLI::error( $result->get_error_message() );
|
||||
}
|
||||
WP_CLI::success( "{$entity}: {$from} → {$mode}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an entity's flat-table values back to native meta — the way out of
|
||||
* aeav_only. Resumable; re-run after an interruption.
|
||||
@@ -918,6 +952,8 @@ WP_CLI::add_command( 'wpdo bridge-status', array( 'TMDO_CLI_V2', 'bridge_status'
|
||||
WP_CLI::add_command( 'tmdo bridge-status', array( 'TMDO_CLI_V2', 'bridge_status' ) );
|
||||
WP_CLI::add_command( 'wpdo bridge-set', array( 'TMDO_CLI_V2', 'bridge_set' ) );
|
||||
WP_CLI::add_command( 'tmdo bridge-set', array( 'TMDO_CLI_V2', 'bridge_set' ) );
|
||||
WP_CLI::add_command( 'wpdo bridge-mode', array( new TMDO_CLI_V2(), 'bridge_mode' ) );
|
||||
WP_CLI::add_command( 'tmdo bridge-mode', array( new TMDO_CLI_V2(), 'bridge_mode' ) );
|
||||
WP_CLI::add_command( 'wpdo bridge-restore-native', array( new TMDO_CLI_V2(), 'bridge_restore_native' ) );
|
||||
WP_CLI::add_command( 'tmdo bridge-restore-native', array( new TMDO_CLI_V2(), 'bridge_restore_native' ) );
|
||||
WP_CLI::add_command( 'wpdo repair-array-literals', array( new TMDO_CLI_V2(), 'repair_array_literals' ) );
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* `wp tmdo bridge-mode <entity> <mode>` (2.2 PR-069): the CLI way to change an
|
||||
* entity's bridge mode. It must go through TMDO_Mode_Manager::set(), so unsafe
|
||||
* jumps and promotions refused by the Promotion Guard never reach the option.
|
||||
* (The Guard matrix itself is covered by PromotionGuardTest.)
|
||||
*/
|
||||
class CliBridgeModeTest extends TestCase {
|
||||
|
||||
protected function setUp(): void {
|
||||
ini_set( 'error_log', '/dev/null' );
|
||||
$GLOBALS['_wp_options'] = array();
|
||||
$GLOBALS['_wp_filter_callbacks'] = array();
|
||||
$GLOBALS['_wp_current_user_can'] = array( 'manage_options' => true );
|
||||
TMDO_Mode_Manager::reset_cache();
|
||||
}
|
||||
|
||||
private function modes( array $modes ): void {
|
||||
$GLOBALS['_wp_options']['wpdo_bridge_modes'] = $modes;
|
||||
TMDO_Mode_Manager::reset_cache();
|
||||
}
|
||||
|
||||
public function test_sets_the_mode_of_one_entity(): void {
|
||||
$this->modes( array( 'user' => 'disabled', 'post' => 'dual_write' ) );
|
||||
|
||||
( new TMDO_CLI_V2() )->bridge_mode( array( 'user', 'dual_write' ), array() );
|
||||
|
||||
$this->assertSame( 'dual_write', TMDO_Mode_Manager::get( 'user' ) );
|
||||
$this->assertSame( 'dual_write', TMDO_Mode_Manager::get( 'post' ), 'other entities untouched' );
|
||||
}
|
||||
|
||||
public function test_an_unsafe_jump_is_refused(): void {
|
||||
$this->modes( array( 'user' => 'disabled' ) );
|
||||
|
||||
( new TMDO_CLI_V2() )->bridge_mode( array( 'user', 'aeav_only' ), array() );
|
||||
|
||||
$this->assertSame( 'disabled', TMDO_Mode_Manager::get( 'user' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_a_promotion_refused_by_the_guard_leaves_the_mode_unchanged(): void {
|
||||
eval( 'namespace HivePress; class Core {}' );
|
||||
$this->modes( array( 'user' => 'shadow_read' ) );
|
||||
|
||||
( new TMDO_CLI_V2() )->bridge_mode( array( 'user', 'aeav_only' ), array() );
|
||||
|
||||
$this->assertSame( 'shadow_read', TMDO_Mode_Manager::get( 'user' ) );
|
||||
$this->assertSame( array( 'user' => 'shadow_read' ), $GLOBALS['_wp_options']['wpdo_bridge_modes'] );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user