diff --git a/js/src/constants.js b/js/src/constants.js index 305d374e31..d7e8387070 100644 --- a/js/src/constants.js +++ b/js/src/constants.js @@ -127,13 +127,6 @@ export const ASSET_FORM_KEY = { ...ASSET_GROUP_KEY, }; -export const GOOGLE_WPCOM_APP_CONNECTED_STATUS = { - APPROVED: 'approved', - DISAPPROVED: 'disapproved', - ERROR: 'error', - DISABLED: 'disabled', -}; - export const APP_RATINGS_BANNER_CONTEXT = 'app_ratings_banner'; export const PRICE_BENCHMARK_CHART_COLORS = { diff --git a/js/src/data/actions.js b/js/src/data/actions.js index 2cbd2ffcc0..725ce620a8 100644 --- a/js/src/data/actions.js +++ b/js/src/data/actions.js @@ -2,7 +2,6 @@ * External dependencies */ import { apiFetch } from '@wordpress/data-controls'; -import { addQueryArgs } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; /** @@ -432,21 +431,6 @@ export function* fetchGoogleAccount() { } } -/** - * Fetch the URL for the user to grant Google's WPCOM app access to WooCommerce product data etc. - * - * @param {'settings'|'setup-mc'} nextPageName The name of the next page to redirect to after authorization. - * @return {string} The URL for the user to continue authorization. - * @throws Will throw an error if the request failed. - */ -export function* fetchWPComAppAuthorizationUrl( nextPageName ) { - const query = { next_page_name: nextPageName }; - const path = addQueryArgs( `${ API_NAMESPACE }/rest-api/authorize`, query ); - - const response = yield apiFetch( { path } ); - return response.auth_url; -} - export function receiveGoogleAccountAccess( data ) { return { type: TYPES.RECEIVE_ACCOUNTS_GOOGLE_ACCESS, @@ -585,13 +569,6 @@ export function* disconnectAllAccounts() { type: TYPES.DISCONNECT_ACCOUNTS_ALL, }; } catch ( error ) { - // Skip any error related to revoking WPCOM token. - if ( error.errors[ `${ API_NAMESPACE }/rest-api/authorize` ] ) { - return { - type: TYPES.DISCONNECT_ACCOUNTS_ALL, - }; - } - handleApiError( error, __( diff --git a/js/src/data/test/disconnectAllAccounts.test.js b/js/src/data/test/disconnectAllAccounts.test.js index d871ebe2b3..2a366fab11 100644 --- a/js/src/data/test/disconnectAllAccounts.test.js +++ b/js/src/data/test/disconnectAllAccounts.test.js @@ -35,29 +35,7 @@ describe( 'Disconnect All Accounts', () => { } ); } ); - it( 'Ignore the error if its thrown from the authorize endpoint', async () => { - mockFetch.mockRejectedValue( { - errors: { - [ `${ API_NAMESPACE }/rest-api/authorize` ]: { - message: - 'No token found associated with the client ID and user', - }, - }, - } ); - - const { result } = renderHook( () => useAppDispatch() ); - - const response = await result.current.disconnectAllAccounts(); - - expect( mockFetch ).toHaveBeenCalledTimes( 1 ); - expect( mockFetch ).toHaveBeenCalledWith( { - path: `${ API_NAMESPACE }/connections`, - method: 'DELETE', - } ); - expect( response ).toEqual( { type: 'DISCONNECT_ACCOUNTS_ALL' } ); - } ); - - it( 'Throw the error if it is not related to the authorize endpoint', async () => { + it( 'Throws the error when the request fails', async () => { mockFetch.mockRejectedValue( { errors: { [ `${ API_NAMESPACE }/ads/connection` ]: { diff --git a/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.js b/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.js deleted file mode 100644 index 13ecdee295..0000000000 --- a/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * External dependencies - */ -import { useCallback, useEffect } from '@wordpress/element'; -import { getQuery, getNewPath, getHistory } from '@woocommerce/navigation'; - -/** - * Internal dependencies - */ -import { GOOGLE_WPCOM_APP_CONNECTED_STATUS } from '~/constants'; -import { useAppDispatch } from '~/data'; -import { API_NAMESPACE } from '~/data/constants'; -import useApiFetchCallback from '~/hooks/useApiFetchCallback'; - -/** - * A hook that calls an API to update Google WPCOM app connected status. - * - * At the end of the authorization for granting access to Google WPCOM app, Google will - * redirect back to the merchant site, either the settings page or onboarding setup account page. - * They will add a query param `google_wpcom_app_status` to the URL, we will store this status to - * the DB by calling an API `PUT /wc/gla/rest-api/authorize`. - */ -const useUpdateRestAPIAuthorizeStatusByUrlQuery = () => { - const { google_wpcom_app_status: googleWPCOMAppStatus, nonce } = getQuery(); - const { invalidateResolution } = useAppDispatch(); - - const path = `${ API_NAMESPACE }/rest-api/authorize`; - const [ fetchUpdateRestAPIAuthorize ] = useApiFetchCallback( { - path, - method: 'PUT', - } ); - - const handleUpdateRestAPIAuthorize = useCallback( async () => { - try { - await fetchUpdateRestAPIAuthorize( { - data: { - status: googleWPCOMAppStatus, - nonce, - }, - } ); - - // Refetch Google MC account so we can get the latest gla_wpcom_rest_api_status. - invalidateResolution( 'getGoogleMCAccount', [] ); - } catch ( e ) { - // Only show in the console when failed to update rest API authorize status - // since the user doesn't need to know about it. - // eslint-disable-next-line no-console - console.error( e.message ); - } - - // Clean up authorization URL queries anyway - const url = getNewPath( { - google_wpcom_app_status: undefined, - nonce: undefined, - } ); - getHistory().replace( url ); - }, [ - fetchUpdateRestAPIAuthorize, - googleWPCOMAppStatus, - invalidateResolution, - nonce, - ] ); - - useEffect( () => { - async function updateStatus() { - await handleUpdateRestAPIAuthorize( googleWPCOMAppStatus ); - } - if ( - Object.values( GOOGLE_WPCOM_APP_CONNECTED_STATUS ).includes( - googleWPCOMAppStatus - ) - ) { - updateStatus(); - } - }, [ googleWPCOMAppStatus, handleUpdateRestAPIAuthorize ] ); -}; - -export default useUpdateRestAPIAuthorizeStatusByUrlQuery; diff --git a/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.test.js b/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.test.js deleted file mode 100644 index e7eef9d7a0..0000000000 --- a/js/src/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery.test.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * External dependencies - */ -import { renderHook, waitFor } from '@testing-library/react'; -import { getQuery, getHistory } from '@woocommerce/navigation'; - -/** - * Internal dependencies - */ -import useUpdateRestAPIAuthorizeStatusByUrlQuery from './useUpdateRestAPIAuthorizeStatusByUrlQuery'; -import useApiFetchCallback from '~/hooks/useApiFetchCallback'; - -jest.mock( '@woocommerce/navigation' ); -jest.mock( '~/hooks/useApiFetchCallback' ); - -describe( 'useUpdateRestAPIAuthorizeStatusByUrlQuery', () => { - let historyReplace; - let fetchUpdateRestAPIAuthorize; - let consoleErrorSpy; - - beforeEach( () => { - historyReplace = jest.fn().mockName( 'getHistory().replace' ); - getHistory.mockReturnValue( { replace: historyReplace } ); - - fetchUpdateRestAPIAuthorize = jest.fn(); - useApiFetchCallback.mockImplementation( () => [ - fetchUpdateRestAPIAuthorize, - ] ); - consoleErrorSpy = jest.spyOn( global.console, 'error' ); - consoleErrorSpy.mockImplementation( jest.fn() ); - } ); - - afterEach( () => { - consoleErrorSpy.mockRestore(); - fetchUpdateRestAPIAuthorize.mockRestore(); - } ); - - it( 'should call fetchUpdateRestAPIAuthorize', () => { - getQuery.mockReturnValue( { - google_wpcom_app_status: 'approved', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 1 ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledWith( { - data: { - status: 'approved', - nonce: 'nonce-123', - }, - } ); - } ); - - it( 'should print console error if fetchUpdateRestAPIAuthorize throws error', () => { - fetchUpdateRestAPIAuthorize.mockImplementation( () => { - throw new Error( 'Nonces mismatch' ); - } ); - getQuery.mockReturnValue( { - google_wpcom_app_status: 'approved', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( consoleErrorSpy ).toHaveBeenCalledWith( 'Nonces mismatch' ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 1 ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledWith( { - data: { - status: 'approved', - nonce: 'nonce-123', - }, - } ); - } ); - - it( 'should call fetchUpdateRestAPIAuthorize if query param is approved', async () => { - getQuery.mockReturnValue( { - google_wpcom_app_status: 'approved', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 1 ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledWith( { - data: { - status: 'approved', - nonce: 'nonce-123', - }, - } ); - await waitFor( () => { - expect( historyReplace ).toHaveBeenCalledTimes( 1 ); - } ); - } ); - - it( 'should call fetchUpdateRestAPIAuthorize if query param is disapproved', async () => { - getQuery.mockReturnValue( { - google_wpcom_app_status: 'disapproved', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 1 ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledWith( { - data: { - status: 'disapproved', - nonce: 'nonce-123', - }, - } ); - await waitFor( () => { - expect( historyReplace ).toHaveBeenCalledTimes( 1 ); - } ); - } ); - - it( 'should call fetchUpdateRestAPIAuthorize if query param is error', async () => { - getQuery.mockReturnValue( { - google_wpcom_app_status: 'error', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 1 ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledWith( { - data: { - status: 'error', - nonce: 'nonce-123', - }, - } ); - await waitFor( () => { - expect( historyReplace ).toHaveBeenCalledTimes( 1 ); - } ); - } ); - - it( 'should not call fetchUpdateRestAPIAuthorize if query param is unknown', () => { - getQuery.mockReturnValue( { - google_wpcom_app_status: 'does-not-exist', - nonce: 'nonce-123', - } ); - renderHook( () => useUpdateRestAPIAuthorizeStatusByUrlQuery() ); - expect( fetchUpdateRestAPIAuthorize ).toHaveBeenCalledTimes( 0 ); - } ); -} ); diff --git a/js/src/pages/settings/index.js b/js/src/pages/settings/index.js index 0c9bcc6da4..32f1198e1f 100644 --- a/js/src/pages/settings/index.js +++ b/js/src/pages/settings/index.js @@ -10,7 +10,6 @@ import { getQuery, getHistory } from '@woocommerce/navigation'; import { API_RESPONSE_CODES } from '~/constants'; import useMenuEffect from '~/hooks/useMenuEffect'; import useGoogleAccount from '~/hooks/useGoogleAccount'; -import useUpdateRestAPIAuthorizeStatusByUrlQuery from '~/hooks/useUpdateRestAPIAuthorizeStatusByUrlQuery'; import { subpaths, getReconnectAccountUrl } from '~/utils/urls'; import { ContactInformationPreview } from '~/components/contact-information'; import TargetAudienceSection from '~/components/target-audience-section'; @@ -44,8 +43,6 @@ const Settings = () => { // Make the component highlight GLA entry in the WC legacy menu. useMenuEffect(); - useUpdateRestAPIAuthorizeStatusByUrlQuery(); - const { google } = useGoogleAccount(); const isReconnectGooglePage = subpath === subpaths.reconnectGoogleAccount; const { hasFinishedResolution, hasGoogleMCConnection } = diff --git a/src/API/Google/Middleware.php b/src/API/Google/Middleware.php index 1ebbb3584e..dcfff3cc3e 100644 --- a/src/API/Google/Middleware.php +++ b/src/API/Google/Middleware.php @@ -538,19 +538,6 @@ protected function get_server_url( string $name = '' ): string { return $name ? trailingslashit( $url ) . $name : $url; } - /** - * Get the Google Shopping Data Integration auth endpoint URL - * - * @return string - */ - public function get_sdi_auth_endpoint(): string { - return $this->container->get( 'connect_server_root' ) - . 'google/google-sdi/v1/credentials/partners/WOO_COMMERCE/merchants/' - . $this->strip_url_protocol( $this->get_site_url() ) - . '/oauth/redirect:generate' - . '?merchant_id=' . $this->options->get_merchant_id(); - } - /** * Get the URL for the Google SDI merchant update endpoint. * @@ -701,47 +688,6 @@ public function is_subaccount(): bool { return boolval( $is_subaccount ); } - /** - * Performs a request to Google Shopping Data Integration (SDI) to get required information in order to form an auth URL. - * - * @return array An array with the JSON response from the WCS server. - * @throws NotFoundExceptionInterface When the container was not found. - * @throws ContainerExceptionInterface When an error happens while retrieving the container. - * @throws Exception When the response status is not successful. - * @see google-sdi in google/services inside WCS - */ - public function get_sdi_auth_params() { - try { - /** @var Client $client */ - $client = $this->container->get( Client::class ); - $result = $client->get( $this->get_sdi_auth_endpoint() ); - $response = json_decode( $result->getBody()->getContents(), true ); - - if ( 200 !== $result->getStatusCode() ) { - do_action( - 'woocommerce_gla_partner_app_auth_failure', - [ - 'error' => 'response', - 'response' => $response, - ] - ); - do_action( 'woocommerce_gla_guzzle_invalid_response', $response, __METHOD__ ); - $error = $response['message'] ?? __( 'Invalid response authenticating partner app.', 'google-listings-and-ads' ); - throw new Exception( $error, $result->getStatusCode() ); - } - - return $response; - - } catch ( ClientExceptionInterface $e ) { - do_action( 'woocommerce_gla_guzzle_client_exception', $e, __METHOD__ ); - - throw new Exception( - $this->client_exception_message( $e, __( 'Error authenticating Google Partner APP.', 'google-listings-and-ads' ) ), - $e->getCode() - ); - } - } - /** * Fetch incentive credits from the Google Ads API. * diff --git a/src/API/Site/Controllers/DisconnectController.php b/src/API/Site/Controllers/DisconnectController.php index 78313f87fd..c441f671c9 100644 --- a/src/API/Site/Controllers/DisconnectController.php +++ b/src/API/Site/Controllers/DisconnectController.php @@ -46,7 +46,6 @@ protected function get_disconnect_callback(): callable { 'mc/connection', 'google/connect', 'jetpack/connect', - 'rest-api/authorize', 'youtube/connection', 'google/onboarding/complete', ]; diff --git a/src/API/Site/Controllers/RestAPI/AuthController.php b/src/API/Site/Controllers/RestAPI/AuthController.php deleted file mode 100644 index 38bb9eecec..0000000000 --- a/src/API/Site/Controllers/RestAPI/AuthController.php +++ /dev/null @@ -1,215 +0,0 @@ - '/google/setup-mc', - 'settings' => '/google/settings', - ]; - - /** - * AuthController constructor. - * - * @param RESTServer $server - * @param OAuthService $oauth_service - * @param AccountService $account_service - */ - public function __construct( RESTServer $server, OAuthService $oauth_service, AccountService $account_service ) { - parent::__construct( $server ); - $this->oauth_service = $oauth_service; - $this->account_service = $account_service; - } - - /** - * Registers the routes for the objects of the controller. - */ - public function register_routes() { - $this->register_route( - 'rest-api/authorize', - [ - [ - 'methods' => TransportMethods::READABLE, - 'callback' => $this->get_authorize_callback(), - 'permission_callback' => $this->get_permission_callback(), - 'args' => $this->get_auth_params(), - ], - [ - 'methods' => TransportMethods::DELETABLE, - 'callback' => $this->delete_authorize_callback(), - 'permission_callback' => $this->get_permission_callback(), - ], - [ - 'methods' => TransportMethods::EDITABLE, - 'callback' => $this->get_update_authorize_callback(), - 'permission_callback' => $this->get_permission_callback(), - 'args' => $this->get_update_authorize_params(), - ], - 'schema' => $this->get_api_response_schema_callback(), - ] - ); - } - - /** - * Get the callback function for the authorization request. - * - * @return callable - */ - protected function get_authorize_callback(): callable { - return function ( Request $request ) { - try { - $next = $request->get_param( 'next_page_name' ); - $path = self::NEXT_PATH_MAPPING[ $next ]; - $auth_url = $this->oauth_service->get_auth_url( $path ); - - $response = [ - 'auth_url' => $auth_url, - ]; - - return $this->prepare_item_for_response( $response, $request ); - } catch ( Exception $e ) { - return $this->response_from_exception( $e ); - } - }; - } - - /** - * Get the callback function for the delete authorization request. - * - * @return callable - */ - protected function delete_authorize_callback(): callable { - return function ( Request $request ) { - try { - $this->oauth_service->revoke_wpcom_api_auth(); - return $this->prepare_item_for_response( [], $request ); - } catch ( Exception $e ) { - return $this->response_from_exception( $e ); - } - }; - } - - /** - * Get the callback function for the update authorize request. - * - * @return callable - */ - protected function get_update_authorize_callback(): callable { - return function ( Request $request ) { - try { - $this->account_service->update_wpcom_api_authorization( $request['status'], $request['nonce'] ); - return [ 'status' => $request['status'] ]; - } catch ( Exception $e ) { - return $this->response_from_exception( $e ); - } - }; - } - - /** - * Get the query params for the authorize request. - * - * @return array - */ - protected function get_auth_params(): array { - return [ - 'next_page_name' => [ - 'description' => __( 'Indicates the next page name mapped to the redirect URL when redirected back from Google WPCOM App authorization.', 'google-listings-and-ads' ), - 'type' => 'string', - 'default' => array_key_first( self::NEXT_PATH_MAPPING ), - 'enum' => array_keys( self::NEXT_PATH_MAPPING ), - 'validate_callback' => 'rest_validate_request_arg', - ], - ]; - } - - /** - * Get the query params for the update authorize request. - * - * @return array - */ - protected function get_update_authorize_params(): array { - return [ - 'status' => [ - 'description' => __( 'The status of the merchant granting access to Google\'s WPCOM app', 'google-listings-and-ads' ), - 'type' => 'string', - 'enum' => OAuthService::ALLOWED_STATUSES, - 'validate_callback' => 'rest_validate_request_arg', - 'required' => true, - ], - 'nonce' => [ - 'description' => __( 'The nonce provided by Google in the URL query parameter when Google redirects back to merchant\'s site', 'google-listings-and-ads' ), - 'type' => 'string', - 'validate_callback' => 'rest_validate_request_arg', - 'required' => true, - ], - ]; - } - - /** - * Get the item schema properties for the controller. - * - * @return array - */ - protected function get_schema_properties(): array { - return [ - 'auth_url' => [ - 'type' => 'string', - 'description' => __( 'The authorization URL for granting access to Google WPCOM App.', 'google-listings-and-ads' ), - 'context' => [ 'view' ], - ], - 'status' => [ - 'type' => 'string', - 'description' => __( 'The status of the merchant granting access to Google\'s WPCOM app', 'google-listings-and-ads' ), - 'enum' => OAuthService::ALLOWED_STATUSES, - 'context' => [ 'view' ], - ], - ]; - } - - /** - * Get the item schema name for the controller. - * - * Used for building the API response schema. - * - * @return string - */ - protected function get_schema_title(): string { - return 'rest_api_authorize'; - } -} diff --git a/src/API/WP/OAuthService.php b/src/API/WP/OAuthService.php index ffd93cd3a5..e85f203d0d 100644 --- a/src/API/WP/OAuthService.php +++ b/src/API/WP/OAuthService.php @@ -3,20 +3,7 @@ namespace Automattic\WooCommerce\GoogleListingsAndAds\API\WP; -use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Middleware; -use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\AccountService; -use Automattic\WooCommerce\GoogleListingsAndAds\HelperTraits\Utilities as UtilitiesTrait; use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service; -use Automattic\WooCommerce\GoogleListingsAndAds\Internal\ContainerAwareTrait; -use Automattic\WooCommerce\GoogleListingsAndAds\Internal\Interfaces\ContainerAwareInterface; -use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface; -use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait; -use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface; -use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Deactivateable; -use Automattic\WooCommerce\GoogleListingsAndAds\Proxies\Jetpack; -use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\Psr\Container\ContainerExceptionInterface; -use Jetpack_Options; -use Exception; defined( 'ABSPATH' ) || exit; @@ -27,227 +14,8 @@ * @since 2.8.0 * @package Automattic\WooCommerce\GoogleListingsAndAds\API\WP */ -class OAuthService implements Service, OptionsAwareInterface, Deactivateable, ContainerAwareInterface { +class OAuthService implements Service { - use OptionsAwareTrait; - use UtilitiesTrait; - use ContainerAwareTrait; - - public const WPCOM_API_URL = 'https://public-api.wordpress.com'; - public const AUTH_URL = '/oauth2/authorize'; - public const RESPONSE_TYPE = 'code'; - public const SCOPE = 'wc-partner-access'; - - public const STATUS_APPROVED = 'approved'; - public const STATUS_DISAPPROVED = 'disapproved'; - public const STATUS_ERROR = 'error'; - - public const ALLOWED_STATUSES = [ - self::STATUS_APPROVED, - self::STATUS_DISAPPROVED, - self::STATUS_ERROR, - ]; - - /** - * Returns WordPress.com OAuth authorization URL. - * https://developer.wordpress.com/docs/oauth2/ - * - * The full auth URL example: - * - * https://public-api.wordpress.com/oauth2/authorize? - * client_id=CLIENT_ID& - * redirect_uri=PARTNER_REDIRECT_URL& - * response_type=code& - * blog=BLOD_ID& - * scope=wc-partner-access& - * state=URL_SAFE_BASE64_ENCODED_STRING - * - * State is a URL safe base64 encoded string. - * E.g. - * state=bm9uY2UtMTIzJnJlZGlyZWN0X3VybD1odHRwcyUzQSUyRiUyRm1lcmNoYW50LXNpdGUuZXhhbXBsZS5jb20lMkZ3cC1hZG1pbiUyRmFkbWluLnBocCUzRnBhZ2UlM0R3Yy1hZG1pbiUyNnBhdGglM0QlMkZnb29nbGUlMkZzZXR1cC1tYw - * - * The decoded content of state is a URL query string where the value of its parameter "store_url" is being URL encoded. - * E.g. - * nonce=nonce-123&store_url=https%3A%2F%2Fmerchant-site.example.com%2Fwp-admin%2Fadmin.php%3Fpage%3Dwc-admin%26path%3D%2Fgoogle%2Fsetup-mc - * - * where its URL decoded version is: - * nonce=nonce-123&store_url=https://merchant-site.example.com/wp-admin/admin.php?page=wc-admin&path=/google/setup-mc - * - * @param string $path A URL parameter for the path within GL&A page, which will be added in the merchant redirect URL. - * - * @return string Auth URL. - * @throws ContainerExceptionInterface When get_data_from_google throws an exception. - */ - public function get_auth_url( string $path ): string { - $google_data = $this->get_data_from_google(); - - $store_url = urlencode_deep( admin_url( "admin.php?page=wc-admin&path={$path}" ) ); - - $state = $this->base64url_encode( - build_query( - [ - 'nonce' => $google_data['nonce'], - 'store_url' => $store_url, - ] - ) - ); - - $auth_url = esc_url_raw( - add_query_arg( - [ - 'blog' => Jetpack_Options::get_option( 'id' ), - 'client_id' => $google_data['client_id'], - 'redirect_uri' => $google_data['redirect_uri'], - 'response_type' => self::RESPONSE_TYPE, - 'scope' => self::SCOPE, - 'state' => $state, - ], - $this->get_wpcom_api_url( self::AUTH_URL ) - ) - ); - - return $auth_url; - } - - /** - * Get a WPCOM REST API URl concatenating the endpoint with the API Domain - * - * @param string $endpoint The endpoint to get the URL for - * - * @return string The WPCOM endpoint with the domain. - */ - protected function get_wpcom_api_url( string $endpoint ): string { - return self::WPCOM_API_URL . $endpoint; - } - - /** - * Calls an API by Google via WCS to get required information in order to form an auth URL. - * - * @return array{client_id: string, redirect_uri: string, nonce: string} An associative array contains required information that is retrieved from Google. - * client_id: Google's WPCOM app client ID, will be used to form the authorization URL. - * redirect_uri: A Google's URL that will be redirected to when the merchant approve the app access. Note that it needs to be matched with the Google WPCOM app client settings. - * nonce: A string returned by Google that we will put it in the auth URL and the redirect_uri. Google will use it to verify the call. - * @throws ContainerExceptionInterface When get_sdi_auth_params throws an exception. - */ - protected function get_data_from_google(): array { - /** @var Middleware $middleware */ - $middleware = $this->container->get( Middleware::class ); - $response = $middleware->get_sdi_auth_params(); - $nonce = $response['nonce']; - $this->options->update( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE, $nonce ); - return [ - 'client_id' => $response['clientId'], - 'redirect_uri' => $response['redirectUri'], - 'nonce' => $nonce, - ]; - } - - /** - * Perform a remote request for revoking OAuth access for the current user. - * - * @return string The body of the response - * @throws Exception If the remote request fails. - */ - public function revoke_wpcom_api_auth(): string { - $args = [ - 'method' => 'DELETE', - 'timeout' => 30, - 'url' => $this->get_wpcom_api_url( '/wpcom/v2/sites/' . Jetpack_Options::get_option( 'id' ) . '/wc/partners/google/revoke-token' ), - 'user_id' => get_current_user_id(), - ]; - - $request = $this->container->get( Jetpack::class )->remote_request( $args ); - - if ( is_wp_error( $request ) ) { - - /** - * When the WPCOM token has been revoked with errors. - * - * @event revoke_wpcom_api_authorization - * @property int status The status of the request. - * @property string error The error message. - * @property int|null blog_id The blog ID. - */ - do_action( - 'woocommerce_gla_track_event', - 'revoke_wpcom_api_authorization', - [ - 'status' => 400, - 'error' => $request->get_error_message(), - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - throw new Exception( $request->get_error_message(), 400 ); - } else { - $body = wp_remote_retrieve_body( $request ); - $status = wp_remote_retrieve_response_code( $request ); - - if ( ! $status || $status !== 200 ) { - $data = json_decode( $body, true ); - $message = $data['message'] ?? 'Error revoking access to WPCOM.'; - - /** - * - * When the WPCOM token has been revoked with errors. - * - * @event revoke_wpcom_api_authorization - * @property int status The status of the request. - * @property string error The error message. - * @property int|null blog_id The blog ID. - */ - do_action( - 'woocommerce_gla_track_event', - 'revoke_wpcom_api_authorization', - [ - 'status' => $status, - 'error' => $message, - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - throw new Exception( $message, $status ); - } - - /** - * When the WPCOM token has been revoked successfully. - * - * @event revoke_wpcom_api_authorization - * @property int status The status of the request. - * @property int|null blog_id The blog ID. - */ - do_action( - 'woocommerce_gla_track_event', - 'revoke_wpcom_api_authorization', - [ - 'status' => 200, - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->container->get( AccountService::class )->reset_wpcom_api_authorization_data(); - return $body; - } - } - - /** - * Deactivate the service. - * - * Revoke token on deactivation. - */ - public function deactivate(): void { - // Try to revoke the token on deactivation. If no token is available, it will throw an exception which we can ignore. - try { - // Attempt to revoke the WPCOM token if setup is complete. - if ( $this->is_jetpack_connected() ) { - $this->revoke_wpcom_api_auth(); - } - } catch ( Exception $e ) { - do_action( - 'woocommerce_gla_error', - sprintf( 'Error revoking the WPCOM token: %s', $e->getMessage() ), - __METHOD__ - ); - } - } + public const STATUS_APPROVED = 'approved'; + public const STATUS_ERROR = 'error'; } diff --git a/src/Internal/DependencyManagement/CoreServiceProvider.php b/src/Internal/DependencyManagement/CoreServiceProvider.php index 8b2bd304d2..280b12495a 100644 --- a/src/Internal/DependencyManagement/CoreServiceProvider.php +++ b/src/Internal/DependencyManagement/CoreServiceProvider.php @@ -23,7 +23,6 @@ use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Settings as GoogleSettings; use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\AdsAssetGroupAsset; use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\RESTControllers; -use Automattic\WooCommerce\GoogleListingsAndAds\API\WP\OAuthService; use Automattic\WooCommerce\GoogleListingsAndAds\Assets\AssetsHandler; use Automattic\WooCommerce\GoogleListingsAndAds\Assets\AssetsHandlerInterface; use Automattic\WooCommerce\GoogleListingsAndAds\Coupon\CouponHelper; @@ -192,7 +191,6 @@ class CoreServiceProvider extends AbstractServiceProvider { AdsAccountService::class => true, MerchantAccountService::class => true, MarketingChannelRegistrar::class => true, - OAuthService::class => true, WPCLIMigrationGTIN::class => true, OnboardingCompleted::class => true, ServiceBasedMerchantState::class => true, @@ -240,9 +238,6 @@ public function register(): void { // Set up MerchantCenter service, and inflect classes that need it. $this->share_with_tags( MerchantCenterService::class ); - // Set up OAuthService service. - $this->share_with_tags( OAuthService::class ); - $this->getContainer() ->inflector( MerchantCenterAwareInterface::class ) ->invokeMethod( 'set_merchant_center_object', [ MerchantCenterService::class ] ); diff --git a/src/Internal/DependencyManagement/RESTServiceProvider.php b/src/Internal/DependencyManagement/RESTServiceProvider.php index 8a9998ec11..f9b22c83d2 100644 --- a/src/Internal/DependencyManagement/RESTServiceProvider.php +++ b/src/Internal/DependencyManagement/RESTServiceProvider.php @@ -63,9 +63,7 @@ use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MerchantCenter\SyncableProductsCountController; use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MerchantCenter\TargetAudienceController; use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\MerchantCenter\PriceBenchmarksController; -use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\RestAPI\AuthController as RestAPIAuthController; use Automattic\WooCommerce\GoogleListingsAndAds\API\Site\Controllers\YouTube\AccountController as YouTubeAccountController; -use Automattic\WooCommerce\GoogleListingsAndAds\API\WP\OAuthService; use Automattic\WooCommerce\GoogleListingsAndAds\API\YouTube\Connection as YouTubeConnection; use Automattic\WooCommerce\GoogleListingsAndAds\DB\ProductFeedQueryHelper; use Automattic\WooCommerce\GoogleListingsAndAds\DB\Query\AttributeMappingRulesQuery; @@ -160,7 +158,6 @@ public function register(): void { $this->share( AttributeMappingCategoriesController::class ); $this->share( AttributeMappingSyncerController::class, ProductSyncStats::class ); $this->share( TourController::class ); - $this->share( RestAPIAuthController::class, OAuthService::class, MerchantAccountService::class ); $this->share( GTINMigrationController::class, JobRepository::class ); $this->share( PriceBenchmarksController::class ); $this->share( RecommendationsController::class, AdsAccountService::class ); diff --git a/src/MerchantCenter/AccountService.php b/src/MerchantCenter/AccountService.php index b447319549..0c5d7f9684 100644 --- a/src/MerchantCenter/AccountService.php +++ b/src/MerchantCenter/AccountService.php @@ -580,108 +580,6 @@ private function prepare_api_error_exception( Exception $e ) { ); } - /** - * Delete the option regarding WPCOM authorization - * - * @return bool - */ - public function reset_wpcom_api_authorization_data(): bool { - $this->delete_wpcom_api_auth_nonce(); - $this->delete_wpcom_api_status_transient(); - return $this->options->delete( OptionsInterface::WPCOM_REST_API_STATUS ); - } - - /** - * Update the status of the merchant granting access to Google's WPCOM app in the database. - * Before updating the status in the DB it will compare the nonce stored in the DB with the nonce passed to the API. - * - * @param string $status The status of the merchant granting access to Google's WPCOM app. - * @param string $nonce The nonce provided by Google in the URL query parameter when Google redirects back to merchant's site. - * - * @return bool - * @throws ExceptionWithResponseData If the stored nonce / nonce from query param is not provided, or the nonces mismatch. - */ - public function update_wpcom_api_authorization( string $status, string $nonce ): bool { - try { - $stored_nonce = $this->options->get( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ); - if ( empty( $stored_nonce ) ) { - throw $this->prepare_exception( - __( 'No stored nonce found in the database, skip updating auth status.', 'google-listings-and-ads' ) - ); - } - - if ( empty( $nonce ) ) { - throw $this->prepare_exception( - __( 'Nonce is not provided, skip updating auth status.', 'google-listings-and-ads' ) - ); - } - - if ( $stored_nonce !== $nonce ) { - $this->delete_wpcom_api_auth_nonce(); - throw $this->prepare_exception( - __( 'Nonces mismatch, skip updating auth status.', 'google-listings-and-ads' ) - ); - } - - $this->delete_wpcom_api_auth_nonce(); - - /** - * When the WPCOM Authorization status has been updated. - * - * @event update_wpcom_api_authorization - * @property string status The status of the request. - * @property int|null blog_id The blog ID. - */ - do_action( - 'woocommerce_gla_track_event', - 'update_wpcom_api_authorization', - [ - 'status' => $status, - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->delete_wpcom_api_status_transient(); - return $this->options->update( OptionsInterface::WPCOM_REST_API_STATUS, $status ); - } catch ( ExceptionWithResponseData $e ) { - - /** - * When the WPCOM Authorization status has been updated with errors. - * - * @event update_wpcom_api_authorization - * @property string status The status of the request. - * @property int|null blog_id The blog ID. - */ - do_action( - 'woocommerce_gla_track_event', - 'update_wpcom_api_authorization', - [ - 'status' => $e->getMessage(), - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - throw $e; - } - } - - /** - * Delete the nonce of "verifying Google is the one redirect back to merchant site and set the auth status" in the database. - * - * @return bool - */ - public function delete_wpcom_api_auth_nonce(): bool { - return $this->options->delete( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ); - } - - /** - * Deletes the transient storing the WPCOM Status data. - */ - public function delete_wpcom_api_status_transient(): void { - $transients = $this->container->get( TransientsInterface::class ); - $transients->delete( TransientsInterface::WPCOM_API_STATUS ); - } - /** * Check if the WPCOM API Status is healthy by doing a request to /wc/partners/google/remote-site-status endpoint in WPCOM. * diff --git a/src/Options/OptionsInterface.php b/src/Options/OptionsInterface.php index 49b99b6023..1e3f483f47 100644 --- a/src/Options/OptionsInterface.php +++ b/src/Options/OptionsInterface.php @@ -30,7 +30,6 @@ interface OptionsInterface { public const DB_VERSION = 'db_version'; public const FILE_VERSION = 'file_version'; public const GOOGLE_CONNECTED = 'google_connected'; - public const GOOGLE_WPCOM_AUTH_NONCE = 'google_wpcom_auth_nonce'; public const INSTALL_TIMESTAMP = 'install_timestamp'; public const INSTALL_VERSION = 'install_version'; public const JETPACK_CONNECTED = 'jetpack_connected'; @@ -95,7 +94,6 @@ interface OptionsInterface { self::UPDATE_ALL_PRODUCTS_LAST_SYNC => true, self::WP_TOS_ACCEPTED => true, self::WPCOM_REST_API_STATUS => true, - self::GOOGLE_WPCOM_AUTH_NONCE => true, self::GTIN_MIGRATION_STATUS => true, self::YOUTUBE_ORDER_IDS_CACHE => true, self::YOUTUBE_EXPORT_FILES => true, diff --git a/tests/Unit/API/Google/MiddlewareTest.php b/tests/Unit/API/Google/MiddlewareTest.php index a6f02554b4..233a2dac43 100644 --- a/tests/Unit/API/Google/MiddlewareTest.php +++ b/tests/Unit/API/Google/MiddlewareTest.php @@ -489,43 +489,6 @@ function ( $home_url ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionPara ); } - public function test_get_sdi_auth_endpoint() { - $this->assertEquals( - $this->middleware->get_sdi_auth_endpoint(), - 'https://connect-server.test/google/google-sdi/v1/credentials/partners/WOO_COMMERCE/merchants/example.org/oauth/redirect:generate?merchant_id=0' - ); - } - - public function test_get_sdi_auth_params() { - $expected_response = [ - 'clientId' => self::TEST_MERCHANT_ID, - 'redirectUri' => 'https://example.com', - 'nonce' => '123', - ]; - - $this->generate_request_mock( $expected_response ); - $this->assertEquals( $this->middleware->get_sdi_auth_params(), $expected_response ); - } - - public function test_get_sdi_auth_params_no_success() { - $this->generate_request_mock( [], 'get', 400 ); - $this->expectException( Exception::class ); - $this->expectExceptionCode( 400 ); - $this->expectExceptionMessage( 'Invalid response authenticating partner app.' ); - $this->middleware->get_sdi_auth_params(); - $this->assertEquals( 1, did_action( 'woocommerce_gla_partner_app_auth_failure' ) ); - $this->assertEquals( 1, did_action( 'woocommerce_gla_guzzle_invalid_response' ) ); - } - - public function test_get_sdi_auth_params_exception() { - $this->generate_request_mock_exception( 'Some exception.' ); - $this->expectException( Exception::class ); - $this->expectExceptionCode( 400 ); - $this->expectExceptionMessage( 'Error authenticating Google Partner APP.' ); - $this->middleware->get_sdi_auth_params(); - $this->assertEquals( 1, did_action( 'woocommerce_gla_guzzle_client_exception' ) ); - } - public function test_get_incentive_credits_success_with_ads_currency() { $this->ads->expects( $this->once() )->method( 'get_ads_currency' )->willReturn( 'GBP' ); $this->wc->expects( $this->once() )->method( 'get_base_country' )->willReturn( 'GB' ); diff --git a/tests/Unit/API/Site/Controllers/RestAPI/AuthControllerTest.php b/tests/Unit/API/Site/Controllers/RestAPI/AuthControllerTest.php deleted file mode 100644 index 2c134ccc2a..0000000000 --- a/tests/Unit/API/Site/Controllers/RestAPI/AuthControllerTest.php +++ /dev/null @@ -1,152 +0,0 @@ -oauth_service = $this->createMock( OAuthService::class ); - $this->account_service = $this->createMock( AccountService::class ); - $this->controller = new AuthController( $this->server, $this->oauth_service, $this->account_service ); - $this->controller->register(); - } - - public function test_authorize() { - $expected_auth_url = 'https://public-api.wordpress.com/oauth2/authorize'; - $expected_auth_url .= '?blog=12345'; - $expected_auth_url .= '&client_id=23456'; - $expected_auth_url .= '&redirect_uri=https://example.com'; - $expected_auth_url .= '&response_type=code'; - $expected_auth_url .= '&scope=wc-partner-access'; - $expected_auth_url .= '&state=base64_encoded_string'; - - $this->oauth_service->expects( $this->once() ) - ->method( 'get_auth_url' ) - ->willReturn( $expected_auth_url ); - - $response = $this->do_request( self::ROUTE_AUTHORIZE ); - - $this->assertEquals( - [ - 'auth_url' => $expected_auth_url, - 'status' => null, - ], - $response->get_data() - ); - - $this->assertEquals( 200, $response->get_status() ); - } - - public function test_authorize_invalid_parameter() { - $response = $this->do_request( self::ROUTE_AUTHORIZE, 'GET', [ 'next_page_name' => 'invalid' ] ); - - $this->assertEquals( 'rest_invalid_param', $response->get_data()['code'] ); - $this->assertEquals( 400, $response->get_status() ); - } - - public function test_authorize_with_error() { - $this->oauth_service->expects( $this->once() ) - ->method( 'get_auth_url' ) - ->willThrowException( new Exception( 'error', 400 ) ); - - $response = $this->do_request( self::ROUTE_AUTHORIZE ); - - $this->assertEquals( [ 'message' => 'error' ], $response->get_data() ); - $this->assertEquals( 400, $response->get_status() ); - } - - public function test_update_authorize() { - $this->account_service->expects( $this->once() ) - ->method( 'update_wpcom_api_authorization' ) - ->willReturn( true ); - - $response = $this->do_request( - self::ROUTE_AUTHORIZE, - 'POST', - [ - 'status' => 'approved', - 'nonce' => 'nonce-123', - ] - ); - - $this->assertEquals( [ 'status' => 'approved' ], $response->get_data() ); - $this->assertEquals( 200, $response->get_status() ); - } - - public function test_update_authorize_missing_status_param() { - $response = $this->do_request( self::ROUTE_AUTHORIZE, 'POST', [ 'nonce' => 'nonce-123' ] ); - - $this->assertEquals( 'Missing parameter(s): status', $response->get_data()['message'] ); - $this->assertEquals( 400, $response->get_status() ); - } - - public function test_update_authorize_missing_nonce_param() { - $response = $this->do_request( self::ROUTE_AUTHORIZE, 'POST', [ 'status' => 'approved' ] ); - - $this->assertEquals( 'Missing parameter(s): nonce', $response->get_data()['message'] ); - $this->assertEquals( 400, $response->get_status() ); - } - - public function test_update_authorize_wrong_status_param() { - $response = $this->do_request( - self::ROUTE_AUTHORIZE, - 'POST', - [ - 'status' => 'wrong-param', - 'nonce' => 'nonce-123', - ] - ); - - $this->assertEquals( 'Invalid parameter(s): status', $response->get_data()['message'] ); - $this->assertEquals( 400, $response->get_status() ); - } - - public function test_revoke_wpcom_token() { - $this->oauth_service->expects( $this->once() )->method( 'revoke_wpcom_api_auth' ); - - $response = $this->do_request( - self::ROUTE_AUTHORIZE, - 'DELETE' - ); - - $this->assertEquals( 200, $response->get_status() ); - } - - public function test_revoke_wpcom_token_with_error() { - $this->oauth_service->expects( $this->once() )->method( 'revoke_wpcom_api_auth' )->willThrowException( new Exception( 'No token found', 400 ) ); - - $response = $this->do_request( - self::ROUTE_AUTHORIZE, - 'DELETE' - ); - - $this->assertEquals( [ 'message' => 'No token found' ], $response->get_data() ); - $this->assertEquals( 400, $response->get_status() ); - } -} diff --git a/tests/Unit/API/WP/OAuthServiceTest.php b/tests/Unit/API/WP/OAuthServiceTest.php deleted file mode 100644 index f47d995ad4..0000000000 --- a/tests/Unit/API/WP/OAuthServiceTest.php +++ /dev/null @@ -1,313 +0,0 @@ -container = new Container(); - $this->middleware = $this->createMock( Middleware::class ); - $this->options = $this->createMock( OptionsInterface::class ); - $this->jp = $this->createMock( Jetpack::class ); - $this->account_service = $this->createMock( AccountService::class ); - - // Mock the Blog ID from Jetpack. - add_filter( - 'jetpack_options', - function ( $value, $name ) { - if ( $name === 'id' ) { - return self::DUMMY_BLOG_ID; - } - - return $value; - }, - 10, - 2 - ); - - // Mock admin URL. - add_filter( - 'admin_url', - function ( $url, $path ) { - return self::DUMMY_ADMIN_URL . $path; - }, - 10, - 2 - ); - - $this->container->addShared( Middleware::class, $this->middleware ); - $this->container->addShared( Jetpack::class, $this->jp ); - $this->container->addShared( AccountService::class, $this->account_service ); - $this->service = new OAuthService(); - $this->service->set_options_object( $this->options ); - $this->service->set_container( $this->container ); - } - - public function test_deactivate_does_not_call_wpcom_api_when_jetpack_not_connected() { - $this->assertInstanceOf( Deactivateable::class, $this->service ); - - $this->jp->expects( $this->never() ) - ->method( 'remote_request' ); - - $this->account_service->expects( $this->never() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->service->deactivate(); - - $this->assertEquals( 0, did_action( 'woocommerce_gla_error' ) ); - } - - public function test_deactivation_ok() { - $this->assertInstanceOf( Deactivateable::class, $this->service ); - - // Mock the options to return true for Jetpack connected. - $this->options->expects( $this->once() )->method( 'get' )->with( OptionsInterface::JETPACK_CONNECTED )->willReturn( true ); - - $this->jp->expects( $this->once() ) - ->method( 'remote_request' )->willReturn( - [ - 'body' => '{"success":true}', - 'response' => [ 'code' => 200 ], - ] - ); - - $this->account_service->expects( $this->once() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->service->deactivate(); - } - - public function test_deactivation_with_wp_error() { - $this->assertInstanceOf( Deactivateable::class, $this->service ); - - // Mock the options to return true for Jetpack connected. - $this->options->expects( $this->once() )->method( 'get' )->with( OptionsInterface::JETPACK_CONNECTED )->willReturn( true ); - - $this->jp->expects( $this->once() ) - ->method( 'remote_request' )->willReturn( new WP_Error( 'error', 'error message' ) ); - - $this->account_service->expects( $this->never() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - // The exception should be caught and ignored. - $this->service->deactivate(); - - $this->assertEquals( 1, did_action( 'woocommerce_gla_error' ) ); - } - - /** - * Test get_auth_url() function. - */ - public function test_get_auth_url() { - $client_id = '12345'; - $redirect_uri = 'https://example.com'; - $nonce = 'nonce-999'; - $blog_id = self::DUMMY_BLOG_ID; - $admin_url = self::DUMMY_ADMIN_URL; - $path = '/google/setup-mc'; - - $expected_response = [ - 'clientId' => $client_id, - 'redirectUri' => $redirect_uri, - 'nonce' => $nonce, - ]; - - $this->middleware->expects( $this->once() ) - ->method( 'get_sdi_auth_params' ) - ->willReturn( $expected_response ); - - $this->options->expects( $this->once() ) - ->method( 'update' ) - ->with( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE, $nonce ); - - $store_url = "{$admin_url}admin.php?page=wc-admin&path={$path}"; - $store_url_encoded = urlencode_deep( $store_url ); - $expected_state_raw = "nonce={$nonce}&store_url={$store_url_encoded}"; - $state = $this->base64url_encode( $expected_state_raw ); - - $expected_auth_url = 'https://public-api.wordpress.com/oauth2/authorize'; - $expected_auth_url .= "?blog={$blog_id}"; - $expected_auth_url .= "&client_id={$client_id}"; - $expected_auth_url .= "&redirect_uri={$redirect_uri}"; - $expected_auth_url .= '&response_type=code'; - $expected_auth_url .= '&scope=wc-partner-access'; - $expected_auth_url .= "&state={$state}"; - $expected_auth_url = esc_url_raw( $expected_auth_url ); - - $auth_url = $this->service->get_auth_url( $path ); - - // Compare the auth URLs. - $this->assertEquals( - $expected_auth_url, - $auth_url - ); - - // Compare the state query parameters from the auth URL. - $parsed_url = wp_parse_url( $auth_url ); - parse_str( $parsed_url['query'], $parsed_query ); - $state_raw = $this->base64url_decode( $parsed_query['state'] ); - $this->assertEquals( - $expected_state_raw, - $state_raw - ); - - // Ensure the base64 encoded state query has correct value. - parse_str( $state_raw, $parsed_state ); - $this->assertEquals( - $nonce, - $parsed_state['nonce'] - ); - $this->assertEquals( - $store_url, - $parsed_state['store_url'] - ); - } - - public function test_revoke_wpcom_api_auth() { - $this->jp->expects( $this->once() ) - ->method( 'remote_request' ) - ->willReturn( - [ - 'body' => '{"success":true}', - 'response' => [ 'code' => 200 ], - ] - ); - - $this->account_service->expects( $this->once() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->account_service->expects( $this->once() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->expect_track_event( - 'revoke_wpcom_api_authorization', - [ - 'status' => 200, - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $response = $this->service->revoke_wpcom_api_auth(); - - $this->assertEquals( '{"success":true}', $response ); - } - - public function test_revoke_wpcom_api_auth_wp_error() { - $this->jp->expects( $this->once() ) - ->method( 'remote_request' ) - ->willReturn( - new WP_Error( 'error', 'error message' ) - ); - - $this->expectException( Exception::class ); - $this->expectExceptionMessage( 'error message' ); - $this->expectExceptionCode( 400 ); - - $this->account_service->expects( $this->never() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->expect_track_event( - 'revoke_wpcom_api_authorization', - [ - 'status' => 400, - 'error' => 'error message', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->service->revoke_wpcom_api_auth(); - } - - public function test_revoke_wpcom_api_auth_status_error() { - $this->jp->expects( $this->once() ) - ->method( 'remote_request' ) - ->willReturn( - [ - 'body' => '{"message":"error message"}', - 'response' => [ 'code' => 400 ], - ] - ); - - $this->expectException( Exception::class ); - $this->expectExceptionMessage( 'error message' ); - $this->expectExceptionCode( 400 ); - - $this->account_service->expects( $this->never() ) - ->method( 'reset_wpcom_api_authorization_data' ); - - $this->expect_track_event( - 'revoke_wpcom_api_authorization', - [ - 'status' => 400, - 'error' => 'error message', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->service->revoke_wpcom_api_auth(); - } -} diff --git a/tests/Unit/MerchantCenter/AccountServiceTest.php b/tests/Unit/MerchantCenter/AccountServiceTest.php index c11367686b..ce5e8a340e 100644 --- a/tests/Unit/MerchantCenter/AccountServiceTest.php +++ b/tests/Unit/MerchantCenter/AccountServiceTest.php @@ -25,10 +25,8 @@ use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Framework\UnitTest; use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Tools\HelperTrait\MerchantTrait; use Automattic\WooCommerce\GoogleListingsAndAds\Vendor\League\Container\Container; -use Automattic\WooCommerce\GoogleListingsAndAds\Tests\Tools\HelperTrait\TrackingTrait; use Exception; use PHPUnit\Framework\MockObject\MockObject; -use Jetpack_Options; defined( 'ABSPATH' ) || exit; @@ -40,7 +38,6 @@ */ class AccountServiceTest extends UnitTest { - use TrackingTrait; use MerchantTrait; /** @var MockObject|Ads $ads */ @@ -923,128 +920,4 @@ public function test_disconnect() { $this->account->disconnect(); } - - public function test_update_wpcom_api_authorization() { - $status = 'approved'; - $nonce = 'nonce-123'; - - $this->options->expects( $this->once() ) - ->method( 'get' ) - ->with( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ) - ->willReturn( 'nonce-123' ); - - $this->options->expects( $this->once() ) - ->method( 'update' ) - ->with( OptionsInterface::WPCOM_REST_API_STATUS, 'approved' ); - - $this->options->expects( $this->once() ) - ->method( 'delete' ) - ->with( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ); - - $this->expect_track_event( - 'update_wpcom_api_authorization', - [ - 'status' => 'approved', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->account->update_wpcom_api_authorization( $status, $nonce ); - } - - public function test_update_wpcom_api_authorization_nonce_not_provided() { - $status = 'approved'; - $nonce = ''; - - $this->options->expects( $this->once() ) - ->method( 'get' ) - ->willReturn( 'nonce-123' ); - - $this->options->expects( $this->never() ) - ->method( 'update' ); - - $this->options->expects( $this->never() ) - ->method( 'delete' ); - - $this->expectException( ExceptionWithResponseData::class ); - $this->expectExceptionMessage( 'Nonce is not provided, skip updating auth status.' ); - - $this->expect_track_event( - 'update_wpcom_api_authorization', - [ - 'status' => 'Nonce is not provided, skip updating auth status.', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->account->update_wpcom_api_authorization( $status, $nonce ); - } - - public function test_update_wpcom_api_authorization_stored_nonce_not_in_db() { - $status = 'approved'; - $nonce = 'nonce-123'; - - $this->options->expects( $this->once() ) - ->method( 'get' ) - ->willReturn( '' ); - - $this->options->expects( $this->never() ) - ->method( 'update' ); - - $this->options->expects( $this->never() ) - ->method( 'delete' ); - - $this->expectException( ExceptionWithResponseData::class ); - $this->expectExceptionMessage( 'No stored nonce found in the database, skip updating auth status.' ); - - $this->expect_track_event( - 'update_wpcom_api_authorization', - [ - 'status' => 'No stored nonce found in the database, skip updating auth status.', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->account->update_wpcom_api_authorization( $status, $nonce ); - } - - public function test_update_wpcom_api_authorization_nonces_mismatch() { - $status = 'approved'; - $nonce = 'nonce-123'; - - $this->options->expects( $this->once() ) - ->method( 'get' ) - ->willReturn( 'nonce-456' ); - - $this->options->expects( $this->never() ) - ->method( 'update' ); - - $this->options->expects( $this->once() ) - ->method( 'delete' ) - ->with( OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ); - - $this->expectException( ExceptionWithResponseData::class ); - $this->expectExceptionMessage( 'Nonces mismatch, skip updating auth status.' ); - - $this->expect_track_event( - 'update_wpcom_api_authorization', - [ - 'status' => 'Nonces mismatch, skip updating auth status.', - 'blog_id' => Jetpack_Options::get_option( 'id' ), - ] - ); - - $this->account->update_wpcom_api_authorization( $status, $nonce ); - } - - public function test_reset_wpcom_api_authorization_data() { - $this->options->expects( $this->exactly( 2 ) ) - ->method( 'delete' ) - ->withConsecutive( - [ OptionsInterface::GOOGLE_WPCOM_AUTH_NONCE ], - [ OptionsInterface::WPCOM_REST_API_STATUS ], - ); - - $this->account->reset_wpcom_api_authorization_data(); - } }