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
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
[]
end
@@ -65,8 +52,9 @@ class ApplicationRecord < ActiveRecord::Base
self.class.available_includes
end
def api_attributes
self.class.api_attributes
# XXX deprecated, shouldn't expose this as an instance method.
def api_attributes(user: CurrentUser.user)
Pundit.policy!([user, nil], self).api_attributes
end
def html_data_attributes

View File

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

View File

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

View File

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

View File

@@ -1125,15 +1125,6 @@ class Post < ApplicationRecord
end
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
hash = {
"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) })
end
def levelblocked?
!CurrentUser.is_gold? && Danbooru.config.restricted_tags.any? { |tag| tag.in?(tag_array) }
def levelblocked?(user = CurrentUser.user)
!user.is_gold? && Danbooru.config.restricted_tags.any? { |tag| tag.in?(tag_array) }
end
def banblocked?
is_banned? && !CurrentUser.is_gold?
def banblocked?(user = CurrentUser.user)
is_banned? && !user.is_gold?
end
def visible?
!safeblocked? && !levelblocked? && !banblocked?
def visible?(user = CurrentUser.user)
!safeblocked? && !levelblocked?(user) && !banblocked?(user)
end
def reload(options = nil)

View File

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

View File

@@ -75,16 +75,7 @@ class PostFlag < ApplicationRecord
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
include ApiMethods
def category
case reason

View File

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

View File

@@ -420,30 +420,6 @@ class User < ApplicationRecord
end
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.
def full_attributes
%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 :dtext_links, as: :model, dependent: :destroy
api_attributes including: [:category_name]
deletable
module SearchMethods

View File

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

View File

@@ -30,4 +30,8 @@ class DmailPolicy < ApplicationPolicy
def permitted_attributes_for_update
[:is_read, :is_deleted]
end
def api_attributes
super + [:key]
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
[:name, :description, :category, :post_ids, :post_ids_string, post_ids: []]
end
def api_attributes
super + [:post_count]
end
end

View File

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

View File

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

View File

@@ -44,7 +44,7 @@ class PostPolicy < ApplicationPolicy
end
def visible?
record.visible?
record.visible?(user)
end
def can_view_uploader?
@@ -85,4 +85,14 @@ class PostPolicy < ApplicationPolicy
(:is_status_locked if can_lock_status?),
].compact
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

View File

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

View File

@@ -51,6 +51,30 @@ class UserPolicy < ApplicationPolicy
].compact
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 :settings?, :edit?
end

View File

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