1
2 """
3
4 @author: Fabio Erculiani <lxnay@sabayonlinux.org>
5 @contact: lxnay@sabayonlinux.org
6 @copyright: Fabio Erculiani
7 @license: GPL-2
8
9 B{Entropy Services Authentication Interfaces}.
10
11 """
12
13 from __future__ import with_statement
14 import os
15 import time
16 import random
17 from entropy.services.skel import Authenticator, RemoteDatabase
18 from entropy.exceptions import *
19 from entropy.const import etpConst
20 from entropy.i18n import _
21
23
24 from entropy import tools as entropyTools
26 Authenticator.__init__(self)
27 RemoteDatabase.__init__(self)
28
29 self.itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
30 self.USER_NORMAL = 0
31 self.USER_INACTIVE = 1
32 self.USER_IGNORE = 2
33 self.USER_FOUNDER = 3
34 self.REGISTERED_USERS_GROUP = 7895
35 self.ADMIN_GROUPS = [7893, 7898]
36 self.MODERATOR_GROUPS = [484]
37 self.DEVELOPER_GROUPS = [7900]
38 self.USERNAME_LENGTH_RANGE = range(3,21)
39 self.PASSWORD_LENGTH_RANGE = range(6,31)
40 self.PRIVMSGS_NO_BOX = -3
41 self.NOTIFY_EMAIL = 0
42 self.FAKE_USERNAME = 'already_authed'
43 self.USER_AGENT = "Entropy/%s (compatible; %s; %s: %s %s %s)" % (
44 etpConst['entropyversion'],
45 "Entropy",
46 "UGC",
47 os.uname()[0],
48 os.uname()[4],
49 os.uname()[2],
50 )
51 self.TABLE_PREFIX = 'phpbb_'
52 self.do_update_session_table = True
53
55 allow_name_chars = self._get_config_value("allow_name_chars")
56 if allow_name_chars == "USERNAME_CHARS_ANY":
57 regex = '.+'
58 elif allow_name_chars == "USERNAME_ALPHA_ONLY":
59 regex = '[A-Za-z0-9]+'
60 elif allow_name_chars == "USERNAME_ALPHA_SPACERS":
61 regex = '[A-Za-z0-9-[\]_+ ]+'
62 elif allow_name_chars == "USERNAME_LETTER_NUM":
63 regex = '[a-zA-Z0-9]+'
64 elif allow_name_chars == "USERNAME_LETTER_NUM_SPACERS":
65 regex = '[-\]_+ [a-zA-Z0-9]+'
66 else:
67 regex = '[\x01-\x7F]+'
68 regex = "^%s$" % (regex,)
69 import re
70 myreg = re.compile(regex)
71 if myreg.match(username):
72 del myreg
73 return True
74 return False
75
77 self.check_connection()
78 self.cursor.execute('SELECT user_id FROM '+self.TABLE_PREFIX+'users WHERE `username_clean` = %s OR LOWER(`username`) = %s', (username_clean,username.lower(),))
79 data = self.cursor.fetchone()
80 if not data: return False
81 if not isinstance(data,dict): return False
82 if not data.has_key('user_id'): return False
83 return True
84
86 self.check_connection()
87 self.cursor.execute('SELECT user_id FROM '+self.TABLE_PREFIX+'users WHERE `user_email` = %s', (email,))
88 data = self.cursor.fetchone()
89 if not data: return False
90 if not isinstance(data,dict): return False
91 if not data.has_key('user_id'): return False
92 return True
93
95 self.check_connection()
96 self.cursor.execute('SELECT disallow_id FROM '+self.TABLE_PREFIX+'disallow WHERE `disallow_username` = %s', (username,))
97 data = self.cursor.fetchone()
98 if not data: return True
99 if not isinstance(data,dict): return True
100 if not data.has_key('disallow_id'): return True
101 return False
102
104
105 try:
106 x = unicode(username.encode('utf-8'),'raw_unicode_escape')
107 del x
108 except (UnicodeDecodeError,UnicodeEncodeError,):
109 return False,'Invalid username'
110 if (""" in username) or ("'" in username) or ('"' in username) or \
111 (" " in username):
112 return False,'Invalid username'
113
114 try:
115 valid = self.validate_username_regex(username)
116 except:
117 return False,'Username contains bad characters'
118 if not valid:
119 return False,'Invalid username'
120
121 exists = self.does_username_exist(username, username_clean)
122 if exists: return False,'Username already taken'
123
124 allowed = self.is_username_allowed(username)
125 if not allowed: return False,'Username not allowed'
126
127 return True,'All fine'
128
130 import binascii
131 return str(binascii.crc32(email.lower())) + str(len(email))
132
134 self.check_connection()
135 self.cursor.execute('UPDATE '+self.TABLE_PREFIX+'users SET user_type = %s WHERE `user_id` = %s', (self.USER_NORMAL,user_id,))
136 return True, user_id
137
139 import re
140 username_clean = username.lower()
141 username_clean = re.sub(r'(?:[\x00-\x1F\x7F]+|(?:\xC2[\x80-\x9F])+)', '', username_clean)
142 username_clean = re.sub(r' {2,}',' ',username_clean)
143 username_clean = username_clean.strip()
144 return username_clean
145
146 - def register_user(self, username, password, email, activate = False):
147
148 if len(username) not in self.USERNAME_LENGTH_RANGE:
149 return False,'Username not in range'
150 if len(password) not in self.PASSWORD_LENGTH_RANGE:
151 return False,'Password not in range'
152 valid = self.entropyTools.is_valid_email(email)
153 if not valid:
154 return False,'Invalid email'
155
156
157 username_clean = self.generate_username_clean(username)
158
159
160 status, err_msg = self.validate_username_string(username, username_clean)
161 if not status: return False,err_msg
162
163
164 exists = self.does_email_exist(email)
165 if exists: return False,'Email already in use'
166
167
168 status, user_id = self.__register(username, username_clean, password, email, activate)
169 if not status:
170 return False, 'Invalid username (duplicated)'
171
172 return True, user_id
173
174
175 - def __register(self, username, username_clean, password, email, activate):
176
177 email_hash = self._generate_email_hash(email)
178 password_hash = self._get_password_hash(password.encode('utf-8'))
179 time_now = int(time.time())
180
181 user_type = self.USER_INACTIVE
182 if activate: user_type = self.USER_NORMAL
183
184 registration_data = {
185 'username': username,
186 'username_clean': username_clean,
187 'user_password': password_hash,
188 'user_pass_convert': 0,
189 'user_email': email.lower(),
190 'user_email_hash': email_hash,
191 'group_id': self.REGISTERED_USERS_GROUP,
192 'user_type': user_type,
193 'user_permissions': '',
194 'user_timezone': self._get_config_value('board_timezone'),
195 'user_dateformat': self._get_config_value('default_dateformat'),
196 'user_lang': self._get_config_value('default_lang'),
197 'user_style': self._get_config_value('default_style'),
198 'user_actkey': '',
199 'user_ip': '',
200 'user_regdate': time_now,
201 'user_passchg': time_now,
202 'user_options': 895,
203 'user_inactive_reason': 0,
204 'user_inactive_time': 0,
205 'user_lastmark': time_now,
206 'user_lastvisit': 0,
207 'user_lastpost_time': 0,
208 'user_lastpage': '',
209 'user_posts': 0,
210 'user_dst': self._get_config_value('board_dst'),
211 'user_colour': '',
212 'user_occ': '',
213 'user_interests': '',
214 'user_avatar': '',
215 'user_avatar_type': 0,
216 'user_avatar_width': 0,
217 'user_avatar_height': 0,
218 'user_new_privmsg': 0,
219 'user_unread_privmsg': 0,
220 'user_last_privmsg': 0,
221 'user_message_rules': 0,
222 'user_full_folder': self.PRIVMSGS_NO_BOX,
223 'user_emailtime': 0,
224 'user_notify': 0,
225 'user_notify_pm': 1,
226 'user_notify_type': self.NOTIFY_EMAIL,
227 'user_allow_pm': 1,
228 'user_allow_viewonline': 1,
229 'user_allow_viewemail': 1,
230 'user_allow_massemail': 1,
231 'user_sig': '',
232 'user_sig_bbcode_uid': '',
233 'user_sig_bbcode_bitfield': '',
234 'user_form_salt': self._get_unique_id(),
235 }
236
237 sql = self._generate_sql('insert', self.TABLE_PREFIX+'users', registration_data)
238 self.cursor.execute(sql)
239 user_id = self.cursor.lastrowid
240
241
242 group_data = {
243 'user_id': user_id,
244 'group_id': self.REGISTERED_USERS_GROUP,
245 'user_pending': 0,
246 }
247 sql = self._generate_sql('insert', self.TABLE_PREFIX+'user_group', group_data)
248 try:
249 self.cursor.execute(sql)
250 except self.mysql_exceptions.IntegrityError, e:
251
252 return False, 1062
253
254
255 self._set_config_value('newest_user_id',user_id)
256 self._set_config_value('newest_username',username)
257 self._set_config_value('num_users',int(self._get_config_value('num_users'))+1)
258 self.cursor.execute('SELECT group_colour FROM '+self.TABLE_PREFIX+'groups WHERE group_id = %s', (group_data['group_id'],))
259 data = self.cursor.fetchone()
260 gcolor = None
261 if isinstance(data,dict):
262 if data.has_key('group_colour'):
263 gcolor = data['group_colour']
264 if gcolor: self._set_config_value('newest_user_colour',gcolor)
265
266 return True, user_id
267
268
270 self.check_connection()
271 self.check_login_data()
272
273 if not self.login_data.has_key('username'):
274 raise PermissionDenied('PermissionDenied: %s' % (_('no username specified'),))
275 elif not self.login_data.has_key('password'):
276 raise PermissionDenied('PermissionDenied: %s' % (_('no password specified'),))
277
278 if not self.login_data['password']:
279 raise PermissionDenied('PermissionDenied: %s' % (_('empty password'),))
280 elif not self.login_data['username']:
281 raise PermissionDenied('PermissionDenied: %s' % (_('empty username'),))
282
283 self.cursor.execute('SELECT * FROM '+self.TABLE_PREFIX+'users WHERE username = %s', (self.login_data['username'],))
284 data = self.cursor.fetchone()
285 if not data:
286 raise PermissionDenied('PermissionDenied: %s' % (_('user not found'),))
287
288 if data['user_pass_convert']:
289 raise PermissionDenied('PermissionDenied: %s' % (
290 _('you need to login on the website to update your password format'),
291 )
292 )
293
294 valid = self._phpbb3_check_hash(self.login_data['password'], data['user_password'])
295 if not valid:
296 raise PermissionDenied('PermissionDenied: %s' % (_('wrong password'),))
297
298 user_type = data['user_type']
299 if (user_type == self.USER_INACTIVE) or (user_type == self.USER_IGNORE):
300 raise PermissionDenied('PermissionDenied: %s' % (_('user inactive'),))
301
302 banned = self.is_user_banned(data['user_id'])
303 if banned:
304 raise PermissionDenied('PermissionDenied: %s' % (_('user banned'),))
305
306 self.login_data.update(data)
307 self.logged_in = True
308 return self.logged_in
309
314
321
329
331 self.check_connection()
332 self.check_login_data()
333 self.check_logged_in()
334
335 self.cursor.execute('SELECT user_birthday FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
336 bday = self.cursor.fetchone()
337 if not bday:
338 return None
339 elif not bday.has_key('user_birthday'):
340 return None
341 return bday['user_birthday']
342
344 self.check_connection()
345 self.check_login_data()
346 self.check_logged_in()
347
348 self.cursor.execute('SELECT username_clean FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
349 data = self.cursor.fetchone()
350 if not data:
351 return ''
352 elif not data.has_key('username_clean'):
353 return ''
354 return data['username_clean']
355
368
370 self.check_connection()
371 self.check_login_data()
372 self.check_logged_in()
373
374 self.cursor.execute('SELECT user_type FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
375 data = self.cursor.fetchone()
376 if data:
377 if data['user_type'] == self.USER_FOUNDER:
378 return True
379
380
381 groups = self.get_user_groups()
382 for group in groups:
383 if group in self.ADMIN_GROUPS:
384 return True
385
386 return False
387
400
402 self.check_connection()
403 self.check_login_data()
404 self.check_logged_in()
405
406 if self.is_moderator():
407 return False
408 elif self.is_administrator():
409 return False
410 elif self.is_developer():
411 return False
412
413 self.cursor.execute('SELECT user_type,user_id FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
414 data = self.cursor.fetchone()
415 if not data:
416 return False
417 if self.is_user_banned(data['user_id']):
418 return False
419 elif data['user_type'] in [self.USER_NORMAL]:
420 return True
421
422 return False
423
424
426 self.check_connection()
427 self.cursor.execute('SELECT ban_userid FROM '+self.TABLE_PREFIX+'banlist WHERE ban_userid = %s', (user,))
428 data = self.cursor.fetchone()
429 if data:
430 return True
431 return False
432
434 self.check_connection()
435 self.check_login_data()
436 self.check_logged_in()
437 groups = self.get_user_groups()
438 if isinstance(group,int):
439 if group in groups:
440 return True
441 elif isinstance(group,basestring):
442 self.cursor.execute('SELECT group_id FROM '+self.TABLE_PREFIX+'groups WHERE group_name = %s', (group,))
443 data = self.cursor.fetchone()
444 if not data:
445 return False
446 elif data['group_id'] in groups:
447 return True
448
449 return False
450
452 self.check_connection()
453 self.check_login_data()
454 self.check_logged_in()
455
456 self.cursor.execute('SELECT '+self.TABLE_PREFIX+'user_group.group_id,'+self.TABLE_PREFIX+'groups.group_name FROM '+self.TABLE_PREFIX+'user_group,'+self.TABLE_PREFIX+'users,'+self.TABLE_PREFIX+'groups WHERE '+self.TABLE_PREFIX+'users.user_id = %s and '+self.TABLE_PREFIX+'users.user_id = '+self.TABLE_PREFIX+'user_group.user_id and '+self.TABLE_PREFIX+'user_group.group_id = '+self.TABLE_PREFIX+'groups.group_id', (self.login_data['user_id'],))
457 data = self.cursor.fetchall()
458 mydata = {}
459 for mydict in data:
460 mydata[mydict['group_id']] = mydict['group_name']
461
462 return mydata
463
465 self.check_connection()
466 self.check_login_data()
467 self.check_logged_in()
468
469 self.cursor.execute('SELECT group_id FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
470 data = self.cursor.fetchone()
471 if data:
472 if data.has_key('group_id'):
473 return data['group_id']
474
475 return -1
476
482
489
491 self.check_connection()
492 self.check_login_data()
493 self.check_logged_in()
494
495 email_hash = self._generate_email_hash(email)
496 mydata = {
497 'user_email_hash': email_hash,
498 'user_email': email.lower(),
499 }
500
501 try:
502 sql = self._generate_sql("update",self.TABLE_PREFIX+'users', mydata, 'user_id = %s' % (self.login_data['user_id'],))
503 self.cursor.execute(sql)
504 return True
505 except Exception:
506 return False
507
509 self.check_connection()
510 self.check_login_data()
511 self.check_logged_in()
512
513 mydata = {
514 'user_password': password_hash,
515 }
516
517 try:
518 sql = self._generate_sql("update",self.TABLE_PREFIX+'users', mydata, 'user_id = %s' % (self.login_data['user_id'],))
519 self.cursor.execute(sql)
520 return True
521 except Exception:
522 return False
523
525 self.check_connection()
526 self.check_login_data()
527 self.check_logged_in()
528 self.cursor.execute('SELECT user_email FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (self.login_data['user_id'],))
529 data = self.cursor.fetchone()
530 if not data:
531 return ''
532 elif not data.has_key('user_email'):
533 return ''
534 return data['user_email']
535
537 self.check_connection()
538 self.check_login_data()
539 self.check_logged_in()
540
541
542 valid_params = [
543 "user_icq","user_yim","user_msnm",
544 "user_jabber","user_website","user_from",
545 "user_interests","user_occ","user_birthday",
546 "user_sig"
547 ]
548
549 my_params = {}
550 for param in valid_params:
551 d = profile_data.get(param)
552 if d == None: continue
553 my_params[param] = d
554
555 if not my_params:
556 return False,'no parameters'
557
558
559
560 b_day = my_params.get('user_birthday')
561 if isinstance(b_day,basestring):
562 import re
563 myre = re.compile("(0[1-9]|[12][0-9]|3[01])[-](0[1-9]|1[012])[-](19|20)\d\d")
564 if not myre.match(b_day):
565 del my_params['user_birthday']
566
567 try:
568 sql = self._generate_sql("update",self.TABLE_PREFIX+'users', my_params, 'user_id = %s' % (self.login_data['user_id'],))
569 self.cursor.execute(sql)
570 return True, None
571 except Exception, e:
572 return False, unicode(e)
573
574
576 self.cursor.execute('UPDATE '+self.TABLE_PREFIX+'config SET config_value = %s WHERE config_name = %s',(data,config_name,))
577
579 self.check_connection()
580 self.cursor.execute('SELECT config_value FROM '+self.TABLE_PREFIX+'config WHERE config_name = %s',(config_name,))
581 myconfig = self.cursor.fetchone()
582 if isinstance(myconfig,dict):
583 if myconfig.has_key('config_value'):
584 return myconfig['config_value']
585 return None
586
588 self.check_connection()
589 time_now = int(time.time())
590 autologin = self._get_config_value("allow_autologin")
591 self.cursor.execute('SELECT user_allow_viewonline FROM '+self.TABLE_PREFIX+'users WHERE user_id = %s', (user_id,))
592 myuserprefs = self.cursor.fetchone()
593 session_admin = 0
594 session_data = {
595 'session_id': None,
596 'session_user_id': user_id,
597 'session_last_visit': time_now,
598 'session_start': time_now,
599 'session_time': time_now,
600 'session_ip': ip_address,
601 'session_browser': self.USER_AGENT,
602 'session_forwarded_for': '',
603 'session_page': 'index.php',
604 'session_viewonline': myuserprefs['user_allow_viewonline'],
605 'session_autologin': autologin,
606 'session_admin': session_admin,
607 'session_forum_id': 0,
608 }
609 import hashlib
610 m = hashlib.md5()
611 m.update(str(user_id)+str(time_now)+str(self.USER_AGENT)+str(ip_address)+str(autologin)+str(myuserprefs['user_allow_viewonline']))
612 session_data['session_id'] = m.hexdigest()
613
614 self.cursor.execute('SELECT * FROM '+self.TABLE_PREFIX+'sessions WHERE session_user_id = %s', (user_id,))
615 mydata = self.cursor.fetchone()
616 do_update = False
617 if mydata:
618 do_update = True
619
620 session_data['session_id'] = mydata['session_id']
621 session_data['session_viewonline'] = mydata['session_viewonline']
622 session_data['session_autologin'] = mydata['session_autologin']
623 session_data['session_forwarded_for'] = mydata['session_forwarded_for']
624 session_data['session_forum_id'] = mydata['session_forum_id']
625 session_data['session_page'] = mydata['session_page']
626 session_data['session_browser'] = mydata['session_browser']
627 session_data['session_admin'] = mydata['session_admin']
628
629 if do_update:
630 where = "session_id = '%s'" % (session_data['session_id'],)
631 del session_data['session_id']
632 sql = self._generate_sql('update', self.TABLE_PREFIX+'sessions', session_data, where)
633 else:
634 sql = self._generate_sql('insert', self.TABLE_PREFIX+'sessions', session_data)
635 if sql:
636 self.cursor.execute(sql)
637 self.dbconn.commit()
638
639
641 self.check_connection()
642 self.cursor.execute('SELECT ban_ip FROM '+self.TABLE_PREFIX+'banlist WHERE ban_ip = %s', (ip,))
643 data = self.cursor.fetchone()
644 if data:
645 return True
646 return False
647
649 import hashlib
650 m = hashlib.md5()
651 rnd = str(abs(hash(os.urandom(1))))
652 m.update(rnd)
653 x = m.hexdigest()[:-16]
654 del m
655 return x
656
658 myrand = 0
659 low_n = 100000
660 high_n = 999999
661 while (myrand < low_n) or (myrand > high_n):
662 try:
663 myrand = hash(os.urandom(1))%high_n
664 except NotImplementedError:
665 random.seed()
666 myrand = random.randint(low_n,high_n)
667 return myrand
668
670
671
672 myrandom = str(self._get_random_number())
673
674 myhash = self._hash_crypt_private(password, self._hash_gensalt_private(myrandom))
675
676 if len(myhash) == 34:
677 return myhash
678
679 import hashlib
680 m = hashlib.md5()
681 m.update(myhash)
682 return m.hexdigest()
683
684
686
687 if (iteration_count_log2 < 4) or (iteration_count_log2 > 31):
688 iteration_count_log2 = 8
689
690 myoutput = '$H$'
691 myoutput += self.itoa64[min(iteration_count_log2 + 5,30)]
692 myoutput += self._hash_encode64(myinput, 6)
693
694 return myoutput
695
697
698 myoutput = '*'
699
700 if setting[:3] != '$H$':
701 return myoutput
702
703 count_log2 = self.itoa64.find(setting[3])
704 if count_log2 == -1: count_log2 = 0
705
706 if (count_log2 < 7) or (count_log2 > 30):
707 return myoutput
708
709 count = 1 << count_log2
710 salt = setting[4:12]
711
712 if len(salt) != 8:
713 return myoutput
714
715 import hashlib
716 m = hashlib.md5()
717 m.update(salt+password)
718 myhash = m.digest()
719 while count:
720 m = hashlib.md5()
721 m.update(myhash+password)
722 myhash = m.digest()
723 count -= 1
724
725 myoutput = setting[:12]
726 myoutput += self._hash_encode64(myhash, 16)
727
728 return myoutput
729
731
732 output = ''
733 i = 0
734 while i < count:
735
736 value = ord(myinput[i])
737 i += 1
738 output += self.itoa64[value & 0x3f]
739 if i < count:
740 value |= ord(myinput[i]) << 8
741
742 output += self.itoa64[(value >> 6) & 0x3f]
743
744 if i >= count:
745 break
746 i += 1
747
748 if i < count:
749 value |= ord(myinput[i]) << 16
750
751 output += self.itoa64[(value >> 12) & 0x3f]
752
753 if (i >= count):
754 break
755 i += 1
756
757 output += self.itoa64[(value >> 18) & 0x3f]
758
759 return output
760
762
763 if len(myhash) == 34:
764 return self._hash_crypt_private(password, myhash) == myhash
765
766 import hashlib
767 m = hashlib.md5()
768 m.update(password)
769 rhash = m.hexdigest()
770 return rhash == myhash
771