mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-23 10:53:34 +03:00
🐛 Improved error message for unauthorized YouTube embeds (#16374)
refs TryGhost/Ghost#16048 - When attempting to embed a Youtube video that has had embedding disabled by its owner/author, Ghost displayed a generic error message that didn't indicate the reason for the failed emebed. - This change updated the error message when Youtube (or any provider) returns 401: Unauthorized to indicate that the owner of the resource has explicitly disabled embedding.
This commit is contained in:
parent
848b2d82a1
commit
27e4523aec
@ -578,3 +578,6 @@ add|ember-template-lint|no-action|25|125|25|125|ba0f8b6ba2697f1b071200d1a3dae9c3
|
||||
add|ember-template-lint|no-action|48|46|48|46|2f3118270fbb1ff6e5da6b0d482ccd21e69df3b5|1681862400000|1692230400000|1697414400000|app/components/modal-post-history.hbs
|
||||
add|ember-template-lint|require-valid-alt-text|13|20|13|20|41dff435a7aba8088be689c6d9b1e76bef081d17|1682035200000|1692403200000|1697587200000|app/components/modal-post-history.hbs
|
||||
add|ember-template-lint|require-valid-alt-text|20|20|20|20|bc0bb4f51567cea7289bfcb30d02932f0f57d0d9|1682035200000|1692403200000|1697587200000|app/components/modal-post-history.hbs
|
||||
add|ember-template-lint|no-action|55|71|55|71|76726a13a086d82dab219df12e86db1773a9de32|1678147200000|1688511600000|1693695600000|lib/koenig-editor/addon/components/koenig-card-embed.hbs
|
||||
add|ember-template-lint|no-action|56|85|56|85|bb78ad59bc384ea0de5e9459da9d85f1735ce141|1678147200000|1688511600000|1693695600000|lib/koenig-editor/addon/components/koenig-card-embed.hbs
|
||||
add|ember-template-lint|no-action|57|38|57|38|3ad187464ff78253a0ea4dd17dcfcf0423f66864|1678147200000|1688511600000|1693695600000|lib/koenig-editor/addon/components/koenig-card-embed.hbs
|
||||
|
@ -40,6 +40,15 @@
|
||||
<div class="miw-100 pa2 ba br2 b--lightgrey-d1 flex items-center justify-center bg-whitegrey-l2 f6 lh-title h10">
|
||||
<div class="ghost-spinner spinner-blue"></div>
|
||||
</div>
|
||||
{{else if this.isUnauthorized}}
|
||||
<div class="miw-100 flex flex-row pa2 pl3 ba br2 b--red-l3 red bg-error-red f7 fw4 lh-title h10 items-center">
|
||||
<span class="mr3">The owner of this URL has disabled embedding.</span>
|
||||
<button type="button" class="red-d2 mr3 fw6 hover-red" {{action "retry"}}><span class="underline">Retry</span></button>
|
||||
<button type="button" class="red-d2 mr-auto fw6 underline hover-red" {{action "insertAsLink"}}><span class="underline">Paste URL as link</span></button>
|
||||
<button type="button" {{action this.deleteCard}} class="nudge-right--2">
|
||||
{{svg-jar "close" class="w3 stroke-red-l3"}}
|
||||
</button>
|
||||
</div>
|
||||
{{else if this.hasError}}
|
||||
<div class="miw-100 flex flex-row pa2 pl3 ba br2 b--red-l3 red bg-error-red f7 fw4 lh-title h10 items-center">
|
||||
<span class="mr3">There was an error when parsing the URL.</span>
|
||||
|
@ -6,6 +6,7 @@ import {NO_CURSOR_MOVEMENT} from './koenig-editor';
|
||||
import {action, computed, set} from '@ember/object';
|
||||
import {utils as ghostHelperUtils} from '@tryghost/helpers';
|
||||
import {isBlank} from '@ember/utils';
|
||||
import {isUnauthorizedError} from 'ember-ajax/errors';
|
||||
import {run} from '@ember/runloop';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
@ -23,6 +24,7 @@ export default class KoenigCardEmbed extends Component {
|
||||
|
||||
// internal properties
|
||||
hasError = false;
|
||||
isUnauthorized = false;
|
||||
|
||||
// closure actions
|
||||
selectCard() {}
|
||||
@ -114,6 +116,7 @@ export default class KoenigCardEmbed extends Component {
|
||||
@action
|
||||
retry() {
|
||||
this.set('hasError', false);
|
||||
this.set('isUnauthorized', false);
|
||||
}
|
||||
|
||||
@action
|
||||
@ -208,6 +211,9 @@ export default class KoenigCardEmbed extends Component {
|
||||
return;
|
||||
}
|
||||
this.set('hasError', true);
|
||||
if (isUnauthorizedError(err)) {
|
||||
this.set('isUnauthorized', true);
|
||||
}
|
||||
}
|
||||
}).drop())
|
||||
convertUrl;
|
||||
|
@ -1,4 +1,4 @@
|
||||
const {extract} = require('oembed-parser');
|
||||
const {extract} = require('@extractus/oembed-extractor');
|
||||
const logging = require('@tryghost/logging');
|
||||
|
||||
/**
|
||||
|
@ -60,6 +60,7 @@
|
||||
"dependencies": {
|
||||
"@sentry/node": "7.50.0",
|
||||
"@tryghost/adapter-base-cache": "0.1.5",
|
||||
"@extractus/oembed-extractor": "^3.1.8",
|
||||
"@tryghost/adapter-cache-redis": "0.0.0",
|
||||
"@tryghost/adapter-manager": "0.0.0",
|
||||
"@tryghost/admin-api-schema": "4.3.0",
|
||||
@ -202,7 +203,6 @@
|
||||
"mysql2": "3.2.0",
|
||||
"nconf": "0.12.0",
|
||||
"node-jose": "2.2.0",
|
||||
"oembed-parser": "1.4.9",
|
||||
"path-match": "1.2.4",
|
||||
"probe-image-size": "7.2.3",
|
||||
"rss": "1.2.2",
|
||||
|
@ -58,6 +58,37 @@ describe('Oembed API', function () {
|
||||
should.exist(res.body.html);
|
||||
});
|
||||
|
||||
it('errors with a useful message when embedding is disabled', async function () {
|
||||
const requestMock = nock('https://www.youtube.com')
|
||||
.get('/oembed')
|
||||
.query(true)
|
||||
.reply(401, {
|
||||
errors: [
|
||||
{
|
||||
message: 'Authorisation error, cannot read oembed.',
|
||||
context: 'URL contains a private resource.',
|
||||
type: 'UnauthorizedError',
|
||||
details: null,
|
||||
property: null,
|
||||
help: null,
|
||||
code: null,
|
||||
id: 'c51228a0-921a-11ed-8abe-6babfda4d18a',
|
||||
ghostErrorCode: null
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const res = await request.get(localUtils.API.getApiQuery('oembed/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DE5yFcdPAGv0'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(401);
|
||||
|
||||
requestMock.isDone().should.be.true();
|
||||
should.exist(res.body.errors);
|
||||
res.body.errors[0].context.should.match(/URL contains a private resource/i);
|
||||
});
|
||||
|
||||
describe('type: bookmark', function () {
|
||||
it('can fetch a bookmark with ?type=bookmark', async function () {
|
||||
const pageMock = nock('http://example.com')
|
||||
|
@ -1,7 +1,7 @@
|
||||
const errors = require('@tryghost/errors');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
const logging = require('@tryghost/logging');
|
||||
const {extract, hasProvider} = require('oembed-parser');
|
||||
const {extract, hasProvider} = require('@extractus/oembed-extractor');
|
||||
const cheerio = require('cheerio');
|
||||
const _ = require('lodash');
|
||||
const charset = require('charset');
|
||||
@ -10,7 +10,8 @@ const iconv = require('iconv-lite');
|
||||
const messages = {
|
||||
noUrlProvided: 'No url provided.',
|
||||
insufficientMetadata: 'URL contains insufficient metadata.',
|
||||
unknownProvider: 'No provider found for supplied URL.'
|
||||
unknownProvider: 'No provider found for supplied URL.',
|
||||
unauthorized: 'URL contains a private resource.'
|
||||
};
|
||||
|
||||
/**
|
||||
@ -53,7 +54,7 @@ const findUrlWithProvider = (url) => {
|
||||
/**
|
||||
* @typedef {object} ICustomProvider
|
||||
* @prop {(url: URL) => Promise<boolean>} canSupportRequest
|
||||
* @prop {(url: URL, externalRequest: IExternalRequest) => Promise<import('oembed-parser').OembedData>} getOEmbedData
|
||||
* @prop {(url: URL, externalRequest: IExternalRequest) => Promise<import('@extractus/oembed-extractor').OembedData>} getOEmbedData
|
||||
*/
|
||||
|
||||
class OEmbed {
|
||||
@ -97,9 +98,15 @@ class OEmbed {
|
||||
try {
|
||||
return await extract(url);
|
||||
} catch (err) {
|
||||
throw new errors.InternalServerError({
|
||||
message: err.message
|
||||
});
|
||||
if (err.message === 'Request failed with error code 401') {
|
||||
throw new errors.UnauthorizedError({
|
||||
message: messages.unauthorized
|
||||
});
|
||||
} else {
|
||||
throw new errors.InternalServerError({
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,6 @@
|
||||
"intl-messageformat",
|
||||
"moment",
|
||||
"moment-timezone",
|
||||
"oembed-parser",
|
||||
"simple-dom",
|
||||
"ember-drag-drop",
|
||||
"normalize.css",
|
||||
|
@ -2589,6 +2589,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.37.0.tgz#cf1b5fa24217fe007f6487a26d765274925efa7d"
|
||||
integrity sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==
|
||||
|
||||
"@extractus/oembed-extractor@^3.1.8":
|
||||
version "3.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@extractus/oembed-extractor/-/oembed-extractor-3.1.8.tgz#79ea7ed65c7688bdf9ee673a0ac5aa122cef5e4e"
|
||||
integrity sha512-k6p8des8ISJY2fuuQDyiUOTlcuPOzWETM2ewF1aywFNSS3EvgGWRwoMsvBKhCrLuvb8NyEKDAJB0SWgt0L793w==
|
||||
dependencies:
|
||||
cross-fetch "^3.1.5"
|
||||
|
||||
"@faker-js/faker@7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.6.0.tgz#9ea331766084288634a9247fcd8b84f16ff4ba07"
|
||||
@ -11749,7 +11756,7 @@ cron-validate@1.4.5, cron-validate@^1.4.1:
|
||||
dependencies:
|
||||
yup "0.32.9"
|
||||
|
||||
cross-fetch@3.1.5, cross-fetch@^3.1.4:
|
||||
cross-fetch@3.1.5, cross-fetch@^3.1.4, cross-fetch@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
|
||||
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
|
||||
|
Loading…
Reference in New Issue
Block a user