diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..7cec57180 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eugen@zeonfederated.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Gemfile b/Gemfile index 7b359af1d..d0b7aaef1 100644 --- a/Gemfile +++ b/Gemfile @@ -14,8 +14,10 @@ gem 'pg', '~> 0.20' gem 'pghero', '~> 1.7' gem 'dotenv-rails', '~> 2.2' -gem 'aws-sdk', '~> 2.9' -gem 'fog-openstack', '~> 0.1' +gem 'fog-aws', '~> 1.4', require: false +gem 'fog-core', '~> 1.45' +gem 'fog-local', '~> 0.4', require: false +gem 'fog-openstack', '~> 0.1', require: false gem 'paperclip', '~> 5.1' gem 'paperclip-av-transcoder', '~> 0.6' @@ -38,14 +40,14 @@ gem 'http', '~> 2.2' gem 'http_accept_language', '~> 2.1' gem 'httplog', '~> 0.99' gem 'idn-ruby', require: 'idn' -gem 'kaminari', '~> 1.0' +gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.1' -gem 'nokogiri', '~> 1.7' +gem 'nokogiri', '~> 1.8' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.0' +gem 'oj', '~> 3.3' gem 'ostatus2', '~> 2.0' -gem 'ox', '~> 2.5' +gem 'ox', '~> 2.8' gem 'pundit', '~> 1.1' gem 'rabl', '~> 0.13' gem 'rack-attack', '~> 5.0' @@ -75,15 +77,15 @@ gem 'json-ld-preloaded', '~> 2.2.1' gem 'rdf-normalize', '~> 0.3.1' group :development, :test do - gem 'fabrication', '~> 2.16' + gem 'fabrication', '~> 2.18' gem 'fuubar', '~> 2.2' gem 'i18n-tasks', '~> 0.9', require: false gem 'pry-rails', '~> 0.3' - gem 'rspec-rails', '~> 3.6' + gem 'rspec-rails', '~> 3.7' end group :test do - gem 'capybara', '~> 2.14' + gem 'capybara', '~> 2.15' gem 'climate_control', '~> 0.2' gem 'faker', '~> 1.7' gem 'microformats', '~> 4.0' @@ -91,13 +93,13 @@ group :test do gem 'rspec-sidekiq', '~> 3.0' gem 'simplecov', '~> 0.14', require: false gem 'webmock', '~> 3.0' - gem 'parallel_tests', '~> 2.14' + gem 'parallel_tests', '~> 2.17' end group :development do gem 'active_record_query_trace', '~> 1.5' gem 'annotate', '~> 2.7' - gem 'better_errors', '~> 2.1' + gem 'better_errors', '~> 2.4' gem 'binding_of_caller', '~> 0.7' gem 'bullet', '~> 5.5' gem 'letter_opener', '~> 1.4' @@ -105,15 +107,15 @@ group :development do gem 'rubocop', require: false gem 'brakeman', '~> 4.0', require: false gem 'bundler-audit', '~> 0.6', require: false - gem 'scss_lint', '~> 0.53', require: false + gem 'scss_lint', '~> 0.55', require: false - gem 'capistrano', '~> 3.8' - gem 'capistrano-rails', '~> 1.2' + gem 'capistrano', '~> 3.10' + gem 'capistrano-rails', '~> 1.3' gem 'capistrano-rbenv', '~> 2.1' gem 'capistrano-yarn', '~> 2.0' end group :production do - gem 'lograge', '~> 0.5' + gem 'lograge', '~> 0.7' gem 'redis-rails', '~> 5.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 14ed0d309..f9c69d538 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -57,25 +57,17 @@ GEM encryptor (~> 3.0.0) av (0.9.0) cocaine (~> 0.5.3) - aws-sdk (2.10.46) - aws-sdk-resources (= 2.10.46) - aws-sdk-core (2.10.46) - aws-sigv4 (~> 1.0) - jmespath (~> 1.0) - aws-sdk-resources (2.10.46) - aws-sdk-core (= 2.10.46) - aws-sigv4 (1.0.2) bcrypt (3.1.11) - better_errors (2.3.0) + better_errors (2.4.0) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) - binding_of_caller (0.7.2) + binding_of_caller (0.7.3) debug_inspector (>= 0.0.1) - bootsnap (1.1.3) + bootsnap (1.1.5) msgpack (~> 1.0) brakeman (4.0.1) - browser (2.5.1) + browser (2.5.2) builder (3.2.3) bullet (5.6.1) activesupport (>= 3.0.0) @@ -83,23 +75,23 @@ GEM bundler-audit (0.6.0) bundler (~> 1.2) thor (~> 0.18) - capistrano (3.9.1) + capistrano (3.10.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) sshkit (>= 1.9.0) - capistrano-bundler (1.2.0) + capistrano-bundler (1.3.0) capistrano (~> 3.1) sshkit (~> 1.2) capistrano-rails (1.3.0) capistrano (~> 3.1) capistrano-bundler (~> 1.1) - capistrano-rbenv (2.1.1) + capistrano-rbenv (2.1.2) capistrano (~> 3.1) sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (2.15.1) + capybara (2.15.4) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) @@ -110,7 +102,7 @@ GEM activesupport charlock_holmes (0.7.5) chunky_png (1.3.8) - cld3 (3.2.0) + cld3 (3.2.1) ffi (>= 1.1.0, < 1.10.0) climate_control (0.2.0) cocaine (0.5.8) @@ -150,16 +142,21 @@ GEM thread thread_safe encryptor (3.0.0) - erubi (1.6.1) - et-orbi (1.0.5) + erubi (1.7.0) + et-orbi (1.0.8) tzinfo excon (0.59.0) execjs (2.7.0) - fabrication (2.16.3) + fabrication (2.18.0) faker (1.8.4) i18n (~> 0.5) fast_blank (1.0.0) ffi (1.9.18) + fog-aws (1.4.1) + fog-core (~> 1.38) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) fog-core (1.45.0) builder excon (~> 0.58) @@ -167,15 +164,20 @@ GEM fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) - fog-openstack (0.1.21) + fog-local (0.4.0) + fog-core (~> 1.27) + fog-openstack (0.1.22) fog-core (>= 1.40) fog-json (>= 1.0) ipaddress (>= 0.8) + fog-xml (0.1.3) + fog-core + nokogiri (>= 1.5.11, < 2.0.0) formatador (0.2.5) fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - globalid (0.4.0) + globalid (0.4.1) activesupport (>= 4.2.0) goldfinger (2.0.1) addressable (~> 2.5) @@ -211,7 +213,8 @@ GEM httplog (0.99.7) colorize rack - i18n (0.8.6) + i18n (0.9.0) + concurrent-ruby (~> 1.0) i18n-tasks (0.9.18) activesupport (>= 4.0.2) ast (>= 2.1.0) @@ -225,29 +228,28 @@ GEM idn-ruby (0.1.0) ipaddress (0.8.3) iso-639 (0.2.8) - jmespath (1.3.1) json (2.1.0) - json-ld (2.1.5) + json-ld (2.1.7) multi_json (~> 1.12) - rdf (~> 2.2) + rdf (~> 2.2, >= 2.2.8) json-ld-preloaded (2.2.2) json-ld (~> 2.1, >= 2.1.5) multi_json (~> 1.11) rdf (~> 2.2) jsonapi-renderer (0.1.3) jwt (1.5.6) - kaminari (1.0.1) + kaminari (1.1.1) activesupport (>= 4.1.0) - kaminari-actionview (= 1.0.1) - kaminari-activerecord (= 1.0.1) - kaminari-core (= 1.0.1) - kaminari-actionview (1.0.1) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) actionview - kaminari-core (= 1.0.1) - kaminari-activerecord (1.0.1) + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) activerecord - kaminari-core (= 1.0.1) - kaminari-core (1.0.1) + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.4.1) @@ -257,18 +259,19 @@ GEM letter_opener (~> 1.0) railties (>= 3.2) link_header (0.0.8) - lograge (0.6.0) + lograge (0.7.1) actionpack (>= 4, < 5.2) activesupport (>= 4, < 5.2) railties (>= 4, < 5.2) request_store (~> 1.0) - loofah (2.0.3) + loofah (2.1.1) + crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.6.6) mime-types (>= 1.16, < 4) mario-redis-lock (1.2.0) redis (~> 3, >= 3.0.5) - method_source (0.8.2) + method_source (0.9.0) microformats (4.0.7) json nokogiri @@ -277,7 +280,7 @@ GEM mime-types-data (3.2016.0521) mimemagic (0.3.2) mini_mime (0.1.4) - mini_portile2 (2.2.0) + mini_portile2 (2.3.0) minitest (5.10.3) msgpack (1.1.0) multi_json (1.12.2) @@ -285,8 +288,8 @@ GEM net-ssh (>= 2.6.5) net-ssh (4.2.0) nio4r (2.1.0) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) + nokogiri (1.8.1) + mini_portile2 (~> 2.3.0) nokogumbo (1.4.13) nokogiri nsa (0.2.4) @@ -294,15 +297,15 @@ GEM concurrent-ruby (~> 1.0.0) sidekiq (>= 3.5.0) statsd-ruby (~> 1.2.0) - oj (3.3.5) - openssl (2.0.5) + oj (3.3.9) + openssl (2.0.6) orm_adapter (0.5.0) ostatus2 (2.0.1) addressable (~> 2.4) http (~> 2.0) nokogiri (~> 1.6) openssl (~> 2.0) - ox (2.6.0) + ox (2.8.1) paperclip (5.1.0) activemodel (>= 4.2.0) activesupport (>= 4.2.0) @@ -313,19 +316,18 @@ GEM av (~> 0.9.0) paperclip (>= 2.5.2) parallel (1.12.0) - parallel_tests (2.15.0) + parallel_tests (2.17.0) parallel parser (2.4.0.0) ast (~> 2.2) pg (0.21.0) pghero (1.7.0) activerecord - pkg-config (1.2.7) + pkg-config (1.2.8) powerpack (0.1.1) - pry (0.10.4) + pry (0.11.2) coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) + method_source (~> 0.9.0) pry-rails (0.3.6) pry (>= 0.10.4) public_suffix (3.0.0) @@ -379,31 +381,31 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake - rake (12.1.0) - rdf (2.2.9) + rake (12.2.1) + rdf (2.2.11) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.3.2) rdf (~> 2.0) - redis (3.3.3) - redis-actionpack (5.0.1) + redis (3.3.5) + redis-actionpack (5.0.2) actionpack (>= 4.0, < 6) redis-rack (>= 1, < 3) - redis-store (>= 1.1.0, < 1.4.0) - redis-activesupport (5.0.3) + redis-store (>= 1.1.0, < 2) + redis-activesupport (5.0.4) activesupport (>= 3, < 6) - redis-store (~> 1.3.0) + redis-store (>= 1.3, < 2) redis-namespace (1.5.3) redis (~> 3.0, >= 3.0.4) - redis-rack (2.0.2) + redis-rack (2.0.3) rack (>= 1.5, < 3) - redis-store (>= 1.2, < 1.4) + redis-store (>= 1.2, < 2) redis-rails (5.0.2) redis-actionpack (>= 5.0, < 6) redis-activesupport (>= 5.0, < 6) redis-store (>= 1.2, < 2) - redis-store (1.3.0) - redis (>= 2.2) + redis-store (1.4.1) + redis (>= 2.2, < 5) request_store (1.3.2) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) @@ -411,27 +413,27 @@ GEM rotp (2.1.2) rqrcode (0.10.1) chunky_png (~> 1.0) - rspec-core (3.6.0) - rspec-support (~> 3.6.0) - rspec-expectations (3.6.0) + rspec-core (3.7.0) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-mocks (3.6.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.6.0) - rspec-rails (3.6.1) + rspec-support (~> 3.7.0) + rspec-rails (3.7.1) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.6.0) - rspec-expectations (~> 3.6.0) - rspec-mocks (~> 3.6.0) - rspec-support (~> 3.6.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-support (~> 3.7.0) rspec-sidekiq (3.0.3) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) - rspec-support (3.6.0) - rubocop (0.50.0) + rspec-support (3.7.0) + rubocop (0.51.0) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) @@ -439,7 +441,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-oembed (0.12.0) - ruby-progressbar (1.8.3) + ruby-progressbar (1.9.0) rufus-scheduler (3.4.2) et-orbi (~> 1.0) safe_yaml (1.0.4) @@ -448,19 +450,19 @@ GEM nokogiri (>= 1.4.4) nokogumbo (~> 1.4.1) sass (3.4.25) - scss_lint (0.54.0) + scss_lint (0.55.0) rake (>= 0.9, < 13) sass (~> 3.4.20) - sidekiq (5.0.4) + sidekiq (5.0.5) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (~> 3.3, >= 3.3.3) + redis (>= 3.3.4, < 5) sidekiq-bulk (0.1.1) activesupport sidekiq - sidekiq-scheduler (2.1.9) - redis (~> 3) + sidekiq-scheduler (2.1.10) + redis (>= 3, < 5) rufus-scheduler (~> 3.2) sidekiq (>= 3) tilt (>= 1.4.0) @@ -477,7 +479,6 @@ GEM json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - slop (3.6.0) sprockets (3.7.1) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -500,9 +501,9 @@ GEM tilt (2.0.8) twitter-text (1.14.7) unf (~> 0.1.0) - tzinfo (1.2.3) + tzinfo (1.2.4) thread_safe (~> 0.1) - tzinfo-data (1.2017.2) + tzinfo-data (1.2017.3) tzinfo (>= 1.0.0) uglifier (3.2.0) execjs (>= 0.3.0, < 3) @@ -517,7 +518,7 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - webpacker (3.0.1) + webpacker (3.0.2) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) @@ -538,19 +539,18 @@ DEPENDENCIES active_record_query_trace (~> 1.5) addressable (~> 2.5) annotate (~> 2.7) - aws-sdk (~> 2.9) - better_errors (~> 2.1) + better_errors (~> 2.4) binding_of_caller (~> 0.7) bootsnap brakeman (~> 4.0) browser bullet (~> 5.5) bundler-audit (~> 0.6) - capistrano (~> 3.8) - capistrano-rails (~> 1.2) + capistrano (~> 3.10) + capistrano-rails (~> 1.3) capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) - capybara (~> 2.14) + capybara (~> 2.15) charlock_holmes (~> 0.7.5) cld3 (~> 3.2.0) climate_control (~> 0.2) @@ -558,9 +558,12 @@ DEPENDENCIES devise-two-factor (~> 3.0) doorkeeper (~> 4.2) dotenv-rails (~> 2.2) - fabrication (~> 2.16) + fabrication (~> 2.18) faker (~> 1.7) fast_blank (~> 1.0) + fog-aws (~> 1.4) + fog-core (~> 1.45) + fog-local (~> 0.4) fog-openstack (~> 0.1) fuubar (~> 2.2) goldfinger (~> 2.0) @@ -574,22 +577,22 @@ DEPENDENCIES idn-ruby iso-639 json-ld-preloaded (~> 2.2.1) - kaminari (~> 1.0) + kaminari (~> 1.1) letter_opener (~> 1.4) letter_opener_web (~> 1.3) link_header (~> 0.0) - lograge (~> 0.5) + lograge (~> 0.7) mario-redis-lock (~> 1.2) microformats (~> 4.0) mime-types (~> 3.1) - nokogiri (~> 1.7) + nokogiri (~> 1.8) nsa (~> 0.2) - oj (~> 3.0) + oj (~> 3.3) ostatus2 (~> 2.0) - ox (~> 2.5) + ox (~> 2.8) paperclip (~> 5.1) paperclip-av-transcoder (~> 0.6) - parallel_tests (~> 2.14) + parallel_tests (~> 2.17) pg (~> 0.20) pghero (~> 1.7) pkg-config (~> 1.2) @@ -609,12 +612,12 @@ DEPENDENCIES redis-namespace (~> 1.5) redis-rails (~> 5.0) rqrcode (~> 0.10) - rspec-rails (~> 3.6) + rspec-rails (~> 3.7) rspec-sidekiq (~> 3.0) rubocop ruby-oembed (~> 0.12) sanitize (~> 4.4) - scss_lint (~> 0.53) + scss_lint (~> 0.55) sidekiq (~> 5.0) sidekiq-bulk (~> 0.1.1) sidekiq-scheduler (~> 2.1) diff --git a/README.md b/README.md index fc8296813..5cf91d52c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ![Mastodon](https://i.imgur.com/NhZc40l.png) ======== -[![Build Status](http://img.shields.io/travis/tootsuite/mastodon.svg)][travis] -[![Code Climate](https://img.shields.io/codeclimate/github/tootsuite/mastodon.svg)][code_climate] +[![Build Status](https://img.shields.io/travis/tootsuite/mastodon.svg)][travis] +[![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate] [travis]: https://travis-ci.org/tootsuite/mastodon [code_climate]: https://codeclimate.com/github/tootsuite/mastodon diff --git a/app/controllers/admin/account_moderation_notes_controller.rb b/app/controllers/admin/account_moderation_notes_controller.rb index 414a875d0..7f69a3363 100644 --- a/app/controllers/admin/account_moderation_notes_controller.rb +++ b/app/controllers/admin/account_moderation_notes_controller.rb @@ -1,31 +1,41 @@ # frozen_string_literal: true -class Admin::AccountModerationNotesController < Admin::BaseController - def create - @account_moderation_note = current_account.account_moderation_notes.new(resource_params) - if @account_moderation_note.save - @target_account = @account_moderation_note.target_account - redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.created_msg') - else - @account = @account_moderation_note.target_account - @moderation_notes = @account.targeted_moderation_notes.latest - render template: 'admin/accounts/show' +module Admin + class AccountModerationNotesController < BaseController + before_action :set_account_moderation_note, only: [:destroy] + + def create + authorize AccountModerationNote, :create? + + @account_moderation_note = current_account.account_moderation_notes.new(resource_params) + + if @account_moderation_note.save + redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.created_msg') + else + @account = @account_moderation_note.target_account + @moderation_notes = @account.targeted_moderation_notes.latest + + render template: 'admin/accounts/show' + end + end + + def destroy + authorize @account_moderation_note, :destroy? + @account_moderation_note.destroy + redirect_to admin_account_path(@account_moderation_note.target_account_id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg') + end + + private + + def resource_params + params.require(:account_moderation_note).permit( + :content, + :target_account_id + ) + end + + def set_account_moderation_note + @account_moderation_note = AccountModerationNote.find(params[:id]) end end - - def destroy - @account_moderation_note = AccountModerationNote.find(params[:id]) - @target_account = @account_moderation_note.target_account - @account_moderation_note.destroy - redirect_to admin_account_path(@target_account.id), notice: I18n.t('admin.account_moderation_notes.destroyed_msg') - end - - private - - def resource_params - params.require(:account_moderation_note).permit( - :content, - :target_account_id - ) - end end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index ffa4dc850..0829bc769 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,29 +2,54 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload] + before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :enable, :disable, :memorialize] before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] + before_action :require_local_account!, only: [:enable, :disable, :memorialize] def index + authorize :account, :index? @accounts = filtered_accounts.page(params[:page]) end def show + authorize @account, :show? @account_moderation_note = current_account.account_moderation_notes.new(target_account: @account) @moderation_notes = @account.targeted_moderation_notes.latest end def subscribe + authorize @account, :subscribe? Pubsubhubbub::SubscribeWorker.perform_async(@account.id) redirect_to admin_account_path(@account.id) end def unsubscribe + authorize @account, :unsubscribe? Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id) redirect_to admin_account_path(@account.id) end + def memorialize + authorize @account, :memorialize? + @account.memorialize! + redirect_to admin_account_path(@account.id) + end + + def enable + authorize @account.user, :enable? + @account.user.enable! + redirect_to admin_account_path(@account.id) + end + + def disable + authorize @account.user, :disable? + @account.user.disable! + redirect_to admin_account_path(@account.id) + end + def redownload + authorize @account, :redownload? + @account.reset_avatar! @account.reset_header! @account.save! @@ -42,6 +67,10 @@ module Admin redirect_to admin_account_path(@account.id) if @account.local? end + def require_local_account! + redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present? + end + def filtered_accounts AccountFilter.new(filter_params).results end diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 11fe326bc..db4839a8f 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -2,7 +2,9 @@ module Admin class BaseController < ApplicationController - before_action :require_admin! + include Authorization + + before_action :require_staff! layout 'admin' end diff --git a/app/controllers/admin/confirmations_controller.rb b/app/controllers/admin/confirmations_controller.rb index 2542e21ee..c10b0ebee 100644 --- a/app/controllers/admin/confirmations_controller.rb +++ b/app/controllers/admin/confirmations_controller.rb @@ -2,15 +2,18 @@ module Admin class ConfirmationsController < BaseController + before_action :set_user + def create - account_user.confirm + authorize @user, :confirm? + @user.confirm! redirect_to admin_accounts_path end private - def account_user - Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) end end end diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 5cce5bce4..693d28b1f 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_custom_emoji, except: [:index, :new, :create] def index - @custom_emojis = filtered_custom_emojis.page(params[:page]) + authorize :custom_emoji, :index? + @custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page]) end def new + authorize :custom_emoji, :create? @custom_emoji = CustomEmoji.new end def create + authorize :custom_emoji, :create? + @custom_emoji = CustomEmoji.new(resource_params) if @custom_emoji.save @@ -22,29 +26,44 @@ module Admin end end + def update + authorize @custom_emoji, :update? + + if @custom_emoji.update(resource_params) + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg') + else + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.update_failed_msg') + end + end + def destroy + authorize @custom_emoji, :destroy? @custom_emoji.destroy redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') end def copy - emoji = CustomEmoji.new(domain: nil, shortcode: @custom_emoji.shortcode, image: @custom_emoji.image) + authorize @custom_emoji, :copy? - if emoji.save + emoji = CustomEmoji.find_or_create_by(domain: nil, shortcode: @custom_emoji.shortcode) + + if emoji.update(image: @custom_emoji.image) flash[:notice] = I18n.t('admin.custom_emojis.copied_msg') else flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg') end - redirect_to admin_custom_emojis_path(params[:page]) + redirect_to admin_custom_emojis_path(page: params[:page]) end def enable + authorize @custom_emoji, :enable? @custom_emoji.update!(disabled: false) redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg') end def disable + authorize @custom_emoji, :disable? @custom_emoji.update!(disabled: true) redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg') end @@ -56,7 +75,7 @@ module Admin end def resource_params - params.require(:custom_emoji).permit(:shortcode, :image) + params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker) end def filtered_custom_emojis diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index 1ab620e03..e383dc831 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_domain_block, only: [:show, :destroy] def index + authorize :domain_block, :index? @domain_blocks = DomainBlock.page(params[:page]) end def new + authorize :domain_block, :create? @domain_block = DomainBlock.new end def create + authorize :domain_block, :create? + @domain_block = DomainBlock.new(resource_params) if @domain_block.save @@ -23,9 +27,12 @@ module Admin end end - def show; end + def show + authorize @domain_block, :show? + end def destroy + authorize @domain_block, :destroy? UnblockDomainService.new.call(@domain_block, retroactive_unblock?) redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg') end diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb index 09275d5dc..01058bf46 100644 --- a/app/controllers/admin/email_domain_blocks_controller.rb +++ b/app/controllers/admin/email_domain_blocks_controller.rb @@ -5,14 +5,18 @@ module Admin before_action :set_email_domain_block, only: [:show, :destroy] def index + authorize :email_domain_block, :index? @email_domain_blocks = EmailDomainBlock.page(params[:page]) end def new + authorize :email_domain_block, :create? @email_domain_block = EmailDomainBlock.new end def create + authorize :email_domain_block, :create? + @email_domain_block = EmailDomainBlock.new(resource_params) if @email_domain_block.save @@ -23,6 +27,7 @@ module Admin end def destroy + authorize @email_domain_block, :destroy? @email_domain_block.destroy redirect_to admin_email_domain_blocks_path, notice: I18n.t('admin.email_domain_blocks.destroyed_msg') end diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index 22f02e5d0..8ed0ea421 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -3,10 +3,12 @@ module Admin class InstancesController < BaseController def index + authorize :instance, :index? @instances = ordered_instances end def resubscribe + authorize :instance, :resubscribe? params.require(:by_domain) Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id)) redirect_to admin_instances_path diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 5a31adecf..4f66ce708 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -2,19 +2,20 @@ module Admin class ReportedStatusesController < BaseController - include Authorization - before_action :set_report before_action :set_status, only: [:update, :destroy] def create - @form = Form::StatusBatch.new(form_status_batch_params) - flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + authorize :status, :update? + + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_report_path(@report) end def update + authorize @status, :update? @status.update(status_params) redirect_to admin_report_path(@report) end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 226467739..745757ee8 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -5,14 +5,17 @@ module Admin before_action :set_report, except: [:index] def index + authorize :report, :index? @reports = filtered_reports.page(params[:page]) end def show + authorize @report, :show? @form = Form::StatusBatch.new end def update + authorize @report, :update? process_report redirect_to admin_report_path(@report) end diff --git a/app/controllers/admin/resets_controller.rb b/app/controllers/admin/resets_controller.rb index 6db648403..00b590bf6 100644 --- a/app/controllers/admin/resets_controller.rb +++ b/app/controllers/admin/resets_controller.rb @@ -2,17 +2,18 @@ module Admin class ResetsController < BaseController - before_action :set_account + before_action :set_user def create - @account.user.send_reset_password_instructions + authorize @user, :reset_password? + @user.send_reset_password_instructions redirect_to admin_accounts_path end private - def set_account - @account = Account.find(params[:account_id]) + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) end end end diff --git a/app/controllers/admin/roles_controller.rb b/app/controllers/admin/roles_controller.rb new file mode 100644 index 000000000..8f8685827 --- /dev/null +++ b/app/controllers/admin/roles_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Admin + class RolesController < BaseController + before_action :set_user + + def promote + authorize @user, :promote? + @user.promote! + redirect_to admin_account_path(@user.account_id) + end + + def demote + authorize @user, :demote? + @user.demote! + redirect_to admin_account_path(@user.account_id) + end + + private + + def set_user + @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) + end + end +end diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index a2f86b8a9..e81290228 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -28,10 +28,13 @@ module Admin ).freeze def edit + authorize :settings, :show? @admin_settings = Form::AdminSettings.new end def update + authorize :settings, :update? + settings_params.each do |key, value| if UPLOAD_SETTINGS.include?(key) upload = SiteUpload.where(var: key).first_or_initialize(var: key) diff --git a/app/controllers/admin/silences_controller.rb b/app/controllers/admin/silences_controller.rb index 81a3008b9..01fb292de 100644 --- a/app/controllers/admin/silences_controller.rb +++ b/app/controllers/admin/silences_controller.rb @@ -5,11 +5,13 @@ module Admin before_action :set_account def create + authorize @account, :silence? @account.update(silenced: true) redirect_to admin_accounts_path end def destroy + authorize @account, :unsilence? @account.update(silenced: false) redirect_to admin_accounts_path end diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index b05000b16..b54a9b824 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -2,8 +2,6 @@ module Admin class StatusesController < BaseController - include Authorization - helper_method :current_params before_action :set_account @@ -12,24 +10,30 @@ module Admin PER_PAGE = 20 def index + authorize :status, :index? + @statuses = @account.statuses + if params[:media] account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct @statuses.merge!(Status.where(id: account_media_status_ids)) end - @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) - @form = Form::StatusBatch.new + @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) + @form = Form::StatusBatch.new end def create - @form = Form::StatusBatch.new(form_status_batch_params) - flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + authorize :status, :update? + + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_account_statuses_path(@account.id, current_params) end def update + authorize @status, :update? @status.update(status_params) redirect_to admin_account_statuses_path(@account.id, current_params) end @@ -60,6 +64,7 @@ module Admin def current_params page = (params[:page] || 1).to_i + { media: params[:media], page: page > 1 && page, diff --git a/app/controllers/admin/subscriptions_controller.rb b/app/controllers/admin/subscriptions_controller.rb index 624a475a3..40500ef43 100644 --- a/app/controllers/admin/subscriptions_controller.rb +++ b/app/controllers/admin/subscriptions_controller.rb @@ -3,6 +3,7 @@ module Admin class SubscriptionsController < BaseController def index + authorize :subscription, :index? @subscriptions = ordered_subscriptions.page(requested_page) end diff --git a/app/controllers/admin/suspensions_controller.rb b/app/controllers/admin/suspensions_controller.rb index 5d9048d94..778feea5e 100644 --- a/app/controllers/admin/suspensions_controller.rb +++ b/app/controllers/admin/suspensions_controller.rb @@ -5,12 +5,14 @@ module Admin before_action :set_account def create + authorize @account, :suspend? Admin::SuspensionWorker.perform_async(@account.id) redirect_to admin_accounts_path end def destroy - @account.update(suspended: false) + authorize @account, :unsuspend? + @account.unsuspend! redirect_to admin_accounts_path end diff --git a/app/controllers/admin/two_factor_authentications_controller.rb b/app/controllers/admin/two_factor_authentications_controller.rb index 69c08f605..5a45d25cd 100644 --- a/app/controllers/admin/two_factor_authentications_controller.rb +++ b/app/controllers/admin/two_factor_authentications_controller.rb @@ -5,6 +5,7 @@ module Admin before_action :set_user def destroy + authorize @user, :disable_2fa? @user.disable_two_factor! redirect_to admin_accounts_path end diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb index 9592cd4bd..22828217d 100644 --- a/app/controllers/api/v1/reports_controller.rb +++ b/app/controllers/api/v1/reports_controller.rb @@ -19,7 +19,7 @@ class Api::V1::ReportsController < Api::BaseController comment: report_params[:comment] ) - User.admins.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later } + User.staff.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later } render json: @report, serializer: REST::ReportSerializer end diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index bc5b8e5d4..997eed6e2 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Api::V1::SearchController < Api::BaseController + include Authorization + RESULTS_LIMIT = 5 before_action -> { doorkeeper_authorize! :read } @@ -9,12 +11,24 @@ class Api::V1::SearchController < Api::BaseController respond_to :json def index - @search = Search.new(search_results) + @search = Search.new(search) render json: @search, serializer: REST::SearchSerializer end private + def search + search_results.tap do |search| + search[:statuses].keep_if do |status| + begin + authorize status, :show? + rescue Mastodon::NotPermittedError + false + end + end + end + end + def search_results SearchService.new.call( params[:q], diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d5eca6ffb..f41a7f9be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -18,6 +18,7 @@ class ApplicationController < ActionController::Base rescue_from ActionController::RoutingError, with: :not_found rescue_from ActiveRecord::RecordNotFound, with: :not_found rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity + rescue_from Mastodon::NotPermittedError, with: :forbidden before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? before_action :check_suspension, if: :user_signed_in? @@ -40,6 +41,10 @@ class ApplicationController < ActionController::Base redirect_to root_path unless current_user&.admin? end + def require_staff! + redirect_to root_path unless current_user&.staff? + end + def check_suspension forbidden if current_user.account.suspended? end diff --git a/app/controllers/concerns/authorization.rb b/app/controllers/concerns/authorization.rb index 7828fe48d..95a37e379 100644 --- a/app/controllers/concerns/authorization.rb +++ b/app/controllers/concerns/authorization.rb @@ -2,6 +2,7 @@ module Authorization extend ActiveSupport::Concern + include Pundit def pundit_user diff --git a/app/controllers/settings/notifications_controller.rb b/app/controllers/settings/notifications_controller.rb index 09839f16e..ce2530c54 100644 --- a/app/controllers/settings/notifications_controller.rb +++ b/app/controllers/settings/notifications_controller.rb @@ -26,7 +26,7 @@ class Settings::NotificationsController < ApplicationController def user_settings_params params.require(:user).permit( notification_emails: %i(follow follow_request reblog favourite mention digest), - interactions: %i(must_be_follower must_be_following) + interactions: %i(must_be_follower must_be_following must_be_following_dm) ) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6d625e7db..7dfab1df1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -35,6 +35,11 @@ module ApplicationHelper Rails.env.production? ? site_title : "#{site_title} (Dev)" end + def can?(action, record) + return false if record.nil? + policy(record).public_send("#{action}?") + end + def fa_icon(icon, attributes = {}) class_names = attributes[:class]&.split(' ') || [] class_names << 'fa' @@ -43,6 +48,10 @@ module ApplicationHelper content_tag(:i, nil, attributes.merge(class: class_names.join(' '))) end + def custom_emoji_tag(custom_emoji) + image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:") + end + def opengraph(property, content) tag(:meta, content: content, property: property) end diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index c23a2e095..a3441e6f9 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -9,6 +9,10 @@ module JsonLdHelper value.is_a?(Array) ? value.first : value end + def as_array(value) + value.is_a?(Array) ? value : [value] + end + def value_or_id(value) value.is_a?(String) || value.nil? ? value : value['id'] end diff --git a/app/javascript/mastodon/actions/pin_statuses.js b/app/javascript/mastodon/actions/pin_statuses.js index 01bf8930b..3f40f6c2d 100644 --- a/app/javascript/mastodon/actions/pin_statuses.js +++ b/app/javascript/mastodon/actions/pin_statuses.js @@ -4,12 +4,13 @@ export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST'; export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS'; export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL'; +import { me } from '../initial_state'; + export function fetchPinnedStatuses() { return (dispatch, getState) => { dispatch(fetchPinnedStatusesRequest()); - const accountId = getState().getIn(['meta', 'me']); - api(getState).get(`/api/v1/accounts/${accountId}/statuses`, { params: { pinned: true } }).then(response => { + api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => { dispatch(fetchPinnedStatusesSuccess(response.data, null)); }).catch(error => { dispatch(fetchPinnedStatusesFail(error)); diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js index b9a2820fe..724b10980 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.js @@ -7,6 +7,7 @@ import Permalink from './permalink'; import IconButton from './icon_button'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { me } from '../initial_state'; const messages = defineMessages({ follow: { id: 'account.follow', defaultMessage: 'Follow' }, @@ -23,7 +24,6 @@ export default class Account extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, - me: PropTypes.string.isRequired, onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, @@ -52,7 +52,7 @@ export default class Account extends ImmutablePureComponent { } render () { - const { account, me, intl, hidden } = this.props; + const { account, intl, hidden } = this.props; if (!account) { return
; diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index e4fa8fa7a..80a8fbdb3 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -137,7 +137,9 @@ export default class ColumnHeader extends React.PureComponent {

- {title} + + {title} +
{backButton} diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index d8e445cef..06f53841d 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -72,6 +72,25 @@ export default class IconButton extends React.PureComponent { overlayed: overlay, }); + if (!animate) { + // Perf optimization: avoid unnecessary components unless + // we actually need to animate. + return ( +