Remove Keybase integration (#17045)

This commit is contained in:
Eugen Rochko 2021-11-26 05:58:18 +01:00 committed by GitHub
parent 12b3ff6c6d
commit 7de0ee7aba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 25 additions and 1215 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,
});

View file

@ -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 {
<div className='account__header__extra'>
<div className='account__header__bio'>
{(fields.size > 0 || identity_proofs.size > 0) && (
{fields.size > 0 && (
<div className='account__header__fields'>
{identity_proofs.map((proof, i) => (
<dl key={i}>
<dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
<dd className='verified'>
<a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'><span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
<Icon id='check' className='verified__mark' />
</span></a>
<a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'><span dangerouslySetInnerHTML={{ __html: ' '+proof.get('provider_username') }} /></a>
</dd>
</dl>
))}
{fields.map((pair, i) => (
<dl key={i}>
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />

View file

@ -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 {
<InnerHeader
account={account}
identity_proofs={identity_proofs}
onFollow={this.handleFollow}
onBlock={this.handleBlock}
onMention={this.handleMention}

View file

@ -21,7 +21,6 @@ import { openModal } from '../../../actions/modal';
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { unfollowModal } from '../../../initial_state';
import { List as ImmutableList } from 'immutable';
const messages = defineMessages({
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
@ -34,7 +33,6 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, { accountId }) => ({
account: getAccount(state, accountId),
domain: state.getIn(['meta', 'domain']),
identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()),
});
return mapStateToProps;

View file

@ -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));

View file

@ -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;
}
};

View file

@ -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,

View file

@ -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) {

View file

@ -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' },

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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') }

View file

@ -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

View file

@ -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'