Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor: Allow composing block editor posts on WordPress.com. #11354

Merged
merged 45 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
67ae7df
Add asetting to allow the JP site to be iframed by WordPress.com.
kwight Feb 14, 2019
f87bb2e
Jetpack_Signature POC
obenland Mar 20, 2019
078f870
Refactor `verify_xml_rpc_signature` to take `$token` and `$signature`
kwight Mar 22, 2019
c48ea08
Add asetting to allow the JP site to be iframed by WordPress.com.
kwight Feb 14, 2019
5bc09cf
Jetpack_Signature POC
obenland Mar 20, 2019
fe7e16f
Refactor `verify_xml_rpc_signature` to take `$token` and `$signature`
kwight Mar 22, 2019
de8ce0b
Remove unnecessary automatic handling for other admin URLs.
kwight Mar 26, 2019
61e6220
Change the module name to "WordPress.com Block Editor".
kwight Mar 26, 2019
de87fb5
Remove unnecessary function.
kwight Mar 27, 2019
7618b10
Correct support page links.
kwight Mar 27, 2019
1c128ad
Rename the module and list it for auto-activation.
kwight Mar 28, 2019
b331c79
Revert files related to the module setting that we no longer need with
kwight Mar 28, 2019
81c7e88
Note auto-activation in the module header.
kwight Mar 28, 2019
9d6d67d
Account for `$timestamp` and $nonce` variables in
kwight Mar 28, 2019
f6226aa
Bail early from `jetpack_framing_allowed` if query param requirements
kwight Mar 29, 2019
47bf256
WIP unit tests
obenland Apr 4, 2019
40888a0
Finish up unit tests
obenland Apr 5, 2019
eb47653
Fix unexpected T_PAAMAYIM_NEKUDOTAYIM parse error
obenland Apr 5, 2019
03e2f8f
Update file inclusion to avoid errors
obenland Apr 5, 2019
d939fd3
Give context to test failures
obenland Apr 5, 2019
01aaf45
Fix unit tests™
obenland Apr 8, 2019
607e7b9
Thin-out test cleanup
obenland Apr 10, 2019
a529ad4
Decode frame-nonce
obenland Apr 15, 2019
f4e9dc6
Revert `verify_xml_rpc_signature` changes.
kwight Apr 18, 2019
878481e
Add the module to the PHPCS whitelist.
kwight Apr 18, 2019
1951033
Revert tests from `verify_xml_rpc_signature` changes.
kwight Apr 18, 2019
1dfec39
Revert Writing settings, since this is an auto-activated feature. I must
kwight Apr 18, 2019
ea56f5e
First pass at jp token salted nonce approach
obenland Apr 18, 2019
2ae9848
Auto-load when connected to WordPress.com. It doesn't need to be a true
kwight Apr 18, 2019
3d6b0b8
Add module heading
obenland Apr 18, 2019
27531a9
Get wpcom_user_id
obenland Apr 18, 2019
ed06a87
Revert "Add module heading"
obenland Apr 18, 2019
3eb55d5
Updates based on great feedback
obenland Apr 18, 2019
35a1c93
Use jetpack user id for nonce creation
obenland Apr 18, 2019
c7a80ac
Check expiration of hashes using explicit value from nonces
mmtr Apr 19, 2019
256eea0
Remove filter once
mmtr Apr 23, 2019
798096b
Simplify return logic.
kwight Apr 23, 2019
da7098c
Check if current user matches external user.
obenland Apr 23, 2019
901ea1e
Verify the nonce first and check later for expiration and user mismatch
mmtr Apr 24, 2019
9c762e3
Use user ID from nonce on the filtered salt
mmtr Apr 24, 2019
227b23b
Rename filename on phpcs whitelist
mmtr Apr 24, 2019
6cf4780
Update property name
obenland Apr 24, 2019
5733664
Add unit test
obenland Apr 24, 2019
f2a651c
Fix wp error handling
obenland Apr 24, 2019
da07959
Use dirname rather than __DIR__
obenland Apr 24, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/phpcs-whitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ module.exports = [
'modules/module-extras.php',
'modules/module-info.php',
'modules/theme-tools/social-menu/',
'modules/wpcom-block-editor/class-jetpack-wpcom-block-editor.php',
];
1 change: 1 addition & 0 deletions modules/module-extras.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
'plugin-search.php',
'simple-payments/simple-payments.php',
'woocommerce-analytics/wp-woocommerce-analytics.php',
'wpcom-block-editor/class-jetpack-wpcom-block-editor.php',
);

// Add connected features to our existing list if the site is currently connected.
Expand Down
157 changes: 157 additions & 0 deletions modules/wpcom-block-editor/class-jetpack-wpcom-block-editor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php
/**
* WordPress.com Block Editor
* Allow new block editor posts to be composed on WordPress.com.
* This is auto-loaded as of Jetpack v7.4 for sites connected to WordPress.com only.
*
* @package Jetpack
*/

/**
* WordPress.com Block editor for Jetpack
*/
class Jetpack_WPCOM_Block_Editor {
/**
* ID of the user who signed the nonce.
*
* @var int
*/
private $nonce_user_id;

/**
* Singleton
*/
public static function init() {
static $instance = false;

if ( ! $instance ) {
$instance = new Jetpack_WPCOM_Block_Editor();
}

return $instance;
}

/**
* Jetpack_WPCOM_Block_Editor constructor.
*/
private function __construct() {
add_action( 'admin_init', array( $this, 'disable_send_frame_options_header' ), 9 );
add_filter( 'admin_body_class', array( $this, 'add_iframed_body_class' ) );
}

/**
* Prevents frame options header from firing if this is a whitelisted iframe request.
*/
public function disable_send_frame_options_header() {
if ( $this->framing_allowed() ) {
remove_action( 'admin_init', 'send_frame_options_header' );
}
}

/**
* Adds custom admin body class if this is a whitelisted iframe request.
*
* @param string $classes Admin body classes.
* @return string
*/
public function add_iframed_body_class( $classes ) {
if ( $this->framing_allowed() ) {
$classes .= ' is-iframed ';
}

return $classes;
}

/**
* Checks whether this is a whitelisted iframe request.
*
* @return bool
*/
public function framing_allowed() {
if ( empty( $_GET['frame-nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return false;
}

$verified = $this->verify_frame_nonce(
$_GET['frame-nonce'], // phpcs:ignore WordPress.Security.NonceVerification
'frame-' . Jetpack_Options::get_option( 'id' )
);

if ( is_wp_error( $verified ) ) {
wp_die( esc_html( $verified ) );
obenland marked this conversation as resolved.
Show resolved Hide resolved
obenland marked this conversation as resolved.
Show resolved Hide resolved
}

if ( $verified && ! defined( 'IFRAME_REQUEST' ) ) {
define( 'IFRAME_REQUEST', true );
}

return (bool) $verified;
}

/**
* Verify that correct nonce was used with time limit.
*
* The user is given an amount of time to use the token, so therefore, since the
* UID and $action remain the same, the independent variable is the time.
*
* @param string $nonce Nonce that was used in the form to verify.
* @param string $action Should give context to what is taking place and be the same when nonce was created.
* @return boolean|WP_Error Whether the nonce is valid.
*/
public function verify_frame_nonce( $nonce, $action ) {
if ( empty( $nonce ) ) {
return false;
}

list( $expiration, $user_id, $hash ) = explode( ':', $nonce, 3 );

$this->nonce_user_id = (int) $user_id;
if ( ! $this->nonce_user_id ) {
return false;
}

$token = Jetpack_Data::get_access_token( $this->nonce_user_id );
if ( ! $token ) {
return false;
}

add_filter( 'salt', array( $this, 'filter_salt' ), 10, 2 );
$expected_hash = wp_hash( "$expiration|$action|{$this->nonce_user_id}", 'jetpack_frame_nonce' );
remove_filter( 'salt', array( $this, 'filter_salt' ) );

if ( ! hash_equals( $hash, $expected_hash ) ) {
return false;
}

obenland marked this conversation as resolved.
Show resolved Hide resolved
if ( time() > $expiration ) {
return new WP_Error( 'nonce_invalid_expired', 'Expired nonce.' );
}

if ( get_current_user_id() !== $this->nonce_user_id ) {
return new WP_Error( 'nonce_invalid_user_mismatch', 'User ID mismatch.' );
}

return true;
}

/**
* Filters the WordPress salt.
*
* @param string $salt Salt for the given scheme.
* @param string $scheme Authentication scheme.
* @return string
*/
public function filter_salt( $salt, $scheme ) {
if ( 'jetpack_frame_nonce' === $scheme ) {
$token = Jetpack_Data::get_access_token( $this->nonce_user_id );

if ( $token ) {
$salt = $token->secret;
}
}

return $salt;
}
}

Jetpack_WPCOM_Block_Editor::init();