From 7de0ee7aba86cffeaeffded7e0699214fb64364e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 26 Nov 2021 05:58:18 +0100 Subject: [PATCH 01/15] Remove Keybase integration (#17045) --- app/controllers/api/proofs_controller.rb | 23 --- .../v1/accounts/identity_proofs_controller.rb | 3 +- .../settings/identity_proofs_controller.rb | 60 ------ .../keybase_proof_config_controller.rb | 9 - .../mastodon/actions/identity_proofs.js | 31 --- .../features/account/components/header.js | 16 +- .../account_timeline/components/header.js | 4 +- .../containers/header_container.js | 2 - .../features/account_timeline/index.js | 2 - .../mastodon/reducers/identity_proofs.js | 25 --- app/javascript/mastodon/reducers/index.js | 2 - app/javascript/styles/mastodon/forms.scss | 62 ------ app/lib/activitypub/adapter.rb | 1 - app/lib/proof_provider.rb | 12 -- app/lib/proof_provider/keybase.rb | 69 ------- app/lib/proof_provider/keybase/badge.rb | 45 ----- .../keybase/config_serializer.rb | 76 ------- app/lib/proof_provider/keybase/serializer.rb | 25 --- app/lib/proof_provider/keybase/verifier.rb | 59 ------ app/lib/proof_provider/keybase/worker.rb | 32 --- app/models/account_identity_proof.rb | 46 ----- app/models/concerns/account_associations.rb | 3 +- app/models/concerns/account_merging.rb | 2 +- .../activitypub/actor_serializer.rb | 5 +- .../rest/identity_proof_serializer.rb | 17 -- .../activitypub/process_account_service.rb | 26 --- app/services/delete_account_service.rb | 2 - app/views/accounts/_bio.html.haml | 10 +- app/views/admin/accounts/show.html.haml | 12 +- .../settings/identity_proofs/_proof.html.haml | 21 -- .../settings/identity_proofs/index.html.haml | 17 -- .../settings/identity_proofs/new.html.haml | 36 ---- config/locales/en.yml | 21 -- config/navigation.rb | 1 - config/routes.rb | 6 - ...1126000907_drop_account_identity_proofs.rb | 13 ++ db/schema.rb | 15 +- .../controllers/api/proofs_controller_spec.rb | 93 --------- .../identity_proofs_controller_spec.rb | 186 ------------------ .../keybase_proof_config_controller_spec.rb | 15 -- .../account_identity_proof_fabricator.rb | 8 - .../proof_provider/keybase/verifier_spec.rb | 82 -------- .../process_account_service_spec.rb | 45 ----- 43 files changed, 25 insertions(+), 1215 deletions(-) delete mode 100644 app/controllers/api/proofs_controller.rb delete mode 100644 app/controllers/settings/identity_proofs_controller.rb delete mode 100644 app/controllers/well_known/keybase_proof_config_controller.rb delete mode 100644 app/javascript/mastodon/actions/identity_proofs.js delete mode 100644 app/javascript/mastodon/reducers/identity_proofs.js delete mode 100644 app/lib/proof_provider.rb delete mode 100644 app/lib/proof_provider/keybase.rb delete mode 100644 app/lib/proof_provider/keybase/badge.rb delete mode 100644 app/lib/proof_provider/keybase/config_serializer.rb delete mode 100644 app/lib/proof_provider/keybase/serializer.rb delete mode 100644 app/lib/proof_provider/keybase/verifier.rb delete mode 100644 app/lib/proof_provider/keybase/worker.rb delete mode 100644 app/models/account_identity_proof.rb delete mode 100644 app/serializers/rest/identity_proof_serializer.rb delete mode 100644 app/views/settings/identity_proofs/_proof.html.haml delete mode 100644 app/views/settings/identity_proofs/index.html.haml delete mode 100644 app/views/settings/identity_proofs/new.html.haml create mode 100644 db/post_migrate/20211126000907_drop_account_identity_proofs.rb delete mode 100644 spec/controllers/api/proofs_controller_spec.rb delete mode 100644 spec/controllers/settings/identity_proofs_controller_spec.rb delete mode 100644 spec/controllers/well_known/keybase_proof_config_controller_spec.rb delete mode 100644 spec/fabricators/account_identity_proof_fabricator.rb delete mode 100644 spec/lib/proof_provider/keybase/verifier_spec.rb diff --git a/app/controllers/api/proofs_controller.rb b/app/controllers/api/proofs_controller.rb deleted file mode 100644 index dd32cd577..000000000 --- a/app/controllers/api/proofs_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -class Api::ProofsController < Api::BaseController - include AccountOwnedConcern - - skip_before_action :require_authenticated_user! - - before_action :set_provider - - def index - render json: @account, serializer: @provider.serializer_class - end - - private - - def set_provider - @provider = ProofProvider.find(params[:provider]) || raise(ActiveRecord::RecordNotFound) - end - - def username_param - params[:username] - end -end diff --git a/app/controllers/api/v1/accounts/identity_proofs_controller.rb b/app/controllers/api/v1/accounts/identity_proofs_controller.rb index 4b5f6902c..48f293f47 100644 --- a/app/controllers/api/v1/accounts/identity_proofs_controller.rb +++ b/app/controllers/api/v1/accounts/identity_proofs_controller.rb @@ -5,8 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController before_action :set_account def index - @proofs = @account.suspended? ? [] : @account.identity_proofs.active - render json: @proofs, each_serializer: REST::IdentityProofSerializer + render json: [] end private diff --git a/app/controllers/settings/identity_proofs_controller.rb b/app/controllers/settings/identity_proofs_controller.rb deleted file mode 100644 index bf2899da6..000000000 --- a/app/controllers/settings/identity_proofs_controller.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -class Settings::IdentityProofsController < Settings::BaseController - before_action :check_required_params, only: :new - - def index - @proofs = AccountIdentityProof.where(account: current_account).order(provider: :asc, provider_username: :asc) - @proofs.each(&:refresh!) - end - - def new - @proof = current_account.identity_proofs.new( - token: params[:token], - provider: params[:provider], - provider_username: params[:provider_username] - ) - - if current_account.username.casecmp(params[:username]).zero? - render layout: 'auth' - else - redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.wrong_user', proving: params[:username], current: current_account.username) - end - end - - def create - @proof = current_account.identity_proofs.where(provider: resource_params[:provider], provider_username: resource_params[:provider_username]).first_or_initialize(resource_params) - @proof.token = resource_params[:token] - - if @proof.save - PostStatusService.new.call(current_user.account, text: post_params[:status_text]) if publish_proof? - redirect_to @proof.on_success_path(params[:user_agent]) - else - redirect_to settings_identity_proofs_path, alert: I18n.t('identity_proofs.errors.failed', provider: @proof.provider.capitalize) - end - end - - def destroy - @proof = current_account.identity_proofs.find(params[:id]) - @proof.destroy! - redirect_to settings_identity_proofs_path, success: I18n.t('identity_proofs.removed') - end - - private - - def check_required_params - redirect_to settings_identity_proofs_path unless [:provider, :provider_username, :username, :token].all? { |k| params[k].present? } - end - - def resource_params - params.require(:account_identity_proof).permit(:provider, :provider_username, :token) - end - - def publish_proof? - ActiveModel::Type::Boolean.new.cast(post_params[:post_status]) - end - - def post_params - params.require(:account_identity_proof).permit(:post_status, :status_text) - end -end diff --git a/app/controllers/well_known/keybase_proof_config_controller.rb b/app/controllers/well_known/keybase_proof_config_controller.rb deleted file mode 100644 index e1d43ecbe..000000000 --- a/app/controllers/well_known/keybase_proof_config_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module WellKnown - class KeybaseProofConfigController < ActionController::Base - def show - render json: {}, serializer: ProofProvider::Keybase::ConfigSerializer, root: 'keybase_config' - end - end -end diff --git a/app/javascript/mastodon/actions/identity_proofs.js b/app/javascript/mastodon/actions/identity_proofs.js deleted file mode 100644 index 103983956..000000000 --- a/app/javascript/mastodon/actions/identity_proofs.js +++ /dev/null @@ -1,31 +0,0 @@ -import api from '../api'; - -export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST'; -export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS'; -export const IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL = 'IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL'; - -export const fetchAccountIdentityProofs = accountId => (dispatch, getState) => { - dispatch(fetchAccountIdentityProofsRequest(accountId)); - - api(getState).get(`/api/v1/accounts/${accountId}/identity_proofs`) - .then(({ data }) => dispatch(fetchAccountIdentityProofsSuccess(accountId, data))) - .catch(err => dispatch(fetchAccountIdentityProofsFail(accountId, err))); -}; - -export const fetchAccountIdentityProofsRequest = id => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST, - id, -}); - -export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS, - accountId, - identity_proofs, -}); - -export const fetchAccountIdentityProofsFail = (accountId, err) => ({ - type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, - accountId, - err, - skipNotFound: true, -}); diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index 4d0a828c7..48ec49d81 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -123,7 +123,7 @@ class Header extends ImmutablePureComponent { } render () { - const { account, intl, domain, identity_proofs } = this.props; + const { account, intl, domain } = this.props; if (!account) { return null; @@ -297,20 +297,8 @@ class Header extends ImmutablePureComponent {
- {(fields.size > 0 || identity_proofs.size > 0) && ( + {fields.size > 0 && (
- {identity_proofs.map((proof, i) => ( -
-
- -
- - - - -
-
- ))} {fields.map((pair, i) => (
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 17b693600..33bea4c17 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -11,7 +11,6 @@ export default class Header extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map, - identity_proofs: ImmutablePropTypes.list, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, @@ -92,7 +91,7 @@ export default class Header extends ImmutablePureComponent { } render () { - const { account, hideTabs, identity_proofs } = this.props; + const { account, hideTabs } = this.props; if (account === null) { return null; @@ -104,7 +103,6 @@ export default class Header extends ImmutablePureComponent { { const mapStateToProps = (state, { accountId }) => ({ account: getAccount(state, accountId), domain: state.getIn(['meta', 'domain']), - identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()), }); return mapStateToProps; diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js index 20f1dba9f..37df2818b 100644 --- a/app/javascript/mastodon/features/account_timeline/index.js +++ b/app/javascript/mastodon/features/account_timeline/index.js @@ -12,7 +12,6 @@ import ColumnBackButton from '../../components/column_back_button'; import { List as ImmutableList } from 'immutable'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; -import { fetchAccountIdentityProofs } from '../../actions/identity_proofs'; import MissingIndicator from 'mastodon/components/missing_indicator'; import TimelineHint from 'mastodon/components/timeline_hint'; import { me } from 'mastodon/initial_state'; @@ -80,7 +79,6 @@ class AccountTimeline extends ImmutablePureComponent { const { accountId, withReplies, dispatch } = this.props; dispatch(fetchAccount(accountId)); - dispatch(fetchAccountIdentityProofs(accountId)); if (!withReplies) { dispatch(expandAccountFeaturedTimeline(accountId)); diff --git a/app/javascript/mastodon/reducers/identity_proofs.js b/app/javascript/mastodon/reducers/identity_proofs.js deleted file mode 100644 index 58af0a5fa..000000000 --- a/app/javascript/mastodon/reducers/identity_proofs.js +++ /dev/null @@ -1,25 +0,0 @@ -import { Map as ImmutableMap, fromJS } from 'immutable'; -import { - IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST, - IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS, - IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, -} from '../actions/identity_proofs'; - -const initialState = ImmutableMap(); - -export default function identityProofsReducer(state = initialState, action) { - switch(action.type) { - case IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST: - return state.set('isLoading', true); - case IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL: - return state.set('isLoading', false); - case IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS: - return state.update(identity_proofs => identity_proofs.withMutations(map => { - map.set('isLoading', false); - map.set('loaded', true); - map.set(action.accountId, fromJS(action.identity_proofs)); - })); - default: - return state; - } -}; diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index e518c8228..53e2dd681 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -32,7 +32,6 @@ import filters from './filters'; import conversations from './conversations'; import suggestions from './suggestions'; import polls from './polls'; -import identity_proofs from './identity_proofs'; import trends from './trends'; import missed_updates from './missed_updates'; import announcements from './announcements'; @@ -69,7 +68,6 @@ const reducers = { notifications, height_cache, custom_emojis, - identity_proofs, lists, listEditor, listAdder, diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 5b71b6334..65f53471d 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -999,68 +999,6 @@ code { } } -.connection-prompt { - margin-bottom: 25px; - - .fa-link { - background-color: darken($ui-base-color, 4%); - border-radius: 100%; - font-size: 24px; - padding: 10px; - } - - &__column { - align-items: center; - display: flex; - flex: 1; - flex-direction: column; - flex-shrink: 1; - max-width: 50%; - - &-sep { - align-self: center; - flex-grow: 0; - overflow: visible; - position: relative; - z-index: 1; - } - - p { - word-break: break-word; - } - } - - .account__avatar { - margin-bottom: 20px; - } - - &__connection { - background-color: lighten($ui-base-color, 8%); - box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); - border-radius: 4px; - padding: 25px 10px; - position: relative; - text-align: center; - - &::after { - background-color: darken($ui-base-color, 4%); - content: ''; - display: block; - height: 100%; - left: 50%; - position: absolute; - top: 0; - width: 1px; - } - } - - &__row { - align-items: flex-start; - display: flex; - flex-direction: row; - } -} - .input.user_confirm_password, .input.user_website { &:not(.field_with_errors) { diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 2d6b87659..776e1d3da 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -18,7 +18,6 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base atom_uri: { 'ostatus' => 'http://ostatus.org#', 'atomUri' => 'ostatus:atomUri' }, conversation: { 'ostatus' => 'http://ostatus.org#', 'inReplyToAtomUri' => 'ostatus:inReplyToAtomUri', 'conversation' => 'ostatus:conversation' }, focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } }, - identity_proof: { 'toot' => 'http://joinmastodon.org/ns#', 'IdentityProof' => 'toot:IdentityProof' }, blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' }, discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' }, voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' }, diff --git a/app/lib/proof_provider.rb b/app/lib/proof_provider.rb deleted file mode 100644 index 102c50f4f..000000000 --- a/app/lib/proof_provider.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -module ProofProvider - SUPPORTED_PROVIDERS = %w(keybase).freeze - - def self.find(identifier, proof = nil) - case identifier - when 'keybase' - ProofProvider::Keybase.new(proof) - end - end -end diff --git a/app/lib/proof_provider/keybase.rb b/app/lib/proof_provider/keybase.rb deleted file mode 100644 index 8e51d7146..000000000 --- a/app/lib/proof_provider/keybase.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase - BASE_URL = ENV.fetch('KEYBASE_BASE_URL', 'https://keybase.io') - DOMAIN = ENV.fetch('KEYBASE_DOMAIN', Rails.configuration.x.web_domain) - - class Error < StandardError; end - - class ExpectedProofLiveError < Error; end - - class UnexpectedResponseError < Error; end - - def initialize(proof = nil) - @proof = proof - end - - def serializer_class - ProofProvider::Keybase::Serializer - end - - def worker_class - ProofProvider::Keybase::Worker - end - - def validate! - unless @proof.token&.size == 66 - @proof.errors.add(:base, I18n.t('identity_proofs.errors.keybase.invalid_token')) - return - end - - # Do not perform synchronous validation for remote accounts - return if @proof.provider_username.blank? || !@proof.account.local? - - if verifier.valid? - @proof.verified = true - @proof.live = false - else - @proof.errors.add(:base, I18n.t('identity_proofs.errors.keybase.verification_failed', kb_username: @proof.provider_username)) - end - end - - def refresh! - worker_class.new.perform(@proof) - rescue ProofProvider::Keybase::Error - nil - end - - def on_success_path(user_agent = nil) - verifier.on_success_path(user_agent) - end - - def badge - @badge ||= ProofProvider::Keybase::Badge.new(@proof.account.username, @proof.provider_username, @proof.token, domain) - end - - def verifier - @verifier ||= ProofProvider::Keybase::Verifier.new(@proof.account.username, @proof.provider_username, @proof.token, domain) - end - - private - - def domain - if @proof.account.local? - DOMAIN - else - @proof.account.domain - end - end -end diff --git a/app/lib/proof_provider/keybase/badge.rb b/app/lib/proof_provider/keybase/badge.rb deleted file mode 100644 index f587b1cc7..000000000 --- a/app/lib/proof_provider/keybase/badge.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase::Badge - include RoutingHelper - - def initialize(local_username, provider_username, token, domain) - @local_username = local_username - @provider_username = provider_username - @token = token - @domain = domain - end - - def proof_url - "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}/sigchain\##{@token}" - end - - def profile_url - "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}" - end - - def icon_url - "#{ProofProvider::Keybase::BASE_URL}/#{@provider_username}/proof_badge/#{@token}?username=#{@local_username}&domain=#{@domain}" - end - - def avatar_url - Rails.cache.fetch("proof_providers/keybase/#{@provider_username}/avatar_url", expires_in: 5.minutes) { remote_avatar_url } || default_avatar_url - end - - private - - def remote_avatar_url - request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/user/pic_url.json", params: { username: @provider_username }) - - request.perform do |res| - json = Oj.load(res.body_with_limit, mode: :strict) - json['pic_url'] if json.is_a?(Hash) - end - rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError - nil - end - - def default_avatar_url - asset_pack_path('media/images/proof_providers/keybase.png') - end -end diff --git a/app/lib/proof_provider/keybase/config_serializer.rb b/app/lib/proof_provider/keybase/config_serializer.rb deleted file mode 100644 index c6c364d31..000000000 --- a/app/lib/proof_provider/keybase/config_serializer.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase::ConfigSerializer < ActiveModel::Serializer - include RoutingHelper - include ActionView::Helpers::TextHelper - - attributes :version, :domain, :display_name, :username, - :brand_color, :logo, :description, :prefill_url, - :profile_url, :check_url, :check_path, :avatar_path, - :contact - - def version - 1 - end - - def domain - ProofProvider::Keybase::DOMAIN - end - - def display_name - Setting.site_title - end - - def logo - { - svg_black: full_asset_url(asset_pack_path('media/images/logo_transparent_black.svg')), - svg_white: full_asset_url(asset_pack_path('media/images/logo_transparent_white.svg')), - svg_full: full_asset_url(asset_pack_path('media/images/logo.svg')), - svg_full_darkmode: full_asset_url(asset_pack_path('media/images/logo.svg')), - } - end - - def brand_color - '#282c37' - end - - def description - strip_tags(Setting.site_short_description.presence || I18n.t('about.about_mastodon_html')) - end - - def username - { min: 1, max: 30, re: '[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?' } - end - - def prefill_url - params = { - provider: 'keybase', - token: '%{sig_hash}', - provider_username: '%{kb_username}', - username: '%{username}', - user_agent: '%{kb_ua}', - } - - CGI.unescape(new_settings_identity_proof_url(params)) - end - - def profile_url - CGI.unescape(short_account_url('%{username}')) - end - - def check_url - CGI.unescape(api_proofs_url(username: '%{username}', provider: 'keybase')) - end - - def check_path - ['signatures'] - end - - def avatar_path - ['avatar'] - end - - def contact - [Setting.site_contact_email.presence || 'unknown'].compact - end -end diff --git a/app/lib/proof_provider/keybase/serializer.rb b/app/lib/proof_provider/keybase/serializer.rb deleted file mode 100644 index d29283600..000000000 --- a/app/lib/proof_provider/keybase/serializer.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase::Serializer < ActiveModel::Serializer - include RoutingHelper - - attribute :avatar - - has_many :identity_proofs, key: :signatures - - def avatar - full_asset_url(object.avatar_original_url) - end - - class AccountIdentityProofSerializer < ActiveModel::Serializer - attributes :sig_hash, :kb_username - - def sig_hash - object.token - end - - def kb_username - object.provider_username - end - end -end diff --git a/app/lib/proof_provider/keybase/verifier.rb b/app/lib/proof_provider/keybase/verifier.rb deleted file mode 100644 index af69b1bfc..000000000 --- a/app/lib/proof_provider/keybase/verifier.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase::Verifier - def initialize(local_username, provider_username, token, domain) - @local_username = local_username - @provider_username = provider_username - @token = token - @domain = domain - end - - def valid? - request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/sig/proof_valid.json", params: query_params) - - request.perform do |res| - json = Oj.load(res.body_with_limit, mode: :strict) - - if json.is_a?(Hash) - json.fetch('proof_valid', false) - else - false - end - end - rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError - false - end - - def on_success_path(user_agent = nil) - url = Addressable::URI.parse("#{ProofProvider::Keybase::BASE_URL}/_/proof_creation_success") - url.query_values = query_params.merge(kb_ua: user_agent || 'unknown') - url.to_s - end - - def status - request = Request.new(:get, "#{ProofProvider::Keybase::BASE_URL}/_/api/1.0/sig/proof_live.json", params: query_params) - - request.perform do |res| - raise ProofProvider::Keybase::UnexpectedResponseError unless res.code == 200 - - json = Oj.load(res.body_with_limit, mode: :strict) - - raise ProofProvider::Keybase::UnexpectedResponseError unless json.is_a?(Hash) && json.key?('proof_valid') && json.key?('proof_live') - - json - end - rescue Oj::ParseError, HTTP::Error, OpenSSL::SSL::SSLError - raise ProofProvider::Keybase::UnexpectedResponseError - end - - private - - def query_params - { - domain: @domain, - kb_username: @provider_username, - username: @local_username, - sig_hash: @token, - } - end -end diff --git a/app/lib/proof_provider/keybase/worker.rb b/app/lib/proof_provider/keybase/worker.rb deleted file mode 100644 index bcdd18cc5..000000000 --- a/app/lib/proof_provider/keybase/worker.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class ProofProvider::Keybase::Worker - include Sidekiq::Worker - - sidekiq_options queue: 'pull', retry: 20, unique: :until_executed - - sidekiq_retry_in do |count, exception| - # Retry aggressively when the proof is valid but not live in Keybase. - # This is likely because Keybase just hasn't noticed the proof being - # served from here yet. - - if exception.class == ProofProvider::Keybase::ExpectedProofLiveError - case count - when 0..2 then 0.seconds - when 2..6 then 1.second - end - end - end - - def perform(proof_id) - proof = proof_id.is_a?(AccountIdentityProof) ? proof_id : AccountIdentityProof.find(proof_id) - status = proof.provider_instance.verifier.status - - # If Keybase thinks the proof is valid, and it exists here in Mastodon, - # then it should be live. Keybase just has to notice that it's here - # and then update its state. That might take a couple seconds. - raise ProofProvider::Keybase::ExpectedProofLiveError if status['proof_valid'] && !status['proof_live'] - - proof.update!(verified: status['proof_valid'], live: status['proof_live']) - end -end diff --git a/app/models/account_identity_proof.rb b/app/models/account_identity_proof.rb deleted file mode 100644 index 10b66cccf..000000000 --- a/app/models/account_identity_proof.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true -# == Schema Information -# -# Table name: account_identity_proofs -# -# id :bigint(8) not null, primary key -# account_id :bigint(8) -# provider :string default(""), not null -# provider_username :string default(""), not null -# token :text default(""), not null -# verified :boolean default(FALSE), not null -# live :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null -# - -class AccountIdentityProof < ApplicationRecord - belongs_to :account - - validates :provider, inclusion: { in: ProofProvider::SUPPORTED_PROVIDERS } - validates :provider_username, format: { with: /\A[a-z0-9_]+\z/i }, length: { minimum: 2, maximum: 30 } - validates :provider_username, uniqueness: { scope: [:account_id, :provider] } - validates :token, format: { with: /\A[a-f0-9]+\z/ }, length: { maximum: 66 } - - validate :validate_with_provider, if: :token_changed? - - scope :active, -> { where(verified: true, live: true) } - - after_commit :queue_worker, if: :saved_change_to_token? - - delegate :refresh!, :on_success_path, :badge, to: :provider_instance - - def provider_instance - @provider_instance ||= ProofProvider.find(provider, self) - end - - private - - def queue_worker - provider_instance.worker_class.perform_async(id) - end - - def validate_with_provider - provider_instance.validate! - end -end diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account_associations.rb index f2a4eae77..f9e7a3bea 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account_associations.rb @@ -7,8 +7,7 @@ module AccountAssociations # Local users has_one :user, inverse_of: :account, dependent: :destroy - # Identity proofs - has_many :identity_proofs, class_name: 'AccountIdentityProof', dependent: :destroy, inverse_of: :account + # E2EE has_many :devices, dependent: :destroy, inverse_of: :account # Timelines diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb index 8d37c6e56..119773e6b 100644 --- a/app/models/concerns/account_merging.rb +++ b/app/models/concerns/account_merging.rb @@ -13,7 +13,7 @@ module AccountMerging owned_classes = [ Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite, - Follow, FollowRequest, Block, Mute, AccountIdentityProof, + Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin, AccountStat, ListAccount, PollVote, Mention, AccountDeletionRequest, AccountNote, FollowRecommendationSuppression ] diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index a7d948976..48707aa16 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -6,8 +6,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer context :security context_extensions :manually_approves_followers, :featured, :also_known_as, - :moved_to, :property_value, :identity_proof, - :discoverable, :olm, :suspended + :moved_to, :property_value, :discoverable, :olm, :suspended attributes :id, :type, :following, :followers, :inbox, :outbox, :featured, :featured_tags, @@ -143,7 +142,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def virtual_attachments - object.suspended? ? [] : (object.fields + object.identity_proofs.active) + object.suspended? ? [] : object.fields end def moved_to diff --git a/app/serializers/rest/identity_proof_serializer.rb b/app/serializers/rest/identity_proof_serializer.rb deleted file mode 100644 index 0e7415935..000000000 --- a/app/serializers/rest/identity_proof_serializer.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class REST::IdentityProofSerializer < ActiveModel::Serializer - attributes :provider, :provider_username, :updated_at, :proof_url, :profile_url - - def proof_url - object.badge.proof_url - end - - def profile_url - object.badge.profile_url - end - - def provider - object.provider.capitalize - end -end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 4ab6912e5..ec5140720 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -27,7 +27,6 @@ class ActivityPub::ProcessAccountService < BaseService create_account if @account.nil? update_account process_tags - process_attachments process_duplicate_accounts! if @options[:verified_webfinger] else @@ -301,23 +300,6 @@ class ActivityPub::ProcessAccountService < BaseService end end - def process_attachments - return if @json['attachment'].blank? - - previous_proofs = @account.identity_proofs.to_a - current_proofs = [] - - as_array(@json['attachment']).each do |attachment| - next unless equals_or_includes?(attachment['type'], 'IdentityProof') - current_proofs << process_identity_proof(attachment) - end - - previous_proofs.each do |previous_proof| - next if current_proofs.any? { |current_proof| current_proof.id == previous_proof.id } - previous_proof.delete - end - end - def process_emoji(tag) return if skip_download? return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank? @@ -334,12 +316,4 @@ class ActivityPub::ProcessAccountService < BaseService emoji.image_remote_url = image_url emoji.save end - - def process_identity_proof(attachment) - provider = attachment['signatureAlgorithm'] - provider_username = attachment['name'] - token = attachment['signatureValue'] - - @account.identity_proofs.where(provider: provider, provider_username: provider_username).find_or_create_by(provider: provider, provider_username: provider_username, token: token) - end end diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb index ac571d7e2..0e3fedfe7 100644 --- a/app/services/delete_account_service.rb +++ b/app/services/delete_account_service.rb @@ -17,7 +17,6 @@ class DeleteAccountService < BaseService domain_blocks featured_tags follow_requests - identity_proofs list_accounts migrations mute_relationships @@ -45,7 +44,6 @@ class DeleteAccountService < BaseService domain_blocks featured_tags follow_requests - identity_proofs list_accounts migrations mute_relationships diff --git a/app/views/accounts/_bio.html.haml b/app/views/accounts/_bio.html.haml index efc26d136..e8a49a1aa 100644 --- a/app/views/accounts/_bio.html.haml +++ b/app/views/accounts/_bio.html.haml @@ -1,16 +1,8 @@ -- proofs = account.identity_proofs.active - fields = account.fields .public-account-bio - - unless fields.empty? && proofs.empty? + - unless fields.empty? .account__header__fields - - proofs.each do |proof| - %dl - %dt= proof.provider.capitalize - %dd.verified - = link_to fa_icon('check'), proof.badge.proof_url, class: 'verified__mark', title: t('accounts.link_verified_on', date: l(proof.updated_at)) - = link_to proof.provider_username, proof.badge.profile_url - - fields.each do |field| %dl %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true) diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 66eb49342..2b6e28e8d 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -8,20 +8,12 @@ = render 'application/card', account: @account - account = @account -- proofs = account.identity_proofs.active - fields = account.fields -- unless fields.empty? && proofs.empty? && account.note.blank? +- unless fields.empty? && account.note.blank? .admin-account-bio - - unless fields.empty? && proofs.empty? + - unless fields.empty? %div .account__header__fields - - proofs.each do |proof| - %dl - %dt= proof.provider.capitalize - %dd.verified - = link_to fa_icon('check'), proof.badge.proof_url, class: 'verified__mark', title: t('accounts.link_verified_on', date: l(proof.updated_at)) - = link_to proof.provider_username, proof.badge.profile_url - - fields.each do |field| %dl %dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true) diff --git a/app/views/settings/identity_proofs/_proof.html.haml b/app/views/settings/identity_proofs/_proof.html.haml deleted file mode 100644 index 14e8e91be..000000000 --- a/app/views/settings/identity_proofs/_proof.html.haml +++ /dev/null @@ -1,21 +0,0 @@ -%tr - %td - = link_to proof.badge.profile_url, class: 'name-tag' do - = image_tag proof.badge.avatar_url, width: 15, height: 15, alt: '', class: 'avatar' - %span.username - = proof.provider_username - %span= "(#{proof.provider.capitalize})" - - %td - - if proof.live? - %span.positive-hint - = fa_icon 'check-circle fw' - = t('identity_proofs.active') - - else - %span.negative-hint - = fa_icon 'times-circle fw' - = t('identity_proofs.inactive') - - %td - = table_link_to 'external-link', t('identity_proofs.view_proof'), proof.badge.proof_url if proof.badge.proof_url - = table_link_to 'trash', t('identity_proofs.remove'), settings_identity_proof_path(proof), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } diff --git a/app/views/settings/identity_proofs/index.html.haml b/app/views/settings/identity_proofs/index.html.haml deleted file mode 100644 index d0ea03ecd..000000000 --- a/app/views/settings/identity_proofs/index.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -- content_for :page_title do - = t('settings.identity_proofs') - -%p= t('identity_proofs.explanation_html') - -- unless @proofs.empty? - %hr.spacer/ - - .table-wrapper - %table.table - %thead - %tr - %th= t('identity_proofs.identity') - %th= t('identity_proofs.status') - %th - %tbody - = render partial: 'settings/identity_proofs/proof', collection: @proofs, as: :proof diff --git a/app/views/settings/identity_proofs/new.html.haml b/app/views/settings/identity_proofs/new.html.haml deleted file mode 100644 index 5e4e9895d..000000000 --- a/app/views/settings/identity_proofs/new.html.haml +++ /dev/null @@ -1,36 +0,0 @@ -- content_for :page_title do - = t('identity_proofs.authorize_connection_prompt') - -.form-container - .oauth-prompt - %h2= t('identity_proofs.authorize_connection_prompt') - - = simple_form_for @proof, url: settings_identity_proofs_url, html: { method: :post } do |f| - = f.input :provider, as: :hidden - = f.input :provider_username, as: :hidden - = f.input :token, as: :hidden - - = hidden_field_tag :user_agent, params[:user_agent] - - .connection-prompt - .connection-prompt__row.connection-prompt__connection - .connection-prompt__column - = image_tag current_account.avatar.url(:original), size: 96, class: 'account__avatar' - - %p= t('identity_proofs.i_am_html', username: content_tag(:strong,current_account.username), service: site_hostname) - - .connection-prompt__column.connection-prompt__column-sep - = fa_icon 'link' - - .connection-prompt__column - = image_tag @proof.badge.avatar_url, size: 96, class: 'account__avatar' - - %p= t('identity_proofs.i_am_html', username: content_tag(:strong, @proof.provider_username), service: @proof.provider.capitalize) - - .connection-prompt__post - = f.input :post_status, label: t('identity_proofs.publicize_checkbox'), as: :boolean, wrapper: :with_label, :input_html => { checked: true } - - = f.input :status_text, as: :text, input_html: { value: t('identity_proofs.publicize_toot', username: @proof.provider_username, service: @proof.provider.capitalize, url: @proof.badge.proof_url), rows: 4 } - - = f.button :button, t('identity_proofs.authorize'), type: :submit - = link_to t('simple_form.no'), settings_identity_proofs_url, class: 'button negative' diff --git a/config/locales/en.yml b/config/locales/en.yml index c98b82801..1aa96ba0f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -985,26 +985,6 @@ en: other: Something isn't quite right yet! Please review %{count} errors below html_validator: invalid_markup: 'contains invalid HTML markup: %{error}' - identity_proofs: - active: Active - authorize: Yes, authorize - authorize_connection_prompt: Authorize this cryptographic connection? - errors: - failed: The cryptographic connection failed. Please try again from %{provider}. - keybase: - invalid_token: Keybase tokens are hashes of signatures and must be 66 hex characters - verification_failed: Keybase does not recognize this token as a signature of Keybase user %{kb_username}. Please retry from Keybase. - wrong_user: Cannot create a proof for %{proving} while logged in as %{current}. Log in as %{proving} and try again. - explanation_html: Here you can cryptographically connect your other identities from other platforms, such as Keybase. This lets other people send you encrypted messages on those platforms and allows them to trust that the content you send them comes from you. - i_am_html: I am %{username} on %{service}. - identity: Identity - inactive: Inactive - publicize_checkbox: 'And toot this:' - publicize_toot: 'It is proven! I am %{username} on %{service}: %{url}' - remove: Remove proof from account - removed: Successfully removed proof from account - status: Verification status - view_proof: View proof imports: errors: over_rows_processing_limit: contains more than %{count} rows @@ -1279,7 +1259,6 @@ en: edit_profile: Edit profile export: Data export featured_tags: Featured hashtags - identity_proofs: Identity proofs import: Import import_and_export: Import and export migrate: Account migration diff --git a/config/navigation.rb b/config/navigation.rb index 477d1c9ff..99743c222 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -7,7 +7,6 @@ SimpleNavigation::Configuration.run do |navigation| n.item :profile, safe_join([fa_icon('user fw'), t('settings.profile')]), settings_profile_url, if: -> { current_user.functional? } do |s| s.item :profile, safe_join([fa_icon('pencil fw'), t('settings.appearance')]), settings_profile_url s.item :featured_tags, safe_join([fa_icon('hashtag fw'), t('settings.featured_tags')]), settings_featured_tags_url - s.item :identity_proofs, safe_join([fa_icon('key fw'), t('settings.identity_proofs')]), settings_identity_proofs_path, highlights_on: %r{/settings/identity_proofs*}, if: proc { current_account.identity_proofs.exists? } end n.item :preferences, safe_join([fa_icon('cog fw'), t('settings.preferences')]), settings_preferences_url, if: -> { current_user.functional? } do |s| diff --git a/config/routes.rb b/config/routes.rb index c7317d173..5f73129ea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,7 +25,6 @@ Rails.application.routes.draw do get '.well-known/nodeinfo', to: 'well_known/nodeinfo#index', as: :nodeinfo, defaults: { format: 'json' } get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger get '.well-known/change-password', to: redirect('/auth/edit') - get '.well-known/keybase-proof-config', to: 'well_known/keybase_proof_config#show' get '/nodeinfo/2.0', to: 'well_known/nodeinfo#show', as: :nodeinfo_schema @@ -146,8 +145,6 @@ Rails.application.routes.draw do resource :confirmation, only: [:new, :create] end - resources :identity_proofs, only: [:index, :new, :create, :destroy] - resources :applications, except: [:edit] do member do post :regenerate @@ -332,9 +329,6 @@ Rails.application.routes.draw do # OEmbed get '/oembed', to: 'oembed#show', as: :oembed - # Identity proofs - get :proofs, to: 'proofs#index' - # JSON / REST API namespace :v1 do resources :statuses, only: [:create, :show, :destroy] do diff --git a/db/post_migrate/20211126000907_drop_account_identity_proofs.rb b/db/post_migrate/20211126000907_drop_account_identity_proofs.rb new file mode 100644 index 000000000..44a6f1f08 --- /dev/null +++ b/db/post_migrate/20211126000907_drop_account_identity_proofs.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class DropAccountIdentityProofs < ActiveRecord::Migration[5.2] + disable_ddl_transaction! + + def up + drop_table :account_identity_proofs + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/schema.rb b/db/schema.rb index 00969daf1..54a46730c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_11_23_212714) do +ActiveRecord::Schema.define(version: 2021_11_26_000907) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -51,18 +51,6 @@ ActiveRecord::Schema.define(version: 2021_11_23_212714) do t.index ["account_id", "domain"], name: "index_account_domain_blocks_on_account_id_and_domain", unique: true end - create_table "account_identity_proofs", force: :cascade do |t| - t.bigint "account_id" - t.string "provider", default: "", null: false - t.string "provider_username", default: "", null: false - t.text "token", default: "", null: false - t.boolean "verified", default: false, null: false - t.boolean "live", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["account_id", "provider", "provider_username"], name: "index_account_proofs_on_account_and_provider_and_username", unique: true - end - create_table "account_migrations", force: :cascade do |t| t.bigint "account_id" t.string "acct", default: "", null: false @@ -1010,7 +998,6 @@ ActiveRecord::Schema.define(version: 2021_11_23_212714) do add_foreign_key "account_conversations", "conversations", on_delete: :cascade add_foreign_key "account_deletion_requests", "accounts", on_delete: :cascade add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade - add_foreign_key "account_identity_proofs", "accounts", on_delete: :cascade add_foreign_key "account_migrations", "accounts", column: "target_account_id", on_delete: :nullify add_foreign_key "account_migrations", "accounts", on_delete: :cascade add_foreign_key "account_moderation_notes", "accounts" diff --git a/spec/controllers/api/proofs_controller_spec.rb b/spec/controllers/api/proofs_controller_spec.rb deleted file mode 100644 index 2fe615005..000000000 --- a/spec/controllers/api/proofs_controller_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'rails_helper' - -describe Api::ProofsController do - let(:alice) { Fabricate(:account, username: 'alice') } - - before do - stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_valid.json?domain=cb6e6126.ngrok.io&kb_username=crypto_alice&sig_hash=111111111111111111111111111111111111111111111111111111111111111111&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":false}') - stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_live.json?domain=cb6e6126.ngrok.io&kb_username=crypto_alice&sig_hash=111111111111111111111111111111111111111111111111111111111111111111&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}') - stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_valid.json?domain=cb6e6126.ngrok.io&kb_username=hidden_alice&sig_hash=222222222222222222222222222222222222222222222222222222222222222222&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}') - stub_request(:get, 'https://keybase.io/_/api/1.0/sig/proof_live.json?domain=cb6e6126.ngrok.io&kb_username=hidden_alice&sig_hash=222222222222222222222222222222222222222222222222222222222222222222&username=alice').to_return(status: 200, body: '{"proof_valid":true,"proof_live":true}') - end - - describe 'GET #index' do - describe 'with a non-existent username' do - it '404s' do - get :index, params: { username: 'nonexistent', provider: 'keybase' } - - expect(response).to have_http_status(:not_found) - end - end - - describe 'with a user that has no proofs' do - it 'is an empty list of signatures' do - get :index, params: { username: alice.username, provider: 'keybase' } - - expect(body_as_json[:signatures]).to eq [] - end - end - - describe 'with a user that has a live, valid proof' do - let(:token1) { '111111111111111111111111111111111111111111111111111111111111111111' } - let(:kb_name1) { 'crypto_alice' } - - before do - Fabricate(:account_identity_proof, account: alice, verified: true, live: true, token: token1, provider_username: kb_name1) - end - - it 'is a list with that proof in it' do - get :index, params: { username: alice.username, provider: 'keybase' } - - expect(body_as_json[:signatures]).to eq [ - { kb_username: kb_name1, sig_hash: token1 }, - ] - end - - describe 'add one that is neither live nor valid' do - let(:token2) { '222222222222222222222222222222222222222222222222222222222222222222' } - let(:kb_name2) { 'hidden_alice' } - - before do - Fabricate(:account_identity_proof, account: alice, verified: false, live: false, token: token2, provider_username: kb_name2) - end - - it 'is a list with both proofs' do - get :index, params: { username: alice.username, provider: 'keybase' } - - expect(body_as_json[:signatures]).to eq [ - { kb_username: kb_name1, sig_hash: token1 }, - { kb_username: kb_name2, sig_hash: token2 }, - ] - end - end - end - - describe 'a user that has an avatar' do - let(:alice) { Fabricate(:account, username: 'alice', avatar: attachment_fixture('avatar.gif')) } - - context 'and a proof' do - let(:token1) { '111111111111111111111111111111111111111111111111111111111111111111' } - let(:kb_name1) { 'crypto_alice' } - - before do - Fabricate(:account_identity_proof, account: alice, verified: true, live: true, token: token1, provider_username: kb_name1) - get :index, params: { username: alice.username, provider: 'keybase' } - end - - it 'has two keys: signatures and avatar' do - expect(body_as_json.keys).to match_array [:signatures, :avatar] - end - - it 'has the correct signatures' do - expect(body_as_json[:signatures]).to eq [ - { kb_username: kb_name1, sig_hash: token1 }, - ] - end - - it 'has the correct avatar url' do - expect(body_as_json[:avatar]).to match "https://cb6e6126.ngrok.io#{alice.avatar.url}" - end - end - end - end -end diff --git a/spec/controllers/settings/identity_proofs_controller_spec.rb b/spec/controllers/settings/identity_proofs_controller_spec.rb deleted file mode 100644 index 16f236227..000000000 --- a/spec/controllers/settings/identity_proofs_controller_spec.rb +++ /dev/null @@ -1,186 +0,0 @@ -require 'rails_helper' - -describe Settings::IdentityProofsController do - include RoutingHelper - render_views - - let(:user) { Fabricate(:user) } - let(:valid_token) { '1'*66 } - let(:kbname) { 'kbuser' } - let(:provider) { 'keybase' } - let(:findable_id) { Faker::Number.number(digits: 5) } - let(:unfindable_id) { Faker::Number.number(digits: 5) } - let(:new_proof_params) do - { provider: provider, provider_username: kbname, token: valid_token, username: user.account.username } - end - let(:status_text) { "i just proved that i am also #{kbname} on #{provider}." } - let(:status_posting_params) do - { post_status: '0', status_text: status_text } - end - let(:postable_params) do - { account_identity_proof: new_proof_params.merge(status_posting_params) } - end - - before do - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:status) { { 'proof_valid' => true, 'proof_live' => true } } - sign_in user, scope: :user - end - - describe 'new proof creation' do - context 'GET #new' do - before do - allow_any_instance_of(ProofProvider::Keybase::Badge).to receive(:avatar_url) { full_pack_url('media/images/void.png') } - end - - context 'with all of the correct params' do - it 'renders the template' do - get :new, params: new_proof_params - expect(response).to render_template(:new) - end - end - - context 'without any params' do - it 'redirects to :index' do - get :new, params: {} - expect(response).to redirect_to settings_identity_proofs_path - end - end - - context 'with params to prove a different, not logged-in user' do - let(:wrong_user_params) { new_proof_params.merge(username: 'someone_else') } - - it 'shows a helpful alert' do - get :new, params: wrong_user_params - expect(flash[:alert]).to eq I18n.t('identity_proofs.errors.wrong_user', proving: 'someone_else', current: user.account.username) - end - end - - context 'with params to prove the same username cased differently' do - let(:capitalized_username) { new_proof_params.merge(username: user.account.username.upcase) } - - it 'renders the new template' do - get :new, params: capitalized_username - expect(response).to render_template(:new) - end - end - end - - context 'POST #create' do - context 'when saving works' do - before do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true } - allow_any_instance_of(AccountIdentityProof).to receive(:on_success_path) { root_url } - end - - it 'serializes a ProofProvider::Keybase::Worker' do - expect(ProofProvider::Keybase::Worker).to receive(:perform_async) - post :create, params: postable_params - end - - it 'delegates redirection to the proof provider' do - expect_any_instance_of(AccountIdentityProof).to receive(:on_success_path) - post :create, params: postable_params - expect(response).to redirect_to root_url - end - - it 'does not post a status' do - expect(PostStatusService).not_to receive(:new) - post :create, params: postable_params - end - - context 'and the user has requested to post a status' do - let(:postable_params_with_status) do - postable_params.tap { |p| p[:account_identity_proof][:post_status] = '1' } - end - - it 'posts a status' do - expect_any_instance_of(PostStatusService).to receive(:call).with(user.account, text: status_text) - - post :create, params: postable_params_with_status - end - end - end - - context 'when saving fails' do - before do - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { false } - end - - it 'redirects to :index' do - post :create, params: postable_params - expect(response).to redirect_to settings_identity_proofs_path - end - - it 'flashes a helpful message' do - post :create, params: postable_params - expect(flash[:alert]).to eq I18n.t('identity_proofs.errors.failed', provider: 'Keybase') - end - end - - context 'it can also do an update if the provider and username match an existing proof' do - before do - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true } - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - Fabricate(:account_identity_proof, account: user.account, provider: provider, provider_username: kbname) - allow_any_instance_of(AccountIdentityProof).to receive(:on_success_path) { root_url } - end - - it 'calls update with the new token' do - expect_any_instance_of(AccountIdentityProof).to receive(:save) do |proof| - expect(proof.token).to eq valid_token - end - - post :create, params: postable_params - end - end - end - end - - describe 'GET #index' do - context 'with no existing proofs' do - it 'shows the helpful explanation' do - get :index - expect(response.body).to match I18n.t('identity_proofs.explanation_html') - end - end - - context 'with two proofs' do - before do - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true } - @proof1 = Fabricate(:account_identity_proof, account: user.account) - @proof2 = Fabricate(:account_identity_proof, account: user.account) - allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') } - allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {} - end - - it 'has the first proof username on the page' do - get :index - expect(response.body).to match /#{Regexp.quote(@proof1.provider_username)}/ - end - - it 'has the second proof username on the page' do - get :index - expect(response.body).to match /#{Regexp.quote(@proof2.provider_username)}/ - end - end - end - - describe 'DELETE #destroy' do - before do - allow_any_instance_of(ProofProvider::Keybase::Verifier).to receive(:valid?) { true } - @proof1 = Fabricate(:account_identity_proof, account: user.account) - allow_any_instance_of(AccountIdentityProof).to receive(:badge) { double(avatar_url: '', profile_url: '', proof_url: '') } - allow_any_instance_of(AccountIdentityProof).to receive(:refresh!) {} - delete :destroy, params: { id: @proof1.id } - end - - it 'redirects to :index' do - expect(response).to redirect_to settings_identity_proofs_path - end - - it 'removes the proof' do - expect(AccountIdentityProof.where(id: @proof1.id).count).to eq 0 - end - end -end diff --git a/spec/controllers/well_known/keybase_proof_config_controller_spec.rb b/spec/controllers/well_known/keybase_proof_config_controller_spec.rb deleted file mode 100644 index 00f251c3c..000000000 --- a/spec/controllers/well_known/keybase_proof_config_controller_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'rails_helper' - -describe WellKnown::KeybaseProofConfigController, type: :controller do - render_views - - describe 'GET #show' do - it 'renders json' do - get :show - - expect(response).to have_http_status(200) - expect(response.media_type).to eq 'application/json' - expect { JSON.parse(response.body) }.not_to raise_exception - end - end -end diff --git a/spec/fabricators/account_identity_proof_fabricator.rb b/spec/fabricators/account_identity_proof_fabricator.rb deleted file mode 100644 index 7b932fa96..000000000 --- a/spec/fabricators/account_identity_proof_fabricator.rb +++ /dev/null @@ -1,8 +0,0 @@ -Fabricator(:account_identity_proof) do - account - provider 'keybase' - provider_username { sequence(:provider_username) { |i| "#{Faker::Lorem.characters(number: 15)}" } } - token { sequence(:token) { |i| "#{i}#{Faker::Crypto.sha1()*2}"[0..65] } } - verified false - live false -end diff --git a/spec/lib/proof_provider/keybase/verifier_spec.rb b/spec/lib/proof_provider/keybase/verifier_spec.rb deleted file mode 100644 index 0081a735d..000000000 --- a/spec/lib/proof_provider/keybase/verifier_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'rails_helper' - -describe ProofProvider::Keybase::Verifier do - let(:my_domain) { Rails.configuration.x.local_domain } - - let(:keybase_proof) do - local_proof = AccountIdentityProof.new( - provider: 'Keybase', - provider_username: 'cryptoalice', - token: '11111111111111111111111111' - ) - - described_class.new('alice', 'cryptoalice', '11111111111111111111111111', my_domain) - end - - let(:query_params) do - "domain=#{my_domain}&kb_username=cryptoalice&sig_hash=11111111111111111111111111&username=alice" - end - - describe '#valid?' do - let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_valid.json' } - - context 'when valid' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":true}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns true' do - expect(keybase_proof.valid?).to eq true - end - end - - context 'when invalid' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":false}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns false' do - expect(keybase_proof.valid?).to eq false - end - end - - context 'with an unexpected api response' do - before do - json_response_body = '{"status":{"code":100,"desc":"wrong size hex_id","fields":{"sig_hash":"wrong size hex_id"},"name":"INPUT_ERROR"}}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'swallows the error and returns false' do - expect(keybase_proof.valid?).to eq false - end - end - end - - describe '#status' do - let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_live.json' } - - context 'with a normal response' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_live":false,"proof_valid":true}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns the status fields as proof_valid and proof_live' do - expect(keybase_proof.status).to include({ 'proof_valid' => true, 'proof_live' => false }) - end - end - - context 'with an unexpected keybase response' do - before do - json_response_body = '{"status":{"code":100,"desc":"missing non-optional field sig_hash","fields":{"sig_hash":"missing non-optional field sig_hash"},"name":"INPUT_ERROR"}}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'raises a ProofProvider::Keybase::UnexpectedResponseError' do - expect { keybase_proof.status }.to raise_error ProofProvider::Keybase::UnexpectedResponseError - end - end - end -end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 1b1d878a7..7728b9ba8 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -30,51 +30,6 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end end - context 'identity proofs' do - let(:payload) do - { - id: 'https://foo.test', - type: 'Actor', - inbox: 'https://foo.test/inbox', - attachment: [ - { type: 'IdentityProof', name: 'Alice', signatureAlgorithm: 'keybase', signatureValue: 'a' * 66 }, - ], - }.with_indifferent_access - end - - it 'parses out of attachment' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - - account = subject.call('alice', 'example.com', payload) - - expect(account.identity_proofs.count).to eq 1 - - proof = account.identity_proofs.first - - expect(proof.provider).to eq 'keybase' - expect(proof.provider_username).to eq 'Alice' - expect(proof.token).to eq 'a' * 66 - end - - it 'removes no longer present proofs' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - - account = Fabricate(:account, username: 'alice', domain: 'example.com') - old_proof = Fabricate(:account_identity_proof, account: account, provider: 'keybase', provider_username: 'Bob', token: 'b' * 66) - - subject.call('alice', 'example.com', payload) - - expect(account.identity_proofs.count).to eq 1 - expect(account.identity_proofs.find_by(id: old_proof.id)).to be_nil - end - - it 'queues a validity check on the proof' do - allow(ProofProvider::Keybase::Worker).to receive(:perform_async) - account = subject.call('alice', 'example.com', payload) - expect(ProofProvider::Keybase::Worker).to have_received(:perform_async) - end - end - context 'when account is not suspended' do let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') } From 9a77a16087e067f90d5ee1543f231b706f500c98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:01:31 +0900 Subject: [PATCH 02/15] Bump @babel/plugin-proposal-decorators from 7.16.0 to 7.16.4 (#17027) Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.16.0 to 7.16.4. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.16.4/packages/babel-plugin-proposal-decorators) --- updated-dependencies: - dependency-name: "@babel/plugin-proposal-decorators" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 477409119..c5dea7296 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "private": true, "dependencies": { "@babel/core": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", "@babel/plugin-transform-react-inline-elements": "^7.16.0", "@babel/plugin-transform-runtime": "^7.16.0", "@babel/preset-env": "^7.16.0", diff --git a/yarn.lock b/yarn.lock index 1d9b95f8d..97f2c602f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -327,10 +327,10 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-decorators@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.0.tgz#515db5f6891611c0d176b63ede0844fbd9be797b" - integrity sha512-ttvhKuVnQwoNQrcTd1oe6o49ahaZ1kns1fsJKzTVOaS/FJDJoK4qzgVS68xzJhYUMgTnbXW6z/T6rlP3lL7tJw== +"@babel/plugin-proposal-decorators@^7.16.4": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.4.tgz#9b35ce0716425a93b978e79099e5f7ba217c1364" + integrity sha512-RESBNX16eNqnBeEVR5sCJpnW0mHiNLNNvGA8PrRuK/4ZJ4TO+6bHleRUuGQYDERVySOKtOhSya/C4MIhwAMAgg== dependencies: "@babel/helper-create-class-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" From 5b8928fef95c842c338482cbc99ecabe763b01d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:01:53 +0900 Subject: [PATCH 03/15] Bump reselect from 4.1.2 to 4.1.4 (#17025) Bumps [reselect](https://github.com/reduxjs/reselect) from 4.1.2 to 4.1.4. - [Release notes](https://github.com/reduxjs/reselect/releases) - [Changelog](https://github.com/reduxjs/reselect/blob/master/CHANGELOG.md) - [Commits](https://github.com/reduxjs/reselect/compare/v4.1.2...v4.1.4) --- updated-dependencies: - dependency-name: reselect dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index c5dea7296..6aa6ac4ce 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ "regenerator-runtime": "^0.13.9", "rellax": "^1.12.1", "requestidlecallback": "^0.3.0", - "reselect": "^4.1.2", + "reselect": "^4.1.4", "rimraf": "^3.0.2", "sass": "^1.43.4", "sass-loader": "^10.2.0", diff --git a/yarn.lock b/yarn.lock index 97f2c602f..6aec81309 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9256,10 +9256,10 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -reselect@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.2.tgz#7bf642992d143d4f3b0f2dca8aa52018808a1d51" - integrity sha512-wg60ebcPOtxcptIUfrr7Jt3h4BR86cCW3R7y4qt65lnNb4yz4QgrXcbSioVsIOYguyz42+XTHIyJ5TEruzkFgQ== +reselect@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.4.tgz#66df0aff41b6ee0f51e2cc17cfaf2c1995916f32" + integrity sha512-i1LgXw8DKSU5qz1EV0ZIKz4yIUHJ7L3bODh+Da6HmVSm9vdL/hG7IpbgzQ3k2XSirzf8/eI7OMEs81gb1VV2fQ== resolve-cwd@^2.0.0: version "2.0.0" From 45f8eb110a545320d9e924acc1228b534d3a9637 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:06:12 +0900 Subject: [PATCH 04/15] Bump eslint-plugin-react from 7.27.0 to 7.27.1 (#17022) Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.27.0 to 7.27.1. - [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases) - [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.27.0...v7.27.1) --- updated-dependencies: - dependency-name: eslint-plugin-react dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6aa6ac4ce..7d78ad83c 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "eslint-plugin-import": "~2.25.3", "eslint-plugin-jsx-a11y": "~6.5.1", "eslint-plugin-promise": "~5.1.1", - "eslint-plugin-react": "~7.27.0", + "eslint-plugin-react": "~7.27.1", "jest": "^27.3.1", "raf": "^3.4.1", "react-intl-translations-manager": "^5.0.3", diff --git a/yarn.lock b/yarn.lock index 6aec81309..2e8099a3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4258,10 +4258,10 @@ eslint-plugin-promise@~5.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz#9674d11c056d1bafac38e4a3a9060be740988d90" integrity sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA== -eslint-plugin-react@~7.27.0: - version "7.27.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.27.0.tgz#f952c76517a3915b81c7788b220b2b4c96703124" - integrity sha512-0Ut+CkzpppgFtoIhdzi2LpdpxxBvgFf99eFqWxJnUrO7mMe0eOiNpou6rvNYeVVV6lWZvTah0BFne7k5xHjARg== +eslint-plugin-react@~7.27.1: + version "7.27.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz#469202442506616f77a854d91babaae1ec174b45" + integrity sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA== dependencies: array-includes "^3.1.4" array.prototype.flatmap "^1.2.5" From 0909874c08bd87a0ce4791a1faa2115f1b2ec9e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 14:06:23 +0900 Subject: [PATCH 05/15] Bump sprockets-rails from 3.4.0 to 3.4.1 (#17021) Bumps [sprockets-rails](https://github.com/rails/sprockets-rails) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/rails/sprockets-rails/releases) - [Commits](https://github.com/rails/sprockets-rails/compare/v3.4.0...v3.4.1) --- updated-dependencies: - dependency-name: sprockets-rails dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 30fe32a22..4f090ad0f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -598,7 +598,7 @@ GEM sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.4.0) + sprockets-rails (3.4.1) actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) From 53aca8aecf33ca47825da8b2d10ecff089df5c64 Mon Sep 17 00:00:00 2001 From: OSAMU SATO Date: Fri, 26 Nov 2021 16:29:53 +0900 Subject: [PATCH 06/15] Add batch_size option to bin/tootctl search deploy (#17049) --- lib/mastodon/search_cli.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb index 2d1ca1c05..e36af24f4 100644 --- a/lib/mastodon/search_cli.rb +++ b/lib/mastodon/search_cli.rb @@ -17,6 +17,7 @@ module Mastodon ].freeze option :concurrency, type: :numeric, default: 2, aliases: [:c], desc: 'Workload will be split between this number of threads' + option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch' option :only, type: :array, enum: %w(accounts tags statuses), desc: 'Only process these indices' desc 'deploy', 'Create or upgrade ElasticSearch indices and populate them' long_desc <<~LONG_DESC @@ -35,6 +36,11 @@ module Mastodon exit(1) end + if options[:batch_size] < 1 + say('Cannot run with this batch_size setting, must be at least 1', :red) + exit(1) + end + indices = begin if options[:only] options[:only].map { |str| "#{str.camelize}Index".constantize } @@ -73,7 +79,7 @@ module Mastodon # is uneconomical. So we only ever add. indices.each do |index| progress.title = "Importing #{index} " - batch_size = 1_000 + batch_size = options[:batch_size] slice_size = (batch_size / options[:concurrency]).ceil index.adapter.default_scope.reorder(nil).find_in_batches(batch_size: batch_size) do |batch| From 06631fdc53d6c464aa9db4e31d35f1fe354bcc95 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Fri, 26 Nov 2021 16:30:02 +0900 Subject: [PATCH 07/15] Fix ElasticSearch to Elasticsearch (#17050) --- .env.nanobox | 2 +- .env.production.sample | 2 +- config/initializers/chewy.rb | 6 +++--- lib/mastodon/search_cli.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.nanobox b/.env.nanobox index d61673836..ad941c947 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -13,7 +13,7 @@ DB_PORT=5432 # DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano -# Optional ElasticSearch configuration +# Optional Elasticsearch configuration ES_ENABLED=true ES_HOST=$DATA_ELASTIC_HOST ES_PORT=9200 diff --git a/.env.production.sample b/.env.production.sample index c24c31c9b..8eeff3794 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -29,7 +29,7 @@ DB_NAME=mastodon_production DB_PASS= DB_PORT=5432 -# ElasticSearch (optional) +# Elasticsearch (optional) # ------------------------ ES_ENABLED=true ES_HOST=localhost diff --git a/config/initializers/chewy.rb b/config/initializers/chewy.rb index fbbcbbcde..f303fc54d 100644 --- a/config/initializers/chewy.rb +++ b/config/initializers/chewy.rb @@ -17,7 +17,7 @@ Chewy.settings = { } # We use our own async strategy even outside the request-response -# cycle, which takes care of checking if ElasticSearch is enabled +# cycle, which takes care of checking if Elasticsearch is enabled # or not. However, mind that for the Rails console, the :urgent # strategy is set automatically with no way to override it. Chewy.root_strategy = :custom_sidekiq @@ -32,8 +32,8 @@ module Chewy end end -# ElasticSearch uses Faraday internally. Faraday interprets the +# Elasticsearch uses Faraday internally. Faraday interprets the # http_proxy env variable by default which leads to issues when # Mastodon is run with hidden services enabled, because -# ElasticSearch is *not* supposed to be accessed through a proxy +# Elasticsearch is *not* supposed to be accessed through a proxy Faraday.ignore_env_proxy = true diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb index e36af24f4..6ad9d7b6a 100644 --- a/lib/mastodon/search_cli.rb +++ b/lib/mastodon/search_cli.rb @@ -19,9 +19,9 @@ module Mastodon option :concurrency, type: :numeric, default: 2, aliases: [:c], desc: 'Workload will be split between this number of threads' option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch' option :only, type: :array, enum: %w(accounts tags statuses), desc: 'Only process these indices' - desc 'deploy', 'Create or upgrade ElasticSearch indices and populate them' + desc 'deploy', 'Create or upgrade Elasticsearch indices and populate them' long_desc <<~LONG_DESC - If ElasticSearch is empty, this command will create the necessary indices + If Elasticsearch is empty, this command will create the necessary indices and then import data from the database into those indices. This command will also upgrade indices if the underlying schema has been From 912c6b3f4933415315c66b530332f6ea8d2cd080 Mon Sep 17 00:00:00 2001 From: zunda Date: Fri, 26 Nov 2021 21:03:35 +0000 Subject: [PATCH 08/15] Upgrade bootsnap for Ruby 3.0.3 (#17064) https://github.com/Shopify/bootsnap/blob/v1.9.3/CHANGELOG.md#193 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 57d78f637..699e7c694 100644 --- a/Gemfile +++ b/Gemfile @@ -26,7 +26,7 @@ gem 'blurhash', '~> 0.1' gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.8' -gem 'bootsnap', '~> 1.9.1', require: false +gem 'bootsnap', '~> 1.9.2', require: false gem 'browser' gem 'charlock_holmes', '~> 0.7.7' gem 'iso-639' diff --git a/Gemfile.lock b/Gemfile.lock index 4f090ad0f..d52af8269 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -104,7 +104,7 @@ GEM debug_inspector (>= 0.0.1) blurhash (0.1.5) ffi (~> 1.14) - bootsnap (1.9.1) + bootsnap (1.9.3) msgpack (~> 1.0) brakeman (5.1.2) browser (4.2.0) @@ -689,7 +689,7 @@ DEPENDENCIES better_errors (~> 2.9) binding_of_caller (~> 1.0) blurhash (~> 0.1) - bootsnap (~> 1.9.1) + bootsnap (~> 1.9.2) brakeman (~> 5.1) browser bullet (~> 6.1) From ddcb9da74fb4ee1233c4c5ba1ab3d9c8edc9aba2 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 26 Nov 2021 22:04:09 +0100 Subject: [PATCH 09/15] Fix opening wrong profile when clicking on username of boosting user in WebUI (#17060) Fixes #16799 --- app/javascript/mastodon/components/status.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 96e6a6c8d..9955046c0 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -146,7 +146,11 @@ class Status extends ImmutablePureComponent { this.handleHotkeyOpen(); } - handleAccountClick = e => { + handlePrependAccountClick = e => { + this.handleAccountClick(e, false); + } + + handleAccountClick = (e, proper = true) => { if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) { return; } @@ -155,7 +159,7 @@ class Status extends ImmutablePureComponent { e.preventDefault(); } - this.handleHotkeyOpenProfile(); + this._openProfile(proper); } handleExpandedToggle = () => { @@ -244,8 +248,12 @@ class Status extends ImmutablePureComponent { } handleHotkeyOpenProfile = () => { + this._openProfile(); + } + + _openProfile = (proper = true) => { const { router } = this.context; - const status = this._properStatus(); + const status = proper ? this._properStatus() : this.props.status; if (!router) { return; @@ -349,7 +357,7 @@ class Status extends ImmutablePureComponent { prepend = (
- }} /> + }} />
); From 0ac7efdc50200020f42b2d94151ecd80144e3d8c Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Sat, 27 Nov 2021 06:08:47 +0900 Subject: [PATCH 10/15] Fix performance of tootctl statuses remove (#17052) * Fix performance of tootctl statuses remove * Fix model class --- lib/mastodon/statuses_cli.rb | 94 +++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/lib/mastodon/statuses_cli.rb b/lib/mastodon/statuses_cli.rb index b9dccdd8a..f841529e0 100644 --- a/lib/mastodon/statuses_cli.rb +++ b/lib/mastodon/statuses_cli.rb @@ -6,6 +6,7 @@ require_relative 'cli_helper' module Mastodon class StatusesCLI < Thor + include CLIHelper include ActionView::Helpers::NumberHelper def self.exit_on_failure? @@ -15,6 +16,8 @@ module Mastodon option :days, type: :numeric, default: 90 option :clean_followed, type: :boolean option :skip_media_remove, type: :boolean + option :vacuum, type: :boolean, default: false, desc: 'Reduce the file size and update the statistics. This option locks the table for a long time, so run it offline' + option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch' desc 'remove', 'Remove unreferenced statuses' long_desc <<~LONG_DESC Remove statuses that are not referenced by local user activity, such as @@ -25,52 +28,89 @@ module Mastodon indices before commencing, and removes them afterward. LONG_DESC def remove + if options[:batch_size] < 1 + say('Cannot run with this batch_size setting, must be at least 1', :red) + exit(1) + end + say('Creating temporary database indices...') - ActiveRecord::Base.connection.add_index(:accounts, :id, name: :index_accounts_local, where: 'domain is null', algorithm: :concurrently) unless ActiveRecord::Base.connection.index_name_exists?(:accounts, :index_accounts_local) - ActiveRecord::Base.connection.add_index(:status_pins, :status_id, name: :index_status_pins_status_id, algorithm: :concurrently) unless ActiveRecord::Base.connection.index_name_exists?(:status_pins, :index_status_pins_status_id) - ActiveRecord::Base.connection.add_index(:media_attachments, :remote_url, name: :index_media_attachments_remote_url, where: 'remote_url is not null', algorithm: :concurrently) unless ActiveRecord::Base.connection.index_name_exists?(:media_attachments, :index_media_attachments_remote_url) + ActiveRecord::Base.connection.add_index(:accounts, :id, name: :index_accounts_local, where: 'domain is null', algorithm: :concurrently, if_not_exists: true) + ActiveRecord::Base.connection.add_index(:status_pins, :status_id, name: :index_status_pins_status_id, algorithm: :concurrently, if_not_exists: true) + ActiveRecord::Base.connection.add_index(:media_attachments, :remote_url, name: :index_media_attachments_remote_url, where: 'remote_url is not null', algorithm: :concurrently, if_not_exists: true) max_id = Mastodon::Snowflake.id_at(options[:days].days.ago) start_at = Time.now.to_f + say('Extract the deletion target... This might take a while...') + + ActiveRecord::Base.connection.create_table('statuses_to_be_deleted', temporary: true) + + # Skip accounts followed by local accounts + clean_followed_sql = 'AND NOT EXISTS (SELECT 1 FROM follows WHERE statuses.account_id = follows.target_account_id)' unless options[:clean_followed] + + ActiveRecord::Base.connection.exec_insert(<<-SQL.squish, 'SQL', [[nil, max_id]]) + INSERT INTO statuses_to_be_deleted (id) + SELECT statuses.id FROM statuses WHERE deleted_at IS NULL AND NOT local AND uri IS NOT NULL AND (id < $1) + AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.in_reply_to_id) + AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses1.id = statuses.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local)) + AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local OR statuses1.id >= $1)) + AND NOT EXISTS (SELECT 1 FROM status_pins WHERE statuses.id = status_id) + AND NOT EXISTS (SELECT 1 FROM mentions WHERE statuses.id = mentions.status_id AND mentions.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) + AND NOT EXISTS (SELECT 1 FROM favourites WHERE statuses.id = favourites.status_id AND favourites.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) + AND NOT EXISTS (SELECT 1 FROM bookmarks WHERE statuses.id = bookmarks.status_id AND bookmarks.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL)) + #{clean_followed_sql} + SQL + + say('Removing temporary database indices to restore write performance...') + + ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true) + ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true) + say('Beginning removal... This might take a while...') - scope = Status.remote.where('id < ?', max_id) - # Skip reblogs of local statuses - scope = scope.where('reblog_of_id NOT IN (SELECT statuses1.id FROM statuses AS statuses1 WHERE statuses1.id = statuses.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local))') - # Skip statuses that are pinned on profiles - scope = scope.where('id NOT IN (SELECT status_pins.status_id FROM status_pins WHERE statuses.id = status_id)') - # Skip statuses that mention local accounts - scope = scope.where('id NOT IN (SELECT mentions.status_id FROM mentions WHERE statuses.id = mentions.status_id AND mentions.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))') - # Skip statuses which have replies - scope = scope.where('id NOT IN (SELECT statuses1.in_reply_to_id FROM statuses AS statuses1 WHERE statuses.id = statuses1.in_reply_to_id)') - # Skip statuses reblogged by local accounts or with recent boosts - scope = scope.where('id NOT IN (SELECT statuses1.reblog_of_id FROM statuses AS statuses1 WHERE statuses.id = statuses1.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local OR statuses1.id >= ?))', max_id) - # Skip statuses favourited by local users - scope = scope.where('id NOT IN (SELECT favourites.status_id FROM favourites WHERE statuses.id = favourites.status_id AND favourites.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))') - # Skip statuses bookmarked by local users - scope = scope.where('id NOT IN (SELECT bookmarks.status_id FROM bookmarks WHERE statuses.id = bookmarks.status_id AND bookmarks.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))') - - unless options[:clean_followed] - # Skip accounts followed by local accounts - scope = scope.where('account_id NOT IN (SELECT follows.target_account_id FROM follows WHERE statuses.account_id = follows.target_account_id)') + klass = Class.new(ApplicationRecord) do |c| + c.table_name = 'statuses_to_be_deleted' end - scope.in_batches.delete_all + Object.const_set('StatusToBeDeleted', klass) + + scope = StatusToBeDeleted + processed = 0 + removed = 0 + progress = create_progress_bar(scope.count.fdiv(options[:batch_size]).ceil) + + scope.reorder(nil).in_batches(of: options[:batch_size]) do |relation| + ids = relation.pluck(:id) + processed += ids.count + removed += Status.unscoped.where(id: ids).delete_all + progress.increment + end + + progress.stop + + if options[:vacuum] + say('Run VACUUM and ANALYZE to statuses...') + + ActiveRecord::Base.connection.execute('VACUUM FULL ANALYZE statuses') + else + say('Run ANALYZE to statuses...') + + ActiveRecord::Base.connection.execute('ANALYZE statuses') + end unless options[:skip_media_remove] say('Beginning removal of now-orphaned media attachments to free up disk space...') Scheduler::MediaCleanupScheduler.new.perform end - say("Done after #{Time.now.to_f - start_at}s", :green) + say("Done after #{Time.now.to_f - start_at}s, removed #{removed} out of #{processed} statuses.", :green) ensure say('Removing temporary database indices to restore write performance...') - ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local) if ActiveRecord::Base.connection.index_name_exists?(:accounts, :index_accounts_local) - ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id) if ActiveRecord::Base.connection.index_name_exists?(:status_pins, :index_status_pins_status_id) - ActiveRecord::Base.connection.remove_index(:media_attachments, name: :index_media_attachments_remote_url) if ActiveRecord::Base.connection.index_name_exists?(:media_attachments, :index_media_attachments_remote_url) + ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true) + ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true) + ActiveRecord::Base.connection.remove_index(:media_attachments, name: :index_media_attachments_remote_url, if_exists: true) end end end From 1630807ee2517e7a9dbb66cbd532a0c46e01abcf Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 26 Nov 2021 22:09:11 +0100 Subject: [PATCH 11/15] Fix color of hashtag column settings inputs (#17058) Fixes #17057 --- app/javascript/styles/mastodon/components.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ed0709b44..0dd3636b6 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3922,7 +3922,8 @@ a.status-card.compact:hover { } &__multi-value__label, - &__input { + &__input, + &__input-container { color: $darker-text-color; } From 3f382e337d5c40db18995a88c0bf6b1b34f031b7 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 26 Nov 2021 22:11:09 +0100 Subject: [PATCH 12/15] Fix searching for additional hashtags in hashtag column (#17054) --- .../containers/column_settings_container.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js b/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js index 5914bbeaf..a4f71f8a3 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js +++ b/app/javascript/mastodon/features/hashtag_timeline/containers/column_settings_container.js @@ -11,21 +11,22 @@ const mapStateToProps = (state, { columnId }) => { return {}; } - return { settings: columns.get(index).get('params') }; + return { + settings: columns.get(index).get('params'), + onLoad (value) { + return api(() => state).get('/api/v2/search', { params: { q: value, type: 'hashtags' } }).then(response => { + return (response.data.hashtags || []).map((tag) => { + return { value: tag.name, label: `#${tag.name}` }; + }); + }); + }, + }; }; const mapDispatchToProps = (dispatch, { columnId }) => ({ onChange (key, value) { dispatch(changeColumnParams(columnId, key, value)); }, - - onLoad (value) { - return api().get('/api/v2/search', { params: { q: value, type: 'hashtags' } }).then(response => { - return (response.data.hashtags || []).map((tag) => { - return { value: tag.name, label: `#${tag.name}` }; - }); - }); - }, }); export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); From 1c826471e7d964f0fdb2dc2b89dcd5a19c017538 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Sat, 27 Nov 2021 06:12:27 +0900 Subject: [PATCH 13/15] Fix admin statuses order(#16937) (#16969) * Fix #16937 * Add test for statuses order --- app/controllers/admin/statuses_controller.rb | 2 +- spec/controllers/admin/statuses_controller_spec.rb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index 58a0eb84c..b3fd4c424 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -14,7 +14,7 @@ module Admin @statuses = @account.statuses.where(visibility: [:public, :unlisted]) if params[:media] - @statuses.merge!(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc') + @statuses = @statuses.merge(Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)).reorder('statuses.id desc') end @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb index d9690d83f..e388caae2 100644 --- a/spec/controllers/admin/statuses_controller_spec.rb +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -8,6 +8,9 @@ describe Admin::StatusesController do let!(:status) { Fabricate(:status, account: account) } let(:media_attached_status) { Fabricate(:status, account: account, sensitive: !sensitive) } let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: media_attached_status) } + let(:last_media_attached_status) { Fabricate(:status, account: account, sensitive: !sensitive) } + let!(:last_media_attachment) { Fabricate(:media_attachment, account: account, status: last_media_attached_status) } + let!(:last_status) { Fabricate(:status, account: account) } let(:sensitive) { true } before do @@ -19,7 +22,8 @@ describe Admin::StatusesController do get :index, params: { account_id: account.id } statuses = assigns(:statuses).to_a - expect(statuses.size).to eq 2 + expect(statuses.size).to eq 4 + expect(statuses.first.id).to eq last_status.id expect(response).to have_http_status(200) end @@ -27,7 +31,8 @@ describe Admin::StatusesController do get :index, params: { account_id: account.id, media: true } statuses = assigns(:statuses).to_a - expect(statuses.size).to eq 1 + expect(statuses.size).to eq 2 + expect(statuses.first.id).to eq last_media_attached_status.id expect(response).to have_http_status(200) end end From d5d44b431a8c1570e5db1c5a894a0b72131a3573 Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 26 Nov 2021 22:09:11 +0100 Subject: [PATCH 14/15] [Glitch] Fix color of hashtag column settings inputs Port 1630807ee2517e7a9dbb66cbd532a0c46e01abcf to glitch-soc Signed-off-by: Claire --- app/javascript/flavours/glitch/styles/components/columns.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss index 04d9b4168..512a04376 100644 --- a/app/javascript/flavours/glitch/styles/components/columns.scss +++ b/app/javascript/flavours/glitch/styles/components/columns.scss @@ -728,7 +728,8 @@ } &__multi-value__label, - &__input { + &__input, + &__input-container { color: $darker-text-color; } From ca5d78816bb31454de28480dec5c6384ed259eef Mon Sep 17 00:00:00 2001 From: Claire Date: Fri, 26 Nov 2021 22:11:09 +0100 Subject: [PATCH 15/15] [Glitch] Fix searching for additional hashtags in hashtag column Port 3f382e337d5c40db18995a88c0bf6b1b34f031b7 to glitch-soc Signed-off-by: Claire --- .../containers/column_settings_container.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/hashtag_timeline/containers/column_settings_container.js index de1db692d..1cf527573 100644 --- a/app/javascript/flavours/glitch/features/hashtag_timeline/containers/column_settings_container.js +++ b/app/javascript/flavours/glitch/features/hashtag_timeline/containers/column_settings_container.js @@ -11,21 +11,22 @@ const mapStateToProps = (state, { columnId }) => { return {}; } - return { settings: columns.get(index).get('params') }; + return { + settings: columns.get(index).get('params'), + onLoad (value) { + return api(() => state).get('/api/v2/search', { params: { q: value, type: 'hashtags' } }).then(response => { + return (response.data.hashtags || []).map((tag) => { + return { value: tag.name, label: `#${tag.name}` }; + }); + }); + }, + }; }; const mapDispatchToProps = (dispatch, { columnId }) => ({ onChange (key, value) { dispatch(changeColumnParams(columnId, key, value)); }, - - onLoad (value) { - return api().get('/api/v2/search', { params: { q: value, type: 'hashtags' } }).then(response => { - return (response.data.hashtags || []).map((tag) => { - return { value: tag.name, label: `#${tag.name}` }; - }); - }); - }, }); export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);