Skip to content

Commit

Permalink
Merge pull request #1271 from dradis/attachments/evidence-note-move
Browse files Browse the repository at this point in the history
Add AttachmentsCopier concern
  • Loading branch information
aapomm committed Jun 26, 2024
2 parents 8f1ed61 + 33256b5 commit bbb8543
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[v#.#.#] ([month] [YYYY])
- Attachments: Copy attachments when moving an evidence/note
- Liquid: Make project-level collections available for Liquid syntax
- Upgraded gems: nokogiri, rails, rexml
- Bugs fixes:
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/concerns/attachments_copier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module AttachmentsCopier
def copy_attachments(record)
record.content.scan(Attachment::SCREENSHOT_REGEX).each do |screenshot_path|
full_screenshot_path, _, _, _, project_id, node_id, filename, _ = screenshot_path

attachment = Attachment.find_by(filename: CGI::unescape(filename), node_id: record.node_id_was)

if attachment
new_attachment = attachment.copy_to(record.node)
new_filename = new_attachment.url_encoded_filename
new_path = full_screenshot_path.gsub(
/nodes\/[0-9]+\/attachments\/.+/,
"nodes/#{new_attachment.node_id}/attachments/#{new_filename}"
)

record.content = record.content.gsub(full_screenshot_path, new_path)
end
end
end
end
3 changes: 3 additions & 0 deletions app/controllers/evidence_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class EvidenceController < NestedNodeResourceController
include AttachmentsCopier
include ConflictResolver
include EvidenceHelper
include LiquidEnabledResource
Expand Down Expand Up @@ -55,6 +56,8 @@ def update
@evidence.assign_attributes(evidence_params)
autogenerate_issue if evidence_params[:issue_id].blank?

copy_attachments(@evidence) if @evidence.node_changed?

if @evidence.save
track_updated(@evidence)
check_for_edit_conflicts(@evidence, updated_at_before_save)
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/notes_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This controller exposes the REST operations required to manage the Note
# resource.
class NotesController < NestedNodeResourceController
include AttachmentsCopier
include ConflictResolver
include LiquidEnabledResource
include Mentioned
Expand Down Expand Up @@ -44,7 +45,11 @@ def edit
# Update the attributes of a Note
def update
updated_at_before_save = @note.updated_at.to_i
if @note.update(note_params)

@note.assign_attributes(note_params)
copy_attachments(@note) if @note.node_changed?

if @note.save
track_updated(@note)
check_for_edit_conflicts(@note, updated_at_before_save)
# if the note has just been moved to another node, we must reload
Expand Down
18 changes: 9 additions & 9 deletions app/models/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
**
=end


# ==Description
# This class in an abstraction layer to the <tt>attachments/</tt> folder. It allows
# access to the folder content in a way that mimics the working of ActiveRecord
Expand Down Expand Up @@ -66,6 +65,8 @@ class Attachment < File
AttachmentPwd = Rails.env.test? ? Rails.root.join('tmp', 'attachments') : Rails.root.join('attachments')
FileUtils.mkdir_p(AttachmentPwd) unless File.exists?(AttachmentPwd)

SCREENSHOT_REGEX = /\!((https?:\/\/.+)|((\/pro)?\/projects\/(\d+)\/nodes\/(\d+)\/attachments\/(.+?)(\(.*\))?))\!/i

# -- Class Methods ---------------------------------------------------------

def self.all(*args)
Expand All @@ -81,7 +82,7 @@ def self.count
end

def self.find_by(filename:, node_id:)
find(filename, conditions: { node_id: node_id } )
find(filename, conditions: { node_id: node_id })
rescue StandardError
end

Expand All @@ -91,17 +92,17 @@ def self.find(*args)
dir = Dir.new(pwd)

# makes the find request and stores it to resources
return_value = case args.first
case args.first
when :all, :first, :last
attachments = []
if options[:conditions] && options[:conditions][:node_id]
node_id = options[:conditions][:node_id].to_s
raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
if (File.exist?( File.join(pwd, node_id)))
if (File.exist?(File.join(pwd, node_id)))
node_dir = Dir.new(pwd.join(node_id)).sort
node_dir.each do |attachment|
next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(pwd.join(node_id, attachment))
attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
attachments << Attachment.new(filename: $1, node_id: node_id.to_i)
end
end
else
Expand All @@ -110,7 +111,7 @@ def self.find(*args)
node_dir = Dir.new(pwd.join(node)).sort
node_dir.each do |attachment|
next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(pwd.join(node, attachment))
attachments << Attachment.new(:filename => $1, :node_id => node.to_i)
attachments << Attachment.new(filename: $1, node_id: node.to_i)
end
end
attachments.sort_by!(&:filename)
Expand All @@ -129,18 +130,17 @@ def self.find(*args)
# in this routine we find the attachment by file name and node id
filename = args.first
attachments = []
raise "You need to supply a node id in the condition parameter" unless options[:conditions] && options[:conditions][:node_id]
raise 'You need to supply a node id in the condition parameter' unless options[:conditions] && options[:conditions][:node_id]
node_id = options[:conditions][:node_id].to_s
raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
node_dir = Dir.new(pwd.join(node_id)).sort
node_dir.each do |attachment|
next unless ((attachment =~ /^(.+)$/) == 0 && $1 == filename)
attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
attachments << Attachment.new(filename: $1, node_id: node_id.to_i)
end
raise "Could not find Attachment with filename #{filename}" if attachments.empty?
attachments.first
end
return return_value
end

def self.model_name
Expand Down
13 changes: 12 additions & 1 deletion spec/features/evidence_moving_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ def create_node(label, parent = nil)
click_move_evidence
end

let(:current_evidence) { @evidence = create(:evidence, node: @node_5) }
let(:content) { "#[Description]#\nTest Evidence\n" }
let(:current_evidence) { @evidence = create(:evidence, content: content, node: @node_5) }

describe 'moving an evidence to a different node' do
let(:attachment) { create(:attachment, filename: 'name with spaces.png', node: @node_5) }
let(:content) { "#[Description]#\n!/projects/#{current_project.id}/nodes/#{@node_5.id}/attachments/#{attachment.filename}!\n" }

before do
# Ensure this works with duplicated attachment
create(:attachment, filename: 'name with spaces.png', node: @node_1)

within('#modal_move_evidence') do
click_link @node_1.label
click_submit
Expand All @@ -46,6 +53,10 @@ def create_node(label, parent = nil)
it 'should redirect to evidence show path' do
expect(current_path).to eq(project_node_evidence_path(current_project, @node_1, current_evidence))
end

it 'should update the attachment reference to the new node' do
expect(current_evidence.reload.content).to include("nodes/#{@node_1.id}")
end
end

describe 'moving a evidence to a similar node', js: true do
Expand Down
13 changes: 12 additions & 1 deletion spec/features/note_moving_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,17 @@ def create_node(label, parent = nil)
click_move_note
end

let(:current_note) { @note = create(:note, node: @node_5) }
let(:text) { "#[Description]#\nTest Note\n" }
let(:current_note) { @note = create(:note, text: text, node: @node_5) }

describe 'moving a note to a different node' do
let(:attachment) { create(:attachment, filename: 'name with spaces.png', node: @node_5) }
let(:text) { "#[Description]#\n!/projects/#{current_project.id}/nodes/#{@node_5.id}/attachments/#{attachment.filename}!\n" }

before do
# Ensure this works with duplicated attachment
create(:attachment, filename: 'name with spaces.png', node: @node_1)

within('#modal_move_note') do
click_link @node_1.label
click_submit
Expand All @@ -47,6 +54,10 @@ def create_node(label, parent = nil)
it 'should redirect to note show path' do
expect(current_path).to eq(project_node_note_path(current_project, @node_1, current_note))
end

it 'should update the attachment reference to the new node' do
expect(current_note.reload.content).to include("nodes/#{@node_1.id}")
end
end

describe 'moving a note to a similar node' do
Expand Down

0 comments on commit bbb8543

Please sign in to comment.