private
- def set_statuses
- @statuses = scope_for_collection
- @statuses = cache_collection(@statuses, Status)
+ def set_items
+ case params[:id]
+ when 'featured'
+ @items = begin
+ # Because in public fetch mode we cache the response, there would be no
+ # benefit from performing the check below, since a blocked account or domain
+ # would likely be served the cache from the reverse proxy anyway
+
+ if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
+ []
+ else
- cache_collection(@account.pinned_statuses, Status)
++ cache_collection(@account.pinned_statuses.not_local_only, Status)
+ end
+ end
+ when 'devices'
+ @items = @account.devices
+ else
+ not_found
+ end
end
def set_size
skip_before_action :require_no_authentication, only: [:create]
skip_before_action :require_functional!
- prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
+ prepend_before_action :set_pack
++
+ include TwoFactorAuthenticationConcern
+ include SignInTokenAuthenticationConcern
before_action :set_instance_presenter, only: [:new]
before_action :set_body_classes
--- /dev/null
+ # frozen_string_literal: true
+
+ module SignInTokenAuthenticationConcern
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :authenticate_with_sign_in_token, if: :sign_in_token_required?, only: [:create]
+ end
+
+ def sign_in_token_required?
+ find_user&.suspicious_sign_in?(request.remote_ip)
+ end
+
+ def valid_sign_in_token_attempt?(user)
+ Devise.secure_compare(user.sign_in_token, user_params[:sign_in_token_attempt])
+ end
+
+ def authenticate_with_sign_in_token
+ user = self.resource = find_user
+
+ if user_params[:sign_in_token_attempt].present? && session[:attempt_user_id]
+ authenticate_with_sign_in_token_attempt(user)
+ elsif user.present? && user.external_or_valid_password?(user_params[:password])
+ prompt_for_sign_in_token(user)
+ end
+ end
+
+ def authenticate_with_sign_in_token_attempt(user)
+ if valid_sign_in_token_attempt?(user)
+ session.delete(:attempt_user_id)
+ remember_me(user)
+ sign_in(user)
+ else
+ flash.now[:alert] = I18n.t('users.invalid_sign_in_token')
+ prompt_for_sign_in_token(user)
+ end
+ end
+
+ def prompt_for_sign_in_token(user)
+ if user.sign_in_token_expired?
+ user.generate_sign_in_token && user.save
+ UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later!
+ end
+
+ session[:attempt_user_id] = user.id
++ use_pack 'auth'
+ @body_classes = 'lighter'
+ render :sign_in_token
+ end
+ end
--- /dev/null
+ # frozen_string_literal: true
+
+ module TwoFactorAuthenticationConcern
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
+ end
+
+ def two_factor_enabled?
+ find_user&.otp_required_for_login?
+ end
+
+ def valid_otp_attempt?(user)
+ user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
+ user.invalidate_otp_backup_code!(user_params[:otp_attempt])
+ rescue OpenSSL::Cipher::CipherError
+ false
+ end
+
+ def authenticate_with_two_factor
+ user = self.resource = find_user
+
+ if user_params[:otp_attempt].present? && session[:attempt_user_id]
+ authenticate_with_two_factor_attempt(user)
+ elsif user.present? && user.external_or_valid_password?(user_params[:password])
+ prompt_for_two_factor(user)
+ end
+ end
+
+ def authenticate_with_two_factor_attempt(user)
+ if valid_otp_attempt?(user)
+ session.delete(:attempt_user_id)
+ remember_me(user)
+ sign_in(user)
+ else
+ flash.now[:alert] = I18n.t('users.invalid_otp_token')
+ prompt_for_two_factor(user)
+ end
+ end
+
+ def prompt_for_two_factor(user)
+ session[:attempt_user_id] = user.id
++ use_pack 'auth'
+ @body_classes = 'lighter'
+ render :two_factor
+ end
+ end
has_many :session_activations, dependent: :destroy
- delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
- :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
+ delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
+ :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_network, :hide_followers_count,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
+ :default_content_type, :system_emoji_font,
to: :settings, prefix: :setting, allow_nil: false
- attr_reader :invite_code
+ attr_reader :invite_code, :sign_in_token_attempt
attr_writer :external
def confirmed?
true
end
+ def suspicious_sign_in?(ip)
+ !otp_required_for_login? && current_sign_in_at.present? && current_sign_in_at < 2.weeks.ago && !recent_ip?(ip)
+ end
+
def functional?
- confirmed? && approved? && !disabled? && !account.suspended? && account.moved_to_account_id.nil?
+ confirmed? && approved? && !disabled? && !account.suspended?
end
def unconfirmed_or_pending?
account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
statuses.each do |status|
- item = serialize_payload(status, ActivityPub::ActivitySerializer, signer: @account, allow_local_only: true)
- item = serialize_payload(ActivityPub::ActivityPresenter.from_status(status), ActivityPub::ActivitySerializer, signer: @account)
++ item = serialize_payload(ActivityPub::ActivityPresenter.from_status(status), ActivityPub::ActivitySerializer, signer: @account, allow_local_only: true)
item.delete(:'@context')
unless item[:type] == 'Announce' || item[:object][:attachment].blank?
- content_for :page_title do
= t('admin.custom_emojis.title')
- - content_for :heading_actions do
- = link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button'
-- content_for :header_tags do
- = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
-
+ - if can?(:create, :custom_emoji)
+ - content_for :heading_actions do
+ = link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button'
.filters
.filter-subset
"@rails/ujs": "^6.0.3",
"array-includes": "^3.1.1",
"arrow-key-navigation": "^1.1.0",
- "autoprefixer": "^9.7.6",
+ "atrament": "0.2.4",
+ "autoprefixer": "^9.8.0",
"axios": "^0.19.2",
"babel-loader": "^8.1.0",
"babel-plugin-lodash": "^3.3.4",
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
"express": "^4.17.1",
- "file-loader": "^5.1.0",
+ "favico.js": "^0.3.10",
+ "file-loader": "^6.0.0",
"font-awesome": "^4.7.0",
"glob": "^7.1.6",
"history": "^4.10.1",
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
- autoprefixer@^9.7.6:
- version "9.7.6"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.6.tgz#63ac5bbc0ce7934e6997207d5bb00d68fa8293a4"
- integrity sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==
+atrament@0.2.4:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/atrament/-/atrament-0.2.4.tgz#6f78196edfcd194e568b7c0b9c88201ec371ac66"
+ integrity sha512-hSA9VwW6COMwvRhSEO4uZweZ91YGOdHqwvslNyrJZG+8mzc4qx/qMsDZBuAeXFeWZO/QKtRjIXguOUy1aNMl3A==
+
+ autoprefixer@^9.8.0:
+ version "9.8.0"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.0.tgz#68e2d2bef7ba4c3a65436f662d0a56a741e56511"
+ integrity sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A==
dependencies:
- browserslist "^4.11.1"
- caniuse-lite "^1.0.30001039"
+ browserslist "^4.12.0"
+ caniuse-lite "^1.0.30001061"
chalk "^2.4.2"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+favico.js@^0.3.10:
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/favico.js/-/favico.js-0.3.10.tgz#80586e27a117f24a8d51c18a99bdc714d4339301"
+ integrity sha1-gFhuJ6EX8kqNUcGKmb3HFNQzkwE=
+
+ fastq@^1.6.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
+ integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
+ dependencies:
+ reusify "^1.0.4"
+
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"