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

Add a warning when Cloudflare blocks REST API requests #8640

Merged
merged 14 commits into from
Aug 8, 2018

Conversation

pento
Copy link
Member

@pento pento commented Aug 7, 2018

Description

When Cloudflare blocks REST API requests, there's nothing in the UI to suggest what the cause is, which is a confusing and frustrating experience.

Adding extra information when we detect the error helps folks determine their best course of action.

This is a temporary measure, to help with issues like #2704, until Cloudflare roll out their fix. As such, the message box is a fairly lazy copy/paste of the existing block warning in the Classic Editor.

How has this been tested?

With Cloudflare:

  • Enable the WAF on your site. You may need to disable rules WP0003 and WP0004, to ensure you're not being whitelisted.
  • Try to save a post.
  • After the save fails, disable rules WP0025A and WP0025B.
  • Try to save it again: the save should succeed.

On your local site, you can simulate the REST API being blocked by Cloudflare with this snippet:

add_action( 'rest_api_init', function() {
	header( 'Content-Type: text/html' );
	status_header( 403 );
	echo '<span>Cloudflare Ray ID: foo</span>';
	die();
} );

Screenshots

screen shot of the Cloudflare error message

Clicking Learn More will redirect you to the Classic Editor, showing this message:

Cloudflare detailed message

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.

@pento pento added the [Type] Enhancement A suggestion for improvement. label Aug 7, 2018
@pento pento added this to the 3.5 milestone Aug 7, 2018
@pento pento requested a review from a team August 7, 2018 03:43
lib/compat.php Outdated
<p><?php _e( 'Your site uses Cloudflare, which provides a Web Application Firewall (WAF) to secure your site against attacks. Unfortunately, some of these WAF rules are incorrectly blocking legitimate access to your site, preventing Gutenberg from functioning correctly.', 'gutenberg' ); ?></p>
<p><?php _e( "We're working closely with Cloudflare to fix this issue, but in the mean time, you can work around it in one of two ways:", 'gutenberg' ); ?></p>
<ul>
<li><?php _e( 'If you have a Cloudflare Pro account, login to Cloudflare, visit the Firewall settings page, open the "Cloudflare Rule Set" details, open the "Cloudflare WordPress" ruleset, then set the rules "WP0025A" and "WP0025A" to "Disable".', 'gutenberg' ); ?></li>
Copy link
Member

@gziolo gziolo Aug 7, 2018

Choose a reason for hiding this comment

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

"WP0025A" and "WP0025A" - the same id is listed twice.

If I follow the description of the PR right, it should be "WP0025A" and "WP0025B"

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

@@ -68,16 +76,24 @@ function apiFetch( options ) {
throw invalidJsonError;
}

const responseClone = response.clone();
Copy link
Member

Choose a reason for hiding this comment

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

Unit test fails with the following message:

[TypeError: r[1] esponse.clone is not a function]

I bet you need to mock it because it is a part of the response.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

@@ -19,7 +19,7 @@ function registerMiddleware( middleware ) {
}

