about summary refs log tree commit diff
path: root/app/controllers
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-07-28 20:40:25 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:16 -0500
commit054e15e4f03eecb174374466581b9662a6b38e24 (patch)
tree80db06ea08762f659878d8ffe2ffb4f54333b9c6 /app/controllers
parent9234fb32e6b2b8bf8fb2184f9b1b57202eb5f625 (diff)
[Privacy] Add options for private accounts
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/accounts_controller.rb24
-rw-r--r--app/controllers/activitypub/outboxes_controller.rb11
-rw-r--r--app/controllers/activitypub/replies_controller.rb8
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb6
-rw-r--r--app/controllers/application_controller.rb42
-rw-r--r--app/controllers/settings/profiles_controller.rb2
-rw-r--r--app/controllers/statuses_controller.rb8
-rw-r--r--app/controllers/tags_controller.rb12
8 files changed, 80 insertions, 33 deletions
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 651da89ad..ebc472087 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -10,6 +10,8 @@ class AccountsController < ApplicationController
   before_action :set_cache_headers
   before_action :set_body_classes
 
+  before_action :require_authenticated!, if: -> { @account.require_auth? || @account.private? }
+
   skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
   skip_before_action :require_functional! #, unless: :whitelist_mode?
 
@@ -20,10 +22,10 @@ class AccountsController < ApplicationController
         expires_in 0, public: true unless user_signed_in? || signed_request_account.present?
 
         @pinned_statuses   = []
-        @endorsed_accounts = @account.endorsed_accounts.to_a.sample(4)
-        @featured_hashtags = @account.featured_tags.order(statuses_count: :desc)
+        @endorsed_accounts = unauthorized? ? [] : @account.endorsed_accounts.to_a.sample(4)
+        @featured_hashtags = unauthorized? ? [] : @account.featured_tags.order(statuses_count: :desc)
 
-        if current_account && @account.blocking?(current_account)
+        if unauthorized?
           @statuses = []
           return
         end
@@ -40,7 +42,9 @@ class AccountsController < ApplicationController
       end
 
       format.rss do
-        expires_in 1.minute, public: !(user_signed_in? || signed_request_account.present?)
+        return forbidden if unauthorized?
+
+        expires_in 1.minute, public: !current_account?
 
         limit     = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE
         @statuses = filtered_statuses.without_reblogs.limit(limit)
@@ -49,7 +53,7 @@ class AccountsController < ApplicationController
       end
 
       format.json do
-        expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
+        expires_in 3.minutes, public: !current_account?
         render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
       end
     end
@@ -152,10 +156,18 @@ class AccountsController < ApplicationController
   end
 
   def restrict_fields_to
-    if signed_request_account.present? || public_fetch_mode?
+    if signed_request_account.present? && !blocked?
       # Return all fields
     else
       %i(id type preferred_username inbox public_key endpoints)
     end
   end
+
+  def blocked?
+    @blocked ||= current_account && @account.blocking?(current_account)
+  end
+
+  def unauthorized?
+    @unauthorized ||= blocked? || (@account.private? && !following?(@account))
+  end
 end
diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb
index 60f1c526b..c4c0ce0c9 100644
--- a/app/controllers/activitypub/outboxes_controller.rb
+++ b/app/controllers/activitypub/outboxes_controller.rb
@@ -10,9 +10,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   before_action :set_statuses
   before_action :set_cache_headers
 
+  before_action :require_authenticated!, if: -> { @account.require_auth? }
+  before_action -> { require_following!(@account) }, if: -> { @account.private? }
+
   def show
     expires_in(page_requested? ? 0 : 3.minutes, public: public_fetch_mode? && !(signed_request_account.present? && page_requested?))
-    render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', target_domain: signed_request_account&.domain
+    render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', target_domain: current_account&.domain
   end
 
   private
@@ -49,7 +52,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   def set_statuses
     return unless page_requested?
 
-    @statuses = if known_visitor?
+    @statuses = if authenticated_or_following?(@account)
                   @account.statuses.without_semiprivate.permitted_for(@account, signed_request_account)
                 else
                   @account.statuses.permitted_for(@account, signed_request_account, user_signed_in: true)
@@ -66,8 +69,4 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   def page_params
     { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
   end
-
-  def known_visitor?
-    @known_visitor ||= user_signed_in? || (signed_request_account.present? && signed_request_account.following?(@account))
-  end
 end
diff --git a/app/controllers/activitypub/replies_controller.rb b/app/controllers/activitypub/replies_controller.rb
index cec571e8a..4d553fc07 100644
--- a/app/controllers/activitypub/replies_controller.rb
+++ b/app/controllers/activitypub/replies_controller.rb
@@ -14,7 +14,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
 
   def index
     expires_in 0, public: public_fetch_mode?
-    render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true, target_domain: signed_request_account&.domain
+    render json: replies_collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', skip_activities: true, target_domain: current_account&.domain
   end
 
   private
@@ -33,7 +33,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
   def set_replies
     @replies = only_other_accounts? ? Status.where.not(account_id: @account.id) : @account.statuses
     @replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
-    @replies = @replies.without_semiprivate unless known_visitor?
+    @replies = @replies.without_semiprivate unless authenticated_or_following?(@account)
     @replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
   end
 
@@ -78,8 +78,4 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
   def page_params
     params_slice(:only_other_accounts, :min_id).merge(page: true)
   end
-
-  def known_visitor?
-    @known_visitor ||= user_signed_in? || (signed_request_account.present? && signed_request_account.following?(@account))
-  end
 end
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index 4735fea8c..1c744ad73 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -26,6 +26,8 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def account_statuses
+    return [] if (@account.private && !following?(@account)) || (@account.require_auth && !current_account?)
+
     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
 
     statuses.merge!(only_media_scope) if truthy_param?(:only_media)
@@ -37,7 +39,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def permitted_account_statuses
-    @account.statuses.permitted_for(@account, current_account, user_signed_in: user_signed_in?)
+    @account.statuses.permitted_for(@account, current_account, user_signed_in: authenticated_or_following?(@account))
   end
 
   def only_media_scope
@@ -49,7 +51,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
     # Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
     # When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
     # and the table will be joined by `Merge Semi Join`, so the query will be slow.
-    @account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
+    @account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account, user_signed_in: authenticated_or_following?(@account))
             .paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
             .reorder(id: :desc).distinct(:id).pluck(:id)
   end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e996c2217..9608f1cf9 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
   include SessionTrackingConcern
   include CacheConcern
   include DomainControlHelper
+  include SignatureVerification
 
   helper_method :current_account
   helper_method :current_session
@@ -71,6 +72,28 @@ class ApplicationController < ActionController::Base
     redirect_to edit_user_registration_path unless current_user.functional?
   end
 
+  def require_authenticated!
+    return if current_account?
+
+    respond_to do |format|
+      format.any { redirect_to edit_user_registration_path }
+      format.json { forbidden }
+    end
+  end
+
+  def require_known!(account)
+    return if authenticated_or_following?(account)
+
+    respond_to do |format|
+      format.any { redirect_to edit_user_registration_path }
+      format.json { forbidden }
+    end
+  end
+
+  def require_following!(account)
+    forbidden unless following?(account)
+  end
+
   def after_sign_out_path_for(_resource_or_scope)
     new_user_session_path
   end
@@ -197,7 +220,7 @@ class ApplicationController < ActionController::Base
   def current_account
     return @current_account if defined?(@current_account)
 
-    @current_account = current_user&.account
+    @current_account = current_user&.account.presence || signed_request_account
   end
 
   def current_session
@@ -225,4 +248,21 @@ class ApplicationController < ActionController::Base
       format.json { render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code }
     end
   end
+
+  def following?(account)
+    return if account.blank?
+
+    @account_following ||= {}
+    return @account_following[account.id] if @account_following[account.id].present?
+
+    @account_following[account.id] = current_account.present? && (current_account.id == account.id || current_account.following?(account))
+  end
+
+  def authenticated_or_following?(account)
+    current_user.functional? || following?(account)
+  end
+
+  def current_account?
+    current_account.present?
+  end
 end
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index d6e3c9863..8c4efa21d 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -24,7 +24,7 @@ class Settings::ProfilesController < Settings::BaseController
 
   def account_params
     params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable,
-                                    :require_dereference, :show_replies, :show_unlisted,
+                                    :require_dereference, :show_replies, :show_unlisted, :private, :require_auth,
                                     fields_attributes: [:name, :value])
   end
 
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index 23cbb8c37..5c977d212 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -9,6 +9,8 @@ class StatusesController < ApplicationController
   layout 'public'
 
   before_action :require_signature!, only: :show, if: -> { request.format == :json && authorized_fetch_mode? }
+  before_action :require_authenticated!, if: -> { @account.require_auth? }
+  before_action -> { require_following!(@account) }, if: -> { request.format != :json && @account.private? }
   before_action :set_status
   before_action :set_instance_presenter
   before_action :set_link_headers
@@ -19,7 +21,7 @@ class StatusesController < ApplicationController
   before_action :set_autoplay, only: :embed
 
   skip_around_action :set_locale, if: -> { request.format == :json }
-  skip_before_action :require_functional!, only: [:show, :embed] #, unless: :whitelist_mode?
+  skip_before_action :require_functional!, only: [:show, :embed] # , unless: :whitelist_mode?
 
   content_security_policy only: :embed do |p|
     p.frame_ancestors(false)
@@ -37,7 +39,7 @@ class StatusesController < ApplicationController
 
       format.json do
         expires_in 3.minutes, public: @status.distributable? && public_fetch_mode?
-        render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter, target_domain: signed_request_account&.domain
+        render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter, target_domain: current_account&.domain
       end
     end
   end
@@ -48,7 +50,7 @@ class StatusesController < ApplicationController
                       content_type: 'application/activity+json',
                       serializer: ActivityPub::ActivitySerializer,
                       adapter: ActivityPub::Adapter,
-                      target_domain: signed_request_account&.domain
+                      target_domain: current_account&.domain
   end
 
   def embed
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 9cba38771..368419ef5 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -9,13 +9,13 @@ class TagsController < ApplicationController
   layout 'public'
 
   before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
-  #before_action :authenticate_user!, if: :whitelist_mode?
+  # before_action :authenticate_user!, if: :whitelist_mode?
   before_action :set_tag
   before_action :set_local
   before_action :set_body_classes
   before_action :set_instance_presenter
 
-  skip_before_action :require_functional! #, unless: :whitelist_mode?
+  skip_before_action :require_functional! # , unless: :whitelist_mode?
 
   def show
     respond_to do |format|
@@ -38,11 +38,11 @@ class TagsController < ApplicationController
         expires_in 3.minutes, public: public_fetch_mode?
 
         @statuses = HashtagQueryService.new.call(@tag, filter_params, current_account, @local)
-        @statuses = @statuses.without_semiprivate unless known_visitor?
+        @statuses = @statuses.without_semiprivate unless authenticated_or_following?(@account)
         @statuses = @statuses.paginate_by_max_id(PAGE_SIZE, params[:max_id])
         @statuses = cache_collection(@statuses, Status)
 
-        render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', target_domain: signed_request_account&.domain
+        render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json', target_domain: current_account&.domain
       end
     end
   end
@@ -77,8 +77,4 @@ class TagsController < ApplicationController
   def filter_params
     params.slice(:any, :all, :none).permit(:any, :all, :none)
   end
-
-  def known_visitor?
-    @known_visitor ||= user_signed_in? || (signed_request_account.present? && signed_request_account.following?(@account))
-  end
 end