/kai/model/human.py

https://bitbucket.org/bbangert/kai/ · Python · 133 lines · 121 code · 9 blank · 3 comment · 6 complexity · c57f7ad7e81be757954a34702dda1dbb MD5 · raw file

  1. import os
  2. import md5
  3. import sha
  4. from datetime import datetime
  5. import pylons
  6. from couchdb.schema import DateTimeField, Document, TextField, ListField, View
  7. from kai.model.paste import Paste
  8. from kai.model.traceback import Traceback
  9. class Human(Document):
  10. """Represents a human user"""
  11. type = TextField(default='Human')
  12. displayname = TextField()
  13. email = TextField()
  14. timezone = TextField()
  15. password = TextField()
  16. created = DateTimeField(default=datetime.utcnow)
  17. last_login = DateTimeField(default=datetime.utcnow)
  18. blog = TextField()
  19. session_id = TextField()
  20. email_token = TextField()
  21. password_token = TextField()
  22. email_token_issue = DateTimeField()
  23. password_token_issue = DateTimeField()
  24. openids = ListField(TextField())
  25. groups = ListField(TextField())
  26. by_openid = View('human', '''
  27. function(doc) {
  28. if (doc.type == 'Human' && doc.openids) {
  29. for (var idx in doc.openids) {
  30. emit(doc.openids[idx], null);
  31. }
  32. }
  33. }''', include_docs=True)
  34. by_displayname = View('human', '''
  35. function(doc) {
  36. if (doc.type == 'Human') {
  37. emit(doc.displayname, null);
  38. }
  39. }''', include_docs=True)
  40. by_email = View('human', '''
  41. function(doc) {
  42. if (doc.type == 'Human') {
  43. emit(doc.email, null);
  44. }
  45. }''', include_docs=True)
  46. by_email_token = View('human', '''
  47. function(doc) {
  48. if (doc.type == 'Human' && doc.email_token) {
  49. emit(doc.email_token, null);
  50. }
  51. }''', include_docs=True)
  52. by_password_token = View('human', '''
  53. function(doc) {
  54. if (doc.type == 'Human' && doc.password_token) {
  55. emit(doc.password_token, null);
  56. }
  57. }''', include_docs=True)
  58. @staticmethod
  59. def hash_password(plain_text):
  60. """Returns a crypted/salted password field
  61. The salt is stored in front of the password, for per user
  62. salts.
  63. """
  64. if isinstance(plain_text, unicode):
  65. plain_text = plain_text.encode('utf-8')
  66. password_salt = sha.new(os.urandom(60)).hexdigest()
  67. crypt = sha.new(plain_text + password_salt).hexdigest()
  68. return password_salt + crypt
  69. def verify_password(self, plain_text):
  70. """Verify a plain text string is the users password"""
  71. if isinstance(plain_text, unicode):
  72. plain_text = plain_text.encode('utf-8')
  73. # Some users don't have passwords, like OpenID users, so they
  74. # can't use a password to login
  75. if not self.password:
  76. return False
  77. password_salt = self.password[:40]
  78. crypt_pass = sha.new(plain_text + password_salt).hexdigest()
  79. if crypt_pass == self.password[40:]:
  80. return True
  81. else:
  82. return False
  83. def email_hash(self):
  84. return md5.md5(self.email).hexdigest()
  85. def generate_token(self):
  86. """Generate's a token for use either for forgot password or
  87. email verification"""
  88. return sha.new(os.urandom(60)).hexdigest()
  89. def valid_password_token(self):
  90. diff = datetime.now() - self.password_token_issue
  91. token_lifespan = pylons.config['sso.password_token_lifespan']
  92. if diff.days < 1 and diff.seconds < token_lifespan:
  93. return True
  94. else:
  95. return False
  96. def in_group(self, group):
  97. if group in list(self.groups):
  98. return True
  99. else:
  100. return False
  101. def process_login(self):
  102. session = pylons.session._current_obj()
  103. session['logged_in'] = True
  104. session['displayname'] = self.displayname
  105. session['user_id'] = self.id
  106. session.save()
  107. self.session_id = session.id
  108. self.store(pylons.tmpl_context.db)
  109. Paste.associate_pasties(self)
  110. Traceback.associate_tracebacks(self)