Merge branch 'develop' of https://code.gab.com/gab/social/gab-social into develop

This commit is contained in:
Fosco Marotto 2021-02-16 16:04:25 -05:00
commit 8d3d7efe34
18 changed files with 58 additions and 214 deletions

View File

@ -10,7 +10,11 @@ class Auth::RegistrationsController < Devise::RegistrationsController
before_action :set_body_classes, only: [:new, :create, :edit, :update]
before_action :set_cache_headers, only: [:edit, :update]
prepend_before_action :check_if_password_email_identical, only: [:create]
if ENV.fetch('GAB_CAPTCHA_CLIENT_KEY', '').empty? || ENV.fetch('GAB_CAPTCHA_CLIENT_KEY', '').nil?
# captcha disabled if key not defined
else
prepend_before_action :check_captcha, only: [:create]
end
def new
set_challenge_buster

View File

@ -4,123 +4,4 @@
# <https://tools.ietf.org/html/draft-cavage-http-signatures-06>
module SignatureVerification
extend ActiveSupport::Concern
def signed_request?
request.headers['Signature'].present?
end
def signature_verification_failure_reason
return @signature_verification_failure_reason if defined?(@signature_verification_failure_reason)
end
def signed_request_account
return @signed_request_account if defined?(@signed_request_account)
unless signed_request?
@signature_verification_failure_reason = 'Request not signed'
@signed_request_account = nil
return
end
if request.headers['Date'].present? && !matches_time_window?
@signature_verification_failure_reason = 'Signed request date outside acceptable time window'
@signed_request_account = nil
return
end
raw_signature = request.headers['Signature']
signature_params = {}
raw_signature.split(',').each do |part|
parsed_parts = part.match(/([a-z]+)="([^"]+)"/i)
next if parsed_parts.nil? || parsed_parts.size != 3
signature_params[parsed_parts[1]] = parsed_parts[2]
end
if incompatible_signature?(signature_params)
@signature_verification_failure_reason = 'Incompatible request signature'
@signed_request_account = nil
return
end
account = nil
if account.nil?
@signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
@signed_request_account = nil
return
end
signature = Base64.decode64(signature_params['signature'])
compare_signed_string = build_signed_string(signature_params['headers'])
return account unless verify_signature(account, signature, compare_signed_string).nil?
account = nil
if account.nil?
@signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
@signed_request_account = nil
return
end
return account unless verify_signature(account, signature, compare_signed_string).nil?
# : todo :
@signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}"
@signed_request_account = nil
end
def request_body
@request_body ||= request.raw_post
end
private
def verify_signature(account, signature, compare_signed_string)
if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
@signed_request_account = account
@signed_request_account
end
rescue OpenSSL::PKey::RSAError
nil
end
def build_signed_string(signed_headers)
signed_headers = 'date' if signed_headers.blank?
signed_headers.downcase.split(' ').map do |signed_header|
if signed_header == Request::REQUEST_TARGET
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
elsif signed_header == 'digest'
"digest: #{body_digest}"
else
"#{signed_header}: #{request.headers[to_header_name(signed_header)]}"
end
end.join("\n")
end
def matches_time_window?
begin
time_sent = Time.httpdate(request.headers['Date'])
rescue ArgumentError
return false
end
(Time.now.utc - time_sent).abs <= 12.hours
end
def body_digest
"SHA-256=#{Digest::SHA256.base64digest(request_body)}"
end
def to_header_name(name)
name.split(/-/).map(&:capitalize).join('-')
end
def incompatible_signature?(signature_params)
signature_params['keyId'].blank? ||
signature_params['signature'].blank?
end
end

View File

