Skip to content

Commit

Permalink
Add connection authentication URL. (#14004)
Browse files Browse the repository at this point in the history
* Moved the authentication request code to the Manager class.

* Fixed the redirect URL problem.

* Update packages/connection/tests/php/test_Manager.php

Co-Authored-By: Kim Brown <50059399+kbrown9@users.noreply.github.com>

* Connection: Fix add_filter call and change method name

* Add the priority and number of accepted args to the add_filter call
  for 'jetpack_api_url'.
* Change the method name from 'build_connect_url" to
  'get_authorization_url'.

* Added additional logic to the connect_user method.

* Modified the method to use passed user instead of current.

* Added an exit call after the redirect.
  • Loading branch information
zinigor authored and kbrown9 committed Nov 15, 2019
1 parent bcafbfb commit b171cc1
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 70 deletions.
150 changes: 90 additions & 60 deletions class.jetpack.php
Original file line number Diff line number Diff line change
Expand Up @@ -4636,86 +4636,116 @@ function build_connect_url( $raw = false, $redirect = false, $from = false, $reg
}

public static function build_authorize_url( $redirect = false, $iframe = false ) {
if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && include_once JETPACK__GLOTPRESS_LOCALES_PATH ) {
$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
}

$roles = new Roles();
$role = $roles->translate_current_user_to_role();
$signed_role = self::connection()->sign_role( $role );
add_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
add_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
add_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );

$user = wp_get_current_user();
if ( $iframe ) {
add_filter( 'jetpack_api_url', array( __CLASS__, 'filter_connect_api_iframe_url' ), 10, 2 );
}

$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
$redirect = $redirect
? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
: $jetpack_admin_page;
$c8n = self::connection();
$url = $c8n->get_authorization_url( wp_get_current_user(), $redirect );

if ( isset( $_REQUEST['is_multisite'] ) ) {
$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
remove_filter( 'jetpack_connect_request_body', array( __CLASS__, 'filter_connect_request_body' ) );
remove_filter( 'jetpack_connect_redirect_url', array( __CLASS__, 'filter_connect_redirect_url' ) );
remove_filter( 'jetpack_connect_processing_url', array( __CLASS__, 'filter_connect_processing_url' ) );

if ( $iframe ) {
remove_filter( 'jetpack_api_url', array( __CLASS__, 'filter_connect_api_iframe_url' ) );
}

$secrets = self::generate_secrets( 'authorize', false, 2 * HOUR_IN_SECONDS );
return $url;
}

