Merge branch 'main' into glitch-soc/merge-upstream
Conflicts: - `app/models/concerns/domain_materializable.rb`: Fixed a code style issue upstream in a PR that got merged in glitch-soc earlier. Changed the code to match upstream's.
This commit is contained in:
		
						commit
						9a3d91f629
					
				
					 22 changed files with 443 additions and 59 deletions
				
			
		
							
								
								
									
										2
									
								
								.github/workflows/check-i18n.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/check-i18n.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -25,7 +25,7 @@ jobs: | |||
|       - name: Set up Ruby | ||||
|         uses: ruby/setup-ruby@v1 | ||||
|         with: | ||||
|           ruby-version: '3.0' | ||||
|           ruby-version: .ruby-version | ||||
|           bundler-cache: true | ||||
|       - name: Check locale file normalization | ||||
|         run: bundle exec i18n-tasks check-normalized | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.github/workflows/linter.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/linter.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -53,7 +53,7 @@ jobs: | |||
|       - name: Set-up Node.js | ||||
|         uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 16.x | ||||
|           node-version-file: .nvmrc | ||||
|           cache: yarn | ||||
|       - name: Install dependencies | ||||
|         run: yarn install --frozen-lockfile | ||||
|  |  | |||
							
								
								
									
										71
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										71
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
								
							|  | @ -3,16 +3,14 @@ | |||
| 
 | ||||
| ENV["PORT"] ||= "3000" | ||||
| 
 | ||||
| $provision = <<SCRIPT | ||||
| 
 | ||||
| cd /vagrant # This is where the host folder/repo is mounted | ||||
| $provisionA = <<SCRIPT | ||||
| 
 | ||||
| # Add the yarn repo + yarn repo keys | ||||
| curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - | ||||
| sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' | ||||
| 
 | ||||
| # Add repo for NodeJS | ||||
| curl -sL https://deb.nodesource.com/setup_14.x | sudo bash - | ||||
| curl -sL https://deb.nodesource.com/setup_16.x | sudo bash - | ||||
| 
 | ||||
| # Add firewall rule to redirect 80 to PORT and save | ||||
| sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} | ||||
|  | @ -33,32 +31,56 @@ sudo apt-get install \ | |||
|   redis-tools \ | ||||
|   postgresql \ | ||||
|   postgresql-contrib \ | ||||
|   yarn \ | ||||
|   libicu-dev \ | ||||
|   libidn11-dev \ | ||||
|   libreadline-dev \ | ||||
|   libpam0g-dev \ | ||||
|   libreadline6-dev \ | ||||
|   autoconf \ | ||||
|   bison \ | ||||
|   build-essential \ | ||||
|   ffmpeg \ | ||||
|   file \ | ||||
|   gcc \ | ||||
|   libffi-dev \ | ||||
|   libgdbm-dev \ | ||||
|   libjemalloc-dev \ | ||||
|   libncurses5-dev \ | ||||
|   libprotobuf-dev \ | ||||
|   libssl-dev \ | ||||
|   libyaml-dev \ | ||||
|   pkg-config \ | ||||
|   protobuf-compiler \ | ||||
|   zlib1g-dev \ | ||||
|   -y | ||||
| 
 | ||||
| # Install rvm | ||||
| read RUBY_VERSION < .ruby-version | ||||
| sudo apt-add-repository -y ppa:rael-gc/rvm | ||||
| sudo apt-get install rvm -y | ||||
| 
 | ||||
| curl -sSL https://rvm.io/mpapis.asc | gpg --import | ||||
| curl -sSL https://rvm.io/pkuczynski.asc | gpg --import | ||||
| sudo usermod -a -G rvm $USER | ||||
| 
 | ||||
| curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION | ||||
| source /home/vagrant/.rvm/scripts/rvm | ||||
| SCRIPT | ||||
| 
 | ||||
| $provisionB = <<SCRIPT | ||||
| 
 | ||||
| source "/etc/profile.d/rvm.sh" | ||||
| 
 | ||||
| # Install Ruby | ||||
| rvm reinstall ruby-$RUBY_VERSION --disable-binary | ||||
| read RUBY_VERSION < /vagrant/.ruby-version | ||||
| rvm install ruby-$RUBY_VERSION --disable-binary | ||||
| 
 | ||||