@ -12,11 +12,12 @@ class CharacterCounter extends React.PureComponent {
render() {
const { text, max } = this.props
const actualRadius = 16
const radius = 12
const actualRadius = 10
const radius = 8
const circumference = 2 * Math.PI * radius
const diff = Math.min(length(text), max) / max
const dashoffset = circumference * (1 - diff)
const circleClass = length(text) > max ? _s.strokeError : _s.strokeBrand
return (
<div className={[_s.d, _s.mr10, _s.jcCenter, _s.aiCenter].join(' ')}>
@ -31,8 +32,8 @@ class CharacterCounter extends React.PureComponent {
cy={actualRadius}
r={radius}
fill='none'
stroke='#e6e6e6'
strokeWidth='2'
className={_s.strokeSecondary}
/>
<circle
style={{
@ -43,9 +44,9 @@ class CharacterCounter extends React.PureComponent {
cx={actualRadius}
cy={actualRadius}
r={radius}
strokeWidth='2'
strokeWidth='2.25'
strokeLinecap='round'
stroke='#21cf7a'
className={circleClass}
/>
</svg>
</div>

View File

@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import {
CX,
MODAL_COMPOSE,
MAX_POST_CHARACTER_COUNT,
POPOVER_COMPOSE_POST_DESTINATION,
} from '../../../constants'
import { openModal } from '../../../actions/modal'
@ -14,6 +15,7 @@ import Avatar from '../../../components/avatar'
import Button from '../../../components/button'
import Icon from '../../../components/icon'
import Text from '../../../components/text'
import CharacterCounter from '../../../components/character_counter'
class ComposeDestinationHeader extends ImmutablePureComponent {
@ -38,6 +40,7 @@ class ComposeDestinationHeader extends ImmutablePureComponent {
composeGroup,
composeGroupId,
formLocation,
text,
} = this.props
const isIntroduction = formLocation === 'introduction'
@ -85,6 +88,10 @@ class ComposeDestinationHeader extends ImmutablePureComponent {
</div>
}
</div>
{
!!text &&
<CharacterCounter max={MAX_POST_CHARACTER_COUNT} text={text} />
}
{
!isModal && !isIntroduction &&
<Button
@ -94,6 +101,7 @@ class ComposeDestinationHeader extends ImmutablePureComponent {
color='tertiary'
icon='fullscreen'
onClick={this.handleOnExpand}
className={_s.ml10}
/>
}
</div>
@ -106,6 +114,7 @@ const mapStateToProps = (state) => {
return {
composeGroupId,
text: state.getIn(['compose', 'text']),
isReply: !!state.getIn(['compose', 'in_reply_to']),
isEdit: state.getIn(['compose', 'id']) !== null,
composeGroup: state.getIn(['groups', composeGroupId]),

View File

@ -54,7 +54,6 @@ class Followers extends ImmutablePureComponent {
scrollKey='followers'
hasMore={hasMore}
isLoading={isLoading}
showLoading={isLoading}
onLoadMore={this.handleLoadMore}
placeholderComponent={AccountPlaceholder}
placeholderCount={4}

View File

@ -54,7 +54,6 @@ class Following extends ImmutablePureComponent {
scrollKey='following'
hasMore={hasMore}
isLoading={isLoading}
showLoading={isLoading}
onLoadMore={this.handleLoadMore}
placeholderComponent={AccountPlaceholder}
placeholderCount={4}

View File

@ -1110,6 +1110,10 @@ pre {
fill: var(--color_primary);
}
.fillSecondary {
fill: var(--text_color_secondary);
}
.fillWhite {
fill: var(--color_white);
}
@ -1133,6 +1137,10 @@ pre {
color: var(--navigation_brand);
}
.strokeBrand { stroke: var(--color_brand); }
.strokeSecondary { stroke: var(--solid_color_secondary); }
.strokeError { stroke: var(--color_red); }
.navigationUnderlineActive:after {
content: '';
display: block;

View File

@ -7,8 +7,6 @@
# username :string default(""), not null
# domain :string
# secret :string default(""), not null
# private_key :text
# public_key :text default(""), not null
# remote_url :string default(""), not null
# salmon_url :string default(""), not null
# hub_url :string default(""), not null
@ -54,6 +52,8 @@
#
class Account < ApplicationRecord
self.ignored_columns = ["private_key"]
self.ignored_columns = ["public_key"]
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
MIN_FOLLOWERS_DISCOVERY = 10
@ -200,10 +200,6 @@ class Account < ApplicationRecord
end
end
def keypair
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
end
def tags_as_strings=(tag_names)
tag_names.map! { |name| name.mb_chars.downcase.to_s }
tag_names.uniq!
@ -279,21 +275,6 @@ class Account < ApplicationRecord
self.fields = tmp
end
def magic_key
modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component|
result = []
until component.zero?
result << [component % 256].pack('C')
component >>= 8
end
result.reverse.join
end
(['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
end
def save_with_optional_media!
save!
rescue ActiveRecord::RecordInvalid
@ -444,7 +425,6 @@ class Account < ApplicationRecord
@emojis ||= CustomEmoji.from_text(emojifiable_text)
end
before_create :generate_keys
before_validation :prepare_contents, if: :local?
before_validation :prepare_username, on: :create
before_destroy :clean_feed_manager
@ -460,14 +440,6 @@ class Account < ApplicationRecord
username&.squish!
end
def generate_keys
return unless local? && !Rails.env.test?
keypair = OpenSSL::PKey::RSA.new(2048)
self.private_key = keypair.to_pem
self.public_key = keypair.public_key.to_pem
end
def normalize_domain
return if local?

View File

@ -18,7 +18,7 @@
# member_count :integer default(0)
# slug :text
# is_private :boolean default(FALSE)
# is_visible :boolean default(FALSE)
# is_visible :boolean default(TRUE)
# tags :string default([]), is an Array
# password :string
# group_category_id :integer

View File

@ -6,7 +6,7 @@
# id :bigint(8) not null, primary key
# list_id :bigint(8) not null
# account_id :bigint(8) not null
# follow_id :bigint(8) default(1)
# follow_id :bigint(8)
#
class ListAccount < ApplicationRecord

View File

@ -101,8 +101,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "username", default: "", null: false
t.string "domain"
t.string "secret", default: "", null: false
t.text "private_key"
t.text "public_key", default: "", null: false
t.string "remote_url", default: "", null: false
t.string "salmon_url", default: "", null: false
t.string "hub_url", default: "", null: false

View File

@ -0,0 +1,5 @@
class RemovePrivateKeyFromAccounts < ActiveRecord::Migration[6.0]
def change
safety_assured { remove_column :accounts, :private_key, :text }
end
end

View File

@ -0,0 +1,5 @@
class RemovePublicKeyFromAccounts < ActiveRecord::Migration[6.0]
def change
safety_assured { remove_column :accounts, :public_key, :text }
end
end

View File

@ -10,9 +10,12 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_01_23_050026) do
ActiveRecord::Schema.define(version: 2021_02_16_022902) do
# These are extensions that must be enabled in order to support this database
enable_extension "mongo_fdw"
enable_extension "pg_stat_statements"
enable_extension "pgstattuple"
enable_extension "plpgsql"
create_table "account_conversations", force: :cascade do |t|
@ -24,7 +27,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.integer "lock_version", default: 0, null: false
t.boolean "unread", default: false, null: false
t.index ["account_id", "conversation_id", "participant_account_ids"], name: "index_unique_conversations", unique: true
t.index ["account_id"], name: "index_account_conversations_on_account_id"
t.index ["conversation_id"], name: "index_account_conversations_on_conversation_id"
end
@ -99,8 +101,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "username", default: "", null: false
t.string "domain"
t.string "secret", default: "", null: false
t.text "private_key"
t.text "public_key", default: "", null: false
t.string "remote_url", default: "", null: false
t.string "salmon_url", default: "", null: false
t.string "hub_url", default: "", null: false
@ -112,11 +112,11 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "url"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.bigint "avatar_file_size"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.string "header_file_name"
t.string "header_content_type"
t.bigint "header_file_size"
t.integer "header_file_size"
t.datetime "header_updated_at"
t.string "avatar_remote_url"
t.datetime "subscription_expires_at"
@ -135,10 +135,10 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "actor_type"
t.boolean "discoverable"
t.string "also_known_as", array: true
t.boolean "is_pro", default: false, null: false
t.datetime "pro_expires_at"
t.datetime "silenced_at"
t.datetime "suspended_at"
t.boolean "is_pro", default: false, null: false
t.datetime "pro_expires_at"
t.boolean "is_verified", default: false, null: false
t.boolean "is_donor", default: false, null: false
t.boolean "is_investor", default: false, null: false
@ -179,7 +179,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.bigint "user_id"
t.string "dump_file_name"
t.string "dump_content_type"
t.bigint "dump_file_size"
t.integer "dump_file_size"
t.datetime "dump_updated_at"
t.boolean "processed", default: false, null: false
t.datetime "created_at", null: false
@ -257,7 +257,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "domain"
t.string "image_file_name"
t.string "image_content_type"
t.bigint "image_file_size"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -325,9 +325,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "group_id"], name: "index_group_accounts_on_account_id_and_group_id", unique: true
t.index ["account_id"], name: "index_group_accounts_on_account_id"
t.index ["group_id", "account_id"], name: "index_group_accounts_on_group_id_and_account_id"
t.index ["group_id"], name: "index_group_accounts_on_group_id"
end
create_table "group_categories", force: :cascade do |t|
@ -342,7 +340,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "group_id"], name: "index_group_join_requests_on_account_id_and_group_id", unique: true
t.index ["account_id"], name: "index_group_join_requests_on_account_id"
t.index ["group_id"], name: "index_group_join_requests_on_group_id"
end
@ -351,7 +348,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.bigint "group_id", null: false
t.index ["group_id"], name: "index_group_pinned_statuses_on_group_id"
t.index ["status_id", "group_id"], name: "index_group_pinned_statuses_on_status_id_and_group_id", unique: true
t.index ["status_id"], name: "index_group_pinned_statuses_on_status_id"
end
create_table "group_removed_accounts", force: :cascade do |t|
@ -360,9 +356,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["account_id", "group_id"], name: "index_group_removed_accounts_on_account_id_and_group_id", unique: true
t.index ["account_id"], name: "index_group_removed_accounts_on_account_id"
t.index ["group_id", "account_id"], name: "index_group_removed_accounts_on_group_id_and_account_id"
t.index ["group_id"], name: "index_group_removed_accounts_on_group_id"
end
create_table "groups", force: :cascade do |t|
@ -371,7 +365,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "description", null: false
t.string "cover_image_file_name"
t.string "cover_image_content_type"
t.bigint "cover_image_file_size"
t.integer "cover_image_file_size"
t.datetime "cover_image_updated_at"
t.boolean "is_nsfw", default: false, null: false
t.boolean "is_featured", default: false, null: false
@ -438,7 +432,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.bigint "status_id"
t.string "file_file_name"
t.string "file_content_type"
t.bigint "file_file_size"
t.integer "file_file_size"
t.datetime "file_updated_at"
t.string "remote_url", default: "", null: false
t.datetime "created_at", null: false
@ -570,6 +564,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.datetime "updated_at", null: false
t.integer "lock_version", default: 0, null: false
t.index ["account_id"], name: "index_polls_on_account_id"
t.index ["created_at"], name: "index_polls_on_created_at"
t.index ["id", "lock_version"], name: "index_polls_on_id_and_lock_version"
t.index ["status_id"], name: "index_polls_on_status_id"
end
@ -580,7 +575,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "description", default: "", null: false
t.string "image_file_name"
t.string "image_content_type"
t.bigint "image_file_size"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "type", default: 0, null: false
t.text "html", default: "", null: false
@ -676,14 +671,13 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.bigint "shortcut_id", null: false
t.string "shortcut_type", default: "", null: false
t.index ["account_id", "shortcut_id", "shortcut_type"], name: "index_shortcuts_on_account_id_and_shortcut_id_and_shortcut_type", unique: true
t.index ["account_id"], name: "index_shortcuts_on_account_id"
end
create_table "site_uploads", force: :cascade do |t|
t.string "var", default: "", null: false
t.string "file_file_name"
t.string "file_content_type"
t.bigint "file_file_size"
t.integer "file_file_size"
t.datetime "file_updated_at"
t.json "meta"
t.datetime "created_at", null: false
@ -705,7 +699,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.bigint "status_id", null: false
t.bigint "status_bookmark_collection_id"
t.index ["account_id", "status_id"], name: "index_status_bookmarks_on_account_id_and_status_id", unique: true
t.index ["account_id"], name: "index_status_bookmarks_on_account_id"
t.index ["status_bookmark_collection_id"], name: "index_status_bookmarks_on_status_bookmark_collection_id"
t.index ["status_id"], name: "index_status_bookmarks_on_status_id"
end
@ -766,6 +759,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.text "markdown"
t.datetime "expires_at"
t.boolean "has_quote"
t.index ["account_id", "id", "visibility", "created_at"], name: "index_statuses_20201206", order: { id: :desc }
t.index ["account_id", "id", "visibility", "updated_at"], name: "index_statuses_20180106", order: { id: :desc }
t.index ["created_at"], name: "index_statuses_on_created_at"
t.index ["group_id"], name: "index_statuses_on_group_id"

View File

@ -101,8 +101,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "username", default: "", null: false
t.string "domain"
t.string "secret", default: "", null: false
t.text "private_key"
t.text "public_key", default: "", null: false
t.string "remote_url", default: "", null: false
t.string "salmon_url", default: "", null: false
t.string "hub_url", default: "", null: false

View File

@ -101,8 +101,6 @@ ActiveRecord::Schema.define(version: 2021_01_23_050026) do
t.string "username", default: "", null: false
t.string "domain"
t.string "secret", default: "", null: false
t.text "private_key"
t.text "public_key", default: "", null: false
t.string "remote_url", default: "", null: false
t.string "salmon_url", default: "", null: false
t.string "hub_url", default: "", null: false

View File

@ -520,15 +520,5 @@ module GabSocial
private
def rotate_keys_for_account(account, delay = 0)
if account.nil?
say('No such account', :red)
exit(1)
end
old_key = account.private_key
new_key = OpenSSL::PKey::RSA.new(2048)
account.update(private_key: new_key.to_pem, public_key: new_key.public_key.to_pem)
end
end
end

View File

@ -1,17 +0,0 @@
# frozen_string_literal: true
task fix_key_pairs: 'gabsocial:fix_key_pairs'
namespace :gabsocial do
desc 'Generates key pairs for migrated accounts'
task :fix_key_pairs => :environment do
Account.select(:id, :username, :private_key, :public_key).all.each do |a|
if a.public_key == "tobefilled"
keypair = OpenSSL::PKey::RSA.new(2048)
private_key = keypair.to_pem
public_key = keypair.public_key.to_pem
a.update_columns private_key: private_key, public_key: public_key
end
end
end
end