/**
* Filter the type of authorization.
* 'calypso' completes authorization on wordpress.com/jetpack/connect
* while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
*
* @since 4.3.3
*
* @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
*/
$auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );
/**
* Filters the connection URL parameter array.
*
* @param Array $args default URL parameters used by the package.
* @return Array the modified URL arguments array.
*/
public static function filter_connect_request_body( $args ) {
if (
Constants::is_defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
&& include_once Constants::get_constant( 'JETPACK__GLOTPRESS_LOCALES_PATH' )
) {
$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
$args['locale'] = isset( $gp_locale ) && isset( $gp_locale->slug )
? $gp_locale->slug
: '';
}

$tracks = new Tracking();
$tracks_identity = $tracks->tracks_get_identity( get_current_user_id() );
$tracking = new Tracking();
$tracks_identity = $tracking->tracks_get_identity( $args['state'] );

$args = urlencode_deep(
$args = array_merge(
$args,
array(
'response_type' => 'code',
'client_id' => Jetpack_Options::get_option( 'id' ),
'redirect_uri' => add_query_arg(
array(
'action' => 'authorize',
'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
'redirect' => urlencode( $redirect ),
),
esc_url( admin_url( 'admin.php?page=jetpack' ) )
),
'state' => $user->ID,
'scope' => $signed_role,
'user_email' => $user->user_email,
'user_login' => $user->user_login,
'is_active' => self::is_active(),
'jp_version' => JETPACK__VERSION,
'auth_type' => $auth_type,
'secret' => $secrets['secret_1'],
'locale' => ( isset( $gp_locale ) && isset( $gp_locale->slug ) ) ? $gp_locale->slug : '',
'blogname' => get_option( 'blogname' ),
'site_url' => site_url(),
'home_url' => home_url(),
'site_icon' => get_site_icon_url(),
'site_lang' => get_locale(),
'_ui' => $tracks_identity['_ui'],
'_ut' => $tracks_identity['_ut'],
'site_created' => self::connection()->get_assumed_site_creation_date(),
'_ui' => $tracks_identity['_ui'],
'_ut' => $tracks_identity['_ut'],
)
);

self::apply_activation_source_to_args( $args );

$connection = self::connection();

$calypso_env = self::get_calypso_env();

if ( ! empty( $calypso_env ) ) {
$args['calypso_env'] = $calypso_env;
}

$api_url = $iframe ? $connection->api_url( 'authorize_iframe' ) : $connection->api_url( 'authorize' );
return $args;
}

/**
* Filters the URL that will process the connection data. It can be different from the URL
* that we send the user to after everything is done.
*
* @param String $processing_url the default redirect URL used by the package.
* @return String the modified URL.
*/
public static function filter_connect_processing_url( $processing_url ) {
$processing_url = admin_url( 'admin.php?page=jetpack' ); // Making PHPCS happy.
return $processing_url;
}

/**
* Filters the redirection URL that is used for connect requests. The redirect
* URL should return the user back to the Jetpack console.
*
* @param String $redirect the default redirect URL used by the package.
* @return String the modified URL.
*/
public static function filter_connect_redirect_url( $redirect ) {
$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
$redirect = $redirect
? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
: $jetpack_admin_page;

if ( isset( $_REQUEST['is_multisite'] ) ) {
$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
}

return $redirect;
}

/**
* Filters the API URL that is used for connect requests. The method
* intercepts only the authorize URL and replaces it with another if needed.
*
* @param String $api_url the default redirect API URL used by the package.
* @param String $relative_url the path of the URL that's being used.
* @return String the modified URL.
*/
public static function filter_connect_api_iframe_url( $api_url, $relative_url ) {

// Short-circuit on anything that is not related to connect requests.
if ( 'authorize' !== $relative_url ) {
return $api_url;
}

$c8n = self::connection();

return add_query_arg( $args, $api_url );
return $c8n->api_url( 'authorize_iframe' );
}

/**
Expand Down
130 changes: 120 additions & 10 deletions packages/connection/src/class-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,38 @@ public function is_connection_owner( $user_id = false ) {
return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && $user_id === $user_token->external_user_id;
}

/**
* Connects the user with a specified ID to a WordPress.com user using the
* remote login flow.
*
* @access public
*
* @param Integer $user_id (optional) the user identifier, defaults to current user.
* @param String $redirect_url the URL to redirect the user to for processing, defaults to
* admin_url().
* @return WP_Error only in case of a failed user lookup.
*/
public function connect_user( $user_id = null, $redirect_url = null ) {
$user = null;
if ( null === $user_id ) {
$user = wp_get_current_user();
} else {
$user = get_user_by( 'ID', $user_id );
}

if ( empty( $user ) ) {
return new \WP_Error( 'user_not_found', 'Attempting to connect a non-existent user.' );
}

if ( null === $redirect_url ) {
$redirect_url = admin_url();
}

// Using wp_redirect intentionally because we're redirecting outside.
wp_redirect( $this->get_authorization_url( $user ) ); // phpcs:ignore WordPress.Security.SafeRedirect
exit();
}

/**
* Unlinks the current user from the linked WordPress.com user.
*
Expand Down Expand Up @@ -1383,18 +1415,96 @@ public function handle_authorization() {

/**
* Builds a URL to the Jetpack connection auth page.
* This needs rethinking.
*
* @param bool $raw If true, URL will not be escaped.
* @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
* If string, will be a custom redirect.
* @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
* @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0.
*
* @return string Connect URL
* @param WP_User $user (optional) defaults to the current logged in user.
* @param String $redirect (optional) a redirect URL to use instead of the default.
* @return string Connect URL.
*/
public function build_connect_url( $raw, $redirect, $from, $register ) {
return array( $raw, $redirect, $from, $register );
public function get_authorization_url( $user = null, $redirect = null ) {

if ( empty( $user ) ) {
$user = wp_get_current_user();
}

$roles = new Roles();
$role = $roles->translate_user_to_role( $user );
$signed_role = $this->sign_role( $role );

/**
* Filter the URL of the first time the user gets redirected back to your site for connection
* data processing.
*
* @since 8.0.0
*
* @param string $redirect_url Defaults to the site admin URL.
*/
$processing_url = apply_filters( 'jetpack_connect_processing_url', admin_url( 'admin.php' ) );

/**
* Filter the URL to redirect the user back to when the authorization process
* is complete.
*
* @since 8.0.0
*
* @param string $redirect_url Defaults to the site URL.
*/
$redirect = apply_filters( 'jetpack_connect_redirect_url', $redirect );

$secrets = $this->generate_secrets( 'authorize', $user->ID, 2 * HOUR_IN_SECONDS );

/**
* Filter the type of authorization.
* 'calypso' completes authorization on wordpress.com/jetpack/connect
* while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
*
* @since 4.3.3
*
* @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
*/
$auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );

/**
* Filters the user connection request data for additional property addition.
*
* @since 8.0.0
*
* @param Array $request_data request data.
*/
$body = apply_filters(
'jetpack_connect_request_body',
array(
'response_type' => 'code',
'client_id' => \Jetpack_Options::get_option( 'id' ),
'redirect_uri' => add_query_arg(
array(
'action' => 'authorize',
'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
'redirect' => rawurlencode( $redirect ),
),
esc_url( $processing_url )
),
'state' => $user->ID,
'scope' => $signed_role,
'user_email' => $user->user_email,
'user_login' => $user->user_login,
'is_active' => $this->is_active(),
'jp_version' => Constants::get_constant( 'JETPACK__VERSION' ),
'auth_type' => $auth_type,
'secret' => $secrets['secret_1'],
'blogname' => get_option( 'blogname' ),
'site_url' => site_url(),
'home_url' => home_url(),
'site_icon' => get_site_icon_url(),
'site_lang' => get_locale(),
'site_created' => $this->get_assumed_site_creation_date(),
)
);

$body = $this->apply_activation_source_to_args( urlencode_deep( $body ) );

$api_url = $this->api_url( 'authorize' );

return add_query_arg( $body, $api_url );
}

/**
Expand Down
12 changes: 12 additions & 0 deletions packages/connection/tests/php/test_Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ function( $filter_name, $return_value ) {
);

$this->apply_filters = $builder->build();

$builder = new MockBuilder();
$builder->setNamespace( __NAMESPACE__ )
->setName( 'wp_redirect' )
->setFunction(
function( $url ) {
$this->arguments_stack['wp_redirect'] [] = [ $url ];
return true;
}
);

$this->wp_redirect = $builder->build();
}

public function tearDown() {
Expand Down

0 comments on commit b171cc1

Please sign in to comment.