Description:
added contest problem access control
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r282:6dab27215603 - - 5 files changed: 77 inserted, 18 deleted

@@ -1,60 +1,75
1 1 class TasksController < ApplicationController
2 2
3 3 before_filter :authenticate, :check_viewability
4 4
5 5 def index
6 6 redirect_to :action => 'list'
7 7 end
8 8
9 9 def list
10 10 @problems = Problem.find_available_problems
11 - @user = User.find(session[:user_id])
12 11 end
13 12
13 + # this has contest-wide access control
14 14 def view
15 15 base_name = params[:file]
16 - if !check_user_viewability(base_name)
16 + base_filename = File.basename("#{base_name}.#{params[:ext]}")
17 + filename = "#{Problem.download_file_basedir}/#{base_filename}"
18 +
19 + if !FileTest.exists?(filename)
17 20 redirect_to :action => 'index' and return
18 21 end
19 22
20 - base_filename = File.basename("#{base_name}.#{params[:ext]}")
21 - filename = "#{Problem.download_file_basedir}/#{base_filename}"
23 + send_file_to_user(filename, base_filename)
24 + end
22 25
23 - if !check_user_viewability(base_name) or !FileTest.exists?(filename)
26 + # this has problem-level access control
27 + def download
28 + problem = Problem.find(params[:id])
29 + if !problem or !problem.available or !@user.can_view_problem? problem
24 30 redirect_to :action => 'index' and return
25 31 end
26 32
33 + base_name = params[:file]
34 + base_filename = File.basename("#{base_name}.#{params[:ext]}")
35 + filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}"
36 + puts "SENDING: #{filename}"
37 +
38 + if !FileTest.exists?(filename)
39 + redirect_to :action => 'index' and return
40 + end
41 +
42 + puts "SENDING: #{filename}"
43 +
44 + send_file_to_user(filename, base_filename)
45 + end
46 +
47 + protected
48 +
49 + def send_file_to_user(filename, base_filename)
27 50 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
28 51 response.headers['Content-Type'] = "application/force-download"
29 52 response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\""
30 53 response.headers["X-Sendfile"] = filename
31 54 response.headers['Content-length'] = File.size(filename)
32 55 render :nothing => true
33 56 else
34 57 if params[:ext]=='pdf'
35 58 content_type = 'application/pdf'
36 59 else
37 60 content_type = 'application/octet-stream'
38 61 end
39 62
40 63 send_file filename, :stream => false, :filename => base_filename, :type => content_type
41 64 end
42 65 end
43 66
44 - protected
45 -
46 67 def check_viewability
47 68 @user = User.find(session[:user_id])
48 69 if @user==nil or !Configuration.show_tasks_to?(@user)
49 70 redirect_to :controller => 'main', :action => 'list'
50 71 return false
51 72 end
52 73 end
53 74
54 - def check_user_viewability(filename)
55 - # individual file access control shall be added here
56 - return false if not @user
57 - return Configuration.show_tasks_to?(@user)
58 - end
59 -
60 75 end
@@ -1,18 +1,19
1 1 module MainHelper
2 2
3 3 def link_to_description_if_any(name, problem, options={})
4 4 if !problem.url.blank?
5 5 return link_to name, problem.url, options
6 6 elsif !problem.description_filename.blank?
7 7 basename, ext = problem.description_filename.split('.')
8 8 options[:controller] = 'tasks'
9 - options[:action] = 'view'
9 + options[:action] = 'download'
10 + options[:id] = problem.id
10 11 options[:file] = basename
11 12 options[:ext] = ext
12 13 return link_to name, options
13 14 else
14 15 return ''
15 16 end
16 17 end
17 18
18 19 end
@@ -137,96 +137,119
137 137 return (Time.now.gmtime + time_limit) - Time.now.gmtime
138 138 else
139 139 finish_time = contest_stat.started_at + time_limit
140 140 current_time = Time.now.gmtime
141 141 if current_time > finish_time
142 142 return 0
143 143 else
144 144 return finish_time - current_time
145 145 end
146 146 end
147 147 else
148 148 return nil
149 149 end
150 150 end
151 151
152 152 def contest_finished?
153 153 if Configuration.contest_mode?
154 154 return false if site==nil
155 155 return site.finished?
156 156 elsif Configuration.indv_contest_mode?
157 157 time_limit = Configuration.contest_time_limit
158 158
159 159 return false if contest_stat==nil
160 160
161 161 return contest_time_left == 0
162 162 else
163 163 return false
164 164 end
165 165 end
166 166
167 167 def contest_started?
168 168 if Configuration.contest_mode?
169 169 return true if site==nil
170 170 return site.started
171 171 else
172 172 return true
173 173 end
174 174 end
175 175
176 176 def update_start_time
177 177 stat = self.contest_stat
178 178 if stat == nil
179 179 stat = UserContestStat.new(:user => self,
180 180 :started_at => Time.now.gmtime)
181 181 stat.save
182 182 end
183 183 end
184 184
185 + def problem_in_user_contests?(problem)
186 + problem_contests = problem.contests.all
187 +
188 + if problem_contests.length == 0 # this is public contest
189 + return true
190 + end
191 +
192 + contests.each do |contest|
193 + if problem_contests.find {|c| c.id == contest.id }
194 + return true
195 + end
196 + end
197 + return false
198 + end
199 +
200 + def can_view_problem?(problem)
201 + if not Configuration.multicontests?
202 + return problem.available
203 + else
204 + return problem_in_user_contests? problem
205 + end
206 + end
207 +
185 208 protected
186 209 def encrypt_new_password
187 210 return if password.blank?
188 211 self.salt = (10+rand(90)).to_s
189 212 self.hashed_password = User.encrypt(self.password,self.salt)
190 213 end
191 214
192 215 def assign_default_site
193 216 # have to catch error when migrating (because self.site is not available).
194 217 begin
195 218 if self.site==nil
196 219 self.site = Site.find_by_name('default')
197 220 if self.site==nil
198 221 self.site = Site.find(1) # when 'default has be renamed'
199 222 end
200 223 end
201 224 rescue
202 225 end
203 226 end
204 227
205 228 def password_required?
206 229 self.hashed_password.blank? || !self.password.blank?
207 230 end
208 231
209 232 def self.encrypt(string,salt)
210 233 Digest::SHA1.hexdigest(salt + string)
211 234 end
212 235
213 236 def uniqueness_of_email_from_activated_users
214 237 user = User.activated_users.find_by_email(self.email)
215 238 if user and (user.login != self.login)
216 239 self.errors.add_to_base("Email has already been taken")
217 240 end
218 241 end
219 242
220 243 def enough_time_interval_between_same_email_registrations
221 244 return if !self.new_record?
222 245 return if self.activated
223 246 open_user = User.find_by_email(self.email,
224 247 :order => 'created_at DESC')
225 248 if open_user and open_user.created_at and
226 249 (open_user.created_at > Time.now.gmtime - 5.minutes)
227 250 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
228 251 end
229 252 end
230 253
231 254 def email_validation?
232 255 begin
@@ -1,30 +1,31
1 1 ActionController::Routing::Routes.draw do |map|
2 2 map.resources :contests
3 3
4 4 map.resources :announcements
5 5 map.resources :sites
6 6
7 7 # The priority is based upon order of creation: first created -> highest priority.
8 8
9 9 # Sample of regular route:
10 10 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
11 11 # Keep in mind you can assign values other than :controller and :action
12 12
13 13 # Sample of named route:
14 14 # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
15 15 # This route can be invoked with purchase_url(:id => product.id)
16 16
17 17 # You can have the root of your site routed by hooking up ''
18 18 # -- just remember to delete public/index.html.
19 19 map.connect '', :controller => 'main', :action => 'login'
20 20
21 21 # Allow downloading Web Service WSDL as a file with an extension
22 22 # instead of a file named 'wsdl'
23 23 map.connect ':controller/service.wsdl', :action => 'wsdl'
24 24
25 25 map.connect 'tasks/view/:file.:ext', :controller => 'tasks', :action => 'view'
26 + map.connect 'tasks/download/:id/:file.:ext', :controller => 'tasks', :action => 'download'
26 27
27 28 # Install the default route as the lowest priority.
28 29 map.connect ':controller/:action/:id.:format'
29 30 map.connect ':controller/:action/:id'
30 31 end
@@ -6,148 +6,167
6 6
7 7 def initialize(problem)
8 8 @problem = problem
9 9 end
10 10
11 11 def import_from_file(tempfile,
12 12 time_limit,
13 13 memory_limit,
14 14 import_to_db=false)
15 15
16 16 dirname = extract(tempfile)
17 17 return false if not dirname
18 18 if not import_to_db
19 19 @log_msg = GraderScript.call_import_problem(@problem.name,
20 20 dirname,
21 21 time_limit,
22 22 memory_limit)
23 23 else
24 24 # Import test data to test pairs.
25 25
26 26 @problem.test_pairs.clear
27 27 if import_test_pairs(dirname)
28 28 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
29 29 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
30 30 else
31 31 @log_msg = "Importing test pair failed. (0 test pairs imported)"
32 32 end
33 33 end
34 34
35 35 @log_msg << import_problem_description(dirname)
36 36 @log_msg << import_problem_pdf(dirname)
37 37
38 38 return true
39 39 end
40 40
41 41 protected
42 42
43 43 def self.long_ext(filename)
44 44 i = filename.index('.')
45 45 len = filename.length
46 46 return filename.slice(i..len)
47 47 end
48 48
49 49 def extract(tempfile)
50 50 testdata_filename = save_testdata_file(tempfile)
51 51 ext = TestdataImporter.long_ext(tempfile.original_filename)
52 52
53 53 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
54 - begin
55 - Dir.mkdir extract_dir
56 - rescue Errno::EEXIST
54 + if File.exists? extract_dir
55 + backup_count = 0
56 + begin
57 + backup_count += 1
58 + backup_dirname = "#{extract_dir}.backup.#{backup_count}"
59 + end while File.exists? backup_dirname
60 + File.rename(extract_dir, backup_dirname)
57 61 end
62 + Dir.mkdir extract_dir
58 63
59 64 if ext=='.tar.gz' or ext=='.tgz'
60 65 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
61 66 elsif ext=='.tar'
62 67 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
63 68 elsif ext=='.zip'
64 69 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
65 70 else
66 71 return nil
67 72 end
68 73
69 74 system(cmd)
70 75
71 76 files = Dir["#{extract_dir}/**/*1*.in"]
72 77 return nil if files.length==0
73 78
74 79 File.delete(testdata_filename)
75 80
76 81 return File.dirname(files[0])
77 82 end
78 83
79 84 def save_testdata_file(tempfile)
80 85 ext = TestdataImporter.long_ext(tempfile.original_filename)
81 86 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
82 87
83 88 return nil if tempfile==""
84 89
85 90 if tempfile.instance_of?(Tempfile)
86 91 tempfile.close
87 92 FileUtils.move(tempfile.path,testdata_filename)
88 93 else
89 94 File.open(testdata_filename, "wb") do |f|
90 95 f.write(tempfile.read)
91 96 end
92 97 end
93 98
94 99 return testdata_filename
95 100 end
96 101
97 102 def import_test_pairs(dirname)
98 103 test_num = 1
99 104 while FileTest.exists? "#{dirname}/#{test_num}.in"
100 105 in_filename = "#{dirname}/#{test_num}.in"
101 106 sol_filename = "#{dirname}/#{test_num}.sol"
102 107
103 108 break if not FileTest.exists? sol_filename
104 109
105 110 test_pair = TestPair.new(:input => open(in_filename).read,
106 111 :solution => open(sol_filename).read,
107 112 :problem => @problem)
108 113 break if not test_pair.save
109 114
110 115 test_num += 1
111 116 end
112 117 return test_num > 1
113 118 end
114 119
115 120 def import_problem_description(dirname)
116 121 html_files = Dir["#{dirname}/*.html"]
117 122 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
118 123 if (html_files.length != 0) or (markdown_files.length != 0)
119 124 description = @problem.description || Description.new
120 125
121 126 if html_files.length != 0
122 127 filename = html_files[0]
123 128 description.markdowned = false
124 129 else
125 130 filename = markdown_files[0]
126 131 description.markdowned = true
127 132 end
128 133
129 134 description.body = open(filename).read
130 135 description.save
131 136 @problem.description = description
132 137 @problem.save
133 138 return "\nProblem description imported from #{filename}."
134 139 else
135 140 return ''
136 141 end
137 142 end
138 143
139 144 def import_problem_pdf(dirname)
140 145 pdf_files = Dir["#{dirname}/*.pdf"]
146 + puts "CHECKING... #{dirname}"
141 147 if pdf_files.length != 0
148 + puts "HAS PDF FILE"
142 149 filename = pdf_files[0]
143 - out_filename = "#{Problem.download_file_basedir}/#{@problem.name}.pdf"
150 +
151 + @problem.save if not @problem.id
152 + out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
153 + if not FileTest.exists? out_dirname
154 + Dir.mkdir out_dirname
155 + end
156 +
157 + out_filename = "#{out_dirname}/#{@problem.name}.pdf"
158 +
159 + if FileTest.exists? out_filename
160 + File.delete out_filename
161 + end
162 +
144 163 File.rename(filename, out_filename)
145 164 @problem.description_filename = "#{@problem.name}.pdf"
146 165 @problem.save
147 166 return "\nProblem pdf imported from #{filename}."
148 167 else
149 168 return ""
150 169 end
151 170 end
152 171
153 172 end
You need to be logged in to leave comments. Login now