Description:
add authentication by CU-CAS from p' krerk
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r396:4b88edeab117 - - 1 file changed: 31 inserted, 6 deleted

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