| # Configure database | ||||
| sudo -u postgres createuser -U postgres vagrant -s | ||||
| sudo -u postgres createdb -U postgres mastodon_development | ||||
| 
 | ||||
| # Install gems and node modules | ||||
| cd /vagrant # This is where the host folder/repo is mounted | ||||
| 
 | ||||
| # Install gems | ||||
| gem install bundler foreman | ||||
| bundle install | ||||
| 
 | ||||
| # Install node modules | ||||
| sudo corepack enable | ||||
| yarn set version classic | ||||
| yarn install | ||||
| 
 | ||||
| # Build Mastodon | ||||
|  | @ -72,18 +94,11 @@ echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile | |||
| 
 | ||||
| SCRIPT | ||||
| 
 | ||||
| $start = <<SCRIPT | ||||
| 
 | ||||
| echo 'To start server' | ||||
| echo '  $ vagrant ssh -c "cd /vagrant && foreman start"' | ||||
| 
 | ||||
| SCRIPT | ||||
| 
 | ||||
| VAGRANTFILE_API_VERSION = "2" | ||||
| 
 | ||||
| Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | ||||
| 
 | ||||
|   config.vm.box = "ubuntu/bionic64" | ||||
|   config.vm.box = "ubuntu/focal64" | ||||
| 
 | ||||
|   config.vm.provider :virtualbox do |vb| | ||||
|     vb.name = "mastodon" | ||||
|  | @ -100,7 +115,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | |||
|     # Use "virtio" network interfaces for better performance. | ||||
|     vb.customize ["modifyvm", :id, "--nictype1", "virtio"] | ||||
|     vb.customize ["modifyvm", :id, "--nictype2", "virtio"] | ||||
| 
 | ||||
|   end | ||||
| 
 | ||||
|   # This uses the vagrant-hostsupdater plugin, and lets you | ||||
|  | @ -118,7 +132,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | |||
|   end | ||||
| 
 | ||||
|   if config.vm.networks.any? { |type, options| type == :private_network } | ||||
|     config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'vers=3', 'tcp', 'actimeo=1'] | ||||
|     config.vm.synced_folder ".", "/vagrant", type: "nfs", mount_options: ['rw', 'actimeo=1'] | ||||
|   else | ||||
|     config.vm.synced_folder ".", "/vagrant" | ||||
|   end | ||||
|  | @ -129,9 +143,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| | |||
|   config.vm.network :forwarded_port, guest: 8080, host: 8080 | ||||
| 
 | ||||
|   # Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision' | ||||
|   config.vm.provision :shell, inline: $provision, privileged: false | ||||
|   config.vm.provision :shell, inline: $provisionA, privileged: false, reset: true | ||||
|   config.vm.provision :shell, inline: $provisionB, privileged: false | ||||
| 
 | ||||
|   # Start up script, runs on every 'vagrant up' | ||||
|   config.vm.provision :shell, inline: $start, run: 'always', privileged: false | ||||
|   config.vm.post_up_message = <<MESSAGE | ||||
| To start server | ||||
|   $ vagrant ssh -c "cd /vagrant && foreman start" | ||||
| MESSAGE | ||||
| 
 | ||||
| end | ||||
|  |  | |||
|  | @ -55,12 +55,14 @@ module Admin | |||
|     def approve | ||||
|       authorize @account.user, :approve? | ||||
|       @account.user.approve! | ||||
|       log_action :approve, @account.user | ||||
|       redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct) | ||||
|     end | ||||
| 
 | ||||
|     def reject | ||||
|       authorize @account.user, :reject? | ||||
|       DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) | ||||
|       log_action :reject, @account.user | ||||
|       redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct) | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,12 +54,14 @@ class Api::V1::Admin::AccountsController < Api::BaseController | |||
|   def approve | ||||
|     authorize @account.user, :approve? | ||||
|     @account.user.approve! | ||||
|     log_action :approve, @account.user | ||||
|     render json: @account, serializer: REST::Admin::AccountSerializer | ||||
|   end | ||||
| 
 | ||||
|   def reject | ||||
|     authorize @account.user, :reject? | ||||
|     DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false) | ||||
|     log_action :reject, @account.user | ||||
|     render_empty | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ class Api::V1::FiltersController < Api::BaseController | |||
| 
 | ||||
