fixed 解决安全问题访问附件,id改为uuid

This commit is contained in:
xxq250 2023-12-12 14:31:21 +08:00
parent 0cb38bce4f
commit d74901cffa
8 changed files with 82 additions and 49 deletions

View File

@ -94,6 +94,7 @@ class AttachmentsController < ApplicationController
@attachment.author_id = current_user.id @attachment.author_id = current_user.id
@attachment.disk_directory = month_folder @attachment.disk_directory = month_folder
@attachment.cloud_url = remote_path @attachment.cloud_url = remote_path
@attachment.uuid = SecureRandom.uuid
@attachment.save! @attachment.save!
else else
logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}" logger.info "文件已存在id = #{@attachment.id}, filename = #{@attachment.filename}"
@ -147,8 +148,9 @@ class AttachmentsController < ApplicationController
if params[:type] == 'history' if params[:type] == 'history'
AttachmentHistory.find params[:id] AttachmentHistory.find params[:id]
else else
Attachment.find params[:id] Attachment.find_by(id: params[:id]) || Attachment.find_by(uuid: params[:id])
end end
tip_exception(404, "您访问的页面不存在或已被删除") if @file.blank?
end end
def delete_file(file_path) def delete_file(file_path)
@ -218,7 +220,7 @@ class AttachmentsController < ApplicationController
def attachment_candown def attachment_candown
unless current_user.admin? || current_user.business? unless current_user.admin? || current_user.business?
candown = true candown = true
if @file.container if @file.container && @file.uuid.nil?
if @file.container.is_a?(Issue) if @file.container.is_a?(Issue)
project = @file.container.project project = @file.container.project
candown = project.is_public || (current_user.logged? && project.member?(current_user)) candown = project.is_public || (current_user.logged? && project.member?(current_user))

View File

@ -299,7 +299,7 @@ module ApplicationHelper
end end
def download_url attachment,options={} def download_url attachment,options={}
attachment_path(attachment,options) attachment&.uuid.present? ? attachment_path(attachment.uuid,options) : attachment_path(attachment,options)
end end
# 耗时:天、小时、分、秒 # 耗时:天、小时、分、秒

View File

@ -1,44 +1,45 @@
# == Schema Information # == Schema Information
# #
# Table name: attachments # Table name: attachments
# #
# id :integer not null, primary key # id :integer not null, primary key
# container_id :integer # container_id :integer
# container_type :string(30) # container_type :string(30)
# filename :string(255) default(""), not null # filename :string(255) default(""), not null
# disk_filename :string(255) default(""), not null # disk_filename :string(255) default(""), not null
# filesize :integer default("0"), not null # filesize :integer default("0"), not null
# content_type :string(255) default("") # content_type :string(255) default("")
# digest :string(60) default(""), not null # digest :string(60) default(""), not null
# downloads :integer default("0"), not null # downloads :integer default("0"), not null
# author_id :integer default("0"), not null # author_id :integer default("0"), not null
# created_on :datetime # created_on :datetime
# description :text(65535) # description :text(65535)
# disk_directory :string(255) # disk_directory :string(255)
# attachtype :integer default("1") # attachtype :integer default("1")
# is_public :integer default("1") # is_public :integer default("1")
# copy_from :integer # copy_from :integer
# quotes :integer default("0") # quotes :integer default("0")
# is_publish :integer default("1") # is_publish :integer default("1")
# publish_time :datetime # publish_time :datetime
# resource_bank_id :integer # resource_bank_id :integer
# unified_setting :boolean default("1") # unified_setting :boolean default("1")
# cloud_url :string(255) default("") # cloud_url :string(255) default("")
# course_second_category_id :integer default("0") # course_second_category_id :integer default("0")
# delay_publish :boolean default("0") # delay_publish :boolean default("0")
# memo_image :boolean default("0") # memo_image :boolean default("0")
# extra_type :integer default("0") # extra_type :integer default("0")
# # uuid :string(255)
# Indexes #
# # Indexes
# index_attachments_on_author_id (author_id) #
# index_attachments_on_container_id_and_container_type (container_id,container_type) # index_attachments_on_author_id (author_id)
# index_attachments_on_course_second_category_id (course_second_category_id) # index_attachments_on_container_id_and_container_type (container_id,container_type)
# index_attachments_on_created_on (created_on) # index_attachments_on_course_second_category_id (course_second_category_id)
# index_attachments_on_is_public (is_public) # index_attachments_on_created_on (created_on)
# index_attachments_on_quotes (quotes) # index_attachments_on_is_public (is_public)
# # index_attachments_on_quotes (quotes)
#
@ -97,6 +98,11 @@ class Attachment < ApplicationRecord
downloads downloads
end end
def generate_uuid
self.uuid = uuid || SecureRandom.uuid
save!
end
def quotes_count def quotes_count
quotes.nil? ? 0 : quotes quotes.nil? ? 0 : quotes
end end

View File

