Fix embed dropdown menu item for unauthenticated users (#25964)
This commit is contained in:
parent
644c5fddd8
commit
41f65edb21
@ -1,25 +1,36 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::Web::EmbedsController < Api::Web::BaseController
|
class Api::Web::EmbedsController < Api::Web::BaseController
|
||||||
before_action :require_user!
|
include Authorization
|
||||||
|
|
||||||
def create
|
before_action :set_status
|
||||||
status = StatusFinder.new(params[:url]).status
|
|
||||||
|
|
||||||
return not_found if status.hidden?
|
def show
|
||||||
|
return not_found if @status.hidden?
|
||||||
|
|
||||||
render json: status, serializer: OEmbedSerializer, width: 400
|
if @status.local?
|
||||||
rescue ActiveRecord::RecordNotFound
|
render json: @status, serializer: OEmbedSerializer, width: 400
|
||||||
oembed = FetchOEmbedService.new.call(params[:url])
|
else
|
||||||
|
return not_found unless user_signed_in?
|
||||||
|
|
||||||
return not_found if oembed.nil?
|
url = ActivityPub::TagManager.instance.url_for(@status)
|
||||||
|
oembed = FetchOEmbedService.new.call(url)
|
||||||
|
return not_found if oembed.nil?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
||||||
rescue ArgumentError
|
rescue ArgumentError
|
||||||
return not_found
|
return not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: oembed
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
render json: oembed
|
def set_status
|
||||||
|
@status = Status.find(params[:id])
|
||||||
|
authorize @status, :show?
|
||||||
|
rescue Mastodon::NotPermittedError
|
||||||
|
not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -258,7 +258,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publicStatus) {
|
if (publicStatus && (signedIn || !isRemote)) {
|
||||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'EMBED',
|
modalType: 'EMBED',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
url: status.get('url'),
|
id: status.get('id'),
|
||||||
onError: error => dispatch(showAlertForError(error)),
|
onError: error => dispatch(showAlertForError(error)),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -205,7 +205,7 @@ class ActionBar extends PureComponent {
|
|||||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShare });
|
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShare });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publicStatus) {
|
if (publicStatus && (signedIn || !isRemote)) {
|
||||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
dispatch(openModal({
|
dispatch(openModal({
|
||||||
modalType: 'EMBED',
|
modalType: 'EMBED',
|
||||||
modalProps: {
|
modalProps: {
|
||||||
url: status.get('url'),
|
id: status.get('id'),
|
||||||
onError: error => dispatch(showAlertForError(error)),
|
onError: error => dispatch(showAlertForError(error)),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -449,7 +449,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
handleEmbed = (status) => {
|
handleEmbed = (status) => {
|
||||||
this.props.dispatch(openModal({
|
this.props.dispatch(openModal({
|
||||||
modalType: 'EMBED',
|
modalType: 'EMBED',
|
||||||
modalProps: { url: status.get('url') },
|
modalProps: { id: status.get('id') },
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ const messages = defineMessages({
|
|||||||
class EmbedModal extends ImmutablePureComponent {
|
class EmbedModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
url: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onError: PropTypes.func.isRequired,
|
onError: PropTypes.func.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
@ -26,11 +26,11 @@ class EmbedModal extends ImmutablePureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { url } = this.props;
|
const { id } = this.props;
|
||||||
|
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
|
||||||
api().post('/api/web/embed', { url }).then(res => {
|
api().get(`/api/web/embeds/${id}`).then(res => {
|
||||||
this.setState({ loading: false, oembed: res.data });
|
this.setState({ loading: false, oembed: res.data });
|
||||||
|
|
||||||
const iframeDocument = this.iframe.contentWindow.document;
|
const iframeDocument = this.iframe.contentWindow.document;
|
||||||
|
@ -296,7 +296,7 @@ namespace :api, format: false do
|
|||||||
|
|
||||||
namespace :web do
|
namespace :web do
|
||||||
resource :settings, only: [:update]
|
resource :settings, only: [:update]
|
||||||
resource :embed, only: [:create]
|
resources :embeds, only: [:show]
|
||||||
resources :push_subscriptions, only: [:create] do
|
resources :push_subscriptions, only: [:create] do
|
||||||
member do
|
member do
|
||||||
put :update
|
put :update
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe Api::Web::EmbedsController do
|
|
||||||
render_views
|
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
|
||||||
|
|
||||||
before { sign_in user }
|
|
||||||
|
|
||||||
describe 'POST #create' do
|
|
||||||
subject(:body) { JSON.parse(response.body, symbolize_names: true) }
|
|
||||||
|
|
||||||
let(:response) { post :create, params: { url: url } }
|
|
||||||
|
|
||||||
context 'when successfully finds status' do
|
|
||||||
let(:status) { Fabricate(:status) }
|
|
||||||
let(:url) { "http://#{Rails.configuration.x.web_domain}/@#{status.account.username}/#{status.id}" }
|
|
||||||
|
|
||||||
it 'returns a right response' do
|
|
||||||
expect(response).to have_http_status 200
|
|
||||||
expect(body[:author_name]).to eq status.account.username
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when fails to find status' do
|
|
||||||
let(:url) { 'https://host.test/oembed.html' }
|
|
||||||
let(:service_instance) { instance_double(FetchOEmbedService) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(FetchOEmbedService).to receive(:new) { service_instance }
|
|
||||||
allow(service_instance).to receive(:call) { call_result }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when successfully fetching oembed' do
|
|
||||||
let(:call_result) { { result: :ok } }
|
|
||||||
|
|
||||||
it 'returns a right response' do
|
|
||||||
expect(response).to have_http_status 200
|
|
||||||
expect(body[:result]).to eq 'ok'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when fails to fetch oembed' do
|
|
||||||
let(:call_result) { nil }
|
|
||||||
|
|
||||||
it 'returns a right response' do
|
|
||||||
expect(response).to have_http_status 404
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
161
spec/requests/api/web/embeds_spec.rb
Normal file
161
spec/requests/api/web/embeds_spec.rb
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe '/api/web/embed' do
|
||||||
|
subject { get "/api/web/embeds/#{id}", headers: headers }
|
||||||
|
|
||||||
|
context 'when accessed anonymously' do
|
||||||
|
let(:headers) { {} }
|
||||||
|
|
||||||
|
context 'when the requested status is local' do
|
||||||
|
let(:id) { status.id }
|
||||||
|
|
||||||
|
context 'when the requested status is public' do
|
||||||
|
let(:status) { Fabricate(:status, visibility: :public) }
|
||||||
|
|
||||||
|
it 'returns JSON with an html attribute' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(body_as_json[:html]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status is private' do
|
||||||
|
let(:status) { Fabricate(:status, visibility: :private) }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status is remote' do
|
||||||
|
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||||
|
let(:status) { Fabricate(:status, visibility: :public, account: remote_account, url: 'https://example.com/statuses/1') }
|
||||||
|
let(:id) { status.id }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status does not exist' do
|
||||||
|
let(:id) { -1 }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an API token' do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
|
||||||
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||||
|
|
||||||
|
context 'when the requested status is local' do
|
||||||
|
let(:id) { status.id }
|
||||||
|
|
||||||
|
context 'when the requested status is public' do
|
||||||
|
let(:status) { Fabricate(:status, visibility: :public) }
|
||||||
|
|
||||||
|
it 'returns JSON with an html attribute' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(body_as_json[:html]).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requesting user is blocked' do
|
||||||
|
before do
|
||||||
|
status.account.block!(user.account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status is private' do
|
||||||
|
let(:status) { Fabricate(:status, visibility: :private) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
user.account.follow!(status.account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status is remote' do
|
||||||
|
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||||
|
let(:status) { Fabricate(:status, visibility: :public, account: remote_account, url: 'https://example.com/statuses/1') }
|
||||||
|
let(:id) { status.id }
|
||||||
|
|
||||||
|
let(:service_instance) { instance_double(FetchOEmbedService) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(FetchOEmbedService).to receive(:new) { service_instance }
|
||||||
|
allow(service_instance).to receive(:call) { call_result }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requesting user is blocked' do
|
||||||
|
before do
|
||||||
|
status.account.block!(user.account)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when successfully fetching OEmbed' do
|
||||||
|
let(:call_result) { { html: 'ok' } }
|
||||||
|
|
||||||
|
it 'returns JSON with an html attribute' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(body_as_json[:html]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when failing to fetch OEmbed' do
|
||||||
|
let(:call_result) { nil }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the requested status does not exist' do
|
||||||
|
let(:id) { -1 }
|
||||||
|
|
||||||
|
it 'returns http not found' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user