From bd53dd521064b12261b82105624cf5f8b9ca9d69 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Mar 2022 08:52:32 +0100 Subject: Change design of federation pages in admin UI (#17704) * Change design of federation pages in admin UI * Fix query performance in instance media attachments measure * Fix reblogs being included in instance languages dimension --- app/controllers/admin/domain_blocks_controller.rb | 4 ---- app/controllers/admin/instances_controller.rb | 26 +++++++---------------- 2 files changed, 8 insertions(+), 22 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index b140c454c..16defc1ea 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -56,10 +56,6 @@ module Admin end end - def show - authorize @domain_block, :show? - end - def destroy authorize @domain_block, :destroy? UnblockDomainService.new.call(@domain_block) diff --git a/app/controllers/admin/instances_controller.rb b/app/controllers/admin/instances_controller.rb index 306ec1f53..5c82331de 100644 --- a/app/controllers/admin/instances_controller.rb +++ b/app/controllers/admin/instances_controller.rb @@ -4,28 +4,26 @@ module Admin class InstancesController < BaseController before_action :set_instances, only: :index before_action :set_instance, except: :index - before_action :set_exhausted_deliveries_days, only: :show def index authorize :instance, :index? + preload_delivery_failures! end def show authorize :instance, :show? + @time_period = (6.days.ago.to_date...Time.now.utc.to_date) end def destroy authorize :instance, :destroy? - Admin::DomainPurgeWorker.perform_async(@instance.domain) - log_action :destroy, @instance redirect_to admin_instances_path, notice: I18n.t('admin.instances.destroyed_msg', domain: @instance.domain) end def clear_delivery_errors authorize :delivery, :clear_delivery_errors? - @instance.delivery_failure_tracker.clear_failures! redirect_to admin_instance_path(@instance.domain) end @@ -33,11 +31,9 @@ module Admin def restart_delivery authorize :delivery, :restart_delivery? - last_unavailable_domain = unavailable_domain - - if last_unavailable_domain.present? + if @instance.unavailable? @instance.delivery_failure_tracker.track_success! - log_action :destroy, last_unavailable_domain + log_action :destroy, @instance.unavailable_domain end redirect_to admin_instance_path(@instance.domain) @@ -45,8 +41,7 @@ module Admin def stop_delivery authorize :delivery, :stop_delivery? - - UnavailableDomain.create(domain: @instance.domain) + unavailable_domain = UnavailableDomain.create!(domain: @instance.domain) log_action :create, unavailable_domain redirect_to admin_instance_path(@instance.domain) end @@ -57,12 +52,11 @@ module Admin @instance = Instance.find(params[:id]) end - def set_exhausted_deliveries_days - @exhausted_deliveries_days = @instance.delivery_failure_tracker.exhausted_deliveries_days - end - def set_instances @instances = filtered_instances.page(params[:page]) + end + + def preload_delivery_failures! warning_domains_map = DeliveryFailureTracker.warning_domains_map @instances.each do |instance| @@ -70,10 +64,6 @@ module Admin end end - def unavailable_domain - UnavailableDomain.find_by(domain: @instance.domain) - end - def filtered_instances InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results end -- cgit From a6ed6845c9cab3b314ce6434b851cc507a71ee62 Mon Sep 17 00:00:00 2001 From: chandrn7 Date: Wed, 9 Mar 2022 06:07:35 -0500 Subject: Allow login through OpenID Connect (#16221) * added OpenID Connect as an SSO option * minor fixes * added comments, removed an option that shouldn't be set * fixed Gemfile.lock * added newline to end of Gemfile.lock * removed tab from Gemfile.lock * remove chomp * codeclimate changes and small name change to make function's purpose clearer * codeclimate fix * added SSO buttons to /about page * minor refactor * minor style change * removed spurious change * removed unecessary conditional from ensure_valid_username and added support for auth.info.name in user_params_from_auth * minor changes --- Gemfile | 1 + Gemfile.lock | 41 ++++++++++++++++++++++ .../auth/omniauth_callbacks_controller.rb | 6 ++-- app/models/concerns/omniauthable.rb | 23 +++++++----- config/initializers/omniauth.rb | 41 ++++++++++++++++++++-- 5 files changed, 97 insertions(+), 15 deletions(-) (limited to 'app/controllers') diff --git a/Gemfile b/Gemfile index b5d15da61..549e774f7 100644 --- a/Gemfile +++ b/Gemfile @@ -40,6 +40,7 @@ end gem 'net-ldap', '~> 0.17' gem 'omniauth-cas', '~> 2.0' gem 'omniauth-saml', '~> 1.10' +gem 'gitlab-omniauth-openid-connect', '~>0.5.0', require: 'omniauth_openid_connect' gem 'omniauth', '~> 1.9' gem 'omniauth-rails_csrf_protection', '~> 0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 87539348c..65343a465 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,6 +68,7 @@ GEM zeitwerk (~> 2.3) addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) + aes_key_wrap (1.1.0) airbrussh (1.4.0) sshkit (>= 1.6.1, != 1.7.0) android_key_attestation (0.3.0) @@ -77,6 +78,7 @@ GEM ast (2.4.2) attr_encrypted (3.1.0) encryptor (~> 3.0.0) + attr_required (1.0.1) awrence (1.1.1) aws-eventstream (1.2.0) aws-partitions (1.558.0) @@ -260,6 +262,10 @@ GEM fuubar (2.5.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) + gitlab-omniauth-openid-connect (0.5.0) + addressable (~> 2.7) + omniauth (~> 1.9) + openid_connect (~> 1.2) globalid (1.0.0) activesupport (>= 5.0) hamlit (2.13.0) @@ -286,6 +292,7 @@ GEM domain_name (~> 0.5) http-form_data (2.3.0) http_accept_language (2.1.1) + httpclient (2.8.3) httplog (1.5.0) rack (>= 1.0) rainbow (>= 2.0.0) @@ -306,6 +313,10 @@ GEM jmespath (1.6.0) json (2.5.1) json-canonicalization (0.3.0) + json-jwt (1.13.0) + activesupport (>= 4.2) + aes_key_wrap + bindata json-ld (3.2.0) htmlentities (~> 4.3) json-canonicalization (~> 0.3) @@ -406,6 +417,16 @@ GEM omniauth-saml (1.10.3) omniauth (~> 1.3, >= 1.3.2) ruby-saml (~> 1.9) + openid_connect (1.2.0) + activemodel + attr_required (>= 1.0.0) + json-jwt (>= 1.5.0) + rack-oauth2 (>= 1.6.1) + swd (>= 1.0.0) + tzinfo + validate_email + validate_url + webfinger (>= 1.0.1) openssl (2.2.0) openssl-signature_algorithm (0.4.0) orm_adapter (0.5.0) @@ -449,6 +470,12 @@ GEM rack (>= 1.0, < 3) rack-cors (1.1.1) rack (>= 2.0.0) + rack-oauth2 (1.16.0) + activesupport + attr_required + httpclient + json-jwt (>= 1.11.0) + rack (>= 2.1.0) rack-proxy (0.7.0) rack rack-test (1.1.0) @@ -608,6 +635,10 @@ GEM stoplight (2.2.1) strong_migrations (0.7.9) activerecord (>= 5) + swd (1.2.0) + activesupport (>= 3) + attr_required (>= 0.0.5) + httpclient (>= 2.4) temple (0.8.2) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -642,6 +673,12 @@ GEM unf_ext (0.0.8) unicode-display_width (2.1.0) uniform_notifier (1.14.2) + validate_email (0.1.6) + activemodel (>= 3.0) + mail (>= 2.2.5) + validate_url (1.0.13) + activemodel (>= 3.0.0) + public_suffix warden (1.2.9) rack (>= 2.0.9) webauthn (3.0.0.alpha1) @@ -654,6 +691,9 @@ GEM safety_net_attestation (~> 0.4.0) securecompare (~> 1.0) tpm-key_attestation (~> 0.9.0) + webfinger (1.1.0) + activesupport + httpclient (>= 2.4) webmock (3.14.0) addressable (>= 2.8.0) crack (>= 0.3.2) @@ -717,6 +757,7 @@ DEPENDENCIES fog-core (<= 2.1.0) fog-openstack (~> 0.3) fuubar (~> 2.5) + gitlab-omniauth-openid-connect (~> 0.5.0) hamlit-rails (~> 0.2) hiredis (~> 0.6) htmlentities (~> 4.3) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 991a50b03..f9cf6d655 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -4,8 +4,6 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController skip_before_action :verify_authenticity_token def self.provides_callback_for(provider) - provider_id = provider.to_s.chomp '_oauth2' - define_method provider do @user = User.find_for_oauth(request.env['omniauth.auth'], current_user) @@ -20,7 +18,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController ) sign_in_and_redirect @user, event: :authentication - set_flash_message(:notice, :success, kind: provider_id.capitalize) if is_navigational_format? + set_flash_message(:notice, :success, kind: Devise.omniauth_configs[provider].strategy.display_name.capitalize) if is_navigational_format? else session["devise.#{provider}_data"] = request.env['omniauth.auth'] redirect_to new_user_registration_url @@ -33,7 +31,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController end def after_sign_in_path_for(resource) - if resource.email_verified? + if resource.email_present? root_path else auth_setup_path(missing_email: '1') diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb index 791a94911..a90d5d888 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/omniauthable.rb @@ -13,7 +13,7 @@ module Omniauthable Devise.omniauth_configs.keys end - def email_verified? + def email_present? email && email !~ TEMP_EMAIL_REGEX end end @@ -40,16 +40,14 @@ module Omniauthable end def create_for_oauth(auth) - # Check if the user exists with provided email if the provider gives us a - # verified email. If no verified email was provided or the user already - # exists, we assign a temporary email and ask the user to verify it on + # Check if the user exists with provided email. If no email was provided, + # we assign a temporary email and ask the user to verify it on # the next step via Auth::SetupController.show strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy assume_verified = strategy&.security&.assume_email_is_verified - email_is_verified = auth.info.verified || auth.info.verified_email || assume_verified + email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified email = auth.info.verified_email || auth.info.email - email = nil unless email_is_verified user = User.find_by(email: email) if email_is_verified @@ -58,7 +56,7 @@ module Omniauthable user = User.new(user_params_from_auth(email, auth)) user.account.avatar_remote_url = auth.info.image if /\A#{URI::DEFAULT_PARSER.make_regexp(%w(http https))}\z/.match?(auth.info.image) - user.skip_confirmation! + user.skip_confirmation! if email_is_verified user.save! user end @@ -71,8 +69,8 @@ module Omniauthable agreement: true, external: true, account_attributes: { - username: ensure_unique_username(auth.uid), - display_name: auth.info.full_name || [auth.info.first_name, auth.info.last_name].join(' '), + username: ensure_unique_username(ensure_valid_username(auth.uid)), + display_name: auth.info.full_name || auth.info.name || [auth.info.first_name, auth.info.last_name].join(' '), }, } end @@ -88,5 +86,12 @@ module Omniauthable username end + + def ensure_valid_username(starting_username) + starting_username = starting_username.split('@')[0] + temp_username = starting_username.gsub(/[^a-z0-9_]+/i, '') + validated_username = temp_username.truncate(30, omission: '') + validated_username + end end end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 1a041ad48..51241e546 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -8,7 +8,8 @@ Devise.setup do |config| # CAS strategy if ENV['CAS_ENABLED'] == 'true' - cas_options = options + cas_options = {} + cas_options[:display_name] = ENV['CAS_DISPLAY_NAME'] || 'cas' cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL'] cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST'] cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT'] @@ -36,7 +37,8 @@ Devise.setup do |config| # SAML strategy if ENV['SAML_ENABLED'] == 'true' - saml_options = options + saml_options = {} + saml_options[:display_name] = ENV['SAML_DISPLAY_NAME'] || 'saml' saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL'] saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER'] saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL'] @@ -64,4 +66,39 @@ Devise.setup do |config| saml_options[:allowed_clock_drift] = ENV['SAML_ALLOWED_CLOCK_DRIFT'] if ENV['SAML_ALLOWED_CLOCK_DRIFT'] config.omniauth :saml, saml_options end + + # OpenID Connect Strategy + if ENV['OIDC_ENABLED'] == 'true' + oidc_options = {} + oidc_options[:display_name] = ENV['OIDC_DISPLAY_NAME'] || 'openid_connect' #OPTIONAL + oidc_options[:issuer] = ENV['OIDC_ISSUER'] if ENV['OIDC_ISSUER'] #NEED + oidc_options[:discovery] = ENV['OIDC_DISCOVERY'] == 'true' if ENV['OIDC_DISCOVERY'] #OPTIONAL (default: false) + oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] #OPTIONAL (default: basic) + scope_string = ENV['OIDC_SCOPE'] if ENV['OIDC_SCOPE'] #NEED + scopes = scope_string.split(',') + oidc_options[:scope] = scopes.map { |x| x.to_sym } + oidc_options[:response_type] = ENV['OIDC_RESPONSE_TYPE'] if ENV['OIDC_RESPONSE_TYPE'] #OPTIONAL (default: code) + oidc_options[:response_mode] = ENV['OIDC_RESPONSE_MODE'] if ENV['OIDC_RESPONSE_MODE'] #OPTIONAL (default: query) + oidc_options[:display] = ENV['OIDC_DISPLAY'] if ENV['OIDC_DISPLAY'] #OPTIONAL (default: page) + oidc_options[:prompt] = ENV['OIDC_PROMPT'] if ENV['OIDC_PROMPT'] #OPTIONAL + oidc_options[:send_nonce] = ENV['OIDC_SEND_NONCE'] == 'true' if ENV['OIDC_SEND_NONCE'] #OPTIONAL (default: true) + oidc_options[:send_scope_to_token_endpoint] = ENV['OIDC_SEND_SCOPE_TO_TOKEN_ENDPOINT'] == 'true' if ENV['OIDC_SEND_SCOPE_TO_TOKEN_ENDPOINT'] #OPTIONAL (default: true) + oidc_options[:post_logout_redirect_uri] = ENV['OIDC_IDP_LOGOUT_REDIRECT_URI'] if ENV['OIDC_IDP_LOGOUT_REDIRECT_URI'] #OPTIONAL + oidc_options[:uid_field] = ENV['OIDC_UID_FIELD'] if ENV['OIDC_UID_FIELD'] #NEED + oidc_options[:client_options] = {} + oidc_options[:client_options][:identifier] = ENV['OIDC_CLIENT_ID'] if ENV['OIDC_CLIENT_ID'] #NEED + oidc_options[:client_options][:secret] = ENV['OIDC_CLIENT_SECRET'] if ENV['OIDC_CLIENT_SECRET'] #NEED + oidc_options[:client_options][:redirect_uri] = ENV['OIDC_REDIRECT_URI'] if ENV['OIDC_REDIRECT_URI'] #NEED + oidc_options[:client_options][:scheme] = ENV['OIDC_HTTP_SCHEME'] if ENV['OIDC_HTTP_SCHEME'] #OPTIONAL (default: https) + oidc_options[:client_options][:host] = ENV['OIDC_HOST'] if ENV['OIDC_HOST'] #OPTIONAL + oidc_options[:client_options][:port] = ENV['OIDC_PORT'] if ENV['OIDC_PORT'] #OPTIONAL + oidc_options[:client_options][:authorization_endpoint] = ENV['OIDC_AUTH_ENDPOINT'] if ENV['OIDC_AUTH_ENDPOINT'] #NEED when discovery != true + oidc_options[:client_options][:token_endpoint] = ENV['OIDC_TOKEN_ENDPOINT'] if ENV['OIDC_TOKEN_ENDPOINT'] #NEED when discovery != true + oidc_options[:client_options][:userinfo_endpoint] = ENV['OIDC_USER_INFO_ENDPOINT'] if ENV['OIDC_USER_INFO_ENDPOINT'] #NEED when discovery != true + oidc_options[:client_options][:jwks_uri] = ENV['OIDC_JWKS_URI'] if ENV['OIDC_JWKS_URI'] #NEED when discovery != true + oidc_options[:client_options][:end_session_endpoint] = ENV['OIDC_END_SESSION_ENDPOINT'] if ENV['OIDC_END_SESSION_ENDPOINT'] #OPTIONAL + oidc_options[:security] = {} + oidc_options[:security][:assume_email_is_verified] = ENV['OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED'] == 'true' #OPTIONAL + config.omniauth :openid_connect, oidc_options + end end -- cgit From b2cd34474b58b8a1f5e01ba73d236551dd0a878f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 9 Mar 2022 20:06:51 +0100 Subject: Add rate limit for editing (#17728) --- app/controllers/api/v1/statuses_controller.rb | 1 + app/models/status.rb | 5 +++-- app/models/status_edit.rb | 4 ++++ app/services/activitypub/process_status_update_service.rb | 4 ++-- app/services/update_status_service.rb | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index f48aeb945..3fe137bfd 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -10,6 +10,7 @@ class Api::V1::StatusesController < Api::BaseController before_action :set_thread, only: [:create] override_rate_limit_headers :create, family: :statuses + override_rate_limit_headers :update, family: :statuses # This API was originally unlimited, pagination cannot be introduced without # breaking backwards-compatibility. Arbitrarily high number to cover most diff --git a/app/models/status.rb b/app/models/status.rb index db10eedc2..12daee2de 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -212,7 +212,7 @@ class Status < ApplicationRecord public_visibility? || unlisted_visibility? end - def snapshot!(account_id: nil, at_time: nil) + def snapshot!(account_id: nil, at_time: nil, rate_limit: true) edits.create!( text: text, spoiler_text: spoiler_text, @@ -221,7 +221,8 @@ class Status < ApplicationRecord media_descriptions: ordered_media_attachments.map(&:description), poll_options: preloadable_poll&.options, account_id: account_id || self.account_id, - created_at: at_time || edited_at + created_at: at_time || edited_at, + rate_limit: rate_limit ) end diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb index 94a387c36..6da9b4b85 100644 --- a/app/models/status_edit.rb +++ b/app/models/status_edit.rb @@ -17,6 +17,8 @@ # class StatusEdit < ApplicationRecord + include RateLimitable + self.ignored_columns = %w( media_attachments_changed ) @@ -26,6 +28,8 @@ class StatusEdit < ApplicationRecord delegate :id, :type, :url, :preview_url, :remote_url, :preview_remote_url, :text_url, :meta, :blurhash, to: :media_attachment end + rate_limit by: :account, family: :statuses + belongs_to :status belongs_to :account, optional: true diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 11afa894f..1260c0482 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -216,13 +216,13 @@ class ActivityPub::ProcessStatusUpdateService < BaseService return if @status.edits.any? - @status.snapshot!(at_time: @status.created_at) + @status.snapshot!(at_time: @status.created_at, rate_limit: false) end def create_edit! return unless significant_changes? - @status.snapshot!(account_id: @account.id) + @status.snapshot!(account_id: @account.id, rate_limit: false) end def skip_download? diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index 1c63ab656..055e5968d 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -131,7 +131,7 @@ class UpdateStatusService < BaseService return if @status.edits.any? - @status.snapshot!(at_time: @status.created_at) + @status.snapshot!(at_time: @status.created_at, rate_limit: false) end def create_edit! -- cgit From bc320d6cec5900fa2fd9d3a46480e242111caaca Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 12 Mar 2022 04:14:25 +0100 Subject: Fix `POST /api/v1/emails/confirmations` not being available after sign-up (#17743) --- .../api/v1/emails/confirmations_controller.rb | 4 +- .../api/v1/emails/confirmations_controller_spec.rb | 64 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 spec/controllers/api/v1/emails/confirmations_controller_spec.rb (limited to 'app/controllers') diff --git a/app/controllers/api/v1/emails/confirmations_controller.rb b/app/controllers/api/v1/emails/confirmations_controller.rb index f1d9954d0..3faaea2fb 100644 --- a/app/controllers/api/v1/emails/confirmations_controller.rb +++ b/app/controllers/api/v1/emails/confirmations_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::Emails::ConfirmationsController < Api::BaseController - before_action :doorkeeper_authorize! + before_action -> { doorkeeper_authorize! :write, :'write:accounts' } before_action :require_user_owned_by_application! before_action :require_user_not_confirmed! @@ -19,6 +19,6 @@ class Api::V1::Emails::ConfirmationsController < Api::BaseController end def require_user_not_confirmed! - render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden if current_user.confirmed? || current_user.unconfirmed_email.blank? + render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden unless !current_user.confirmed? || current_user.unconfirmed_email.present? end end diff --git a/spec/controllers/api/v1/emails/confirmations_controller_spec.rb b/spec/controllers/api/v1/emails/confirmations_controller_spec.rb new file mode 100644 index 000000000..15ac31cbc --- /dev/null +++ b/spec/controllers/api/v1/emails/confirmations_controller_spec.rb @@ -0,0 +1,64 @@ +require 'rails_helper' + +RSpec.describe Api::V1::Emails::ConfirmationsController, type: :controller do + let(:confirmed_at) { nil } + let(:user) { Fabricate(:user, confirmed_at: confirmed_at) } + let(:app) { Fabricate(:application) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes, application: app) } + let(:scopes) { 'write' } + + describe '#create' do + context 'with an oauth token' do + before do + allow(controller).to receive(:doorkeeper_token) { token } + end + + context 'from a random app' do + it 'returns http forbidden' do + post :create + expect(response).to have_http_status(:forbidden) + end + end + + context 'from an app that created the account' do + before do + user.update(created_by_application: token.application) + end + + context 'when the account is already confirmed' do + let(:confirmed_at) { Time.now.utc } + + it 'returns http forbidden' do + post :create + expect(response).to have_http_status(:forbidden) + end + + context 'but user changed e-mail and has not confirmed it' do + before do + user.update(email: 'foo@bar.com') + end + + it 'returns http success' do + post :create + expect(response).to have_http_status(:success) + end + end + end + + context 'when the account is unconfirmed' do + it 'returns http success' do + post :create + expect(response).to have_http_status(:success) + end + end + end + end + + context 'without an oauth token' do + it 'returns http unauthorized' do + post :create + expect(response).to have_http_status(:unauthorized) + end + end + end +end -- cgit From 80c4db160ecfd201cb82ed320cbe3eb32236ad20 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 14 Mar 2022 17:34:53 +0100 Subject: Fix blank screen when trying to copy emoji with a too large file size (#1718) Fixes #1714 --- app/controllers/admin/custom_emojis_controller.rb | 3 +++ config/locales-glitch/en.yml | 3 +++ 2 files changed, 6 insertions(+) (limited to 'app/controllers') diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 71efb543e..47138bf6c 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -35,6 +35,9 @@ module Admin flash[:alert] = I18n.t('admin.accounts.no_account_selected') rescue Mastodon::NotPermittedError flash[:alert] = I18n.t('admin.custom_emojis.not_permitted') + rescue ActiveRecord::RecordInvalid => e + error_message = action_from_button == 'copy' ? 'admin.custom_emojis.batch_copy_error' : 'admin.custom_emojis.batch_error' + flash[:alert] = I18n.t(error_message, message: e.message) ensure redirect_to admin_custom_emojis_path(filter_params) end diff --git a/config/locales-glitch/en.yml b/config/locales-glitch/en.yml index 7fd0683c9..3b554f4a2 100644 --- a/config/locales-glitch/en.yml +++ b/config/locales-glitch/en.yml @@ -1,6 +1,9 @@ --- en: admin: + custom_emojis: + batch_copy_error: 'An error occurred when copying some of the selected emoji: %{message}' + batch_error: 'An error occurred: %{message}' settings: captcha_enabled: desc_html: This relies on external scripts from hCaptcha, which may be a security and privacy concern. In addition, this can make the registration process significantly less accessible to some (especially disabled) people. For these reasons, please consider alternative measures such as approval-based or invite-based registration.
Users that have been invited through a limited-use invite will not need to solve a CAPTCHA -- cgit From e6ffbfb5e7684880e9252f72f9cb00b151c28d10 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 15 Mar 2022 04:11:29 +0100 Subject: Add `types` param to `GET /api/v1/notifications` in REST API (#17767) * Add `types` param to `GET /api/v1/notifications` in REST API * Improve tests --- app/controllers/api/v1/notifications_controller.rb | 19 +++--- app/models/notification.rb | 24 +++++-- .../api/v1/notifications_controller_spec.rb | 79 +++++++--------------- 3 files changed, 49 insertions(+), 73 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb index f9d780839..6d464997e 100644 --- a/app/controllers/api/v1/notifications_controller.rb +++ b/app/controllers/api/v1/notifications_controller.rb @@ -35,13 +35,18 @@ class Api::V1::NotificationsController < Api::BaseController limit_param(DEFAULT_NOTIFICATIONS_LIMIT), params_slice(:max_id, :since_id, :min_id) ) + Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses| cache_collection(target_statuses, Status) end end def browserable_account_notifications - current_account.notifications.without_suspended.browserable(exclude_types, from_account) + current_account.notifications.without_suspended.browserable( + types: Array(browserable_params[:types]), + exclude_types: Array(browserable_params[:exclude_types]), + from_account_id: browserable_params[:account_id] + ) end def target_statuses_from_notifications @@ -72,17 +77,11 @@ class Api::V1::NotificationsController < Api::BaseController @notifications.first.id end - def exclude_types - val = params.permit(exclude_types: [])[:exclude_types] || [] - val = [val] unless val.is_a?(Enumerable) - val - end - - def from_account - params[:account_id] + def browserable_params + params.permit(:account_id, types: [], exclude_types: []) end def pagination_params(core_params) - params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params) + params.slice(:limit, :account_id, :types, :exclude_types).permit(:limit, :account_id, types: [], exclude_types: []).merge(core_params) end end diff --git a/app/models/notification.rb b/app/models/notification.rb index 9bf296386..ba94b54d1 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -63,13 +63,6 @@ class Notification < ApplicationRecord scope :without_suspended, -> { joins(:from_account).merge(Account.without_suspended) } - scope :browserable, ->(exclude_types = [], account_id = nil) { - scope = all - scope = where(from_account_id: account_id) if account_id.present? - scope = scope.where(type: TYPES - exclude_types.map(&:to_sym)) unless exclude_types.empty? - scope - } - def type @type ||= (super || LEGACY_TYPE_CLASS_MAP[activity_type]).to_sym end @@ -90,6 +83,23 @@ class Notification < ApplicationRecord end class << self + def browserable(types: [], exclude_types: [], from_account_id: nil) + requested_types = begin + if types.empty? + TYPES + else + types.map(&:to_sym) & TYPES + end + end + + requested_types -= exclude_types.map(&:to_sym) + + all.tap do |scope| + scope.merge!(where(from_account_id: from_account_id)) if from_account_id.present? + scope.merge!(where(type: requested_types)) unless requested_types.size == TYPES.size + end + end + def preload_cache_collection_target_statuses(notifications, &_block) notifications.group_by(&:type).each do |type, grouped_notifications| associations = TARGET_STATUS_INCLUDES_BY_TYPE[type] diff --git a/spec/controllers/api/v1/notifications_controller_spec.rb b/spec/controllers/api/v1/notifications_controller_spec.rb index f8df6589f..46e177c0e 100644 --- a/spec/controllers/api/v1/notifications_controller_spec.rb +++ b/spec/controllers/api/v1/notifications_controller_spec.rb @@ -70,23 +70,23 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do end it 'includes reblog' do - expect(assigns(:notifications).map(&:activity)).to include(@reblog_of_first_status) + expect(body_as_json.map { |x| x[:type] }).to include 'reblog' end it 'includes mention' do - expect(assigns(:notifications).map(&:activity)).to include(@mention_from_status) + expect(body_as_json.map { |x| x[:type] }).to include 'mention' end it 'includes favourite' do - expect(assigns(:notifications).map(&:activity)).to include(@favourite) + expect(body_as_json.map { |x| x[:type] }).to include 'favourite' end it 'includes follow' do - expect(assigns(:notifications).map(&:activity)).to include(@follow) + expect(body_as_json.map { |x| x[:type] }).to include 'follow' end end - describe 'from specified user' do + describe 'with account_id param' do before do get :index, params: { account_id: third.account.id } end @@ -95,28 +95,12 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do expect(response).to have_http_status(200) end - it 'includes favourite' do - expect(assigns(:notifications).map(&:activity)).to include(@second_favourite) - end - - it 'excludes favourite' do - expect(assigns(:notifications).map(&:activity)).to_not include(@favourite) - end - - it 'excludes mention' do - expect(assigns(:notifications).map(&:activity)).to_not include(@mention_from_status) - end - - it 'excludes reblog' do - expect(assigns(:notifications).map(&:activity)).to_not include(@reblog_of_first_status) - end - - it 'excludes follow' do - expect(assigns(:notifications).map(&:activity)).to_not include(@follow) + it 'returns only notifications from specified user' do + expect(body_as_json.map { |x| x[:account][:id] }.uniq).to eq [third.account.id.to_s] end end - describe 'from nonexistent user' do + describe 'with invalid account_id param' do before do get :index, params: { account_id: 'foo' } end @@ -125,54 +109,37 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do expect(response).to have_http_status(200) end - it 'excludes favourite' do - expect(assigns(:notifications).map(&:activity)).to_not include(@favourite) - end - - it 'excludes second favourite' do - expect(assigns(:notifications).map(&:activity)).to_not include(@second_favourite) - end - - it 'excludes mention' do - expect(assigns(:notifications).map(&:activity)).to_not include(@mention_from_status) - end - - it 'excludes reblog' do - expect(assigns(:notifications).map(&:activity)).to_not include(@reblog_of_first_status) - end - - it 'excludes follow' do - expect(assigns(:notifications).map(&:activity)).to_not include(@follow) + it 'returns nothing' do + expect(body_as_json.size).to eq 0 end end - describe 'with excluded mentions' do + describe 'with excluded_types param' do before do - get :index, params: { exclude_types: ['mention'] } + get :index, params: { exclude_types: %w(mention) } end it 'returns http success' do expect(response).to have_http_status(200) end - it 'includes reblog' do - expect(assigns(:notifications).map(&:activity)).to include(@reblog_of_first_status) - end - - it 'excludes mention' do - expect(assigns(:notifications).map(&:activity)).to_not include(@mention_from_status) + it 'returns everything but excluded type' do + expect(body_as_json.size).to_not eq 0 + expect(body_as_json.map { |x| x[:type] }.uniq).to_not include 'mention' end + end - it 'includes favourite' do - expect(assigns(:notifications).map(&:activity)).to include(@favourite) + describe 'with types param' do + before do + get :index, params: { types: %w(mention) } end - it 'includes third favourite' do - expect(assigns(:notifications).map(&:activity)).to include(@second_favourite) + it 'returns http success' do + expect(response).to have_http_status(200) end - it 'includes follow' do - expect(assigns(:notifications).map(&:activity)).to include(@follow) + it 'returns only requested type' do + expect(body_as_json.map { |x| x[:type] }.uniq).to eq ['mention'] end end end -- cgit