Skip to content

Commit

Permalink
IDC: add ip_requester option (#34753)
Browse files Browse the repository at this point in the history
  • Loading branch information
bindlegirl committed Jan 17, 2024
1 parent 4a749d0 commit 69da2cd
Show file tree
Hide file tree
Showing 27 changed files with 266 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adding support for IDC when site URL is an IP address.
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public static function get_option_names( $type = 'compact' ) {
'dismissed_backup_review_restore', // (bool) Determines if the component review request is dismissed for successful restore requests.
'dismissed_backup_review_backups', // (bool) Determines if the component review request is dismissed for successful backup requests.
'identity_crisis_url_secret', // (array) The IDC URL secret and its expiration date.
'identity_crisis_ip_requester', // (array) The IDC IP address and its expiration date.
'dismissed_welcome_banner', // (bool) Determines if the welcome banner has been dismissed or not.
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adding support for IDC when site URL is an IP address.
2 changes: 1 addition & 1 deletion projects/packages/identity-crisis/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"link-template": "https://github.com/Automattic/jetpack-identity-crisis/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-trunk": "0.14.x-dev"
"dev-trunk": "0.15.x-dev"
}
},
"config": {
Expand Down
2 changes: 1 addition & 1 deletion projects/packages/identity-crisis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jetpack-identity-crisis",
"version": "0.14.1",
"version": "0.15.0-alpha",
"description": "Jetpack Identity Crisis",
"main": "_inc/admin.jsx",
"repository": {
Expand Down
135 changes: 125 additions & 10 deletions projects/packages/identity-crisis/src/class-identity-crisis.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Identity_Crisis {
/**
* Package Version
*/
const PACKAGE_VERSION = '0.14.1';
const PACKAGE_VERSION = '0.15.0-alpha';

/**
* Persistent WPCOM blog ID that stays in the options after disconnect.
Expand Down Expand Up @@ -92,6 +92,7 @@ private function __construct() {
add_filter( 'jetpack_remote_request_url', array( $this, 'add_idc_query_args_to_url' ) );

add_filter( 'jetpack_connection_validate_urls_for_idc_mitigation_response', array( static::class, 'add_secret_to_url_validation_response' ) );
add_filter( 'jetpack_connection_validate_urls_for_idc_mitigation_response', array( static::class, 'add_ip_requester_to_url_validation_response' ) );

add_filter( 'jetpack_options', array( static::class, 'reverse_wpcom_urls_for_idc' ) );

Expand Down Expand Up @@ -209,10 +210,18 @@ public function add_idc_query_args_to_url( $url ) {
|| self::validate_sync_error_idc_option() ) {
return $url;
}
$home_url = Urls::home_url();
$site_url = Urls::site_url();
$hostname = wp_parse_url( $site_url, PHP_URL_HOST );

// If request is from an IP, make sure ip_requester option is set
if ( self::url_is_ip( $hostname ) ) {
self::maybe_update_ip_requester( $hostname );
}

$query_args = array(
'home' => Urls::home_url(),
'siteurl' => Urls::site_url(),
'home' => $home_url,
'siteurl' => $site_url,
);

if ( self::should_handle_idc() ) {
Expand All @@ -223,10 +232,6 @@ public function add_idc_query_args_to_url( $url ) {
$query_args['migrate_for_idc'] = true;
}

if ( self::url_is_ip() ) {
$query_args['url_secret'] = URL_Secret::create_secret( 'URL_argument_secret_failed' );
}

if ( is_multisite() ) {
$query_args['multisite'] = true;
}
Expand Down Expand Up @@ -1359,11 +1364,16 @@ public static function add_secret_to_url_validation_response( array $response )
/**
* Check if URL is an IP.
*
* @param string $hostname The hostname to check.
* @return bool
*/
public static function url_is_ip() {
$hostname = wp_parse_url( Urls::site_url(), PHP_URL_HOST );
$is_ip = filter_var( $hostname, FILTER_VALIDATE_IP ) !== false ? true : false;
public static function url_is_ip( $hostname = null ) {

if ( ! $hostname ) {
$hostname = wp_parse_url( Urls::site_url(), PHP_URL_HOST );
}

$is_ip = filter_var( $hostname, FILTER_VALIDATE_IP ) !== false ? $hostname : false;
return $is_ip;
}

Expand Down Expand Up @@ -1394,4 +1404,109 @@ public static function register_request_body( array $params ) {
public static function site_registered( $blog_id ) {
update_option( static::PERSISTENT_BLOG_ID_OPTION_NAME, (int) $blog_id, false );
}

/**
* Check if we need to update the ip_requester option.
*
* @param string $hostname The hostname to check.
*
* @return void
*/
public static function maybe_update_ip_requester( $hostname ) {
// Check if transient exists
$transient_key = ip2long( $hostname );
if ( $transient_key && ! get_transient( 'jetpack_idc_ip_requester_' . $transient_key ) ) {
self::set_ip_requester_for_idc( $hostname, $transient_key );
}
}

/**
* If URL is an IP, add the IP value to the ip_requester option with its expiry value.
*
* @param string $hostname The hostname to check.
* @param int $transient_key The transient key.
*/
public static function set_ip_requester_for_idc( $hostname, $transient_key ) {
// Check if option exists
$data = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );

$ip_requester = array(
'ip' => $hostname,
'expires_at' => time() + 360,
);

// If not set, initialize it
if ( empty( $data ) ) {
$data = array( $ip_requester );
} else {
$updated_data = array();
$updated_value = false;

// Remove expired values and update existing IP
foreach ( $data as $item ) {
if ( time() > $item['expires_at'] ) {
continue; // Skip expired IP
}

if ( $item['ip'] === $hostname ) {
$item['expires_at'] = time() + 360;
$updated_value = true;
}

$updated_data[] = $item;
}

if ( ! $updated_value || empty( $updated_data ) ) {
$updated_data[] = $ip_requester;
}

$data = $updated_data;
}

self::update_ip_requester( $data, $transient_key );
}

/**
* Update the ip_requester option and set a transient to expire in 5 minutes.
*
* @param array $data The data to be updated.
* @param int $transient_key The transient key.
*
* @return void
*/
public static function update_ip_requester( $data, $transient_key ) {
// Update the option
$updated = Jetpack_Options::update_option( 'identity_crisis_ip_requester', $data );
// Set a transient to expire in 5 minutes
if ( $updated ) {
$transient_name = 'jetpack_idc_ip_requester_' . $transient_key;
set_transient( $transient_name, $data, 300 );
}
}

/**
* Adds `ip_requester` to the `jetpack.idcUrlValidation` URL validation endpoint.
*
* @param array $response The enpoint response that we're modifying.
*
* @return array
*/
public static function add_ip_requester_to_url_validation_response( array $response ) {
$requesters = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );
if ( $requesters ) {
// Loop through the requesters and add the IP to the response if it's not expired
$i = 0;
foreach ( $requesters as $ip ) {
if ( $ip['expires_at'] > time() ) {
$response['ip_requester'][] = $ip['ip'];
}
// Limit the response to five IPs
$i = ++$i;
if ( $i === 5 ) {
break;
}
}
return $response;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1089,4 +1089,61 @@ public function test_site_registered() {
$this->assertFalse( $option_before );
$this->assertSame( $blog_id, $option_after );
}

/**
* Test the `set_ip_requester_for_idc()` method.
*
* @return void
*/
public function testAddIPRequesterForIdc() {
Identity_Crisis::init();

update_option( 'siteurl', 'http://72.182.131.109/' );
$hostname = wp_parse_url( get_site_url(), PHP_URL_HOST );
$transient_key = ip2long( $hostname );

// Call the method to be tested
Identity_Crisis::set_ip_requester_for_idc( $hostname, $transient_key );
$result = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );

// Assert that the the ip was added to the option
$this->assertIsArray( $result );

// Assert that the ip and expiry date are added
$expected_ip = '72.182.131.109';
foreach ( $result as $ip ) {
$this->assertEquals( $expected_ip, $ip['ip'] );
$this->assertTrue( is_int( $ip['expires_at'] ) );
}

// Test with another IP address
update_option( 'siteurl', 'http://33.182.100.200/' );
$hostname = wp_parse_url( get_site_url(), PHP_URL_HOST );
$transient_key = ip2long( $hostname );
Identity_Crisis::set_ip_requester_for_idc( $hostname, $transient_key );
$result2 = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );

$expected_ip2 = '33.182.100.200';
$expected_ip_array = array( $expected_ip, $expected_ip2 );

foreach ( $result2 as $ip ) {
$this->assertContains( $ip['ip'], $expected_ip_array );
}

// Test deleting expired IPs
$expired_ip = array(
'ip' => '99.182.100.777',
'expires_at' => 1111,
);
$result2[] = $expired_ip;

$expected_ip3 = '99.182.100.777';

Identity_Crisis::set_ip_requester_for_idc( $hostname, $transient_key );
$result3 = Jetpack_Options::get_option( 'identity_crisis_ip_requester' );

foreach ( $result3 as $ip ) {
$this->assertNotContains( $expected_ip3, $ip );
}
}
}
5 changes: 5 additions & 0 deletions projects/plugins/backup/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


4 changes: 2 additions & 2 deletions projects/plugins/backup/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions projects/plugins/boost/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


5 changes: 5 additions & 0 deletions projects/plugins/boost/changelog/add-ip-requester-option#2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


4 changes: 2 additions & 2 deletions projects/plugins/boost/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions projects/plugins/inspect/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


5 changes: 5 additions & 0 deletions projects/plugins/jetpack/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: other
Comment: Updated composer.lock.


4 changes: 2 additions & 2 deletions projects/plugins/jetpack/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions projects/plugins/migration/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


4 changes: 2 additions & 2 deletions projects/plugins/migration/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions projects/plugins/protect/changelog/add-ip-requester-option
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: changed
Comment: Updated composer.lock.


Loading

0 comments on commit 69da2cd

Please sign in to comment.