Implement roles and visibility (#128)
* Add migrations to add user roles and party visibility. * Update schema.rb * Add admin check in User model * Implement rudimentary visibility of teams * Adds checks to Party model * Hides parties from collection views depending on visibility * Disallows viewing private parties if you're not the owner * Add a party's visibility to blueprint * Add admin mode The API Controller checks if the user is logged in and whether they are an admin, and checks for the X-Admin-Mode header * Implement admin mode overrides * Add admin_mode to authorize * Note to self: Implement user editing by admins * Fix syntax error with equality in SQL * Fix syntax error with method name * Fix bug in who can see restricted parties * Add privacy control to user profiles
This commit is contained in:
parent
c79d2717cc
commit
8381c668bc
9 changed files with 600 additions and 525 deletions
|
|
@ -35,7 +35,7 @@ module Api
|
|||
view :minimal do
|
||||
fields :name, :element, :shortcode, :favorited, :remix,
|
||||
:extra, :full_auto, :clear_time, :auto_guard, :auto_summon,
|
||||
:created_at, :updated_at
|
||||
:visibility, :created_at, :updated_at
|
||||
|
||||
field :guidebooks do |p|
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ module Api
|
|||
@current_user
|
||||
end
|
||||
|
||||
def admin_mode
|
||||
if current_user && current_user.admin? && request.headers['X-Admin-Mode']
|
||||
@admin_mode ||= request.headers['X-Admin-Mode'] == 'true'
|
||||
end
|
||||
|
||||
@admin_mode
|
||||
end
|
||||
|
||||
def edit_key
|
||||
@edit_key ||= request.headers['X-Edit-Key'] if request.headers['X-Edit-Key']
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ module Api
|
|||
end
|
||||
|
||||
def show
|
||||
# If a party is private, check that the user is the owner or an admin
|
||||
if (@party.private? && !current_user) || (@party.private? && not_owner && !admin_mode)
|
||||
return render_unauthorized_response
|
||||
end
|
||||
|
||||
return render json: PartyBlueprint.render(@party, view: :full, root: :party) if @party
|
||||
|
||||
render_not_found_response('project')
|
||||
|
|
@ -90,7 +95,7 @@ module Api
|
|||
conditions = build_filters
|
||||
conditions[:favorites] = { user_id: current_user.id }
|
||||
|
||||
query = build_query(conditions, true)
|
||||
query = build_query(conditions, favorites: true)
|
||||
query = apply_includes(query, params[:includes]) if params[:includes].present?
|
||||
query = apply_excludes(query, params[:excludes]) if params[:excludes].present?
|
||||
|
||||
|
|
@ -104,7 +109,11 @@ module Api
|
|||
private
|
||||
|
||||
def authorize
|
||||
render_unauthorized_response if @party.user != current_user || @party.edit_key != edit_key
|
||||
render_unauthorized_response if (not_owner && !admin_mode) || (@party.edit_key != edit_key && !admin_mode)
|
||||
end
|
||||
|
||||
def not_owner
|
||||
current_user && @party.user != current_user
|
||||
end
|
||||
|
||||
def build_filters
|
||||
|
|
@ -152,11 +161,12 @@ module Api
|
|||
value.to_i unless value.blank? || value.to_i == -1
|
||||
end
|
||||
|
||||
def build_query(conditions, favorites = false)
|
||||
def build_query(conditions, favorites: false)
|
||||
query = Party.distinct
|
||||
.joins(weapons: [:object], summons: [:object], characters: [:object])
|
||||
.group('parties.id')
|
||||
.where(conditions)
|
||||
.where(privacy(favorites: favorites))
|
||||
.where(name_quality)
|
||||
.where(user_quality)
|
||||
.where(original)
|
||||
|
|
@ -242,6 +252,16 @@ module Api
|
|||
})
|
||||
end
|
||||
|
||||
def privacy(favorites: false)
|
||||
return if admin_mode
|
||||
|
||||
if favorites
|
||||
'visibility < 3'
|
||||
else
|
||||
'visibility = 1'
|
||||
end
|
||||
end
|
||||
|
||||
def user_quality
|
||||
'user_id IS NOT NULL' unless request.params[:user_quality].blank? || request.params[:user_quality] == 'false'
|
||||
end
|
||||
|
|
@ -329,6 +349,7 @@ module Api
|
|||
:description,
|
||||
:raid_id,
|
||||
:job_id,
|
||||
:visibility,
|
||||
:accessory_id,
|
||||
:skill0_id,
|
||||
:skill1_id,
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ module Api
|
|||
render_validation_error_response(@user)
|
||||
end
|
||||
|
||||
# TODO: Allow admins to update other users
|
||||
|
||||
def update
|
||||
render json: UserBlueprint.render(@user, view: :minimal) if @user.update(user_params)
|
||||
end
|
||||
|
|
@ -61,6 +63,7 @@ module Api
|
|||
.where(name_quality)
|
||||
.where(user_quality)
|
||||
.where(original)
|
||||
.where(privacy)
|
||||
.order(created_at: :desc)
|
||||
.paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE)
|
||||
.each do |party|
|
||||
|
|
@ -113,9 +116,18 @@ module Api
|
|||
hash[:created_at] = start_time..DateTime.current unless params['recency'].blank?
|
||||
|
||||
# Advanced filters: Team parameters
|
||||
hash[:full_auto] = params['full_auto'].to_i unless params['full_auto'].blank? || params['full_auto'].to_i == -1
|
||||
hash[:auto_guard] = params['auto_guard'].to_i unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1
|
||||
hash[:charge_attack] = params['charge_attack'].to_i unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1
|
||||
unless params['full_auto'].blank? || params['full_auto'].to_i == -1
|
||||
hash[:full_auto] =
|
||||
params['full_auto'].to_i
|
||||
end
|
||||
unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1
|
||||
hash[:auto_guard] =
|
||||
params['auto_guard'].to_i
|
||||
end
|
||||
unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1
|
||||
hash[:charge_attack] =
|
||||
params['charge_attack'].to_i
|
||||
end
|
||||
|
||||
# Turn count of 0 will not be displayed, so disallow on the frontend or set default to 1
|
||||
# How do we do the same for button count since that can reasonably be 1?
|
||||
|
|
@ -131,38 +143,44 @@ module Api
|
|||
end
|
||||
|
||||
def original
|
||||
unless params.key?('original') || params['original'].blank? || params['original'] == '0'
|
||||
"source_party_id IS NULL"
|
||||
end
|
||||
return if params.key?('original') || params['original'].blank? || params['original'] == '0'
|
||||
|
||||
'source_party_id IS NULL'
|
||||
end
|
||||
|
||||
def user_quality
|
||||
unless params.key?('user_quality') || params[:user_quality].nil? || params[:user_quality] == "0"
|
||||
"user_id IS NOT NULL"
|
||||
end
|
||||
return if params.key?('user_quality') || params[:user_quality].nil? || params[:user_quality] == '0'
|
||||
|
||||
'user_id IS NOT NULL'
|
||||
end
|
||||
|
||||
def name_quality
|
||||
low_quality = [
|
||||
"Untitled",
|
||||
"Remix of Untitled",
|
||||
"Remix of Remix of Untitled",
|
||||
"Remix of Remix of Remix of Untitled",
|
||||
"Remix of Remix of Remix of Remix of Untitled",
|
||||
"Remix of Remix of Remix of Remix of Remix of Untitled",
|
||||
"無題",
|
||||
"無題のリミックス",
|
||||
"無題のリミックスのリミックス",
|
||||
"無題のリミックスのリミックスのリミックス",
|
||||
"無題のリミックスのリミックスのリミックスのリミックス",
|
||||
"無題のリミックスのリミックスのリミックスのリミックスのリミックス"
|
||||
'Untitled',
|
||||
'Remix of Untitled',
|
||||
'Remix of Remix of Untitled',
|
||||
'Remix of Remix of Remix of Untitled',
|
||||
'Remix of Remix of Remix of Remix of Untitled',
|
||||
'Remix of Remix of Remix of Remix of Remix of Untitled',
|
||||
'無題',
|
||||
'無題のリミックス',
|
||||
'無題のリミックスのリミックス',
|
||||
'無題のリミックスのリミックスのリミックス',
|
||||
'無題のリミックスのリミックスのリミックスのリミックス',
|
||||
'無題のリミックスのリミックスのリミックスのリミックスのリミックス'
|
||||
]
|
||||
|
||||
joined_names = low_quality.map { |name| "'#{name}'" }.join(',')
|
||||
|
||||
unless params.key?('name_quality') || params[:name_quality].nil? || params[:name_quality] == "0"
|
||||
return if params.key?('name_quality') || params[:name_quality].nil? || params[:name_quality] == '0'
|
||||
|
||||
"name NOT IN (#{joined_names})"
|
||||
end
|
||||
|
||||
def privacy
|
||||
return if admin_mode
|
||||
|
||||
'visibility = 1' if current_user != @user
|
||||
end
|
||||
|
||||
# Specify whitelisted properties that can be modified.
|
||||
|
|
|
|||
|
|
@ -105,17 +105,29 @@ class Party < ApplicationRecord
|
|||
end
|
||||
|
||||
def is_remix
|
||||
self.source_party != nil
|
||||
!source_party.nil?
|
||||
end
|
||||
|
||||
def remixes
|
||||
Party.where(source_party_id: self.id)
|
||||
Party.where(source_party_id: id)
|
||||
end
|
||||
|
||||
def blueprint
|
||||
PartyBlueprint
|
||||
end
|
||||
|
||||
def public?
|
||||
visibility == 1
|
||||
end
|
||||
|
||||
def unlisted?
|
||||
visibility == 2
|
||||
end
|
||||
|
||||
def private?
|
||||
visibility == 3
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_shortcode
|
||||
|
|
@ -123,10 +135,10 @@ class Party < ApplicationRecord
|
|||
end
|
||||
|
||||
def set_edit_key
|
||||
if !self.user
|
||||
return if user
|
||||
|
||||
self.edit_key = Digest::SHA1.hexdigest([Time.now, rand].join)
|
||||
end
|
||||
end
|
||||
|
||||
def random_string
|
||||
num_chars = 6
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ class User < ApplicationRecord
|
|||
favorites.map(&:party)
|
||||
end
|
||||
|
||||
def admin?
|
||||
role == 9
|
||||
end
|
||||
|
||||
def blueprint
|
||||
UserBlueprint
|
||||
end
|
||||
|
|
|
|||
5
db/migrate/20230824222028_add_role_to_users.rb
Normal file
5
db/migrate/20230824222028_add_role_to_users.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class AddRoleToUsers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :users, :role, :integer, default: 1, null: false
|
||||
end
|
||||
end
|
||||
5
db/migrate/20230824222107_add_visibility_to_parties.rb
Normal file
5
db/migrate/20230824222107_add_visibility_to_parties.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class AddVisibilityToParties < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :parties, :visibility, :integer, default: 1, null: false
|
||||
end
|
||||
end
|
||||
962
db/schema.rb
962
db/schema.rb
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue