instiki/app/models/web.rb
Jacques Distler 5d15e3f39d Security: Instiki 0.16.2
On Webs with file uploads enabled, uploaded files were stored
(in version 0.16.1 and earlier) in the public/ directory.

This was a security threat. A miscreant could upload a .html file.
When a user clicked on the link to the file, it was opened (unsanitized)
in the browser.

As of version 0.16.2, uploaded files are stored in the webs/
directory. Now, when the user clicks on the link, the file is sent
with the

    Content-Disposition: attachment

header set, which causes the file to be downloaded, rather than opened
in the browser. As always, files downloaded from the internets should be
treated with caution. At least, this way, they are not aoutomatically 
opened in the browser.

To move your existing uploaded files to the new location, do a

     rake upgrade_instiki
2009-01-26 00:21:30 -06:00

162 lines
4.2 KiB
Ruby

class Web < ActiveRecord::Base
has_many :pages, :dependent => :destroy
has_many :wiki_files, :dependent => :destroy
def wiki
Wiki.new
end
def settings_changed?(markup, safe_mode, brackets_only)
self.markup != markup ||
self.safe_mode != safe_mode ||
self.brackets_only != brackets_only
end
def add_page(name, content, time, author, renderer)
page = page(name) || Page.new(:web => self, :name => name)
page.revise(content, time, author, renderer)
end
def authors
connection.select_all(
'SELECT DISTINCT r.author AS author ' +
'FROM revisions r ' +
'JOIN pages p ON p.id = r.page_id ' +
'WHERE p.web_id = ' + self.id.to_s +
' ORDER by 1 '
).collect { |row| row['author'] }
end
def categories
select.map { |page| page.categories }.flatten.uniq.sort
end
def page(name)
pages.find(:first, :conditions => ['name = ?', name])
end
def last_page
return Page.find(:first, :order => 'id desc', :conditions => ['web_id = ?', self.id])
end
def has_page?(name)
Page.count(:conditions => ['web_id = ? AND name = ?', id, name]) > 0
end
def has_file?(file_name)
WikiFile.find_by_file_name(file_name) != nil
end
def file_list(sort_order = 'file_name')
WikiFile.all(:order => sort_order, :conditions => ['web_id = ?', id])
end
def pages_that_link_to(file_name)
WikiReference.pages_that_link_to_file(self, file_name)
end
def description(file_name)
file = WikiFile.find_by_file_name(file_name)
file.description if file
end
def markup
read_attribute('markup').to_sym
end
def page_names_by_author
connection.select_all(
'SELECT DISTINCT r.author AS author, p.name AS page_name ' +
'FROM revisions r ' +
'JOIN pages p ON r.page_id = p.id ' +
"WHERE p.web_id = #{self.id} " +
'ORDER by p.name'
).inject({}) { |result, row|
author, page_name = row['author'], row['page_name']
result[author] = [] unless result.has_key?(author)
result[author] << page_name
result
}
end
def remove_pages(pages_to_be_removed)
pages_to_be_removed.each { |p| p.destroy }
end
def revised_at
select.most_recent_revision
end
def select(&condition)
PageSet.new(self, pages, condition)
end
def select_all
PageSet.new(self, pages, nil)
end
def to_param
address
end
def create_files_directory
return unless allow_uploads == 1
dummy_file = self.wiki_files.build(:file_name => '0', :description => '0', :content => '0')
File.umask(0002)
dir = File.dirname(dummy_file.content_path)
begin
require 'fileutils'
FileUtils.mkdir_p dir
dummy_file.save
dummy_file.destroy
rescue => e
logger.error("Failed create files directory for #{self.address}: #{e}")
raise "Instiki could not create directory to store uploaded files. " +
"Please make sure that Instiki is allowed to create directory " +
"#{File.expand_path(dir)} and add files to it."
end
end
private
# Returns an array of all the wiki words in any current revision
def wiki_words
pages.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
end
# Returns an array of all the page names on this web
def page_names
pages.map { |p| p.name }
end
protected
before_save :sanitize_markup
after_save :create_files_directory
before_validation :validate_address
validates_uniqueness_of :address
validates_length_of :color, :in => 3..6
def sanitize_markup
self.markup = markup.to_s
end
def validate_address
unless address == CGI.escape(address)
self.errors.add(:address, 'should contain only valid URI characters')
raise Instiki::ValidationError.new("#{self.class.human_attribute_name('address')} #{errors.on(:address)}")
end
end
def default_web?
defined? DEFAULT_WEB and self.address == DEFAULT_WEB
end
def files_path
if default_web?
"#{RAILS_ROOT}/webs/files"
else
"#{RAILS_ROOT}/webs/#{self.address}/files"
end
end
end