refactor(2meet-data-optimizer): stop registering the sso group in core
- TMDO_Member_Fields no longer registers user/sso; the definition moved unchanged (hash 03543f19) to 2meet-data-optimizer-sso-addon. - missing_integration_addons(): 2meet-hub-core or 2meet-spoke-sso without the SSO AddOn is reported (admin notice + Site Health). - The deprecated-AddOn notice points hub / spoke users to the SSO AddOn. - Sites must activate the SSO AddOn before this ships (PR-041); aeav_only sites without it fall back to orphan-group restore. - Verify: core unit 659 / integration 475, SSO 6, HP 142/49, WC 10/22 green, phpcs clean - PR: PR-040
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
* mobile-bridge, playlist, quotation, spoke) only registered tables and fields
|
||||
* on the partner's behalf. Partner plugins now register themselves with the
|
||||
* TMDO_Anti_EAV_Aware trait (docs/PARTNER-REGISTRATION.md), so these AddOns
|
||||
* are deprecated and will be removed.
|
||||
* are deprecated and will be removed. The hub and spoke AddOns are replaced
|
||||
* by 2meet-data-optimizer-sso-addon.
|
||||
*
|
||||
* @package WP_Data_Optimizer
|
||||
*/
|
||||
@@ -79,5 +80,11 @@ final class TMDO_Deprecated_Addons_Notice {
|
||||
),
|
||||
esc_html__( '改寫方式見 2meet-data-optimizer 外掛內的 docs/PARTNER-REGISTRATION.md;partner 外掛完成自行註冊後即可停用對應 AddOn。', '2meet-data-optimizer' )
|
||||
);
|
||||
if ( array_intersect( $active, array( 'hub', 'spoke' ) ) ) {
|
||||
printf(
|
||||
'<div class="notice notice-warning"><p>%s</p></div>',
|
||||
esc_html__( 'Hub / Spoke AddOn 由 2meet-data-optimizer-sso-addon 取代:啟用它之後即可停用這兩個 AddOn。', '2meet-data-optimizer' )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ class TMDO_Compatibility {
|
||||
if ( self::is_woocommerce_active() && ! class_exists( 'TMDO_Woocommerce_Bootstrap' ) ) {
|
||||
$missing['WooCommerce'] = '2meet-data-optimizer-woocommerce-addon';
|
||||
}
|
||||
// 2meet-hub-core / 2meet-spoke-sso read and write the user `sso` group it owns.
|
||||
if ( ( class_exists( 'TMHC_Plugin', false ) || class_exists( 'TMSO_Plugin', false ) ) && ! class_exists( 'TMDO_SSO_Integration', false ) ) {
|
||||
$missing['2meet Hub / Spoke SSO'] = '2meet-data-optimizer-sso-addon';
|
||||
}
|
||||
return $missing;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers four user entity groups for membership, activity, profile, and SSO.
|
||||
* Registers the user entity groups for membership, activity and profile.
|
||||
*
|
||||
* Groups → flat tables:
|
||||
* membership → wp_wpdo_user_membership (tier, points, expiry — high-freq search)
|
||||
* activity → wp_wpdo_user_activity (login counters, last-active — high-freq write)
|
||||
* profile → wp_wpdo_user_profile (display fields, specialties — low-freq write)
|
||||
* sso → wp_wpdo_user_sso (Hub token cache, replaces _tmso_* usermeta)
|
||||
* (sso → wp_wpdo_user_sso is owned by 2meet-data-optimizer-sso-addon)
|
||||
*
|
||||
* Called from TMDO_Core::run() before wpdo_register_entity_fields fires.
|
||||
* Pattern mirrors TMDO_WooCommerce::register() / register_user_entity_fields().
|
||||
@@ -61,7 +61,6 @@ final class TMDO_Member_Fields {
|
||||
self::register_membership_group();
|
||||
self::register_activity_group();
|
||||
self::register_profile_group();
|
||||
self::register_sso_group();
|
||||
self::register_core_profile_group();
|
||||
self::register_social_group();
|
||||
self::register_commerce_group();
|
||||
@@ -213,70 +212,6 @@ final class TMDO_Member_Fields {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hub/Spoke SSO token cache — replaces _tmso_* usermeta.
|
||||
*
|
||||
* Silent refresh fires every 15 min per user; at 10M users this is a
|
||||
* high-frequency EAV hot-spot. A flat table + object cache hit cuts DB
|
||||
* load 10–50× versus a wp_usermeta EAV scan per refresh.
|
||||
*
|
||||
* last_id_token is NOT stored in plaintext (privacy + volume). Only the
|
||||
* SHA-256 hash is kept for SLO token comparison.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function register_sso_group(): void {
|
||||
TMDO_Entity_Registry::register_group(
|
||||
'user',
|
||||
'sso',
|
||||
array(
|
||||
array(
|
||||
'key' => 'hub_global_user_id',
|
||||
'type' => 'text',
|
||||
'searchable' => true,
|
||||
'label' => 'Hub global user UUID (2mso_user_mapping.global_user_id bridge key)',
|
||||
),
|
||||
array(
|
||||
'key' => 'picture_url',
|
||||
'type' => 'text',
|
||||
'label' => 'Social / SSO profile picture URL',
|
||||
),
|
||||
array(
|
||||
'key' => 'last_id_token_hash',
|
||||
'type' => 'text',
|
||||
'label' => 'SHA-256(last_id_token) for SLO comparison — no plaintext stored',
|
||||
),
|
||||
array(
|
||||
'key' => 'refresh_token_enc',
|
||||
'type' => 'textarea',
|
||||
'label' => 'Encrypted refresh token (TMSO_Crypto — key-versioned enc_vN:ciphertext)',
|
||||
),
|
||||
array(
|
||||
'key' => 'token_expires_at',
|
||||
'type' => 'datetime',
|
||||
'searchable' => true,
|
||||
'label' => 'SSO token expiry (set to past to force re-auth on next request)',
|
||||
),
|
||||
array(
|
||||
'key' => 'sso_last_login_at',
|
||||
'type' => 'datetime',
|
||||
'label' => 'Last SSO-initiated login datetime',
|
||||
),
|
||||
array(
|
||||
'key' => 'sso_login_count',
|
||||
'type' => 'integer',
|
||||
'default' => 0,
|
||||
'label' => 'SSO login count',
|
||||
),
|
||||
array(
|
||||
'key' => 'known_login_ips',
|
||||
'type' => 'textarea',
|
||||
'label' => 'Recent login IPs JSON array (new-device notification) — replaces _tmso_known_ips usermeta',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* WP core user fields stored as multi-row EAV in wp_usermeta.
|
||||
*
|
||||
|
||||
@@ -43,6 +43,17 @@ class DeprecatedAddonsNoticeTest extends TestCase {
|
||||
$this->assertStringContainsString( 'PARTNER-REGISTRATION.md', $html );
|
||||
}
|
||||
|
||||
public function test_hub_and_spoke_point_to_the_sso_addon(): void {
|
||||
$GLOBALS['_wp_options']['active_plugins'] = array( '2meet-data-optimizer-spoke-addon/2meet-data-optimizer-spoke-addon.php' );
|
||||
|
||||
ob_start();
|
||||
TMDO_Deprecated_Addons_Notice::render();
|
||||
$html = (string) ob_get_clean();
|
||||
|
||||
$this->assertStringContainsString( '2meet-data-optimizer-spoke-addon', $html );
|
||||
$this->assertStringContainsString( '2meet-data-optimizer-sso-addon', $html );
|
||||
}
|
||||
|
||||
public function test_no_notice_when_none_active_or_user_cannot_manage(): void {
|
||||
ob_start();
|
||||
TMDO_Deprecated_Addons_Notice::render();
|
||||
|
||||
@@ -39,12 +39,18 @@ class MemberFieldsRegistrationTest extends TestCase {
|
||||
public function test_all_four_groups_are_registered(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
|
||||
foreach ( array( 'membership', 'activity', 'profile', 'sso' ) as $group ) {
|
||||
foreach ( array( 'membership', 'activity', 'profile' ) as $group ) {
|
||||
$fields = WPDO_Entity_Registry::get_group_fields( 'user', $group );
|
||||
$this->assertNotEmpty( $fields, "Group '{$group}' should have registered fields." );
|
||||
}
|
||||
}
|
||||
|
||||
public function test_core_leaves_the_sso_group_to_the_sso_addon(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
|
||||
$this->assertSame( array(), WPDO_Entity_Registry::get_group_fields( 'user', 'sso' ) );
|
||||
}
|
||||
|
||||
public function test_core_registers_no_hp_user_group(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
|
||||
@@ -148,41 +154,6 @@ class MemberFieldsRegistrationTest extends TestCase {
|
||||
$this->assertTrue( (bool) $field['fulltext'] );
|
||||
}
|
||||
|
||||
// ── sso group ────────────────────────────────────────────────────────────
|
||||
|
||||
public function test_sso_group_has_eight_fields(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
$fields = WPDO_Entity_Registry::get_group_fields( 'user', 'sso' );
|
||||
// v3.4.4: known_login_ips registered (was leaking to wp_usermeta via set_entity).
|
||||
$this->assertCount( 8, $fields );
|
||||
$this->assertNotNull( $this->find_field( $fields, 'known_login_ips' ) );
|
||||
}
|
||||
|
||||
public function test_hub_global_user_id_is_searchable(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
$fields = WPDO_Entity_Registry::get_group_fields( 'user', 'sso' );
|
||||
$field = $this->find_field( $fields, 'hub_global_user_id' );
|
||||
|
||||
$this->assertTrue( (bool) $field['searchable'] );
|
||||
}
|
||||
|
||||
public function test_token_expires_at_is_datetime_searchable(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
$fields = WPDO_Entity_Registry::get_group_fields( 'user', 'sso' );
|
||||
$field = $this->find_field( $fields, 'token_expires_at' );
|
||||
|
||||
$this->assertSame( 'datetime', $field['type'] );
|
||||
$this->assertTrue( (bool) $field['searchable'] );
|
||||
}
|
||||
|
||||
public function test_refresh_token_enc_is_textarea(): void {
|
||||
WPDO_Member_Fields::register_entity_fields();
|
||||
$fields = WPDO_Entity_Registry::get_group_fields( 'user', 'sso' );
|
||||
$field = $this->find_field( $fields, 'refresh_token_enc' );
|
||||
|
||||
$this->assertSame( 'textarea', $field['type'] );
|
||||
}
|
||||
|
||||
// ── register() hooks add_action ──────────────────────────────────────────
|
||||
|
||||
public function test_register_hooks_wpdo_register_entity_fields(): void {
|
||||
|
||||
@@ -79,6 +79,27 @@ class MissingIntegrationAddonsTest extends TestCase {
|
||||
$this->assertSame( array( 'WooCommerce' => '2meet-data-optimizer-woocommerce-addon' ), TMDO_Compatibility::missing_integration_addons() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_hub_or_spoke_without_the_sso_addon_is_reported(): void {
|
||||
eval( 'final class TMSO_Plugin {}' );
|
||||
|
||||
$this->assertSame( array( '2meet Hub / Spoke SSO' => '2meet-data-optimizer-sso-addon' ), TMDO_Compatibility::missing_integration_addons() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function test_hub_with_the_sso_addon_is_fine(): void {
|
||||
eval( 'final class TMHC_Plugin {}' );
|
||||
eval( 'final class TMDO_SSO_Integration {}' );
|
||||
|
||||
$this->assertSame( array(), TMDO_Compatibility::missing_integration_addons() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
|
||||
Reference in New Issue
Block a user