@@ -26,7 +26,7 class Attachment < ActiveRecord::Base | |||||
26 | validates_length_of :filename, :maximum => 255 |
|
26 | validates_length_of :filename, :maximum => 255 | |
27 | validates_length_of :disk_filename, :maximum => 255 |
|
27 | validates_length_of :disk_filename, :maximum => 255 | |
28 | validates_length_of :description, :maximum => 255 |
|
28 | validates_length_of :description, :maximum => 255 | |
29 | validate :validate_max_file_size |
|
29 | validate :validate_max_file_size, :validate_file_extension | |
30 | attr_protected :id |
|
30 | attr_protected :id | |
31 |
|
31 | |||
32 | acts_as_event :title => :filename, |
|
32 | acts_as_event :title => :filename, | |
@@ -69,6 +69,15 class Attachment < ActiveRecord::Base | |||||
69 | end |
|
69 | end | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
|
72 | def validate_file_extension | |||
|
73 | if @temp_file | |||
|
74 | extension = File.extname(filename) | |||
|
75 | unless self.class.valid_extension?(extension) | |||
|
76 | errors.add(:base, l(:error_attachment_extension_not_allowed, :extension => extension)) | |||
|
77 | end | |||
|
78 | end | |||
|
79 | end | |||
|
80 | ||||
72 | def file=(incoming_file) |
|
81 | def file=(incoming_file) | |
73 | unless incoming_file.nil? |
|
82 | unless incoming_file.nil? | |
74 | @temp_file = incoming_file |
|
83 | @temp_file = incoming_file | |
@@ -333,6 +342,22 class Attachment < ActiveRecord::Base | |||||
333 | end |
|
342 | end | |
334 | end |
|
343 | end | |
335 |
|
344 | |||
|
345 | # Returns true if the extension is allowed, otherwise false | |||
|
346 | def self.valid_extension?(extension) | |||
|
347 | extension = extension.downcase.sub(/\A\.+/, '') | |||
|
348 | ||||
|
349 | denied, allowed = [:attachment_extensions_denied, :attachment_extensions_allowed].map do |setting| | |||
|
350 | Setting.send(setting).to_s.split(",").map {|s| s.strip.downcase.sub(/\A\.+/, '')}.reject(&:blank?) | |||
|
351 | end | |||
|
352 | if denied.present? && denied.include?(extension) | |||
|
353 | return false | |||
|
354 | end | |||
|
355 | unless allowed.blank? || allowed.include?(extension) | |||
|
356 | return false | |||
|
357 | end | |||
|
358 | true | |||
|
359 | end | |||
|
360 | ||||
336 | private |
|
361 | private | |
337 |
|
362 | |||
338 | # Physically deletes the file from the file system |
|
363 | # Physically deletes the file from the file system |
@@ -3,6 +3,12 | |||||
3 | <div class="box tabular settings"> |
|
3 | <div class="box tabular settings"> | |
4 | <p><%= setting_text_field :attachment_max_size, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p> |
|
4 | <p><%= setting_text_field :attachment_max_size, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p> | |
5 |
|
5 | |||
|
6 | <p><%= setting_text_area :attachment_extensions_allowed %> | |||
|
7 | <em class="info"><%= l(:text_comma_separated) %> <%= l(:label_example) %>: txt, png</em></p> | |||
|
8 | ||||
|
9 | <p><%= setting_text_area :attachment_extensions_denied %> | |||
|
10 | <em class="info"><%= l(:text_comma_separated) %> <%= l(:label_example) %>: js, swf</em></p> | |||
|
11 | ||||
6 | <p><%= setting_text_field :file_max_size_displayed, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p> |
|
12 | <p><%= setting_text_field :file_max_size_displayed, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p> | |
7 |
|
13 | |||
8 | <p><%= setting_text_field :diff_max_lines_displayed, :size => 6 %></p> |
|
14 | <p><%= setting_text_field :diff_max_lines_displayed, :size => 6 %></p> |
@@ -210,6 +210,7 en: | |||||
210 | error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file" |
|
210 | error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file" | |
211 | error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below" |
|
211 | error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below" | |
212 | error_can_not_read_import_file: "An error occurred while reading the file to import" |
|
212 | error_can_not_read_import_file: "An error occurred while reading the file to import" | |
|
213 | error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed" | |||
213 |
|
214 | |||
214 | mail_subject_lost_password: "Your %{value} password" |
|
215 | mail_subject_lost_password: "Your %{value} password" | |
215 | mail_body_lost_password: 'To change your password, click on the following link:' |
|
216 | mail_body_lost_password: 'To change your password, click on the following link:' | |
@@ -426,6 +427,8 en: | |||||
426 | setting_link_copied_issue: Link issues on copy |
|
427 | setting_link_copied_issue: Link issues on copy | |
427 | setting_max_additional_emails: Maximum number of additional email addresses |
|
428 | setting_max_additional_emails: Maximum number of additional email addresses | |
428 | setting_search_results_per_page: Search results per page |
|
429 | setting_search_results_per_page: Search results per page | |
|
430 | setting_attachment_extensions_allowed: Allowed extensions | |||
|
431 | setting_attachment_extensions_denied: Disallowed extensions | |||
429 |
|
432 | |||
430 | permission_add_project: Create project |
|
433 | permission_add_project: Create project | |
431 | permission_add_subprojects: Create subprojects |
|
434 | permission_add_subprojects: Create subprojects |
@@ -230,6 +230,7 fr: | |||||
230 | error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide" |
|
230 | error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide" | |
231 | error_invalid_csv_file_or_settings: "Le fichier n'est pas un fichier CSV ou n'est pas conforme aux paramètres sélectionnés" |
|
231 | error_invalid_csv_file_or_settings: "Le fichier n'est pas un fichier CSV ou n'est pas conforme aux paramètres sélectionnés" | |
232 | error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ importer" |
|
232 | error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ importer" | |
|
233 | error_attachment_extension_not_allowed: "L'extension %{extension} n'est pas autorisΓ©e" | |||
233 |
|
234 | |||
234 | mail_subject_lost_password: "Votre mot de passe %{value}" |
|
235 | mail_subject_lost_password: "Votre mot de passe %{value}" | |
235 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
|
236 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' | |
@@ -446,6 +447,8 fr: | |||||
446 | setting_link_copied_issue: Lier les demandes lors de la copie |
|
447 | setting_link_copied_issue: Lier les demandes lors de la copie | |
447 | setting_max_additional_emails: Nombre maximal d'adresses email additionnelles |
|
448 | setting_max_additional_emails: Nombre maximal d'adresses email additionnelles | |
448 | setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page |
|
449 | setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page | |
|
450 | setting_attachment_extensions_allowed: Extensions autorisΓ©es | |||
|
451 | setting_attachment_extensions_denied: Extensions non autorisΓ©es | |||
449 |
|
452 | |||
450 | permission_add_project: CrΓ©er un projet |
|
453 | permission_add_project: CrΓ©er un projet | |
451 | permission_add_subprojects: CrΓ©er des sous-projets |
|
454 | permission_add_subprojects: CrΓ©er des sous-projets |
@@ -55,6 +55,10 session_timeout: | |||||
55 | attachment_max_size: |
|
55 | attachment_max_size: | |
56 | format: int |
|
56 | format: int | |
57 | default: 5120 |
|
57 | default: 5120 | |
|
58 | attachment_extensions_allowed: | |||
|
59 | default: | |||
|
60 | attachment_extensions_denied: | |||
|
61 | default: | |||
58 | issues_export_limit: |
|
62 | issues_export_limit: | |
59 | format: int |
|
63 | format: int | |
60 | default: 500 |
|
64 | default: 500 |
@@ -122,6 +122,45 class AttachmentTest < ActiveSupport::TestCase | |||||
122 | end |
|
122 | end | |
123 | end |
|
123 | end | |
124 |
|
124 | |||
|
125 | def test_extension_should_be_validated_against_allowed_extensions | |||
|
126 | with_settings :attachment_extensions_allowed => "txt, png" do | |||
|
127 | a = Attachment.new(:container => Issue.find(1), | |||
|
128 | :file => mock_file_with_options(:original_filename => "test.png"), | |||
|
129 | :author => User.find(1)) | |||
|
130 | assert_save a | |||
|
131 | ||||
|
132 | a = Attachment.new(:container => Issue.find(1), | |||
|
133 | :file => mock_file_with_options(:original_filename => "test.jpeg"), | |||
|
134 | :author => User.find(1)) | |||
|
135 | assert !a.save | |||
|
136 | end | |||
|
137 | end | |||
|
138 | ||||
|
139 | def test_extension_should_be_validated_against_denied_extensions | |||
|
140 | with_settings :attachment_extensions_denied => "txt, png" do | |||
|
141 | a = Attachment.new(:container => Issue.find(1), | |||
|
142 | :file => mock_file_with_options(:original_filename => "test.jpeg"), | |||
|
143 | :author => User.find(1)) | |||
|
144 | assert_save a | |||
|
145 | ||||
|
146 | a = Attachment.new(:container => Issue.find(1), | |||
|
147 | :file => mock_file_with_options(:original_filename => "test.png"), | |||
|
148 | :author => User.find(1)) | |||
|
149 | assert !a.save | |||
|
150 | end | |||
|
151 | end | |||
|
152 | ||||
|
153 | def test_valid_extension_should_be_case_insensitive | |||
|
154 | with_settings :attachment_extensions_allowed => "txt, Png" do | |||
|
155 | assert Attachment.valid_extension?(".pnG") | |||
|
156 | assert !Attachment.valid_extension?(".jpeg") | |||
|
157 | end | |||
|
158 | with_settings :attachment_extensions_denied => "txt, Png" do | |||
|
159 | assert !Attachment.valid_extension?(".pnG") | |||
|
160 | assert Attachment.valid_extension?(".jpeg") | |||
|
161 | end | |||
|
162 | end | |||
|
163 | ||||
125 | def test_description_length_should_be_validated |
|
164 | def test_description_length_should_be_validated | |
126 | a = Attachment.new(:description => 'a' * 300) |
|
165 | a = Attachment.new(:description => 'a' * 300) | |
127 | assert !a.save |
|
166 | assert !a.save |
General Comments 0
You need to be logged in to leave comments.
Login now