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

User invitation: Mail the newly added user with WPCom invite #35234

Merged
merged 14 commits into from
Jan 29, 2024
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/improve_invite_form
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

SSO: When creating a new users, mail the users with an invitation to WPCom.
161 changes: 106 additions & 55 deletions projects/plugins/jetpack/modules/sso.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @package automattic/jetpack
*/

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Roles;
use Automattic\Jetpack\Status;
Expand Down Expand Up @@ -54,6 +55,10 @@ private function __construct() {
add_action( 'jetpack_site_before_disconnected', array( static::class, 'disconnect' ) );
add_action( 'wp_login', array( 'Jetpack_SSO', 'clear_cookies_after_login' ) );

// When adding a new user via the admin, we want to intercept the core invitation email and send it via WordPress.com.
add_filter( 'wp_send_new_user_notification_to_user', array( $this, 'intercept_core_invitation_email' ), 10, 2 );
add_action( 'user_new_form', array( $this, 'render_invitation_email_message' ) );

// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
add_action( 'login_form_jetpack-sso', '__return_true' );

Expand Down Expand Up @@ -83,6 +88,52 @@ private function __construct() {
}
}

/**
* Render the invitation email message.
*/
public function render_invitation_email_message() {
printf( '<div class="notice inline notice-large notice-info">%1s</div>', esc_html__( 'The user will be automatically invited to WordPress.com.', 'jetpack' ) );
renancarvalho marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Intercept the core invitation email.
*
* @param bool $notify Whether to notify the user or not.
* @param WP_User $user The user object.
*
* @return bool Whether to notify the user or not via core email.
*/
public function intercept_core_invitation_email( $notify, $user ) {
if ( ! $user instanceof WP_User ) {
return $user;
renancarvalho marked this conversation as resolved.
Show resolved Hide resolved
}

$email = $user->user_email;
$role = $user->roles[0];
renancarvalho marked this conversation as resolved.
Show resolved Hide resolved
$locale = get_user_locale( $user->ID );
$blog_id = Jetpack_Options::get_option( 'id' );
$url = '/sites/' . $blog_id . '/invites/new';
$url = add_query_arg( 'locale', $locale, $url );

Client::wpcom_json_api_request_as_user(
$url,
'2', // Api version
array(
'method' => 'POST',
),
array(
'invitees' => array(
array(
'email_or_username' => $email,
'role' => $role,
),
),
)
);
// returning false prevents the user to be notified by the core email.
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should investigate some error handling. We may want to block adding a user unless the invite works because otherwise the user won't be able to log in any way.

}

/**
* Returns the single instance of the Jetpack_SSO object
*
Expand Down Expand Up @@ -119,7 +170,7 @@ public function sso_reminder_logout_wpcom( $errors ) {
if ( ! empty( $errors->errors['loggedout'] ) ) {
$logout_message = wp_kses(
sprintf(
/* translators: %1$s is a link to the WordPress.com account settings page. */
/* translators: %1$s is a link to the WordPress.com account settings page. */
__( 'If you are on a shared computer, remember to also <a href="%1$s">log out of WordPress.com</a>.', 'jetpack' ),
'https://wordpress.com/me'
),
Expand Down Expand Up @@ -320,10 +371,10 @@ public function render_require_two_step() {
<input
type="checkbox"
name="jetpack_sso_require_two_step"
<?php checked( Jetpack_SSO_Helpers::is_two_step_required() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); ?>
<?php checked( Jetpack_SSO_Helpers::is_two_step_required() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); ?>
>
<?php esc_html_e( 'Require Two-Step Authentication', 'jetpack' ); ?>
<?php esc_html_e( 'Require Two-Step Authentication', 'jetpack' ); ?>
renancarvalho marked this conversation as resolved.
Show resolved Hide resolved
</label>
<?php
}
Expand Down Expand Up @@ -352,10 +403,10 @@ public function render_match_by_email() {
<input
type="checkbox"
name="jetpack_sso_match_by_email"
<?php checked( Jetpack_SSO_Helpers::match_by_email() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); ?>
<?php checked( Jetpack_SSO_Helpers::match_by_email() ); ?>
<?php disabled( Jetpack_SSO_Helpers::is_match_by_email_checkbox_disabled() ); ?>
>
<?php esc_html_e( 'Match by Email', 'jetpack' ); ?>
<?php esc_html_e( 'Match by Email', 'jetpack' ); ?>
</label>
<?php
}
Expand Down Expand Up @@ -510,7 +561,7 @@ public static function save_cookies() {

setcookie(
'jetpack_sso_original_request',
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw().
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw().
esc_url_raw( set_url_scheme( ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) ),
time() + HOUR_IN_SECONDS,
COOKIEPATH,
Expand Down Expand Up @@ -540,47 +591,47 @@ public function login_form() {
}

$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) )
: false;
? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) )
: false;
$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) )
: false;
? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) )
: false;

