anonymous group photoblog software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

268 lines
7.5 KiB

  1. '''Staff management.'''
  2. import time
  3. from sqlalchemy.sql import select, func
  4. import strings
  5. import model
  6. import misc
  7. import config, config_defaults
  8. from util import WakaError, local
  9. # TODO: Synchronized cache of staff personnel objects.
  10. # _staff = {}
  11. # Staff class types.
  12. ADMIN = 'admin'
  13. GLOBAL_MOD = 'globmod'
  14. MODERATOR = 'mod'
  15. CLASSES = (ADMIN, GLOBAL_MOD, MODERATOR)
  16. # Login cookie expire times (in seconds).
  17. SAVED_LOGIN_EXPIRE = 365 * 24 * 3600
  18. UNSAVED_LOGIN_EXPIRE = 3600
  19. # Staff Accounts and Logins
  20. class LoginData(object):
  21. '''Class for interfacing with prefetched login data.'''
  22. def __init__(self, user, addr):
  23. self.addr = addr
  24. self.crypt = crypt_pass(user.password, addr)
  25. self.username = user.username
  26. self.cookie = ','.join((self.username, self.crypt))
  27. def make_cookie(self, save_login=False):
  28. expires = time.time() + (SAVED_LOGIN_EXPIRE if save_login
  29. else UNSAVED_LOGIN_EXPIRE)
  30. misc.make_cookies(wakaadmin=self.cookie, httponly=1, expires=expires)
  31. # the following isn't http only
  32. wakaadminsave = '1' if save_login else ''
  33. misc.make_cookies(wakaadminsave=wakaadminsave, expires=expires)
  34. # Class for representing staff
  35. class StaffMember(object):
  36. '''A staff object for acquiring and updating personnel account
  37. information. Use the class factory method to initialize:
  38. >>> StaffMember.get('SirDerpDeeDoo')
  39. To create new staff accounts, use the add_staff() function instead.'''
  40. def __init__(self, username):
  41. session = model.Session()
  42. table = model.account
  43. sql = table.select().where(table.c.username == username)
  44. row = session.execute(sql).fetchone()
  45. self._table = table
  46. if row is None:
  47. raise LoginError('Staff not found.')
  48. # Grab parameters from database. Password is pre-encrypted.
  49. self.username = username
  50. self._password = row.password
  51. self._class = row.account
  52. self._reign = row.reign.split(',')
  53. self._disabled = row.disabled
  54. self._update_dict = {}
  55. # Not logged in yet.
  56. self._login_data = None
  57. def _update_db(self, **kwargs):
  58. self._update_dict.update(kwargs)
  59. @property
  60. def password(self):
  61. return self._password
  62. @password.setter
  63. def password(self, new):
  64. if len(new) < 8:
  65. raise WakaError('Passwords should be at least eight characters!')
  66. new = misc.hide_critical_data(new, config.SECRET)
  67. self._update_db(password=new)
  68. self._password = new
  69. @property
  70. def reign(self):
  71. return self._reign
  72. @reign.setter
  73. def reign(self, board_list):
  74. reign_str = ','.join(board_list)
  75. self._update_db(reign=reign_str)
  76. self._reign = board_list
  77. @property
  78. def account(self):
  79. return self._class
  80. @account.setter
  81. def account(self, new):
  82. if new in CLASSES:
  83. self._update_db(account=new)
  84. self._class = new
  85. else:
  86. raise WakaError('Invalid class name %s' % new)
  87. @property
  88. def login_data(self):
  89. return self._login_data
  90. @property
  91. def disabled(self):
  92. return self._disabled
  93. @disabled.setter
  94. def disabled(self, disable):
  95. self._disabled = bool(disable)
  96. if disable:
  97. disable = 1
  98. else:
  99. disable = 0
  100. self._update_db(disabled=disable)
  101. def login_host(self, ip):
  102. login_data = LoginData(self, ip)
  103. self._login_data = login_data
  104. return login_data
  105. def logout_user(self):
  106. self._login_data = None
  107. def flush_db(self):
  108. session = model.Session()
  109. table = self._table
  110. if len(self._update_dict) > 0:
  111. db_update = table.update()\
  112. .where(table.c.username == self.username)\
  113. .values(**self._update_dict)
  114. session.execute(db_update)
  115. @classmethod
  116. def get(cls, username):
  117. # if username in _staff:
  118. # return _staff[username]
  119. staff_obj = cls(username)
  120. # _staff[username] = staff_obj
  121. return staff_obj
  122. @classmethod
  123. def get_from_cookie(cls, cookie_str):
  124. if not cookie_str or not cookie_str.count(','):
  125. raise LoginError('Cookie data missing.')
  126. remote = local.environ['REMOTE_ADDR']
  127. (username, crypt) = cookie_str.split(',')
  128. staff_entry = cls.get(username)
  129. cache = staff_entry.login_data
  130. if cache and cache.addr == remote:
  131. # The host is already logged in.
  132. pass
  133. elif crypt != crypt_pass(staff_entry.password, remote):
  134. raise LoginError(strings.WRONGPASS)
  135. elif staff_entry.disabled:
  136. raise LoginError('You have been disabled.')
  137. else:
  138. # NOTE: This will overwrite the current network address login.
  139. staff_entry.login_host(remote)
  140. # Needs save_login parameter. Only useful once sessions and
  141. # user caches are implemented.
  142. # staff_entry.login_data.make_cookie()
  143. return staff_entry
  144. def check_access(self, board_name):
  145. if self.account == MODERATOR and board_name not in self.reign:
  146. raise WakaError('Access to this board (%s) denied.' % board_name)
  147. def add_staff(username, pt_password, account, reign):
  148. if not username:
  149. raise WakaError('A username is necessary.')
  150. if not pt_password:
  151. raise WakaError('A password is necessary.')
  152. if len(pt_password) < 8:
  153. raise WakaError('Passwords should be eight characters minimum.')
  154. if len(reign) == 0 and account == MODERATOR:
  155. raise WakaError('Board reign not specified for moderator account.')
  156. # Check whether the user exists already.
  157. try:
  158. StaffMember.get(username)
  159. except LoginError:
  160. # User not found. Good.
  161. pass
  162. else:
  163. raise WakaError('Username exists.')
  164. session = model.Session()
  165. table = model.account
  166. password = misc.hide_critical_data(pt_password, config.SECRET)
  167. reign_str = ','.join(reign)
  168. sql = table.insert().values(username=username, password=password,
  169. account=account, reign=reign_str,
  170. disabled=0)
  171. session.execute(sql)
  172. def del_staff(username):
  173. session = model.Session()
  174. table = model.account
  175. sql = table.delete(table.c.username == username)
  176. session.execute(sql)
  177. # try:
  178. # del _staff[username]
  179. # except AttributeError:
  180. # pass
  181. def edit_staff(username, clear_pass=None, new_class=None, reign=None,
  182. disable=None):
  183. staff_obj = StaffMember.get(username)
  184. if clear_pass:
  185. staff_obj.password = clear_pass
  186. if new_class and new_class in CLASSES:
  187. staff_obj.account = new_class
  188. if reign:
  189. staff_obj.reign = reign
  190. if disable is not None:
  191. staff_obj.disabled = disable
  192. staff_obj.flush_db()
  193. def staff_exists():
  194. session = model.Session()
  195. table = model.account
  196. sql = select([func.count()], table)
  197. row = session.execute(sql).fetchone()
  198. return row[0] != 0
  199. def check_password(cookie_str):
  200. return StaffMember.get_from_cookie(cookie_str)
  201. def crypt_pass(cleartext, remote):
  202. return misc.hide_critical_data(','.join((cleartext, remote)),
  203. config.SECRET)
  204. class LoginError(WakaError):
  205. '''WakaError subclass to discriminate login-related exceptions (bad
  206. password/username combinations).'''
  207. pass