Description:
merge
Commit status:
[Not Reviewed]
References:
merge java
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r688:b31aedc6a96e - - 1 file changed: 6 inserted, 1 deleted

@@ -1,411 +1,416
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10 has_and_belongs_to_many :groups
10 has_and_belongs_to_many :groups
11
11
12 has_many :test_requests, -> {order(submitted_at: DESC)}
12 has_many :test_requests, -> {order(submitted_at: DESC)}
13
13
14 has_many :messages, -> { order(created_at: DESC) },
14 has_many :messages, -> { order(created_at: DESC) },
15 :class_name => "Message",
15 :class_name => "Message",
16 :foreign_key => "sender_id"
16 :foreign_key => "sender_id"
17
17
18 has_many :replied_messages, -> { order(created_at: DESC) },
18 has_many :replied_messages, -> { order(created_at: DESC) },
19 :class_name => "Message",
19 :class_name => "Message",
20 :foreign_key => "receiver_id"
20 :foreign_key => "receiver_id"
21
21
22 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
22 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
23
23
24 belongs_to :site
24 belongs_to :site
25 belongs_to :country
25 belongs_to :country
26
26
27 has_and_belongs_to_many :contests, -> { order(:name); uniq}
27 has_and_belongs_to_many :contests, -> { order(:name); uniq}
28
28
29 scope :activated_users, -> {where activated: true}
29 scope :activated_users, -> {where activated: true}
30
30
31 validates_presence_of :login
31 validates_presence_of :login
32 validates_uniqueness_of :login
32 validates_uniqueness_of :login
33 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
33 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
34 validates_length_of :login, :within => 3..30
34 validates_length_of :login, :within => 3..30
35
35
36 validates_presence_of :full_name
36 validates_presence_of :full_name
37 validates_length_of :full_name, :minimum => 1
37 validates_length_of :full_name, :minimum => 1
38
38
39 validates_presence_of :password, :if => :password_required?
39 validates_presence_of :password, :if => :password_required?
40 validates_length_of :password, :within => 4..20, :if => :password_required?
40 validates_length_of :password, :within => 4..20, :if => :password_required?
41 validates_confirmation_of :password, :if => :password_required?
41 validates_confirmation_of :password, :if => :password_required?
42
42
43 validates_format_of :email,
43 validates_format_of :email,
44 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
44 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 :if => :email_validation?
45 :if => :email_validation?
46 validate :uniqueness_of_email_from_activated_users,
46 validate :uniqueness_of_email_from_activated_users,
47 :if => :email_validation?
47 :if => :email_validation?
48 validate :enough_time_interval_between_same_email_registrations,
48 validate :enough_time_interval_between_same_email_registrations,
49 :if => :email_validation?
49 :if => :email_validation?
50
50
51 # these are for ytopc
51 # these are for ytopc
52 # disable for now
52 # disable for now
53 #validates_presence_of :province
53 #validates_presence_of :province
54
54
55 attr_accessor :password
55 attr_accessor :password
56
56
57 before_save :encrypt_new_password
57 before_save :encrypt_new_password
58 before_save :assign_default_site
58 before_save :assign_default_site
59 before_save :assign_default_contest
59 before_save :assign_default_contest
60
60
61 # this is for will_paginate
61 # this is for will_paginate
62 cattr_reader :per_page
62 cattr_reader :per_page
63 @@per_page = 50
63 @@per_page = 50
64
64
65 def self.authenticate(login, password)
65 def self.authenticate(login, password)
66 user = find_by_login(login)
66 user = find_by_login(login)
67 if user
67 if user
68 return user if user.authenticated?(password)
68 return user if user.authenticated?(password)
69 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
69 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
70 user.password = password
70 user.password = password
71 user.save
71 user.save
72 return user
72 return user
73 end
73 end
74 end
74 end
75 end
75 end
76
76
77 def authenticated?(password)
77 def authenticated?(password)
78 if self.activated
78 if self.activated
79 hashed_password == User.encrypt(password,self.salt)
79 hashed_password == User.encrypt(password,self.salt)
80 else
80 else
81 false
81 false
82 end
82 end
83 end
83 end
84
84
85 def authenticated_by_pop3?(password)
85 def authenticated_by_pop3?(password)
86 Net::POP3.enable_ssl
86 Net::POP3.enable_ssl
87 pop = Net::POP3.new('pops.it.chula.ac.th')
87 pop = Net::POP3.new('pops.it.chula.ac.th')
88 authen = true
88 authen = true
89 begin
89 begin
90 pop.start(login, password)
90 pop.start(login, password)
91 pop.finish
91 pop.finish
92 return true
92 return true
93 rescue
93 rescue
94 return false
94 return false
95 end
95 end
96 end
96 end
97
97
98 def authenticated_by_cucas?(password)
98 def authenticated_by_cucas?(password)
99 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
99 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
100 appid = '41508763e340d5858c00f8c1a0f5a2bb'
100 appid = '41508763e340d5858c00f8c1a0f5a2bb'
101 appsecret ='d9cbb5863091dbe186fded85722a1e31'
101 appsecret ='d9cbb5863091dbe186fded85722a1e31'
102 post_args = {
102 post_args = {
103 'appid' => appid,
103 'appid' => appid,
104 'appsecret' => appsecret,
104 'appsecret' => appsecret,
105 'username' => login,
105 'username' => login,
106 'password' => password
106 'password' => password
107 }
107 }
108
108
109 #simple call
109 #simple call
110 begin
110 begin
111 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
111 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
112 http.use_ssl = true
112 http.use_ssl = true
113 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
113 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
114 result = [ ]
114 result = [ ]
115 http.start do |http|
115 http.start do |http|
116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 resp = http.request(req,param)
118 resp = http.request(req,param)
119 result = JSON.parse resp.body
119 result = JSON.parse resp.body
120 end
120 end
121 return true if result["type"] == "beanStudent"
121 return true if result["type"] == "beanStudent"
122 rescue => e
122 rescue => e
123 return false
123 return false
124 end
124 end
125 return false
125 return false
126 end
126 end
127
127
128 def admin?
128 def admin?
129 self.roles.detect {|r| r.name == 'admin' }
129 self.roles.detect {|r| r.name == 'admin' }
130 end
130 end
131
131
132 def email_for_editing
132 def email_for_editing
133 if self.email==nil
133 if self.email==nil
134 "(unknown)"
134 "(unknown)"
135 elsif self.email==''
135 elsif self.email==''
136 "(blank)"
136 "(blank)"
137 else
137 else
138 self.email
138 self.email
139 end
139 end
140 end
140 end
141
141
142 def email_for_editing=(e)
142 def email_for_editing=(e)
143 self.email=e
143 self.email=e
144 end
144 end
145
145
146 def alias_for_editing
146 def alias_for_editing
147 if self.alias==nil
147 if self.alias==nil
148 "(unknown)"
148 "(unknown)"
149 elsif self.alias==''
149 elsif self.alias==''
150 "(blank)"
150 "(blank)"
151 else
151 else
152 self.alias
152 self.alias
153 end
153 end
154 end
154 end
155
155
156 def alias_for_editing=(e)
156 def alias_for_editing=(e)
157 self.alias=e
157 self.alias=e
158 end
158 end
159
159
160 def activation_key
160 def activation_key
161 if self.hashed_password==nil
161 if self.hashed_password==nil
162 encrypt_new_password
162 encrypt_new_password
163 end
163 end
164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
165 end
165 end
166
166
167 def verify_activation_key(key)
167 def verify_activation_key(key)
168 key == activation_key
168 key == activation_key
169 end
169 end
170
170
171 def self.random_password(length=5)
171 def self.random_password(length=5)
172 chars = 'abcdefghjkmnopqrstuvwxyz'
172 chars = 'abcdefghjkmnopqrstuvwxyz'
173 password = ''
173 password = ''
174 length.times { password << chars[rand(chars.length - 1)] }
174 length.times { password << chars[rand(chars.length - 1)] }
175 password
175 password
176 end
176 end
177
177
178 def self.find_non_admin_with_prefix(prefix='')
178 def self.find_non_admin_with_prefix(prefix='')
179 users = User.all
179 users = User.all
180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
181 end
181 end
182
182
183 # Contest information
183 # Contest information
184
184
185 def self.find_users_with_no_contest()
185 def self.find_users_with_no_contest()
186 users = User.all
186 users = User.all
187 return users.find_all { |u| u.contests.length == 0 }
187 return users.find_all { |u| u.contests.length == 0 }
188 end
188 end
189
189
190
190
191 def contest_time_left
191 def contest_time_left
192 if GraderConfiguration.contest_mode?
192 if GraderConfiguration.contest_mode?
193 return nil if site==nil
193 return nil if site==nil
194 return site.time_left
194 return site.time_left
195 elsif GraderConfiguration.indv_contest_mode?
195 elsif GraderConfiguration.indv_contest_mode?
196 time_limit = GraderConfiguration.contest_time_limit
196 time_limit = GraderConfiguration.contest_time_limit
197 if time_limit == nil
197 if time_limit == nil
198 return nil
198 return nil
199 end
199 end
200 if contest_stat==nil or contest_stat.started_at==nil
200 if contest_stat==nil or contest_stat.started_at==nil
201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 else
202 else
203 finish_time = contest_stat.started_at + time_limit
203 finish_time = contest_stat.started_at + time_limit
204 current_time = Time.now.gmtime
204 current_time = Time.now.gmtime
205 if current_time > finish_time
205 if current_time > finish_time
206 return 0
206 return 0
207 else
207 else
208 return finish_time - current_time
208 return finish_time - current_time
209 end
209 end
210 end
210 end
211 else
211 else
212 return nil
212 return nil
213 end
213 end
214 end
214 end
215
215
216 def contest_finished?
216 def contest_finished?
217 if GraderConfiguration.contest_mode?
217 if GraderConfiguration.contest_mode?
218 return false if site==nil
218 return false if site==nil
219 return site.finished?
219 return site.finished?
220 elsif GraderConfiguration.indv_contest_mode?
220 elsif GraderConfiguration.indv_contest_mode?
221 return false if self.contest_stat(true)==nil
221 return false if self.contest_stat(true)==nil
222 return contest_time_left == 0
222 return contest_time_left == 0
223 else
223 else
224 return false
224 return false
225 end
225 end
226 end
226 end
227
227
228 def contest_started?
228 def contest_started?
229 if GraderConfiguration.indv_contest_mode?
229 if GraderConfiguration.indv_contest_mode?
230 stat = self.contest_stat
230 stat = self.contest_stat
231 return ((stat != nil) and (stat.started_at != nil))
231 return ((stat != nil) and (stat.started_at != nil))
232 elsif GraderConfiguration.contest_mode?
232 elsif GraderConfiguration.contest_mode?
233 return true if site==nil
233 return true if site==nil
234 return site.started
234 return site.started
235 else
235 else
236 return true
236 return true
237 end
237 end
238 end
238 end
239
239
240 def update_start_time
240 def update_start_time
241 stat = self.contest_stat
241 stat = self.contest_stat
242 if stat.nil? or stat.started_at.nil?
242 if stat.nil? or stat.started_at.nil?
243 stat ||= UserContestStat.new(:user => self)
243 stat ||= UserContestStat.new(:user => self)
244 stat.started_at = Time.now.gmtime
244 stat.started_at = Time.now.gmtime
245 stat.save
245 stat.save
246 end
246 end
247 end
247 end
248
248
249 def problem_in_user_contests?(problem)
249 def problem_in_user_contests?(problem)
250 problem_contests = problem.contests.all
250 problem_contests = problem.contests.all
251
251
252 if problem_contests.length == 0 # this is public contest
252 if problem_contests.length == 0 # this is public contest
253 return true
253 return true
254 end
254 end
255
255
256 contests.each do |contest|
256 contests.each do |contest|
257 if problem_contests.find {|c| c.id == contest.id }
257 if problem_contests.find {|c| c.id == contest.id }
258 return true
258 return true
259 end
259 end
260 end
260 end
261 return false
261 return false
262 end
262 end
263
263
264 def available_problems_group_by_contests
264 def available_problems_group_by_contests
265 contest_problems = []
265 contest_problems = []
266 pin = {}
266 pin = {}
267 contests.enabled.each do |contest|
267 contests.enabled.each do |contest|
268 available_problems = contest.problems.available
268 available_problems = contest.problems.available
269 contest_problems << {
269 contest_problems << {
270 :contest => contest,
270 :contest => contest,
271 :problems => available_problems
271 :problems => available_problems
272 }
272 }
273 available_problems.each {|p| pin[p.id] = true}
273 available_problems.each {|p| pin[p.id] = true}
274 end
274 end
275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
276 contest_problems << {
276 contest_problems << {
277 :contest => nil,
277 :contest => nil,
278 :problems => other_avaiable_problems
278 :problems => other_avaiable_problems
279 }
279 }
280 return contest_problems
280 return contest_problems
281 end
281 end
282
282
283 def solve_all_available_problems?
283 def solve_all_available_problems?
284 available_problems.each do |p|
284 available_problems.each do |p|
285 u = self
285 u = self
286 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
286 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
287 return false if !p or !sub or sub.points < p.full_score
287 return false if !p or !sub or sub.points < p.full_score
288 end
288 end
289 return true
289 return true
290 end
290 end
291
291
292 def available_problems
292 def available_problems
293 if not GraderConfiguration.multicontests?
293 if not GraderConfiguration.multicontests?
294 if GraderConfiguration.use_problem_group?
294 if GraderConfiguration.use_problem_group?
295 return available_problems_in_group
295 return available_problems_in_group
296 else
296 else
297 return Problem.available_problems
297 return Problem.available_problems
298 end
298 end
299 else
299 else
300 contest_problems = []
300 contest_problems = []
301 pin = {}
301 pin = {}
302 contests.enabled.each do |contest|
302 contests.enabled.each do |contest|
303 contest.problems.available.each do |problem|
303 contest.problems.available.each do |problem|
304 if not pin.has_key? problem.id
304 if not pin.has_key? problem.id
305 contest_problems << problem
305 contest_problems << problem
306 end
306 end
307 pin[problem.id] = true
307 pin[problem.id] = true
308 end
308 end
309 end
309 end
310 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
310 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
311 return contest_problems + other_avaiable_problems
311 return contest_problems + other_avaiable_problems
312 end
312 end
313 end
313 end
314
314
315 def available_problems_in_group
315 def available_problems_in_group
316 problem = []
316 problem = []
317 self.groups.each do |group|
317 self.groups.each do |group|
318 group.problems.where(available: true).each { |p| problem << p }
318 group.problems.where(available: true).each { |p| problem << p }
319 end
319 end
320 - problem.uniq!.sort! do |a,b|
320 + problem.uniq!
321 + if problem
322 + problem.sort! do |a,b|
321 case
323 case
322 when a.date_added < b.date_added
324 when a.date_added < b.date_added
323 1
325 1
324 when a.date_added > b.date_added
326 when a.date_added > b.date_added
325 -1
327 -1
326 else
328 else
327 a.name <=> b.name
329 a.name <=> b.name
328 end
330 end
329 end
331 end
330 return problem
332 return problem
333 + else
334 + return []
335 + end
331 end
336 end
332
337
333 def can_view_problem?(problem)
338 def can_view_problem?(problem)
334 if not GraderConfiguration.multicontests?
339 if not GraderConfiguration.multicontests?
335 return problem.available
340 return problem.available
336 else
341 else
337 return problem_in_user_contests? problem
342 return problem_in_user_contests? problem
338 end
343 end
339 end
344 end
340
345
341 def self.clear_last_login
346 def self.clear_last_login
342 User.update_all(:last_ip => nil)
347 User.update_all(:last_ip => nil)
343 end
348 end
344
349
345 protected
350 protected
346 def encrypt_new_password
351 def encrypt_new_password
347 return if password.blank?
352 return if password.blank?
348 self.salt = (10+rand(90)).to_s
353 self.salt = (10+rand(90)).to_s
349 self.hashed_password = User.encrypt(self.password,self.salt)
354 self.hashed_password = User.encrypt(self.password,self.salt)
350 end
355 end
351
356
352 def assign_default_site
357 def assign_default_site
353 # have to catch error when migrating (because self.site is not available).
358 # have to catch error when migrating (because self.site is not available).
354 begin
359 begin
355 if self.site==nil
360 if self.site==nil
356 self.site = Site.find_by_name('default')
361 self.site = Site.find_by_name('default')
357 if self.site==nil
362 if self.site==nil
358 self.site = Site.find(1) # when 'default has be renamed'
363 self.site = Site.find(1) # when 'default has be renamed'
359 end
364 end
360 end
365 end
361 rescue
366 rescue
362 end
367 end
363 end
368 end
364
369
365 def assign_default_contest
370 def assign_default_contest
366 # have to catch error when migrating (because self.site is not available).
371 # have to catch error when migrating (because self.site is not available).
367 begin
372 begin
368 if self.contests.length == 0
373 if self.contests.length == 0
369 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
374 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
370 if default_contest
375 if default_contest
371 self.contests = [default_contest]
376 self.contests = [default_contest]
372 end
377 end
373 end
378 end
374 rescue
379 rescue
375 end
380 end
376 end
381 end
377
382
378 def password_required?
383 def password_required?
379 self.hashed_password.blank? || !self.password.blank?
384 self.hashed_password.blank? || !self.password.blank?
380 end
385 end
381
386
382 def self.encrypt(string,salt)
387 def self.encrypt(string,salt)
383 Digest::SHA1.hexdigest(salt + string)
388 Digest::SHA1.hexdigest(salt + string)
384 end
389 end
385
390
386 def uniqueness_of_email_from_activated_users
391 def uniqueness_of_email_from_activated_users
387 user = User.activated_users.find_by_email(self.email)
392 user = User.activated_users.find_by_email(self.email)
388 if user and (user.login != self.login)
393 if user and (user.login != self.login)
389 self.errors.add(:base,"Email has already been taken")
394 self.errors.add(:base,"Email has already been taken")
390 end
395 end
391 end
396 end
392
397
393 def enough_time_interval_between_same_email_registrations
398 def enough_time_interval_between_same_email_registrations
394 return if !self.new_record?
399 return if !self.new_record?
395 return if self.activated
400 return if self.activated
396 open_user = User.find_by_email(self.email,
401 open_user = User.find_by_email(self.email,
397 :order => 'created_at DESC')
402 :order => 'created_at DESC')
398 if open_user and open_user.created_at and
403 if open_user and open_user.created_at and
399 (open_user.created_at > Time.now.gmtime - 5.minutes)
404 (open_user.created_at > Time.now.gmtime - 5.minutes)
400 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
405 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
401 end
406 end
402 end
407 end
403
408
404 def email_validation?
409 def email_validation?
405 begin
410 begin
406 return VALIDATE_USER_EMAILS
411 return VALIDATE_USER_EMAILS
407 rescue
412 rescue
408 return false
413 return false
409 end
414 end
410 end
415 end
411 end
416 end
You need to be logged in to leave comments. Login now