?>
<div id="jetpack-sso-wrap">
<?php
/**
* Allow extension above Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_above_wpcom' );

if ( $display_name && $gravatar ) :
?>
<?php
/**
* Allow extension above Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_above_wpcom' );

if ( $display_name && $gravatar ) :
?>
<div id="jetpack-sso-wrap__user">
<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />

<h2>
<?php
echo wp_kses(
/* translators: %s a user display name. */
sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
array( 'span' => true )
);
?>
<?php
echo wp_kses(
/* translators: %s a user display name. */
sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
array( 'span' => true )
);
?>
</h2>
</div>

<?php endif; ?>
<?php endif; ?>


<div id="jetpack-sso-wrap__action">
<?php echo $this->build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?>
<?php echo $this->build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?>

<?php if ( $display_name && $gravatar ) : ?>
<?php if ( $display_name && $gravatar ) : ?>
<a rel="nofollow" class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
</a>
Expand Down Expand Up @@ -610,18 +661,18 @@ public function login_form() {
<?php endif; ?>
</div>

<?php
/**
* Allow extension below Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_below_wpcom' );

if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) :
?>
<?php
/**
* Allow extension below Jetpack's SSO form.
*
* @module sso
*
* @since 8.6.0
*/
do_action( 'jetpack_sso_login_form_below_wpcom' );

if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) :
?>
<div class="jetpack-sso-or">
<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
</div>
Expand All @@ -637,9 +688,9 @@ public function login_form() {
esc_html_e( 'Log in with WordPress.com', 'jetpack' )
?>
</a>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
<?php
}

/**
Expand Down Expand Up @@ -762,8 +813,8 @@ public static function delete_connection_for_user( $user_id ) {
*/
public static function request_initial_nonce() {
$nonce = ! empty( $_COOKIE['jetpack_sso_nonce'] )
? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) )
: false;
? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) )
: false;

if ( ! $nonce ) {
$xml = new Jetpack_IXR_Client();
Expand Down Expand Up @@ -904,8 +955,8 @@ public function handle_login() {
}

$user_found_with = $new_user_override_role
? 'user_created_new_user_override'
: 'user_created_users_can_register';
? 'user_created_new_user_override'
: 'user_created_users_can_register';
} else {
$tracking->record_user_event(
'sso_login_failed',
Expand Down Expand Up @@ -991,7 +1042,7 @@ public function handle_login() {

add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
wp_safe_redirect(
/** This filter is documented in core/src/wp-login.php */
/** This filter is documented in core/src/wp-login.php */
apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
);
exit;
Expand Down Expand Up @@ -1032,8 +1083,8 @@ public static function profile_page_url() {
public function build_sso_button( $args = array(), $is_primary = false ) {
$url = $this->build_sso_button_url( $args );
$classes = $is_primary
? 'jetpack-sso button button-primary'
: 'jetpack-sso button';
? 'jetpack-sso button button-primary'
: 'jetpack-sso button';

return sprintf(
'<a rel="nofollow" href="%1$s" class="%2$s">%3$s %4$s</a>',
Expand Down
Loading