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: 76 inserted, 17 deleted

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