@@ -26,7 +26,7 class Attachment < ActiveRecord::Base | |||
|
26 | 26 | validates_length_of :filename, :maximum => 255 |
|
27 | 27 | validates_length_of :disk_filename, :maximum => 255 |
|
28 | 28 | validates_length_of :description, :maximum => 255 |
|
29 | validate :validate_max_file_size | |
|
29 | validate :validate_max_file_size, :validate_file_extension | |
|
30 | 30 | attr_protected :id |
|
31 | 31 | |
|
32 | 32 | acts_as_event :title => :filename, |
@@ -69,6 +69,15 class Attachment < ActiveRecord::Base | |||
|
69 | 69 | end |
|
70 | 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 | 81 | def file=(incoming_file) |
|
73 | 82 | unless incoming_file.nil? |
|
74 | 83 | @temp_file = incoming_file |
@@ -333,6 +342,22 class Attachment < ActiveRecord::Base | |||
|
333 | 342 | end |
|
334 | 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 | 361 | private |
|
337 | 362 | |
|
338 | 363 | # Physically deletes the file from the file system |
@@ -3,6 +3,12 | |||
|
3 | 3 | <div class="box tabular settings"> |
|
4 | 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 | 12 | <p><%= setting_text_field :file_max_size_displayed, :size => 6 %> <%= l(:"number.human.storage_units.units.kb") %></p> |
|
7 | 13 | |
|
8 | 14 | <p><%= setting_text_field :diff_max_lines_displayed, :size => 6 %></p> |
@@ -210,6 +210,7 en: | |||
|
210 | 210 | error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file" |
|
211 | 211 | error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below" |
|
212 | 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 | 215 | mail_subject_lost_password: "Your %{value} password" |
|
215 | 216 | mail_body_lost_password: 'To change your password, click on the following link:' |
@@ -426,6 +427,8 en: | |||
|
426 | 427 | setting_link_copied_issue: Link issues on copy |
|
427 | 428 | setting_max_additional_emails: Maximum number of additional email addresses |
|
428 | 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 | 433 | permission_add_project: Create project |
|
431 | 434 | permission_add_subprojects: Create subprojects |
@@ -230,6 +230,7 fr: | |||
|
230 | 230 | error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide" |
|
231 | 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 | 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 | 235 | mail_subject_lost_password: "Votre mot de passe %{value}" |
|
235 | 236 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
@@ -446,6 +447,8 fr: | |||
|
446 | 447 | setting_link_copied_issue: Lier les demandes lors de la copie |
|
447 | 448 | setting_max_additional_emails: Nombre maximal d'adresses email additionnelles |
|
448 | 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 | 453 | permission_add_project: CrΓ©er un projet |
|
451 | 454 | permission_add_subprojects: CrΓ©er des sous-projets |
@@ -55,6 +55,10 session_timeout: | |||
|
55 | 55 | attachment_max_size: |
|
56 | 56 | format: int |
|
57 | 57 | default: 5120 |
|
58 | attachment_extensions_allowed: | |
|
59 | default: | |
|
60 | attachment_extensions_denied: | |
|
61 | default: | |
|
58 | 62 | issues_export_limit: |
|
59 | 63 | format: int |
|
60 | 64 | default: 500 |
@@ -122,6 +122,45 class AttachmentTest < ActiveSupport::TestCase | |||
|
122 | 122 | end |
|
123 | 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 | 164 | def test_description_length_should_be_validated |
|
126 | 165 | a = Attachment.new(:description => 'a' * 300) |
|
127 | 166 | assert !a.save |
General Comments 0
You need to be logged in to leave comments.
Login now