function checkCloudflareError( error ) {
if ( error.indexOf( 'Cloudflare Ray ID' ) >= 0 ) {
if ( error.indexOf && error.indexOf( 'Cloudflare Ray ID' ) >= 0 ) {
Copy link
Member

@gziolo gziolo Aug 7, 2018

Choose a reason for hiding this comment

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

Or maybe:

 typeof error === 'string'

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

@gziolo
Copy link
Member

gziolo commented Aug 7, 2018

It works as advertised. I would personally consider wrapping those setting names and errors codes like WP0025A with strong/em to make them easier to distinguish from the big blob of text.

When I edit post in Gutenberg and try to save it as draft I can see the notice, but when I click link to learn more I see need to accept dialog informing that my changes weren't saved. It would be great to tweak it to ensure that everyone easily navigates to the Classic Editor.

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

I noticed that current implementation only show noticed when you try to update the status of the post. However there are many more cases when it can fail like:

  • fetching taxonomies
  • fetching authors
  • dynamic blocks (latest posts, categories, ...)

And probably many more.

@gziolo gziolo added the [Priority] High Used to indicate top priority items that need quick attention label Aug 7, 2018
@pento
Copy link
Member Author

pento commented Aug 7, 2018

I noticed that current implementation only show noticed when you try to update the status of the post.

Yeah, I was originally looking at implementing it for all requests (1bd7f2a). There were a couple of problems with this approach:

  • api-fetch didn't seem like the right place to create UI for showing a message.
  • Making api-fetch rely on the data package (so that it could wipe the post dirty state, preventing the "save your changes" warning you ran into) seemed like a worse idea. 🙂

So, I went with the post save action as being a reasonable tradeoff, where the user should notice the message when the first autosave happens, before they've really got into writing anything.

@gziolo
Copy link
Member

gziolo commented Aug 7, 2018

@youknowriad implemented plugins API for api-fetch, I’m wondering if we could use it to scan all requests and show the notice only once when it happens for the first time.

When you visit Gutenberg for the first time, Document sidebar is expanded. This means it will trigger network requests for taxonomies and authors. You should be able to display a notice for Cloudflare users almost immediately. This would mitigate another concern I shared in the PR that you need to accept a dialog before you navigate away.

lib/compat.php Outdated
<p><?php _e( 'Your site uses Cloudflare, which provides a Web Application Firewall (WAF) to secure your site against attacks. Unfortunately, some of these WAF rules are incorrectly blocking legitimate access to your site, preventing Gutenberg from functioning correctly.', 'gutenberg' ); ?></p>
<p><?php _e( "We're working closely with Cloudflare to fix this issue, but in the mean time, you can work around it in one of two ways:", 'gutenberg' ); ?></p>
<ul>
<li><?php _e( 'If you have a Cloudflare Pro account, login to Cloudflare, visit the Firewall settings page, open the "Cloudflare Rule Set" details, open the "Cloudflare WordPress" ruleset, then set the rules "WP0025A" and "WP0025B" to "Disable".', 'gutenberg' ); ?></li>
Copy link
Member

Choose a reason for hiding this comment

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

Grammar: As a verb, the correct usage is "log in" as two words.

http://grammarist.com/spelling/log-in-login/

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

@@ -68,16 +76,24 @@ function apiFetch( options ) {
throw invalidJsonError;
}

const responseClone = response.clone();
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need to clone the response? (Add as an inline code comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

'cloudflare-error': '',
} );

const cloudflaredMessage = error && 'cloudflare_error' === error.code ?
Copy link
Member

Choose a reason for hiding this comment

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

Typo: cloudflared => cloudflare

Edit: Or... intentional? I guess?

Copy link
Member Author

Choose a reason for hiding this comment

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

Intentional, the reason for this even needing to exist is silly, and given that it'll (hopefully) be reverted next week, we can be a little silly with the variable names, too.

</p> :
noticeMessage;

dispatch( createErrorNotice( cloudflaredMessage, { id: SAVE_POST_NOTICE_ID } ) );
Copy link
Member

Choose a reason for hiding this comment

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

Not loving how non-isolated the Cloudflare behaviors are.

Copy link
Member Author

Choose a reason for hiding this comment

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

Same, but as I mentioned above, this is super temporary, it'll disappear before too long.

return response.json()
.catch( () => {
throw invalidJsonError;
return responseClone.text()
.then( ( text ) => {
Copy link
Member

Choose a reason for hiding this comment

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

Aside: Async/Await could make this nicer to read:

.catch( async () => {
	const text = await responseClone.text();
	checkCloudflareError( text );
	throw invalidJsonError;
} );

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

lib/compat.php Outdated
<li>
<?php
printf(
/* translators: link to a comment in the Gutenberg repo */
Copy link
Member

Choose a reason for hiding this comment

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

This should be /* translators: %s: link to a comment in the Gutenberg GitHub repository */

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

lib/compat.php Outdated
<p>
<?php
printf(
/* translators: link to an issue in the Gutenberg repo */
Copy link
Member

Choose a reason for hiding this comment

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

Again missing the %s in the comment + I wouldn't shorten repository here.

/* translators: %s: link to an issue in the Gutenberg GitHub repository */

Copy link
Member Author

Choose a reason for hiding this comment

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

👍🏻

@pento
Copy link
Member Author

pento commented Aug 8, 2018

I've fixed all the little things that needed to be fixed, but I haven't tried to re-architect it to catch all API requests.

I also haven't fixed the "unsaved changes" dialog appearing, I guess we could do something to UnsavedChangesWarning to not show it under these circumstances, or to fake the result of isEditedPostDirty().

It's kind of hacky, and not ideal, but hopefully it'll be reverted by this time next week, so I'm not too concerned. 🙂

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

Let's get it in. It is much better then it was before. Given that we hope to remove it before 3.6 gets out, I'm fine with the current proposal. Tested again, still works as intended.

@gziolo gziolo merged commit ac2fa9a into master Aug 8, 2018
@youknowriad youknowriad deleted the add/cloudflare-warning branch August 8, 2018 09:49
@aduth
Copy link
Member

aduth commented Oct 31, 2018

This is a temporary measure, to help with issues like #2704, until Cloudflare roll out their fix.

Is it still needed? Where can I follow progress on the removal of this code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Priority] High Used to indicate top priority items that need quick attention [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants