models: factor out api_attributes to policies.

Refactor models so that we define attribute API permissions in policy
files instead of directly in models.

This is cleaner because a) permissions are better handled by policies
and b) which attributes are visible to the API is an API-level concern
that models shouldn't have to care about.

This fixes an issue with not being able to precompile CSS/JS assets
unless the database was up and running. This was a problem when building
Docker images because we don't have a database at build time. We needed
the database because `api_attributes` was a class-level macro in some
places, which meant it ran at boot time, but this triggered a database
call because api_attributes used database introspection to get the list
of allowed API attributes.
This commit is contained in:
evazion
2020-06-08 18:38:02 -05:00
parent b6ed63841d
commit eacb4d4df3
20 changed files with 81 additions and 79 deletions

View File

@@ -31,19 +31,6 @@ class ApplicationRecord < ActiveRecord::Base
concerning :ApiMethods do concerning :ApiMethods do
class_methods do class_methods do
def api_attributes(*attributes, including: [])
return @api_attributes if @api_attributes
if attributes.present?
@api_attributes = attributes
else
@api_attributes = attribute_types.reject { |name, attr| attr.type.in?([:inet, :tsvector]) }.keys.map(&:to_sym)
end
@api_attributes += including
@api_attributes
end
def available_includes def available_includes
[] []
end end
@@ -65,8 +52,9 @@ class ApplicationRecord < ActiveRecord::Base
self.class.available_includes self.class.available_includes
end end
def api_attributes # XXX deprecated, shouldn't expose this as an instance method.
self.class.api_attributes def api_attributes(user: CurrentUser.user)
Pundit.policy!([user, nil], self).api_attributes
end end
def html_data_attributes def html_data_attributes

View File

@@ -12,7 +12,6 @@ class Dmail < ApplicationRecord
after_save :update_unread_dmail_count after_save :update_unread_dmail_count
after_commit :send_email, on: :create after_commit :send_email, on: :create
api_attributes including: [:key]
deletable deletable
scope :read, -> { where(is_read: true) } scope :read, -> { where(is_read: true) }

View File

@@ -1,8 +1,6 @@
class ModAction < ApplicationRecord class ModAction < ApplicationRecord
belongs_to :creator, :class_name => "User" belongs_to :creator, :class_name => "User"
api_attributes including: [:category_id]
# ####DIVISIONS##### # ####DIVISIONS#####
# Groups: 0-999 # Groups: 0-999
# Individual: 1000-1999 # Individual: 1000-1999

View File

@@ -13,7 +13,6 @@ class Pool < ApplicationRecord
after_save :create_version after_save :create_version
after_create :synchronize! after_create :synchronize!
api_attributes including: [:post_count]
deletable deletable
scope :series, -> { where(category: "series") } scope :series, -> { where(category: "series") }

View File

@@ -1125,15 +1125,6 @@ class Post < ApplicationRecord
end end
module ApiMethods module ApiMethods
def api_attributes
attributes = super
attributes += [:has_large, :has_visible_children, :is_favorited?] + TagCategory.categories.map {|x| "tag_string_#{x}".to_sym}
attributes += [:file_url, :large_file_url, :preview_file_url] if visible?
attributes -= [:md5, :file_ext] if !visible?
attributes -= [:fav_string] if !CurrentUser.is_moderator?
attributes
end
def legacy_attributes def legacy_attributes
hash = { hash = {
"has_comments" => last_commented_at.present?, "has_comments" => last_commented_at.present?,
@@ -1471,16 +1462,16 @@ class Post < ApplicationRecord
CurrentUser.safe_mode? && (rating != "s" || Danbooru.config.safe_mode_restricted_tags.any? { |tag| tag.in?(tag_array) }) CurrentUser.safe_mode? && (rating != "s" || Danbooru.config.safe_mode_restricted_tags.any? { |tag| tag.in?(tag_array) })
end end
def levelblocked? def levelblocked?(user = CurrentUser.user)
!CurrentUser.is_gold? && Danbooru.config.restricted_tags.any? { |tag| tag.in?(tag_array) } !user.is_gold? && Danbooru.config.restricted_tags.any? { |tag| tag.in?(tag_array) }
end end
def banblocked? def banblocked?(user = CurrentUser.user)
is_banned? && !CurrentUser.is_gold? is_banned? && !user.is_gold?
end end
def visible? def visible?(user = CurrentUser.user)
!safeblocked? && !levelblocked? && !banblocked? !safeblocked? && !levelblocked?(user) && !banblocked?(user)
end end
def reload(options = nil) def reload(options = nil)

View File

@@ -73,10 +73,4 @@ class PostDisapproval < ApplicationRecord
message = nil if message.blank? message = nil if message.blank?
super(message) super(message)
end end
def api_attributes
attributes = super
attributes -= [:creator_id] unless Pundit.policy!([CurrentUser.user, nil], self).can_view_creator?
attributes
end
end end

View File

@@ -75,16 +75,7 @@ class PostFlag < ApplicationRecord
end end
end end
module ApiMethods
def api_attributes
attributes = super + [:category]
attributes -= [:creator_id] unless Pundit.policy!([CurrentUser.user, nil], self).can_view_flagger?
attributes
end
end
extend SearchMethods extend SearchMethods
include ApiMethods
def category def category
case reason case reason

View File

@@ -267,10 +267,6 @@ class PostVersion < ApplicationRecord
post.save! post.save!
end end
def api_attributes
super + [:obsolete_added_tags, :obsolete_removed_tags, :unchanged_tags]
end
def self.available_includes def self.available_includes
[:updater, :post] [:updater, :post]
end end

View File

@@ -420,30 +420,6 @@ class User < ApplicationRecord
end end
module ApiMethods module ApiMethods
def api_attributes
attributes = %i[
id created_at name inviter_id level
post_upload_count post_update_count note_update_count is_banned
can_approve_posts can_upload_free level_string
]
if id == CurrentUser.user.id
attributes += BOOLEAN_ATTRIBUTES
attributes += %i[
updated_at last_logged_in_at last_forum_read_at
comment_threshold default_image_size
favorite_tags blacklisted_tags time_zone per_page
custom_style favorite_count api_regen_multiplier
api_burst_limit remaining_api_limit statement_timeout
favorite_group_limit favorite_limit tag_query_limit
is_comment_limited?
max_saved_searches theme
]
end
attributes
end
# extra attributes returned for /users/:id.json but not for /users.json. # extra attributes returned for /users/:id.json but not for /users.json.
def full_attributes def full_attributes
%i[ %i[

View File

@@ -18,7 +18,6 @@ class WikiPage < ApplicationRecord
has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy has_many :versions, -> {order("wiki_page_versions.id ASC")}, :class_name => "WikiPageVersion", :dependent => :destroy
has_many :dtext_links, as: :model, dependent: :destroy has_many :dtext_links, as: :model, dependent: :destroy
api_attributes including: [:category_name]
deletable deletable
module SearchMethods module SearchMethods

View File

@@ -69,4 +69,8 @@ class ApplicationPolicy
def permitted_attributes_for_edit def permitted_attributes_for_edit
permitted_attributes_for_update permitted_attributes_for_update
end end
def api_attributes
record.class.attribute_types.reject { |name, attr| attr.type.in?([:inet, :tsvector]) }.keys.map(&:to_sym)
end
end end

View File

@@ -30,4 +30,8 @@ class DmailPolicy < ApplicationPolicy
def permitted_attributes_for_update def permitted_attributes_for_update
[:is_read, :is_deleted] [:is_read, :is_deleted]
end end
def api_attributes
super + [:key]
end
end end

View File

@@ -0,0 +1,5 @@
class ModActionPolicy < ApplicationPolicy
def api_attributes
super + [:category_id]
end
end

View File

@@ -22,4 +22,8 @@ class PoolPolicy < ApplicationPolicy
def permitted_attributes def permitted_attributes
[:name, :description, :category, :post_ids, :post_ids_string, post_ids: []] [:name, :description, :category, :post_ids, :post_ids_string, post_ids: []]
end end
def api_attributes
super + [:post_count]
end
end end

View File

@@ -10,4 +10,10 @@ class PostDisapprovalPolicy < ApplicationPolicy
def permitted_attributes def permitted_attributes
[:post_id, :reason, :message] [:post_id, :reason, :message]
end end
def api_attributes
attributes = super
attributes -= [:creator_id] unless can_view_creator?
attributes
end
end end

View File

@@ -10,4 +10,10 @@ class PostFlagPolicy < ApplicationPolicy
def permitted_attributes def permitted_attributes
[:post_id, :reason] [:post_id, :reason]
end end
def api_attributes
attributes = super + [:category]
attributes -= [:creator_id] unless can_view_flagger?
attributes
end
end end

View File

@@ -44,7 +44,7 @@ class PostPolicy < ApplicationPolicy
end end
def visible? def visible?
record.visible? record.visible?(user)
end end
def can_view_uploader? def can_view_uploader?
@@ -85,4 +85,14 @@ class PostPolicy < ApplicationPolicy
(:is_status_locked if can_lock_status?), (:is_status_locked if can_lock_status?),
].compact ].compact
end end
def api_attributes
attributes = super
attributes += [:has_large, :has_visible_children, :is_favorited?]
attributes += TagCategory.categories.map {|x| "tag_string_#{x}".to_sym}
attributes += [:file_url, :large_file_url, :preview_file_url] if visible?
attributes -= [:md5, :file_ext] if !visible?
attributes -= [:fav_string] if !user.is_moderator?
attributes
end
end end

View File

@@ -6,4 +6,8 @@ class PostVersionPolicy < ApplicationPolicy
def can_mass_undo? def can_mass_undo?
user.is_builder? user.is_builder?
end end
def api_attributes
super + [:obsolete_added_tags, :obsolete_removed_tags, :unchanged_tags]
end
end end

View File

@@ -51,6 +51,30 @@ class UserPolicy < ApplicationPolicy
].compact ].compact
end end
def api_attributes
attributes = %i[
id created_at name inviter_id level
post_upload_count post_update_count note_update_count is_banned
can_approve_posts can_upload_free level_string
]
if record.id == user.id
attributes += User::BOOLEAN_ATTRIBUTES
attributes += %i[
updated_at last_logged_in_at last_forum_read_at
comment_threshold default_image_size
favorite_tags blacklisted_tags time_zone per_page
custom_style favorite_count api_regen_multiplier
api_burst_limit remaining_api_limit statement_timeout
favorite_group_limit favorite_limit tag_query_limit
is_comment_limited?
max_saved_searches theme
]
end
attributes
end
alias_method :profile?, :show? alias_method :profile?, :show?
alias_method :settings?, :edit? alias_method :settings?, :edit?
end end

View File

@@ -14,4 +14,8 @@ class WikiPagePolicy < ApplicationPolicy
def permitted_attributes def permitted_attributes
[:title, :body, :other_names, :other_names_string, :is_deleted, (:is_locked if can_edit_locked?)].compact [:title, :body, :other_names, :other_names_string, :is_deleted, (:is_locked if can_edit_locked?)].compact
end end
def api_attributes
super + [:category_name]
end
end end