@ -224,13 +224,23 @@ class Issue < ApplicationRecord
# 关附件到功能 # 关附件到功能
def associate_attachment_container def associate_attachment_container
return if self.project_id == 0
att_ids = [] att_ids = []
# 附件的格式为(/api/attachments/ + 附件id的形式提取出id进行附件属性关联做附件访问权限控制 # 附件的格式为(/api/attachments/ + 附件id的形式提取出id进行附件属性关联做附件访问权限控制
att_ids += self.description.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]} att_ids += self.description.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]}
att_ids += self.description.to_s.scan(/\/api\/attachments\/.+\"/).map{|s|s.match(/\d+/)[0]} att_ids += self.description.to_s.scan(/\/api\/attachments\/.+\"/).map{|s|s.match(/\d+/)[0]}
att_ids += self.description.to_s.scan(/\/api\/attachments\/\d+/).map{|s|s.match(/\d+/)[0]} att_ids += self.description.to_s.scan(/\/api\/attachments\/\d+/).map{|s|s.match(/\d+/)[0]}
if att_ids.present? if att_ids.present?
Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: "Project") Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: 'Project')
end
att_ids2 = []
# uuid_regex= /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
# 附件的格式为(/api/attachments/ + uuid的形式提取出id进行附件属性关联做附件访问权限控制
att_ids2 += self.description.to_s.scan(/\(\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\)/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]}
att_ids2 += self.description.to_s.scan(/\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]}
if att_ids2.present?
Attachment.where(uuid: att_ids2).where("container_type IS NULL OR container_type = 'Issue'").update_all(container_id: self.project_id, container_type: 'Project')
end end
end end

View File

@ -61,6 +61,7 @@ class Journal < ApplicationRecord
# 关附件到功能 # 关附件到功能
def associate_attachment_container def associate_attachment_container
return if self.issue&.project_id.to_i == 0
att_ids = [] att_ids = []
# 附件的格式为(/api/attachments/ + 附件id的形式提取出id进行附件属性关联做附件访问权限控制 # 附件的格式为(/api/attachments/ + 附件id的形式提取出id进行附件属性关联做附件访问权限控制
att_ids += self.notes.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]} att_ids += self.notes.to_s.scan(/\(\/api\/attachments\/.+\)/).map{|s|s.match(/\d+/)[0]}
@ -69,6 +70,15 @@ class Journal < ApplicationRecord
if att_ids.present? if att_ids.present?
Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Journal'").update_all(container_id: self.issue.project_id, container_type: "Project") Attachment.where(id: att_ids).where("container_type IS NULL OR container_type = 'Journal'").update_all(container_id: self.issue.project_id, container_type: "Project")
end end
att_ids2 = []
# uuid_regex= /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
# 附件的格式为(/api/attachments/ + uuid的形式提取出id进行附件属性关联做附件访问权限控制
att_ids2 += self.notes.to_s.scan(/\(\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\)/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]}
att_ids2 += self.notes.to_s.scan(/\/api\/attachments\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/).map{|s|s.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/)[0]}
if att_ids2.present?
Attachment.where(uuid: att_ids).where("container_type IS NULL OR container_type = 'Journal'").update_all(container_id: self.issue.project_id, container_type: "Project")
end
end end
def operate_content def operate_content

View File

@ -31,7 +31,7 @@ module Api::V1::Issues::Concerns::Checkable
def check_attachments (attachment_ids) def check_attachments (attachment_ids)
raise ApplicationService::Error, "请输入正确的附件ID数组" unless attachment_ids.is_a?(Array) raise ApplicationService::Error, "请输入正确的附件ID数组" unless attachment_ids.is_a?(Array)
attachment_ids.each do |aid| attachment_ids.each do |aid|
raise ApplicationService::Error, "请输入正确的附件ID" unless Attachment.exists?(id: aid) raise ApplicationService::Error, "请输入正确的附件ID" unless Attachment.exists?(id: aid) || Attachment.exists?(uuid: aid)
end end
end end

View File

@ -9,7 +9,7 @@ module Api::V1::Issues::Concerns::Loadable
end end
def load_attachments(attachment_ids) def load_attachments(attachment_ids)
@attachments = Attachment.where(id: attachment_ids) @attachments = Attachment.where(id: attachment_ids).or(Attachment.where(uuid: attachment_ids))
end end
def load_atme_receivers(receivers_login) def load_atme_receivers(receivers_login)

View File

@ -1,2 +1,7 @@
json.id @attachment.id json.id @attachment.uuid
json.filesize @attachment.filesize json.title @attachment.title
json.filesize number_to_human_size(@attachment.filesize)
json.is_pdf @attachment.is_pdf?
json.url Rails.application.config_for(:configuration)['platform_url'] + (@attachment.is_pdf? ? download_url(@attachment,disposition:"inline") : download_url(@attachment)).to_s
json.created_on @attachment.created_on.strftime("%Y-%m-%d %H:%M")
json.content_type @attachment.content_type