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.
 
 
 
 
 
 

925 lines
34 KiB

  1. '''Dynamic panels for administrative work.'''
  2. import time
  3. import os
  4. import re
  5. from datetime import datetime
  6. from sqlalchemy.sql import or_, and_, not_, exists, select, func
  7. import staff_tasks
  8. import interboard
  9. import strings
  10. import staff
  11. import model
  12. import str_format
  13. import misc
  14. from util import WakaError, local, make_http_forward
  15. from template import Template
  16. import config
  17. # Destination codes.
  18. HOME_PANEL = 'mpanel'
  19. BAN_PANEL = 'banspanel'
  20. SPAM_PANEL = 'spampanel'
  21. REPORTS_PANEL = 'reportspanel'
  22. STAFF_PANEL = 'staffpanel'
  23. TRASH_PANEL = 'trashpanel'
  24. POST_SEARCH_PANEL = 'postsearchpanel'
  25. SQL_PANEL = 'sqlpanel'
  26. PROXY_PANEL = 'proxypanel'
  27. SECURITY_PANEL = 'securitypanel'
  28. STAFF_ACTIVITY_PANEL = 'stafflog'
  29. BAN_POPUP = 'banpopup'
  30. BAN_EDIT_POPUP = 'baneditwindow'
  31. DEL_STAFF_CONFIRM = 'delstaffwindow'
  32. DISABLE_STAFF_CONFIRM = 'disablestaffwindow'
  33. ENABLE_STAFF_CONFIRM = 'enablestaffwindow'
  34. EDIT_STAFF_CONFIRM = 'editstaffwindow'
  35. ADD_STAFF_CONFIRM = 'addstaffwindow'
  36. DELETE_ALL_CONFIRM = 'deleteallwindow'
  37. INTERFACE_MAPPING = {}
  38. def interface_for(dest_code):
  39. def decorator(f):
  40. INTERFACE_MAPPING[dest_code] = f
  41. return f
  42. return decorator
  43. def admin_only(f):
  44. '''StaffInterface templating function decorator: Indicate and enforce
  45. admin-only pages.'''
  46. def ret_func(*args, **kwargs):
  47. self = args[0]
  48. if self.user.account != staff.ADMIN:
  49. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  50. f(*args, **kwargs)
  51. return ret_func
  52. def global_only(f):
  53. '''StaffInterface templating function decorator: Indicate and enforce
  54. pages for global staff people only.'''
  55. def ret_func(*args, **kwargs):
  56. self = args[0]
  57. if self.user.account == staff.MODERATOR:
  58. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  59. f(*args, **kwargs)
  60. return ret_func
  61. class StaffInterface(Template):
  62. '''Specialized template.Template class for dynamic administrative
  63. pages served by Wakarimasen.'''
  64. def __init__(self, cookie, board=None, dest=None, page=None,
  65. perpage=50, **kwargs):
  66. try:
  67. self.user = staff.StaffMember.get_from_cookie(cookie)
  68. except staff.LoginError:
  69. Template.__init__(self, 'admin_login_template', login_task=dest)
  70. return
  71. if not dest:
  72. dest = HOME_PANEL
  73. self.admin = cookie
  74. # TODO: Check if mod is banned.
  75. if not page:
  76. if dest in (HOME_PANEL, TRASH_PANEL):
  77. # Adjust for different pagination scheme. (Blame Wakaba.)
  78. page = 0
  79. else:
  80. page = 1
  81. if not str(perpage).isdigit():
  82. perpage = 50
  83. # The page attribute is not always a pure integer (thread pages).
  84. if str(page).isdigit():
  85. page = int(page)
  86. self.page = page
  87. self.perpage = int(perpage)
  88. self.board = local.environ['waka.board']
  89. if dest not in INTERFACE_MAPPING:
  90. dest = HOME_PANEL
  91. INTERFACE_MAPPING[dest](self, **kwargs)
  92. # Convert user reign list into a list of dictionaries, for
  93. # templating.
  94. reign = []
  95. if self.user.account == staff.MODERATOR:
  96. reign = [{'board_entry' : entry} for entry in self.user.reign]
  97. else:
  98. if self.board:
  99. reign = interboard.get_all_boards\
  100. (check_board_name=self.board.name)
  101. else:
  102. reign = interboard.get_all_boards()
  103. # Set global form variables.
  104. Template.update_parameters(self, username=self.user.username,
  105. type=self.user.account,
  106. admin=cookie,
  107. boards_select=reign,
  108. boards=reign,
  109. page=self.page,
  110. perpage=self.perpage)
  111. @interface_for(HOME_PANEL)
  112. def make_admin_home_panel(self):
  113. # Update perpage attribute: it is determined here by board options.
  114. board = self.board
  115. self.perpage = board.options['IMAGES_PER_PAGE']
  116. # Get reports.
  117. reports = board.get_local_reports()
  118. # Send to Template initializer.
  119. kwargs = {}
  120. threads = []
  121. if str(self.page).startswith('t'):
  122. self.page = self.page[1:]
  123. posts = board.get_thread_posts(self.page)
  124. threads.append({'posts' : posts})
  125. kwargs = {'lockedthread' : posts[0].locked,
  126. 'parent' : self.page,
  127. 'thread' : self.page}
  128. else:
  129. # Grab count of all threads.
  130. table = board.table
  131. session = model.Session()
  132. sql = select([func.count()], table.c.parent == 0)
  133. thread_count = session.execute(sql).fetchone()[0]
  134. total = (thread_count + self.perpage - 1) / self.perpage
  135. if total <= self.page and total > 0:
  136. # Set page number to last page if exceeding total.
  137. # Pages are 0-indexed.
  138. self.page = total - 1
  139. # Get partial board posts.
  140. pagethreads = board.get_some_threads(self.page)
  141. (pages, prevpage, nextpage)\
  142. = board.get_board_page_data(self.page, total,
  143. admin_page='mpanel')
  144. threads = board.parse_page_threads(pagethreads)
  145. kwargs = {'pages' : pages,
  146. 'prevpage' : prevpage,
  147. 'nextpage' : nextpage}
  148. Template.__init__(self, 'post_panel_template',
  149. postform=board.options['ALLOW_TEXTONLY'] or
  150. board.options['ALLOW_IMAGES'],
  151. image_inp=board.options['ALLOW_IMAGES'],
  152. threads=threads,
  153. reportedposts=reports,
  154. **kwargs)
  155. @admin_only
  156. @interface_for(STAFF_PANEL)
  157. def make_admin_staff_panel(self):
  158. session = model.Session()
  159. table = model.account
  160. sql = table.select().order_by(table.c.account.asc(),
  161. table.c.username.asc())
  162. query = session.execute(sql)
  163. users = [dict(row.items()) for row in query]
  164. rowtype = 1
  165. for row in users:
  166. # Alternate between values 1 and 2.
  167. rowtype ^= 0x3
  168. row['rowtype'] = rowtype
  169. # Get latest action from DB.
  170. action_table = model.activity
  171. action_grab_sql = action_table.select()\
  172. .where(action_table.c.username == row['username'])\
  173. .order_by(action_table.c.date.desc())
  174. last_action = session.execute(action_grab_sql).fetchone()
  175. # Copy to row.
  176. if last_action:
  177. row['action'] = last_action['action']
  178. row['actiondate'] = last_action['date']
  179. else:
  180. row['action'] = None
  181. row['actiondate'] = None
  182. Template.__init__(self, 'staff_management', users=users)
  183. @admin_only
  184. @interface_for(STAFF_ACTIVITY_PANEL)
  185. def make_admin_activity_panel(self, view='', user_to_view=None,
  186. action_to_view=None, ip_to_view=None,
  187. post_to_view=None, sortby_name='date',
  188. sortby_dir='desc'):
  189. board = self.board
  190. template_view = 'staff_activity_unfiltered'
  191. action_name = action_content = ''
  192. table = model.activity
  193. account_table = model.account
  194. dual_table_select = [account_table.c.username,
  195. account_table.c.account,
  196. account_table.c.disabled,
  197. table.c.action,
  198. table.c.info,
  199. table.c.date,
  200. table.c.ip]
  201. sql = select(dual_table_select,
  202. from_obj=[table.join(account_table,
  203. table.c.username == model.account.c.username)])
  204. rooturl_args = dict(task='stafflog', board=board.name,
  205. view=view, sortby=sortby_name, order=sortby_dir, _amp=True)
  206. if view == 'user':
  207. if not user_to_view:
  208. raise WakaError('Please select a user to view.')
  209. template_view = 'staff_activity_by_user'
  210. sql = sql.where(table.c.username == user_to_view)
  211. rooturl_args['usertoview'] = user_to_view
  212. elif view == 'action':
  213. if not action_to_view:
  214. raise WakaError('Please select an action to view.')
  215. template_view = 'staff_activity_by_actions'
  216. (action_name, action_content) \
  217. = staff_tasks.get_action_name(action_to_view, 1)
  218. sql = sql.where(table.c.action == action_to_view)
  219. rooturl_args['actiontoview'] = action_to_view
  220. elif view == 'ip':
  221. if not ip_to_view:
  222. raise WakaError('Please specify an IP address to view.')
  223. template_view = 'staff_activity_by_ip_address'
  224. sql = sql.where(table.c.info.like('%' + ip_to_view + '%'))
  225. rooturl_args['iptoview'] = ip_to_view
  226. elif view == 'post':
  227. if not post_to_view:
  228. raise WakaError('Post key missing.')
  229. template_view = 'staff_activity_by_post'
  230. sql = sql.where(table.c.info.like('%' + post_to_view + '%'))
  231. rooturl_args['posttoview'] = post_to_view
  232. rooturl = misc.make_script_url(**rooturl_args)
  233. # Acquire staff info.
  234. session = model.Session()
  235. staff_get = model.account.select()
  236. staff = session.execute(staff_get).fetchall()
  237. # Establish list of hidden inputs.
  238. inputs = [
  239. {'name' : 'actiontoview', 'value' : action_to_view},
  240. {'name' : 'task', 'value' : 'stafflog'},
  241. {'name' : 'posttoview', 'value' : post_to_view},
  242. {'name' : 'usertoview', 'value' : user_to_view},
  243. {'name' : 'iptoview', 'value' : ip_to_view},
  244. {'name' : 'order', 'value' : sortby_dir},
  245. {'name' : 'sortby', 'value' : sortby_name},
  246. {'name' : 'view', 'value': view}
  247. ]
  248. if self.board:
  249. inputs.append({'name' : 'board', 'value' : self.board.name})
  250. # Apply sorting.
  251. if sortby_name and hasattr(table.c, sortby_name):
  252. order_col = getattr(table.c, sortby_name)
  253. if sortby_dir.lower() == 'asc':
  254. sort_spec = order_col.asc()
  255. else:
  256. sort_spec = order_col.desc()
  257. sql = sql.order_by(sort_spec)
  258. res = model.Page(sql, self.page, self.perpage)
  259. Template.__init__(self, template_view,
  260. user_to_view=user_to_view,
  261. entries=res.rows,
  262. staff=staff,
  263. rowcount=res.total_entries,
  264. numberofpages=res.total_pages,
  265. view=view,
  266. order=sortby_dir,
  267. action_name=action_name,
  268. content_name=action_content,
  269. sortby=sortby_name,
  270. number_of_pages=res.total_pages,
  271. rooturl=rooturl,
  272. inputs=inputs)
  273. @interface_for(BAN_PANEL)
  274. def make_admin_ban_panel(self, ip=''):
  275. session = model.Session()
  276. table = model.admin
  277. sql = select([model.activity.c.username,
  278. table.c.num,
  279. table.c.type,
  280. table.c.comment,
  281. table.c.ival1,
  282. table.c.ival2,
  283. table.c.sval1,
  284. table.c.total,
  285. table.c.expiration],
  286. from_obj=[table.outerjoin(model.activity,
  287. and_(table.c.num == model.activity.c.admin_id,
  288. table.c.type == model.activity.c.action))])\
  289. .order_by(table.c.type.asc(), table.c.num.asc())
  290. # TODO: We should be paginating, but the page needs to be
  291. # adjusted first.
  292. # res = model.Page(sql, self.page, self.perpage)
  293. query = session.execute(sql)
  294. bans = [dict(row.items()) for row in query]
  295. rowtype = 1
  296. prevtype = ''
  297. for row in bans:
  298. prevtype = row
  299. if prevtype != row['type']:
  300. row['divider'] = 1
  301. # Alternate between values 1 and 2.
  302. rowtype ^= 0x3
  303. row['rowtype'] = rowtype
  304. if row['expiration']:
  305. row['expirehuman'] = misc.make_date(row['expiration'])
  306. else:
  307. row['expirehuman'] = 'Never'
  308. if row['total'] == 'yes':
  309. row['browsingban'] = 'No'
  310. else:
  311. row['browsingban'] = 'Yes'
  312. Template.__init__(self, 'ban_panel_template', bans=bans, ip=ip)
  313. @interface_for(BAN_EDIT_POPUP)
  314. def make_ban_edit_popup(self, num):
  315. session = model.Session()
  316. table = model.admin
  317. sql = table.select().where(table.c.num == num)
  318. row = session.execute(sql).fetchone()
  319. if row.expiration:
  320. expiration = datetime.utcfromtimestamp(row.expiration)
  321. else:
  322. expiration = datetime.utcnow()
  323. Template.__init__(self, 'edit_window', hash=[row],
  324. year=expiration.year,
  325. month=expiration.month,
  326. day=expiration.day,
  327. hour=expiration.hour,
  328. min=expiration.minute,
  329. sec=expiration.second)
  330. @interface_for(BAN_POPUP)
  331. def make_ban_popup(self, ip, delete=''):
  332. Template.__init__(self, 'ban_window', ip=ip, delete=delete)
  333. @global_only
  334. @interface_for(SPAM_PANEL)
  335. def make_admin_spam_panel(self):
  336. # TODO: Paginate this, too.
  337. spam_list = []
  338. for filename in config.SPAM_FILES:
  339. with open(filename, 'r') as f:
  340. spam_list.extend([str_format.clean_string(l) for l in f])
  341. spamlines = len(spam_list)
  342. spam = ''.join(spam_list)
  343. Template.__init__(self, 'spam_panel_template', spam=spam,
  344. spamlines=spamlines)
  345. @interface_for(REPORTS_PANEL)
  346. def make_admin_report_panel(self, sortby='date', order='desc'):
  347. sortby_type = sortby
  348. sortby_dir = order
  349. table = model.report
  350. sql = table.select()
  351. # Enforce limited moderation reign.
  352. if self.user.account == staff.MODERATOR:
  353. sql = sql.where(table.c.board.in_(self.user.reign))
  354. # Determine order.
  355. if sortby_type in ('board', 'postnum', 'date'):
  356. try:
  357. column = getattr(table.c, sortby_type)
  358. except AttributeError:
  359. raise WakaError('Sort-by column is absent from table.')
  360. sort = column.desc
  361. if sortby_dir == 'asc':
  362. sort = column.asc
  363. sql = sql.order_by(sort(), table.c.date.desc())
  364. else:
  365. sql = sql.order_by(table.c.date.desc())
  366. # Paginate.
  367. res = model.Page(sql, self.page, self.perpage)
  368. # Hidden input fields.
  369. inputs = [{'name' : 'task', 'value' : 'reports'},
  370. {'name' : 'order', 'value' : sortby_dir},
  371. {'name' : 'sortby', 'value' : sortby_type}]
  372. rooturl = misc.make_script_url(task='reports', sortby=sortby_type,
  373. order=sortby_dir, _amp=True)
  374. Template.__init__(self, 'report_panel_template',
  375. reports=res.rows,
  376. sortby=sortby_type,
  377. order=sortby_dir,
  378. number_of_pages=res.total_pages,
  379. rowcount=res.total_entries,
  380. inputs=inputs,
  381. rooturl=rooturl)
  382. # NOTE: For this and other make_*_window functions, I took out
  383. # the sanity checks and instead delegated them to the non-interface
  384. # functions, the idea being that they should not come up
  385. # under normal usage of the interface (without compromising security).
  386. @interface_for(DEL_STAFF_CONFIRM)
  387. def make_del_staff_window(self, username):
  388. Template.__init__(self, 'staff_delete_template',
  389. user_to_delete=username)
  390. @interface_for(DISABLE_STAFF_CONFIRM)
  391. def make_disable_staff_window(self, username):
  392. Template.__init__(self, 'staff_disable_template',
  393. user_to_disable=username)
  394. @interface_for(ENABLE_STAFF_CONFIRM)
  395. def make_enable_staff_window(self, username):
  396. Template.__init__(self, 'staff_enable_template',
  397. user_to_enable=username)
  398. @interface_for(EDIT_STAFF_CONFIRM)
  399. def make_edit_staff_window(self, username):
  400. boards = interboard.get_all_boards()
  401. edited_user = staff.StaffMember.get(username)
  402. for board in boards:
  403. if board in edited_user.reign:
  404. board['underpower'] = True
  405. Template.__init__(self, 'staff_edit_template',
  406. user_to_edit=username,
  407. boards=boards)
  408. @interface_for(TRASH_PANEL)
  409. def make_admin_trash_panel(self):
  410. board = self.board
  411. table = model.backup
  412. session = model.Session()
  413. template_kwargs = {}
  414. # List of current threads *and* orphaned posts.
  415. threads = []
  416. if str(self.page).startswith('t'):
  417. self.page = self.page[1:]
  418. sql = table.select().where(and_(or_(table.c.postnum == self.page,
  419. table.c.parent == self.page),
  420. table.c.board_name == board.name))\
  421. .order_by(table.c.timestampofarchival.desc(),
  422. table.c.postnum.asc())
  423. thread = [dict(x.items()) for x in session.execute(sql).fetchall()]
  424. if not thread:
  425. raise WakaError('Thread not found.')
  426. threads = [{'posts' : thread}]
  427. template_kwargs = {
  428. 'postform' : board.options['ALLOW_TEXTONLY'] or
  429. board.options['ALLOW_IMAGES'],
  430. 'image_inp' : board.options['ALLOW_IMAGES'],
  431. 'textonly_inp' : 0,
  432. 'threads' : threads,
  433. 'thread' : self.page,
  434. 'parent' : self.page
  435. }
  436. elif config.POST_BACKUP:
  437. max_res = board.options['IMAGES_PER_PAGE']
  438. sqlcond = and_(or_(table.c.parent == 0,
  439. and_(table.c.parent > 0, not_(exists([table.c.num],
  440. table.c.parent == table.c.postnum)))),
  441. table.c.board_name == board.name)
  442. # Acquire the number of full threads *and* orphaned posts.
  443. sql = select([func.count()], sqlcond, table)\
  444. .order_by(table.c.timestampofarchival.desc(),
  445. table.c.postnum.asc())
  446. thread_ct = session.execute(sql).fetchone()[0]
  447. total = int(thread_ct + max_res - 1) / max_res
  448. offset = self.page * max_res
  449. (pages, prevpage, nextpage) \
  450. = board.get_board_page_data(self.page, total,
  451. admin_page='postbackups')
  452. last_page = len(pages) - 1
  453. if self.page > last_page and last_page > 0:
  454. self.page = last_page
  455. sql = table.select().where(sqlcond)\
  456. .order_by(table.c.timestampofarchival.desc(),
  457. table.c.num.asc())\
  458. .limit(board.options['IMAGES_PER_PAGE'])\
  459. .offset(offset)
  460. threads = [{'posts' : [dict(x.items())]} \
  461. for x in session.execute(sql)]
  462. # Loop through 'posts' key in each dictionary in the threads
  463. # list.
  464. for item in threads:
  465. thread = item['posts']
  466. threadnum = thread[0]['postnum']
  467. postcount = imgcount = shownimages = 0
  468. # Orphaned?
  469. item['standalone'] = 0
  470. if not thread[0]['parent']:
  471. sql = select([func.count(), func.count(table.c.image)],
  472. table.c.parent == threadnum,
  473. table)
  474. (postcount, imgcount) = session.execute(sql).fetchone()
  475. max_res = board.options['REPLIES_PER_THREAD']
  476. offset = postcount - imgcount if postcount > max_res \
  477. else 0
  478. sql = table.select().where(table.c.parent == threadnum)\
  479. .order_by(table.c.timestampofarchival.desc(),
  480. table.c.postnum.asc())\
  481. .limit(max_res)\
  482. .offset(offset)
  483. thread.extend([dict(x.items()) \
  484. for x in session.execute(sql)])
  485. else:
  486. item['standalone'] = 1
  487. for post in thread:
  488. #image_dir \
  489. # = os.path.join(board.path, board.options['IMG_DIR'])
  490. #thumb_dir \
  491. # = os.path.join(board.path, board.options['THUMB_DIR'])
  492. base_thumb = os.path.basename(post['thumbnail'] or '')
  493. base_image = os.path.basename(post['image'] or '')
  494. #base_filename = (post['image'] or '')\
  495. # .replace(image_dir, '').lstrip('/')
  496. backup_dir = os.path.join(board.url,
  497. board.options['ARCHIVE_DIR'],
  498. board.options['BACKUP_DIR'])
  499. if post['image']:
  500. post['image'] = os.path.join(backup_dir, base_image)
  501. shownimages += 1
  502. if re.match(board.options['THUMB_DIR'],
  503. post['thumbnail'] or ''):
  504. post['thumbnail'] \
  505. = os.path.join(backup_dir, base_thumb)
  506. item['omit'] = postcount - max_res if postcount > max_res\
  507. else 0
  508. item['omitimages'] = imgcount - shownimages \
  509. if imgcount > shownimages else 0
  510. template_kwargs = {'postform' \
  511. : board.options['ALLOW_TEXTONLY'] or
  512. board.options['ALLOW_IMAGES'],
  513. 'image_inp' : board.options['ALLOW_IMAGES'],
  514. 'textonly_inp' \
  515. : board.options['ALLOW_IMAGES'] and
  516. board.options['ALLOW_TEXTONLY'],
  517. 'nextpage' : nextpage,
  518. 'prevpage' : prevpage,
  519. 'threads' : threads,
  520. 'pages' : pages}
  521. Template.__init__(self, 'backup_panel_template', **template_kwargs)
  522. @interface_for(POST_SEARCH_PANEL)
  523. def make_admin_post_search_panel(self, search, text, caller='internal'):
  524. board = self.board
  525. session = model.Session()
  526. table = board.table
  527. self.user.check_access(board.name)
  528. popup = caller != 'board'
  529. if search.find('IP Address') != -1:
  530. try:
  531. sql = table.select()\
  532. .where(table.c.ip == misc.dot_to_dec(text))
  533. except ValueError:
  534. raise WakaError('Please enter a valid IP.')
  535. search_type = 'IP'
  536. elif search.find('Text String') != -1:
  537. sql = table.select().where(table.c.comment.like('%'+text+'%'))
  538. search_type = 'text string'
  539. elif search.find('Author') != -1:
  540. sql = table.select().where(or_(table.c.name.like('%'+text+'%'),
  541. table.c.trip.like('%'+text+'%')))
  542. search_type = 'author'
  543. else:
  544. sql = table.select().where(table.c.num == text)
  545. search_type = 'ID'
  546. if search_type != 'ID':
  547. page = model.Page(sql, self.page, self.perpage)
  548. rowcount = page.total_entries
  549. total_pages = page.total_pages
  550. posts = page.rows
  551. if not posts:
  552. raise WakaError("No posts found for %s %s" % (search_type, text))
  553. else:
  554. rowcount = total_pages = 1
  555. row = session.execute(sql).fetchone()
  556. if not row:
  557. raise WakaError("Post not found. (It may have just been"
  558. " deleted.)")
  559. posts = [row]
  560. inputs = [
  561. {'name': 'board', 'value': board.name},
  562. {'name' : 'task', 'value' : 'searchposts'},
  563. {'name' : 'text', 'value' : text},
  564. {'name': 'caller', 'value': caller},
  565. {'name' : 'search', 'value': search}
  566. ]
  567. rooturl = misc.make_script_url(task='searchposts', board=board.name,
  568. caller=caller, search=search, text=text, _amp=True)
  569. Template.__init__(self, 'post_search', num=id,
  570. posts=posts, search=search, text=text,
  571. inputs=inputs, number_of_pages=total_pages,
  572. rooturl=rooturl, rowcount=rowcount, popup=popup)
  573. @interface_for(SQL_PANEL)
  574. def make_sql_interface_panel(self, sql='', nuke=''):
  575. if self.user.account != staff.ADMIN:
  576. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  577. results = []
  578. if sql or nuke:
  579. if nuke != local.environ['waka.board'].options['NUKE_PASS']:
  580. raise WakaError(strings.WRONGPASS)
  581. session = model.Session()
  582. if sql:
  583. for sql in sql.split('\r\n'):
  584. # Execute teh string.
  585. try:
  586. results.append('>>> ' + sql)
  587. row = session.execute(sql)
  588. except Exception as errstr:
  589. results.append('ERROR: %s' % (errstr))
  590. else:
  591. try:
  592. results.append(str(row.fetchall()))
  593. except:
  594. results.append('OK')
  595. else:
  596. # Remove board table contents and board list entry.
  597. try:
  598. board = local.environ['waka.board']
  599. board.table.drop(bind=model.engine, checkfirst=True)
  600. del model._boards[board.name]
  601. model.common.delete().where(model.common.c.board \
  602. == board.name)
  603. except Exception as errstr:
  604. results.append('ERROR %s' % (errstr))
  605. else:
  606. results.append('Comment table dropped. You should '
  607. 'delete/move the board folder now.')
  608. else:
  609. results.append('Leave textarea blank to delete the board.\n\n'
  610. '(It is recommended that you disable site access '
  611. 'while entering SQL or deleting boards.)')
  612. Template.__init__(self, 'sql_interface_template',
  613. results='<br />'.join(results))
  614. @interface_for(PROXY_PANEL)
  615. def make_admin_proxy_panel(self):
  616. session = model.Session()
  617. table = model.proxy
  618. query = table.select().order_by(table.c.timestamp.asc())
  619. rows = session.execute(query)
  620. Template.__init__(self, 'proxy_panel_template',
  621. scanned=rows, now=time.time())
  622. @interface_for(SECURITY_PANEL)
  623. def make_admin_script_security_panel(self):
  624. session = model.Session()
  625. table = model.passprompt
  626. rows = session.execute(table.select())
  627. now = time.time()
  628. entries = []
  629. for row in rows:
  630. row = dict(row)
  631. if row['passfail']:
  632. row['expiration']\
  633. = config.PASSFAIL_ROLLBACK - now + row['timestamp']
  634. else:
  635. row['expiration']\
  636. = config.PASSPROMPT_EXPIRE_TO_FAILURE - now \
  637. + row['timestamp']
  638. entries.append(row)
  639. Template.__init__(self, 'script_security_panel', entries=entries)
  640. @interface_for(DELETE_ALL_CONFIRM)
  641. def make_delete_all_window(self, **kwargs):
  642. Template.__init__(self, 'delete_crap_confirm', **kwargs)
  643. def add_staff_proxy(cookie, mpass, usertocreate, passtocreate, account, reign):
  644. user = staff.StaffMember.get_from_cookie(cookie)
  645. if user.account != staff.ADMIN:
  646. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  647. if account == staff.ADMIN and mpass != config.ADMIN_PASS:
  648. raise WakaError('Incorrect management password.')
  649. staff.add_staff(usertocreate, passtocreate, account, reign)
  650. board = local.environ['waka.board']
  651. return make_http_forward(misc.make_script_url(task='staff',
  652. board=board.name), config.ALTERNATE_REDIRECT)
  653. def del_staff_proxy(cookie, mpass, username):
  654. user = staff.StaffMember.get_from_cookie(cookie)
  655. if user.account != staff.ADMIN:
  656. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  657. user_to_kill = staff.StaffMember.get(username)
  658. if user_to_kill.account == staff.ADMIN and mpass != config.ADMIN_PASS:
  659. raise WakaError('Incorrect management password.')
  660. staff.del_staff(username)
  661. board = local.environ['waka.board']
  662. return make_http_forward(misc.make_script_url(task='staff',
  663. board=board.name), config.ALTERNATE_REDIRECT)
  664. def edit_staff_proxy(cookie, mpass, username, newpassword=None, newclass=None,
  665. originalpassword='', reign=None, disable=None):
  666. user = staff.StaffMember.get_from_cookie(cookie)
  667. if user.username == username:
  668. if misc.hide_critical_data(originalpassword, config.SECRET) \
  669. != user.password:
  670. raise WakaError(strings.WRONGPASS)
  671. newclass = None
  672. reign = None
  673. elif user.account == staff.ADMIN:
  674. edited_user = staff.StaffMember.get(username)
  675. if edited_user.account == staff.ADMIN and mpass != config.ADMIN_PASS:
  676. raise WakaError('Incorrect management password.')
  677. else:
  678. raise WakaError(strings.INSUFFICIENTPRIVILEGES)
  679. staff.edit_staff(username, clear_pass=newpassword, new_class=newclass,
  680. reign=reign, disable=disable)
  681. board = local.environ['waka.board']
  682. forward_task = 'admin' if user.username == username else 'staff'
  683. return make_http_forward(misc.make_script_url(task=forward_task,
  684. board=board.name), config.ALTERNATE_REDIRECT)
  685. def clear_login_cookies():
  686. misc.make_cookies(wakaadmin='', wakaadminsave='0', expires=0)
  687. def do_login(username=None, password=None, save_login=False,
  688. cookie=None, board=None, nexttask=HOME_PANEL):
  689. bad_pass = False
  690. staff_entry = None
  691. if not staff.staff_exists():
  692. return make_first_time_setup_gateway()
  693. elif username and password:
  694. # Login via login form entry.
  695. try:
  696. staff_entry = staff.StaffMember.get(username)
  697. except staff.LoginError:
  698. # Bad username.
  699. bad_pass = True
  700. else:
  701. crypt_pass = misc.hide_critical_data(password, config.SECRET)
  702. if crypt_pass == staff_entry.password:
  703. remote = local.environ['REMOTE_ADDR']
  704. staff_entry.login_host(remote)
  705. else:
  706. bad_pass = True
  707. elif cookie:
  708. # Attempt automatic login.
  709. try:
  710. staff_entry = staff.StaffMember.get_from_cookie(cookie)
  711. except staff.LoginError:
  712. clear_login_cookies()
  713. bad_pass = True
  714. else:
  715. # No login credentials given.
  716. bad_pass = True
  717. if bad_pass:
  718. return Template('admin_login_template')
  719. else:
  720. login = staff_entry.login_data
  721. login.make_cookie(save_login=save_login)
  722. return StaffInterface(login.cookie, dest=nexttask, board=board)
  723. def do_logout(cookie):
  724. # Clear login cache.
  725. try:
  726. user = staff.StaffMember.get_from_cookie(cookie)
  727. user.logout_user()
  728. except staff.LoginError:
  729. pass
  730. clear_login_cookies()
  731. board = local.environ['waka.board']
  732. return make_http_forward(misc.make_script_url(task='admin',
  733. board=board.name), config.ALTERNATE_REDIRECT)
  734. def make_first_time_setup_gateway():
  735. # TODO: Make sure we're in secure mode (HTTPS)
  736. return Template('first_time_setup')
  737. def do_first_time_setup(cookie, username, password):
  738. # Checks.
  739. if cookie != staff.crypt_pass(config.ADMIN_PASS,
  740. local.environ['REMOTE_ADDR']):
  741. return make_first_time_setup_gateway()
  742. if not username:
  743. raise WakaError('Missing username.')
  744. if not password:
  745. raise WakaError('Missing password.')
  746. staff.add_staff(username, password, staff.ADMIN, [])
  747. board = local.environ['waka.board']
  748. return make_http_forward(misc.make_script_url(task='loginpanel',
  749. board=board.name), config.ALTERNATE_REDIRECT)
  750. def make_first_time_setup_page(cookie):
  751. if not hasattr(config, 'ADMIN_PASS'):
  752. raise WakaError("ADMIN_PASS not set in config")
  753. if cookie == config.ADMIN_PASS:
  754. cookie = staff.crypt_pass(cookie, local.environ['REMOTE_ADDR'])
  755. return Template('account_setup', admin=cookie)
  756. else:
  757. return make_first_time_setup_gateway()