|   def create | ||||
|     ApplicationRecord.transaction do | ||||
|       filter_category = current_account.custom_filters.create!(resource_params) | ||||
|       filter_category = current_account.custom_filters.create!(filter_params) | ||||
|       @filter = filter_category.keywords.create!(keyword_params) | ||||
|     end | ||||
| 
 | ||||
|  | @ -52,11 +52,11 @@ class Api::V1::FiltersController < Api::BaseController | |||
|   end | ||||
| 
 | ||||
|   def resource_params | ||||
|     params.permit(:phrase, :expires_in, :irreversible, context: []) | ||||
|     params.permit(:phrase, :expires_in, :irreversible, :whole_word, context: []) | ||||
|   end | ||||
| 
 | ||||
|   def filter_params | ||||
|     resource_params.slice(:expires_in, :irreversible, :context) | ||||
|     resource_params.slice(:phrase, :expires_in, :irreversible, :context) | ||||
|   end | ||||
| 
 | ||||
|   def keyword_params | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import { registrationsOpen, me } from 'mastodon/initial_state'; | |||
| import Avatar from 'mastodon/components/avatar'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { openModal } from 'mastodon/actions/modal'; | ||||
| 
 | ||||
| const Account = connect(state => ({ | ||||
|   account: state.getIn(['accounts', me]), | ||||
|  | @ -15,7 +16,14 @@ const Account = connect(state => ({ | |||
|   </Link> | ||||
| )); | ||||
| 
 | ||||
| export default @withRouter | ||||
| const mapDispatchToProps = (dispatch) => ({ | ||||
|   openClosedRegistrationsModal() { | ||||
|     dispatch(openModal('CLOSED_REGISTRATIONS')); | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| export default @connect(null, mapDispatchToProps) | ||||
| @withRouter | ||||
| class Header extends React.PureComponent { | ||||
| 
 | ||||
|   static contextTypes = { | ||||
|  | @ -23,12 +31,13 @@ class Header extends React.PureComponent { | |||
|   }; | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     openClosedRegistrationsModal: PropTypes.func, | ||||
|     location: PropTypes.object, | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { signedIn } = this.context.identity; | ||||
|     const { location } = this.props; | ||||
|     const { location, openClosedRegistrationsModal } = this.props; | ||||
| 
 | ||||
|     let content; | ||||
| 
 | ||||
|  | @ -40,10 +49,26 @@ class Header extends React.PureComponent { | |||
|         </> | ||||
|       ); | ||||
|     } else { | ||||
|       let signupButton; | ||||
| 
 | ||||
|       if (registrationsOpen) { | ||||
|         signupButton = ( | ||||
|           <a href='/auth/sign_up' className='button button-tertiary'> | ||||
|             <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /> | ||||
|           </a> | ||||
|         ); | ||||
|       } else { | ||||
|         signupButton = ( | ||||
|           <button className='button button-tertiary' onClick={openClosedRegistrationsModal}> | ||||
|             <FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /> | ||||
|           </button> | ||||
|         ); | ||||
|       } | ||||
| 
 | ||||
|       content = ( | ||||
|         <> | ||||
|           <a href='/auth/sign_in' className='button'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a> | ||||
|           <a href={registrationsOpen ? '/auth/sign_up' : 'https://joinmastodon.org/servers'} className='button button-tertiary'><FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' /></a> | ||||
|           {signupButton} | ||||
|         </> | ||||
|       ); | ||||
|     } | ||||
|  |  | |||
|  | @ -2217,6 +2217,7 @@ $ui-header-height: 55px; | |||
|   z-index: 2; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   overflow: hidden; | ||||
| 
 | ||||
|   &__logo { | ||||
|     display: inline-flex; | ||||
|  | @ -2233,10 +2234,15 @@ $ui-header-height: 55px; | |||
|     align-items: center; | ||||
|     gap: 10px; | ||||
|     padding: 0 10px; | ||||
|     overflow: hidden; | ||||
| 
 | ||||
|     .button { | ||||
|       flex: 0 0 auto; | ||||
|     } | ||||
| 
 | ||||
|     .button-tertiary { | ||||
|       flex-shrink: 1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -7138,10 +7144,12 @@ noscript { | |||
| 
 | ||||
|       .verified { | ||||
|         border: 1px solid rgba($valid-value-color, 0.5); | ||||
|         margin-top: -1px; | ||||
| 
 | ||||
|         &:first-child { | ||||
|           border-top-left-radius: 4px; | ||||
|           border-top-right-radius: 4px; | ||||
|           margin-top: 0; | ||||
|         } | ||||
| 
 | ||||
|         &:last-child { | ||||
|  | @ -7973,7 +7981,8 @@ noscript { | |||
|   width: 600px; | ||||
|   background: $ui-base-color; | ||||
|   border-radius: 8px; | ||||
|   overflow: hidden; | ||||
|   overflow-x: hidden; | ||||
|   overflow-y: auto; | ||||
|   position: relative; | ||||
|   display: block; | ||||
|   padding: 20px; | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ class AccountMigration < ApplicationRecord | |||
| 
 | ||||
|   def set_target_account | ||||
|     self.target_account = ResolveAccountService.new.call(acct, skip_cache: true) | ||||
|   rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error | ||||
|   rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError | ||||
|     # Validation will take care of it | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ module DomainMaterializable | |||
| 
 | ||||
|     Instance.refresh | ||||
|     count_unique_subdomains! | ||||
| 
 | ||||
|   end | ||||
| 
 | ||||
|   def count_unique_subdomains! | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ class CustomFilter < ApplicationRecord | |||
|   end | ||||
| 
 | ||||
|   def irreversible=(value) | ||||
|     self.action = value ? :hide : :warn | ||||
|     self.action = ActiveModel::Type::Boolean.new.cast(value) ? :hide : :warn | ||||
|   end | ||||
| 
 | ||||
|   def irreversible? | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ class Form::Redirect | |||
| 
 | ||||
|   def set_target_account | ||||
|     @target_account = ResolveAccountService.new.call(acct, skip_cache: true) | ||||
|   rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error | ||||
|   rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error, Addressable::URI::InvalidURIError | ||||
|     # Validation will take care of it | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -71,6 +71,7 @@ module Mastodon | |||
|       :af, | ||||
|       :ar, | ||||
|       :ast, | ||||
|       :be, | ||||
|       :bg, | ||||
|       :bn, | ||||
|       :br, | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ default: &default | |||
|   adapter: postgresql | ||||
|   pool: <%= ENV["DB_POOL"] || ENV['MAX_THREADS'] || 5 %> | ||||
|   timeout: 5000 | ||||
|   connect_timeout: 15 | ||||
|   encoding: unicode | ||||
|   sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %> | ||||
| 
 | ||||
|  |  | |||
|  | @ -79,69 +79,72 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1] | |||
|     safety_assured do | ||||
|       AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| | ||||
|         next if log.account.nil? | ||||
|         log.update(human_identifier: log.account.acct) | ||||
|         log.update_attribute('human_identifier', log.account.acct) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| | ||||
|         next if log.user.nil? | ||||
|         log.update(human_identifier: log.user.account.acct, route_param: log.user.account_id) | ||||
|         log.update_attribute('human_identifier', log.user.account.acct) | ||||
|         log.update_attribute('route_param', log.user.account_id) | ||||
|       end | ||||
| 
 | ||||
|       Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') | ||||
| 
 | ||||
|       AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| | ||||
|         next if log.domain_block.nil? | ||||
|         log.update(human_identifier: log.domain_block.domain) | ||||
|         log.update_attribute('human_identifier', log.domain_block.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| | ||||
|         next if log.domain_allow.nil? | ||||
|         log.update(human_identifier: log.domain_allow.domain) | ||||
|         log.update_attribute('human_identifier', log.domain_allow.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| | ||||
|         next if log.email_domain_block.nil? | ||||
|         log.update(human_identifier: log.email_domain_block.domain) | ||||
|         log.update_attribute('human_identifier', log.email_domain_block.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| | ||||
|         next if log.unavailable_domain.nil? | ||||
|         log.update(human_identifier: log.unavailable_domain.domain) | ||||
|         log.update_attribute('human_identifier', log.unavailable_domain.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| | ||||
|         next if log.status.nil? | ||||
|         log.update(human_identifier: log.status.account.acct, permalink: log.status.uri) | ||||
|         log.update_attribute('human_identifier', log.status.account.acct) | ||||
|         log.update_attribute('permalink', log.status.uri) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| | ||||
|         next if log.account_warning.nil? | ||||
|         log.update(human_identifier: log.account_warning.account.acct) | ||||
|         log.update_attribute('human_identifier', log.account_warning.account.acct) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| | ||||
|         next if log.announcement.nil? | ||||
|         log.update(human_identifier: log.announcement.text) | ||||
|         log.update_attribute('human_identifier', log.announcement.text) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| | ||||
|         next if log.ip_block.nil? | ||||
|         log.update(human_identifier: "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") | ||||
|         log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| | ||||
|         next if log.custom_emoji.nil? | ||||
|         log.update(human_identifier: log.custom_emoji.shortcode) | ||||
|         log.update_attribute('human_identifier', log.custom_emoji.shortcode) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| | ||||
|         next if log.canonical_email_block.nil? | ||||
|         log.update(human_identifier: log.canonical_email_block.canonical_email_hash) | ||||
|         log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| | ||||
|         next if log.appeal.nil? | ||||
|         log.update(human_identifier: log.appeal.account.acct, route_param: log.appeal.account_warning_id) | ||||
|         log.update_attribute('human_identifier', log.appeal.account.acct) | ||||
|         log.update_attribute('route_param', log.appeal.account_warning_id) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -0,0 +1,153 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   class Account < ApplicationRecord | ||||
|     # Dummy class, to make migration possible across version changes | ||||
|     has_one :user, inverse_of: :account | ||||
| 
 | ||||
|     def local? | ||||
|       domain.nil? | ||||
|     end | ||||
| 
 | ||||
|     def acct | ||||
|       local? ? username : "#{username}@#{domain}" | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   class User < ApplicationRecord | ||||
|     # Dummy class, to make migration possible across version changes | ||||
|     belongs_to :account | ||||
|   end | ||||
| 
 | ||||
|   class Status < ApplicationRecord | ||||
|     include RoutingHelper | ||||
| 
 | ||||
|     # Dummy class, to make migration possible across version changes | ||||
|     belongs_to :account | ||||
| 
 | ||||
|     def local? | ||||
|       attributes['local'] || attributes['uri'].nil? | ||||
|     end | ||||
| 
 | ||||
|     def uri | ||||
|       local? ? activity_account_status_url(account, self) : attributes['uri'] | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   class DomainBlock < ApplicationRecord; end | ||||
|   class DomainAllow < ApplicationRecord; end | ||||
|   class EmailDomainBlock < ApplicationRecord; end | ||||
|   class UnavailableDomain < ApplicationRecord; end | ||||
| 
 | ||||
|   class AccountWarning < ApplicationRecord | ||||
|     # Dummy class, to make migration possible across version changes | ||||
|     belongs_to :account | ||||
|   end | ||||
| 
 | ||||
|   class Announcement < ApplicationRecord; end | ||||
|   class IpBlock < ApplicationRecord; end | ||||
|   class CustomEmoji < ApplicationRecord; end | ||||
|   class CanonicalEmailBlock < ApplicationRecord; end | ||||
| 
 | ||||
|   class Appeal < ApplicationRecord | ||||
|     # Dummy class, to make migration possible across version changes | ||||
|     belongs_to :account | ||||
|   end | ||||
| 
 | ||||
|   class AdminActionLog < ApplicationRecord | ||||
|     # Dummy class, to make migration possible across version changes | ||||
| 
 | ||||
|     # Cannot use usual polymorphic support because of namespacing issues | ||||
|     belongs_to :status, foreign_key: :target_id | ||||
|     belongs_to :account, foreign_key: :target_id | ||||
|     belongs_to :user, foreign_key: :user_id | ||||
|     belongs_to :domain_block, foreign_key: :target_id | ||||
|     belongs_to :domain_allow, foreign_key: :target_id | ||||
|     belongs_to :email_domain_block, foreign_key: :target_id | ||||
|     belongs_to :unavailable_domain, foreign_key: :target_id | ||||
|     belongs_to :account_warning, foreign_key: :target_id | ||||
|     belongs_to :announcement, foreign_key: :target_id | ||||
|     belongs_to :ip_block, foreign_key: :target_id | ||||
|     belongs_to :custom_emoji, foreign_key: :target_id | ||||
|     belongs_to :canonical_email_block, foreign_key: :target_id | ||||
|     belongs_to :appeal, foreign_key: :target_id | ||||
|   end | ||||
| 
 | ||||
|   def up | ||||
|     safety_assured do | ||||
|       AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log| | ||||
|         next if log.account.nil? | ||||
|         log.update_attribute('human_identifier', log.account.acct) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log| | ||||
|         next if log.user.nil? | ||||
|         log.update_attribute('human_identifier', log.user.account.acct) | ||||
|         log.update_attribute('route_param', log.user.account_id) | ||||
|       end | ||||
| 
 | ||||
|       Admin::ActionLog.where(target_type: 'Report', human_identifier: nil).in_batches.update_all('human_identifier = target_id::text') | ||||
| 
 | ||||
|       AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log| | ||||
|         next if log.domain_block.nil? | ||||
|         log.update_attribute('human_identifier', log.domain_block.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log| | ||||
|         next if log.domain_allow.nil? | ||||
|         log.update_attribute('human_identifier', log.domain_allow.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log| | ||||
|         next if log.email_domain_block.nil? | ||||
|         log.update_attribute('human_identifier', log.email_domain_block.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log| | ||||
|         next if log.unavailable_domain.nil? | ||||
|         log.update_attribute('human_identifier', log.unavailable_domain.domain) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log| | ||||
|         next if log.status.nil? | ||||
|         log.update_attribute('human_identifier', log.status.account.acct) | ||||
|         log.update_attribute('permalink', log.status.uri) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log| | ||||
|         next if log.account_warning.nil? | ||||
|         log.update_attribute('human_identifier', log.account_warning.account.acct) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log| | ||||
|         next if log.announcement.nil? | ||||
|         log.update_attribute('human_identifier', log.announcement.text) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log| | ||||
|         next if log.ip_block.nil? | ||||
|         log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}") | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log| | ||||
|         next if log.custom_emoji.nil? | ||||
|         log.update_attribute('human_identifier', log.custom_emoji.shortcode) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log| | ||||
|         next if log.canonical_email_block.nil? | ||||
|         log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash) | ||||
|       end | ||||
| 
 | ||||
|       AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log| | ||||
|         next if log.appeal.nil? | ||||
|         log.update_attribute('human_identifier', log.appeal.account.acct) | ||||
|         log.update_attribute('route_param', log.appeal.account_warning_id) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def down; end | ||||
| end | ||||
|  | @ -10,7 +10,7 @@ | |||
| # | ||||
| # It's strongly recommended that you check this file into your version control system. | ||||
| 
 | ||||
| ActiveRecord::Schema.define(version: 2022_11_04_133904) do | ||||
| ActiveRecord::Schema.define(version: 2022_12_06_114142) do | ||||
| 
 | ||||
|   # These are extensions that must be enabled in order to support this database | ||||
|   enable_extension "plpgsql" | ||||
|  |  | |||
|  | @ -43,6 +43,16 @@ namespace :tests do | |||
|         puts 'CustomFilterKeyword records not created as expected' | ||||
|         exit(1) | ||||
|       end | ||||
| 
 | ||||
|       unless Admin::ActionLog.find_by(target_type: 'DomainBlock', target_id: 1).human_identifier == 'example.org' | ||||
|         puts 'Admin::ActionLog domain block records not updated as expected' | ||||
|         exit(1) | ||||
|       end | ||||
| 
 | ||||
|       unless Admin::ActionLog.find_by(target_type: 'EmailDomainBlock', target_id: 1).human_identifier == 'example.org' | ||||
|         puts 'Admin::ActionLog email domain block records not updated as expected' | ||||
|         exit(1) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     desc 'Populate the database with test data for 2.4.3' | ||||
|  | @ -84,8 +94,8 @@ namespace :tests do | |||
|         VALUES | ||||
|           (1, 'destroy', 'Account', 1, now(), now()), | ||||
|           (1, 'destroy', 'User', 1, now(), now()), | ||||
|           (1, 'destroy', 'DomainBlock', 1312, now(), now()), | ||||
|           (1, 'destroy', 'EmailDomainBlock', 1312, now(), now()), | ||||
|           (1, 'destroy', 'DomainBlock', 1, now(), now()), | ||||
|           (1, 'destroy', 'EmailDomainBlock', 1, now(), now()), | ||||
|           (1, 'destroy', 'Status', 1, now(), now()), | ||||
|           (1, 'destroy', 'CustomEmoji', 3, now(), now()); | ||||
|       SQL | ||||
|  |  | |||
|  | @ -147,6 +147,87 @@ RSpec.describe Admin::AccountsController, type: :controller do | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'POST #approve' do | ||||
|     subject { post :approve, params: { id: account.id } } | ||||
| 
 | ||||
|     let(:current_user) { Fabricate(:user, role: role) } | ||||
|     let(:account) { user.account } | ||||
|     let(:user) { Fabricate(:user) } | ||||
| 
 | ||||
|     before do | ||||
|       account.user.update(approved: false) | ||||
|     end | ||||
| 
 | ||||
|     context 'when user is admin' do | ||||
|       let(:role) { UserRole.find_by(name: 'Admin') } | ||||
| 
 | ||||
|       it 'succeeds in approving account' do | ||||
|         is_expected.to redirect_to admin_accounts_path(status: 'pending') | ||||
|         expect(user.reload).to be_approved | ||||
|       end | ||||
| 
 | ||||
|       it 'logs action' do | ||||
|         is_expected.to have_http_status :found | ||||
| 
 | ||||
|         log_item = Admin::ActionLog.last | ||||
| 
 | ||||
|         expect(log_item).to_not be_nil | ||||
|         expect(log_item.action).to eq :approve | ||||
|         expect(log_item.account_id).to eq current_user.account_id | ||||
|         expect(log_item.target_id).to eq account.user.id | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when user is not admin' do | ||||
|       let(:role) { UserRole.everyone } | ||||
| 
 | ||||
|       it 'fails to approve account' do | ||||
|         is_expected.to have_http_status :forbidden | ||||
|         expect(user.reload).not_to be_approved | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'POST #reject' do | ||||
|     subject { post :reject, params: { id: account.id } } | ||||
| 
 | ||||
|     let(:current_user) { Fabricate(:user, role: role) } | ||||
|     let(:account) { user.account } | ||||
|     let(:user) { Fabricate(:user) } | ||||
| 
 | ||||
|     before do | ||||
|       account.user.update(approved: false) | ||||
|     end | ||||
| 
 | ||||
|     context 'when user is admin' do | ||||
|       let(:role) { UserRole.find_by(name: 'Admin') } | ||||
| 
 | ||||
|       it 'succeeds in rejecting account' do | ||||
|         is_expected.to redirect_to admin_accounts_path(status: 'pending') | ||||
|       end | ||||
| 
 | ||||
|       it 'logs action' do | ||||
|         is_expected.to have_http_status :found | ||||
| 
 | ||||
|         log_item = Admin::ActionLog.last | ||||
| 
 | ||||
|         expect(log_item).to_not be_nil | ||||
|         expect(log_item.action).to eq :reject | ||||
|         expect(log_item.account_id).to eq current_user.account_id | ||||
|         expect(log_item.target_id).to eq account.user.id | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when user is not admin' do | ||||
|       let(:role) { UserRole.everyone } | ||||
| 
 | ||||
|       it 'fails to reject account' do | ||||
|         is_expected.to have_http_status :forbidden | ||||
|         expect(user.reload).not_to be_approved | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'POST #redownload' do | ||||
|     subject { post :redownload, params: { id: account.id } } | ||||
| 
 | ||||
|  |  | |||
|  | @ -100,6 +100,15 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do | |||
|     it 'approves user' do | ||||
|       expect(account.reload.user_approved?).to be true | ||||
|     end | ||||
| 
 | ||||
|     it 'logs action' do | ||||
|       log_item = Admin::ActionLog.last | ||||
| 
 | ||||
|       expect(log_item).to_not be_nil | ||||
|       expect(log_item.action).to eq :approve | ||||
|       expect(log_item.account_id).to eq user.account_id | ||||
|       expect(log_item.target_id).to eq account.user.id | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'POST #reject' do | ||||
|  | @ -118,6 +127,15 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do | |||
|     it 'removes user' do | ||||
|       expect(User.where(id: account.user.id).count).to eq 0 | ||||
|     end | ||||
| 
 | ||||
|     it 'logs action' do | ||||
|       log_item = Admin::ActionLog.last | ||||
| 
 | ||||
|       expect(log_item).to_not be_nil | ||||
|       expect(log_item.action).to eq :reject | ||||
|       expect(log_item.account_id).to eq user.account_id | ||||
|       expect(log_item.target_id).to eq account.user.id | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'POST #enable' do | ||||
|  |  | |||
|  | @ -22,9 +22,11 @@ RSpec.describe Api::V1::FiltersController, type: :controller do | |||
| 
 | ||||
|   describe 'POST #create' do | ||||
|     let(:scopes) { 'write:filters' } | ||||
|     let(:irreversible) { true } | ||||
|     let(:whole_word)   { false } | ||||
| 
 | ||||
|     before do | ||||
|       post :create, params: { phrase: 'magic', context: %w(home), irreversible: true } | ||||
|       post :create, params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word } | ||||
|     end | ||||
| 
 | ||||
|     it 'returns http success' do | ||||
|  | @ -34,11 +36,29 @@ RSpec.describe Api::V1::FiltersController, type: :controller do | |||
|     it 'creates a filter' do | ||||
|       filter = user.account.custom_filters.first | ||||
|       expect(filter).to_not be_nil | ||||
|       expect(filter.keywords.pluck(:keyword)).to eq ['magic'] | ||||
|       expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] | ||||
|       expect(filter.context).to eq %w(home) | ||||
|       expect(filter.irreversible?).to be true | ||||
|       expect(filter.irreversible?).to be irreversible | ||||
|       expect(filter.expires_at).to be_nil | ||||
|     end | ||||
| 
 | ||||
|     context 'with different parameters' do | ||||
|       let(:irreversible) { false } | ||||
|       let(:whole_word)   { true } | ||||
| 
 | ||||
|       it 'returns http success' do | ||||
|         expect(response).to have_http_status(200) | ||||
|       end | ||||
| 
 | ||||
|       it 'creates a filter' do | ||||
|         filter = user.account.custom_filters.first | ||||
|         expect(filter).to_not be_nil | ||||
|         expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]] | ||||
|         expect(filter.context).to eq %w(home) | ||||
|         expect(filter.irreversible?).to be irreversible | ||||
|         expect(filter.expires_at).to be_nil | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe 'GET #show' do | ||||
|  |  | |||
|  | @ -1,5 +1,48 @@ | |||
| require 'rails_helper' | ||||
| 
 | ||||
| RSpec.describe AccountMigration, type: :model do | ||||
|   describe 'validations' do | ||||
|     let(:source_account) { Fabricate(:account) } | ||||
|     let(:target_acct)    { target_account.acct } | ||||
| 
 | ||||
|     let(:subject) { AccountMigration.new(account: source_account, acct: target_acct) } | ||||
| 
 | ||||
|     context 'with valid properties' do | ||||
|       let(:target_account) { Fabricate(:account, username: 'target', domain: 'remote.org') } | ||||
| 
 | ||||
|       before do | ||||
|         target_account.aliases.create!(acct: source_account.acct) | ||||
| 
 | ||||
|         service_double = double | ||||
|         allow(ResolveAccountService).to receive(:new).and_return(service_double) | ||||
|         allow(service_double).to receive(:call).with(target_acct, anything).and_return(target_account) | ||||
|       end | ||||
| 
 | ||||
|       it 'passes validations' do | ||||
|         expect(subject).to be_valid | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with unresolveable account' do | ||||
|       let(:target_acct) { 'target@remote' } | ||||
| 
 | ||||
|       before do | ||||
|         service_double = double | ||||
|         allow(ResolveAccountService).to receive(:new).and_return(service_double) | ||||
|         allow(service_double).to receive(:call).with(target_acct, anything).and_return(nil) | ||||
|       end | ||||
| 
 | ||||
|       it 'has errors on acct field' do | ||||
|         expect(subject).to model_have_error_on_field(:acct) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with a space in the domain part' do | ||||
|       let(:target_acct) { 'target@remote. org' } | ||||
| 
 | ||||
|       it 'has errors on acct field' do | ||||
|         expect(subject).to model_have_error_on_field(:acct) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue