app.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  1. import datetime as datetime
  2. from urllib.parse import urlparse
  3. import webbrowser
  4. from emailtool.emailer import Emailer
  5. from flask import Flask, render_template, request, redirect, url_for, session, send_file
  6. from gevent.pywsgi import WSGIServer
  7. import socket
  8. import logging
  9. from peewee import Model, CharField, DateTimeField, SqliteDatabase, BooleanField, fn
  10. import hashlib
  11. import os
  12. import csv
  13. import time
  14. import threading
  15. import atexit
  16. db_name = 'app.db'
  17. db = SqliteDatabase(db_name, check_same_thread=False)
  18. class User(Model):
  19. username = CharField()
  20. password = CharField()
  21. date_created = DateTimeField(default=datetime.datetime.now)
  22. logged_in = BooleanField(default=False)
  23. class Meta:
  24. database = db
  25. class IlsUser(Model):
  26. username = CharField()
  27. email = CharField()
  28. reset_datetime = CharField()
  29. class Meta:
  30. database = db
  31. class Settings(Model):
  32. name = CharField()
  33. value = CharField()
  34. class Meta:
  35. database = db
  36. class Schedule(Model):
  37. interval = CharField()
  38. class Meta:
  39. database = db
  40. class Log(Model):
  41. date = DateTimeField(default=datetime.datetime.now)
  42. username = CharField()
  43. action = CharField()
  44. class Meta:
  45. database = db
  46. class PasswordResetLog(Model):
  47. date = CharField(default=datetime.datetime.now().strftime('%Y-%m-%d'))
  48. username = CharField()
  49. class Meta:
  50. database = db
  51. class EmailReminderLog(Model):
  52. date = CharField(default=datetime.datetime.now().strftime('%Y-%m-%d'))
  53. username = CharField()
  54. email = CharField()
  55. interval = CharField()
  56. sent = BooleanField(default=False)
  57. ignore = BooleanField(default=False)
  58. class Meta:
  59. database = db
  60. def reminder_cleanup():
  61. reminders = EmailReminderLog.select().where(EmailReminderLog.date < datetime.datetime.now().strftime('%Y-%m-%d')).execute()
  62. for reminder in reminders:
  63. reminder.delete_instance()
  64. def scheduler(stop_event):
  65. while not stop_event.is_set():
  66. try:
  67. #db.connect()
  68. # Get all scheduled intervals from the DB
  69. intervals = Schedule.select().execute()
  70. users = IlsUser.select().execute()
  71. # Get settings
  72. domain_name_setting = Settings.get(Settings.name == 'Domain Name')
  73. domain_name = domain_name_setting.value
  74. password_reset_interval_setting = Settings.get(Settings.name == 'Password Reset Interval')
  75. password_reset_interval = password_reset_interval_setting.value
  76. smtp_host_setting = Settings.get(Settings.name == 'SMTP Host')
  77. smtp_host = smtp_host_setting.value
  78. smtp_port_setting = Settings.get(Settings.name == 'SMTP Port')
  79. smtp_port = smtp_port_setting.value
  80. smtp_username_setting = Settings.get(Settings.name == 'SMTP Username')
  81. smtp_username = smtp_username_setting.value
  82. smtp_password_setting = Settings.get(Settings.name == 'SMTP Password')
  83. smtp_password = smtp_password_setting.value
  84. suspend_setting = Settings.get(Settings.name == 'Suspend Scheduler')
  85. suspend = suspend_setting.value
  86. reset_link = "http://%s:%s" % (get_hostname(), http_port)
  87. #reset_link_setting = Settings.get(Settings.name == 'Password Reset URL')
  88. #reset_link = reset_link_setting.value
  89. # Loop through each user and calculate the number of days until the users password
  90. # expires based on the password_reset_interval value.
  91. # Loop through all users
  92. if suspend is False:
  93. # Schedule emails to be sent for each user and interval
  94. for user in users:
  95. # get the scheduled intervals
  96. for interval in intervals:
  97. # Calculate the date (yyyy-mm-dd) that the password will expire and subtract the interval
  98. # from that date to get the date that the email should be sent.
  99. # Example: If the password reset interval is 90 days and the current date is 2019-01-01
  100. # then the password will expire on 2019-04-01. If the interval is 30 days then the
  101. # email should be sent on 2019-03-02.
  102. # Calculate the date that the password will expire
  103. password_expiration_date = datetime.datetime.strptime(user.reset_datetime, "%Y-%m-%d") + datetime.timedelta(days=int(password_reset_interval))
  104. # Subtract the interval from the password expiration date to get the date that the email should be sent
  105. email_date = password_expiration_date - datetime.timedelta(days=int(interval.interval))
  106. # check if reminder has already been saved to the DB
  107. reminder = EmailReminderLog.select().where(EmailReminderLog.date == email_date.strftime('%Y-%m-%d'), EmailReminderLog.username == user.username, EmailReminderLog.interval == interval.interval).execute()
  108. if reminder:
  109. continue
  110. else:
  111. # Store the date in the DB
  112. EmailReminderLog.create(date=email_date.strftime('%Y-%m-%d'), email=user.email, username=user.username, interval=interval.interval).save()
  113. # Get all email reminders from the DB
  114. # if today's date matches the EmailReminderLog date then send the email and mark the reminder as sent
  115. reminders = EmailReminderLog.select().where(EmailReminderLog.date == datetime.datetime.now().strftime('%Y-%m-%d'), EmailReminderLog.ignore == 0, EmailReminderLog.sent == 0).execute()
  116. for reminder in reminders:
  117. # Send the email
  118. emailer = Emailer(smtp_host, smtp_port, smtp_username, smtp_password)
  119. if reminder.interval == 1:
  120. # 1 day reminder email
  121. emailer.send_email(reminder.email, 'ILS Password Reset Reminder',
  122. 'The password for ILS account with the username "'
  123. + reminder.username + '" will expire in ' + reminder.interval +
  124. ' day. To reset your password, please click the following link: ' + reset_link + '. If the password is not reset today the account will be locked.')
  125. else:
  126. # more than a day reminder email
  127. emailer.send_email(reminder.email, 'ILS Password Reset Reminder',
  128. 'The password for ILS account with the username "'
  129. + reminder.username + '" will expire in ' + reminder.interval +
  130. ' days. To reset your password, please click the following link: ' + reset_link)
  131. # Mark the reminder as sent
  132. reminder.sent = True
  133. reminder.save()
  134. # Remove all reminders from the DB that are older than today's date
  135. reminder_cleanup()
  136. #db.close()
  137. except Exception as e:
  138. pass
  139. time.sleep(1) # Sleep for 5 seconds
  140. # Encrypt the password with SHA256
  141. def encrypt_password(password):
  142. hash = hashlib.sha256(password.encode('utf-8')).hexdigest()
  143. return hash
  144. #db.connect()
  145. # Check for DB tables and create if they don't exist
  146. if db.table_exists('user') is False:
  147. db.create_tables([User, ])
  148. User.create(username='admin', password=encrypt_password('admin'),
  149. date_created=datetime.datetime.now(), logged_in=False).save()
  150. if db.table_exists('ilsuser') is False:
  151. db.create_tables([IlsUser, ])
  152. if db.table_exists('settings') is False:
  153. db.create_tables([Settings, ])
  154. Settings.create(name='Debug Mode', value=False).save()
  155. Settings.create(name='HTTP Port', value=5055).save()
  156. Settings.create(name='SMTP Host', value="").save()
  157. Settings.create(name='SMTP Port', value="587").save()
  158. Settings.create(name='SMTP Username', value="").save()
  159. Settings.create(name='SMTP Password', value="").save()
  160. Settings.create(name='Domain Name', value="lynx").save()
  161. Settings.create(name='Password Reset URL', value="https://terminal.idaho-lynx.org").save()
  162. Settings.create(name='Password Reset Interval', value="90").save()
  163. Settings.create(name='Suspend Scheduler', value=False).save()
  164. if db.table_exists('log') is False:
  165. db.create_tables([Log, ])
  166. if db.table_exists('password_reset_log') is False:
  167. db.create_tables([PasswordResetLog, ])
  168. if db.table_exists('schedule') is False:
  169. db.create_tables([Schedule, ])
  170. if db.table_exists('email_reminder_log') is False:
  171. db.create_tables([EmailReminderLog, ])
  172. settings = Settings.select().execute()
  173. debug_setting = Settings.get(Settings.name == 'Debug Mode')
  174. debug = debug_setting.value
  175. http_port_setting = Settings.get(Settings.name == 'HTTP Port')
  176. http_port = http_port_setting.value
  177. domain_name_setting = Settings.get(Settings.name == 'Domain Name')
  178. domain_name = domain_name_setting.value
  179. password_reset_url_setting = Settings.get(Settings.name == 'Password Reset URL')
  180. password_reset_url = password_reset_url_setting.value
  181. password_reset_interval_setting = Settings.get(Settings.name == 'Password Reset Interval')
  182. password_reset_interval = password_reset_interval_setting.value
  183. log = logging.getLogger('werkzeug')
  184. log.setLevel(logging.INFO)
  185. if debug.lower() == 'true':
  186. debug = True
  187. else:
  188. debug = False
  189. #db.close()
  190. def shutdown_session(exception=None):
  191. print('Stopping HTTP Service...')
  192. http_server.stop()
  193. # Get the systems hostname
  194. def get_hostname():
  195. return socket.gethostname()
  196. # Get systems IP address
  197. def get_ip_address():
  198. return socket.gethostbyname(socket.gethostname())
  199. # Method to check if a URL is valid using regex
  200. def is_valid_url(url):
  201. try:
  202. result = urlparse(url)
  203. if all([result.scheme, result.netloc]):
  204. url = '{uri.scheme}://{uri.netloc}/'.format(uri=result)
  205. else:
  206. url = False
  207. return url
  208. except:
  209. return False
  210. def requires_auth():
  211. if 'username' in session:
  212. username = session['username']
  213. user = User.get(User.username == username)
  214. if user.logged_in is True:
  215. return True
  216. else:
  217. return False
  218. else:
  219. return False
  220. def admin_password_check():
  221. admin_user = User.get(User.username == 'admin')
  222. if admin_user.password == encrypt_password('admin'):
  223. return True
  224. # Start the scheduler loop in another thread
  225. shutdown_scheduler = threading.Event()
  226. scheduler_thread = threading.Thread(target=scheduler, args=(shutdown_scheduler,))
  227. scheduler_thread.start()
  228. # Start the HTTP Server
  229. app = Flask(__name__)
  230. #app.secret_key = os.urandom(24)
  231. app.secret_key = "DEVELOPMENT"
  232. def format_time_ago(timestamp):
  233. """Calculate the time passed since a datetime stamp and format it as a human-readable string."""
  234. now = datetime.datetime.now()
  235. diff = now - datetime.datetime.strptime(timestamp, "%Y-%m-%d")
  236. if diff.days > 365:
  237. years = diff.days // 365
  238. return f"{years} year{'s' if years > 1 else ''} ago"
  239. if diff.days > 30:
  240. months = diff.days // 30
  241. return f"{months} month{'s' if months > 1 else ''} ago"
  242. if diff.days > 0:
  243. return f"{diff.days} day{'s' if diff.days > 1 else ''} ago"
  244. return ""
  245. # Create method for time until
  246. def format_time_until(timestamp):
  247. """Calculate the time until a datetime stamp and format it as a human-readable string."""
  248. # Convert timestamp into datetime object
  249. timestamp = datetime.datetime.strptime(timestamp, '%Y-%m-%d')
  250. now = datetime.datetime.now()
  251. diff = timestamp - now
  252. if diff.days > 365:
  253. years = diff.days // 365
  254. return f"in {years} year{'s' if years > 1 else ''}"
  255. # return months and days
  256. if diff.days > 30:
  257. months = diff.days // 30
  258. days = diff.days % 30
  259. return f"in {months} month{'s' if months > 1 else ''} and {days} day{'s' if days > 1 else ''}"
  260. # return days
  261. if diff.days > 0:
  262. return f"in {diff.days} day{'s' if diff.days > 1 else ''}"
  263. # return hours and minutes
  264. if diff.seconds > 3600:
  265. hours = diff.seconds // 3600
  266. minutes = (diff.seconds % 3600) // 60
  267. return f"in {hours} hour{'s' if hours > 1 else ''} and {minutes} minute{'s' if minutes > 1 else ''}"
  268. # return minutes
  269. if diff.seconds > 60:
  270. minutes = diff.seconds // 60
  271. return f"in {minutes} minute{'s' if minutes > 1 else ''}"
  272. if diff.days > 30:
  273. months = diff.days // 30
  274. return f"in {months} month{'s' if months > 1 else ''}"
  275. if diff.days > 0:
  276. return f"in {diff.days} day{'s' if diff.days > 1 else ''}"
  277. return "Today"
  278. app.jinja_env.filters['time_since'] = format_time_ago
  279. app.jinja_env.filters['time_until'] = format_time_until
  280. '''
  281. @app.before_request
  282. def before_request():
  283. db.connect()
  284. @app.after_request
  285. def after_request(response):
  286. db.close()
  287. return response
  288. '''
  289. @app.route('/admin/password/reset', methods=['GET', 'POST'])
  290. def admin_password_reset():
  291. message = None
  292. if request.method == 'POST':
  293. password = request.form.get('password')
  294. password_confirm = request.form.get('password_confirm')
  295. if password != password_confirm:
  296. message = 'Passwords do not match'
  297. else:
  298. try:
  299. user = User.get(User.username == 'admin')
  300. user.password = encrypt_password(password)
  301. user.logged_in = True
  302. user.save()
  303. session['username'] = 'admin'
  304. return redirect(url_for('admin'))
  305. except Exception as e:
  306. message = 'Username not found'
  307. context = {
  308. 'message': message,
  309. }
  310. return render_template('admin_password_reset.html', context=context)
  311. # Create a route for the home page
  312. @app.route('/', methods=['GET', 'POST'])
  313. def index():
  314. error = None
  315. reset = False
  316. reset_url = is_valid_url(password_reset_url)
  317. reset_url_error = False
  318. if reset_url is False:
  319. reset_url_error = True
  320. if request.method == 'POST':
  321. username = request.form.get('username')
  322. # Check for the username in the DB
  323. try:
  324. user = IlsUser.filter(IlsUser.username == username).first()
  325. except Exception as e:
  326. user = None
  327. if user:
  328. # Remove emailreinderlogs from this user
  329. try:
  330. reminders = EmailReminderLog.select().where(EmailReminderLog.username == user.username).execute()
  331. for reminder in reminders:
  332. reminder.delete_instance()
  333. except Exception as e:
  334. pass
  335. # Reset login datetime
  336. user.reset_datetime = datetime.datetime.now().strftime("%Y-%m-%d")
  337. PasswordResetLog.create(username=user.username, date_created=datetime.datetime.now()).save()
  338. user.save()
  339. # Open the reset URL in a new tab if the URL is valid
  340. if reset_url is not False:
  341. webbrowser.open_new_tab(str(reset_url))
  342. # Set reset to True to pass back to the view to display the correct content back to the user.
  343. reset = True
  344. else:
  345. error = 'Invalid username'
  346. context = {
  347. 'domain': domain_name,
  348. 'error': error,
  349. 'reset': reset,
  350. 'reset_url': reset_url,
  351. 'reset_url_error': reset_url_error,
  352. }
  353. return render_template('index.html', context=context)
  354. # Create a route for admin page
  355. @app.route('/admin/')
  356. def admin():
  357. if admin_password_check():
  358. return redirect(url_for('admin_password_reset'))
  359. # Check to see if user is logged in
  360. if not requires_auth():
  361. return redirect(url_for('login'))
  362. # Get settings
  363. try:
  364. password_reset_interval = Settings.get(Settings.name == 'Password Reset Interval')
  365. except Exception as e:
  366. password_reset_interval = None
  367. # Get a count of all IlsUsers
  368. try:
  369. ils_users = IlsUser.select().count()
  370. except Exception as e:
  371. ils_users = None
  372. # Get a count of all EmailReminders that are not ignored or sent
  373. try:
  374. total_email_reminders = EmailReminderLog.select().where(EmailReminderLog.sent == False, EmailReminderLog.ignore == False).count()
  375. except Exception as e:
  376. total_email_reminders = None
  377. # Get a list ils users that have passwords expiring in the next 7 days
  378. ils_expiring_users = list()
  379. try:
  380. # Get a list of ILS users that have passwords expiring in the next 7 days
  381. ils_users_expiring = IlsUser.select().where(IlsUser.reset_datetime + datetime.timedelta(days=int(password_reset_interval.value)) <= datetime.datetime.now() + datetime.timedelta(days=7)).execute()
  382. for user in ils_users_expiring:
  383. user = IlsUser.get(IlsUser.username == user.username)
  384. ils_expiring_users.append(user)
  385. except Exception as e:
  386. ils_users_expiring = None
  387. # get a list of email notifications sent in the last 7 days
  388. try:
  389. # Get a list of all future email reminders
  390. email_reminders = EmailReminderLog.select().order_by(-EmailReminderLog.date).limit(15).execute()
  391. except Exception as e:
  392. email_reminders = None
  393. context = {
  394. 'ils_user_count': ils_users,
  395. 'ils_users_expiring': ils_expiring_users,
  396. 'email_reminders': email_reminders,
  397. 'total_email_reminders': total_email_reminders,
  398. }
  399. return render_template('admin.html', context=context)
  400. @app.route('/admin/users/', methods=['GET', 'POST'])
  401. def admin_users():
  402. # Check to see if user is logged in
  403. if not requires_auth():
  404. return redirect(url_for('login'))
  405. message = None
  406. if request.method == 'POST':
  407. username = request.form.get('username')
  408. password = request.form.get('password')
  409. confirm_password = request.form.get('confirm_password')
  410. # Check to see if username already exists
  411. try:
  412. user = User.filter(User.username == username).first()
  413. except Exception as e:
  414. user = None
  415. if user:
  416. message = 'Username already exists'
  417. else:
  418. if password == confirm_password:
  419. User.create(username=username, password=encrypt_password(password),
  420. date_created=datetime.datetime.now(), logged_in=False).save()
  421. message = 'User created successfully'
  422. Log.create(username=session['username'], action='Created admin user: %s' % username, ).save()
  423. else:
  424. message = 'Passwords do not match'
  425. # Get all admin users from the DB
  426. users = User.select().execute()
  427. context = {
  428. 'users': users,
  429. 'message': message,
  430. }
  431. return render_template('admin_users.html', context=context)
  432. @app.route('/admin/users/edit/<int:id>', methods=['GET', 'POST'])
  433. def admin_users_edit(id):
  434. # Check to see if user is logged in
  435. if not requires_auth():
  436. return redirect(url_for('login'))
  437. # Get the user from the DB
  438. user = User.get(User.id == id)
  439. message = None
  440. if request.method == 'POST':
  441. username = request.form.get('username')
  442. password = request.form.get('password')
  443. confirm_password = request.form.get('confirm_password')
  444. # Check to see if username already exists
  445. all_users = list()
  446. users = User.select().execute()
  447. for u in users:
  448. if u.username != user.username:
  449. all_users.append(u.username)
  450. if username in all_users:
  451. message = 'Username already exists'
  452. else:
  453. user.username = username
  454. if password is not None or password != '':
  455. if password == confirm_password:
  456. user.password = encrypt_password(password)
  457. else:
  458. message = 'Passwords do not match'
  459. user.save()
  460. message = 'User updated successfully'
  461. Log.create(username=session['username'], action='Updated admin user: %s' % username, ).save()
  462. context = {
  463. 'user': user,
  464. 'message': message,
  465. }
  466. return render_template('admin_user_edit.html', context=context)
  467. @app.route('/admin/users/delete/<int:id>', methods=['GET', 'POST'])
  468. def admin_users_delete(id):
  469. # Check to see if user is logged in
  470. if not requires_auth():
  471. return redirect(url_for('login'))
  472. # Get the user from the DB
  473. user = User.get(User.id == id)
  474. if user.username != 'admin':
  475. # Unset session variable if the user is deleting their own account
  476. if user.username == session['username']:
  477. session.pop('username', None)
  478. username = user.username
  479. user.delete_instance()
  480. Log.create(username=session['username'], action='Removed admin user: %s' % username, ).save()
  481. return redirect(url_for('admin_users'))
  482. @app.route('/admin/users/ils', methods=['GET', 'POST'])
  483. def admin_ils_users():
  484. # Check to see if user is logged in
  485. if not requires_auth():
  486. return redirect(url_for('login'))
  487. message = None
  488. if request.method == 'POST':
  489. username = request.form.get('username')
  490. email = request.form.get('email')
  491. date = request.form.get('date')
  492. print(date)
  493. # Check to see if username already exists
  494. try:
  495. user = IlsUser.filter(IlsUser.username == username).first()
  496. except Exception as e:
  497. user = None
  498. # Check if date field was submitted if so convert to datetime object
  499. if date is None or date == '':
  500. date = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
  501. if user:
  502. message = 'Username already exists'
  503. else:
  504. IlsUser.create(username=username, email=email, reset_datetime=date).save()
  505. message = 'ILS User: %s created successfully' % username
  506. Log.create(username=session['username'], action='Created ILS User: %s' % username, ).save()
  507. # Get all admin users from the DB
  508. users = IlsUser.select().execute()
  509. context = {
  510. 'users': users,
  511. 'message': message,
  512. }
  513. return render_template('admin_ils_users.html', context=context)
  514. @app.route('/admin/users/ils/delete/<int:id>', methods=['GET', 'POST'])
  515. def admin_ils_users_delete(id):
  516. # Check to see if user is logged in
  517. if not requires_auth():
  518. return redirect(url_for('login'))
  519. # Get the user from the DB
  520. user = IlsUser.get(IlsUser.id == id)
  521. username = user.username
  522. # Remove scheduled email reminders
  523. try:
  524. email_reminders = EmailReminderLog.select().where(EmailReminderLog.username == user.username).execute()
  525. for reminder in email_reminders:
  526. reminder.delete_instance()
  527. except Exception as e:
  528. pass
  529. user.delete_instance()
  530. Log.create(username=session['username'], action='Removed ILS user: %s' % username, ).save()
  531. return redirect(url_for('admin_ils_users'))
  532. @app.route('/admin/users/ils/delete/all', methods=['GET', 'POST'])
  533. def admin_ils_users_delete_all():
  534. if not requires_auth():
  535. return redirect(url_for('login'))
  536. # Set the suspend scheduler to True
  537. settings = Settings.select().execute()
  538. for setting in settings:
  539. if setting.name == 'Suspend Scheduler':
  540. setting.value = 'True'
  541. setting.save()
  542. # Remove scheduled email reminders
  543. try:
  544. email_reminders = EmailReminderLog.select().execute()
  545. for reminder in email_reminders:
  546. reminder.delete_instance()
  547. except Exception as e:
  548. pass
  549. # Remove all ILS users
  550. users = IlsUser.select().execute()
  551. for user in users:
  552. user.delete_instance()
  553. # Set the suspend scheduler to False
  554. settings = Settings.select().execute()
  555. for setting in settings:
  556. if setting.name == 'Suspend Scheduler':
  557. setting.value = 'True'
  558. setting.save()
  559. return redirect(url_for('admin_ils_users'))
  560. @app.route('/admin/users/ils/edit/<int:id>', methods=['GET', 'POST'])
  561. def admin_ils_users_edit(id):
  562. # Check to see if user is logged in
  563. if not requires_auth():
  564. return redirect(url_for('login'))
  565. # Get the user from the DB
  566. user = IlsUser.get(IlsUser.id == id)
  567. message = None
  568. if request.method == 'POST':
  569. username = request.form.get('username')
  570. email = request.form.get('email')
  571. date = request.form.get('date')
  572. # Check to see if username already exists
  573. all_users = list()
  574. users = IlsUser.select().execute()
  575. for u in users:
  576. if u.username != user.username:
  577. all_users.append(u.username)
  578. if username in all_users:
  579. message = 'Username already exists'
  580. else:
  581. # Remove all scheduled email reminders. They will automatically be rescheduled with the new information.
  582. try:
  583. email_reminders = EmailReminderLog.select().where(EmailReminderLog.username == user.username).execute()
  584. for reminder in email_reminders:
  585. reminder.delete_instance()
  586. except Exception as e:
  587. pass
  588. user.username = username
  589. user.email = email
  590. if date is not None and date != '':
  591. user.reset_datetime = date
  592. else:
  593. user.reset_datetime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
  594. user.save()
  595. message = 'User updated successfully'
  596. Log.create(username=session['username'], action='Updated ILS user: %s' % username, ).save()
  597. context = {
  598. 'user': user,
  599. 'message': message,
  600. }
  601. return render_template('admin_ils_user_edit.html', context=context)
  602. # create a route for generating a CSV file for download
  603. @app.route('/admin/users/ils/csv/download', methods=['GET', 'POST'])
  604. def admin_ils_users_csv_download():
  605. # Check to see if user is logged in
  606. if not requires_auth():
  607. return redirect(url_for('login'))
  608. # Create a CSV file with the users and don't add a blank line between rows
  609. with open('ils-users_%s.csv' % datetime.datetime.now().strftime('%Y-%m-%d'), 'w', newline='') as f:
  610. writer = csv.writer(f)
  611. writer.writerow(['ILS Username', 'Email', 'Last Password Reset (YYYY-MM-DD)'])
  612. users = IlsUser.select().execute()
  613. for user in users:
  614. date = datetime.datetime.strptime(user.reset_datetime, '%Y-%m-%d').strftime('%Y-%m-%d')
  615. writer.writerow([user.username, user.email, date])
  616. Log.create(username=session['username'], action='Downloaded ILS user CSV file.').save()
  617. # return the CSV file to the user
  618. return send_file('ils-users_%s.csv' % datetime.datetime.now().strftime('%Y-%m-%d'), as_attachment=True)
  619. @app.route('/admin/users/ils/csv/import', methods=['GET', 'POST'])
  620. def admin_ils_users_csv_import():
  621. # Check to see if user is logged in
  622. if not requires_auth():
  623. return redirect(url_for('login'))
  624. message = None
  625. if request.method == 'POST':
  626. csv_file = request.files['csv']
  627. if csv_file.filename != '':
  628. csv_file.save(os.path.join('uploads', csv_file.filename))
  629. with open(os.path.join('uploads', csv_file.filename), 'r') as f:
  630. reader = csv.reader(f)
  631. for row in reader:
  632. username = row[0]
  633. email = row[1]
  634. reset_date = row[2]
  635. try:
  636. reset_date = datetime.datetime.strptime(reset_date, '%m/%d/%Y').strftime('%Y-%m-%d')
  637. except Exception as e:
  638. pass
  639. try:
  640. reset_date = datetime.datetime.strptime(reset_date, '%Y-%m-%d').strftime('%Y-%m-%d')
  641. except Exception as e:
  642. pass
  643. # ignore the header row
  644. if username == 'ILS Username':
  645. continue
  646. # ignore blank rows
  647. if username == '':
  648. continue
  649. # Check if user already exists and if it does update the entry
  650. try:
  651. user = IlsUser.filter(IlsUser.username == username).first()
  652. except Exception as e:
  653. user = None
  654. if user:
  655. user.email = email
  656. # Check if the reset date is blank and if it is set it to the current date
  657. if reset_date == '' or reset_date is None:
  658. user.reset_datetime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
  659. else:
  660. user.reset_datetime = reset_date
  661. user.save()
  662. else:
  663. if reset_date == '' or reset_date is None:
  664. user.reset_datetime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
  665. else:
  666. reset_datetime = reset_date
  667. IlsUser.create(username=username, email=email, reset_datetime=reset_datetime).save()
  668. # Delete the uploaded file
  669. os.remove(os.path.join('uploads', csv_file.filename))
  670. return redirect(url_for('admin_ils_users'))
  671. context = {
  672. 'message': message,
  673. }
  674. return render_template('csv.html', context=context)
  675. @app.route('/admin/settings', methods=['GET', 'POST'])
  676. def settings():
  677. # Check to see if user is logged in
  678. if not requires_auth():
  679. return redirect(url_for('login'))
  680. message = None
  681. # Process form submission
  682. if request.method == 'POST':
  683. # Assign form values to variables
  684. id = request.form.get('id')
  685. value = request.form.get('value')
  686. # Check if the setting exists
  687. try:
  688. setting = Settings.get(Settings.id == id)
  689. except Exception as e:
  690. print(e)
  691. setting = None
  692. if setting:
  693. # Scrub the values based on the setting name
  694. if setting.name == 'Debug Mode':
  695. if value.lower() == 'true':
  696. value = True
  697. else:
  698. value = False
  699. value = bool(value)
  700. elif setting.name == 'HTTP Port':
  701. value = str(value)
  702. elif setting.name == 'SMTP Host':
  703. value = str(value)
  704. elif setting.name == 'SMTP Port':
  705. value = str(value)
  706. elif setting.name == 'SMTP Username':
  707. value = str(value)
  708. elif setting.name == 'SMTP Password':
  709. value = str(value)
  710. elif setting.name == 'Domain Name':
  711. value = str(value)
  712. elif setting.name == 'Password Reset URL':
  713. value = str(value)
  714. elif setting.name == 'Password Reset Interval':
  715. if value.isdigit():
  716. value = int(value)
  717. else:
  718. value = setting.value
  719. # Update the setting
  720. old_value = setting.value
  721. setting.value = value
  722. setting.save()
  723. if setting.name == 'SMTP Password':
  724. Log.create(username=session['username'], action='SMTP Password was changed.').save()
  725. else:
  726. Log.create(username=session['username'], action='Changed %s setting from "%s" to "%s"' % (setting.name,
  727. old_value,
  728. setting.value)).save()
  729. message = '%s updated successfully' % setting.name
  730. # Get settings from DB
  731. all_settings = Settings.select().execute()
  732. context = {
  733. 'settings': all_settings,
  734. 'message': message,
  735. }
  736. return render_template('settings.html', context=context)
  737. @app.route('/admin/schedule', methods=['GET', 'POST'])
  738. def schedule():
  739. # Check to see if user is logged in
  740. if not requires_auth():
  741. return redirect(url_for('login'))
  742. message = None
  743. # Get all schedules from the DB
  744. schedules = Schedule.select().order_by(Schedule.interval.cast("INTEGER")).execute()
  745. # add schedule
  746. if request.method == 'POST':
  747. # Assign form values to variables
  748. interval = request.form.get('interval')
  749. # Check if interval is a number
  750. try:
  751. int(interval)
  752. except Exception as e:
  753. print(e)
  754. message = 'Error creating schedule. Value submitted must be a whole number.'
  755. return render_template('schedule.html', context={'message': message, 'schedules': schedules})
  756. # Check if the schedule already exists
  757. try:
  758. schedule = Schedule.get(Schedule.interval == interval)
  759. except Exception as e:
  760. print(e)
  761. schedule = None
  762. if schedule:
  763. message = 'Schedule already exists'
  764. else:
  765. # Create the schedule
  766. Schedule.create(interval=interval).save()
  767. Log.create(username=session['username'], action='Created schedule for %s day interval.' % interval).save()
  768. message = 'Schedule: %s created successfully' % interval
  769. schedules = Schedule.select().order_by(Schedule.interval.cast("INTEGER")).execute()
  770. context = {
  771. 'schedules': schedules,
  772. 'message': message,
  773. }
  774. return render_template('schedule.html', context=context)
  775. @app.route('/admin/schedule/emails')
  776. def scheduled_emails():
  777. # Check to see if user is logged in
  778. if not requires_auth():
  779. return redirect(url_for('login'))
  780. # Get all future emailreminderlogs
  781. reminders = EmailReminderLog.select().where(EmailReminderLog.date > datetime.datetime.now(), EmailReminderLog.ignore == False).order_by(
  782. -EmailReminderLog.date).execute()
  783. context = {
  784. 'reminders': reminders,
  785. }
  786. return render_template('scheduled_emails.html', context=context)
  787. @app.route('/admin/schedule/emails/clear')
  788. def reset_scheduled_emails():
  789. # Check to see if user is logged in
  790. if not requires_auth():
  791. return redirect(url_for('login'))
  792. # Get all future emailreminderlogs
  793. reminders = EmailReminderLog.select().execute()
  794. for reminder in reminders:
  795. reminder.delete_instance()
  796. time.sleep(3)
  797. return redirect(url_for('scheduled_emails'))
  798. # remove schedule
  799. @app.route('/admin/schedule/remove/reminder/<int:id>')
  800. def reminder_remove(id):
  801. # Check to see if user is logged in
  802. if not requires_auth():
  803. return redirect(url_for('login'))
  804. # Get EmailReminderLogs with the interval of the schedule being removed
  805. email_reminder_log = EmailReminderLog.get(EmailReminderLog.id == id)
  806. Log.create(username=session['username'],
  807. action='Removed %s day reminder for %s (%s)' % (email_reminder_log.interval, email_reminder_log.username, email_reminder_log.email)).save()
  808. email_reminder_log.ignore = True
  809. email_reminder_log.save()
  810. return redirect(url_for('scheduled_emails'))
  811. # remove schedule
  812. @app.route('/admin/schedule/remove/<int:id>', methods=['GET', 'POST'])
  813. def schedule_remove(id):
  814. # Check to see if user is logged in
  815. if not requires_auth():
  816. return redirect(url_for('login'))
  817. # Get Schedule by id
  818. try:
  819. schedule = Schedule.get(Schedule.id == id)
  820. except Exception as e:
  821. schedule = None
  822. if schedule:
  823. # Get EmailReminderLogs with the interval of the schedule being removed
  824. email_reminder_logs = EmailReminderLog.select().where(EmailReminderLog.interval == schedule.interval).execute()
  825. for email_reminder_log in email_reminder_logs:
  826. email_reminder_log.delete_instance()
  827. schedule.delete_instance()
  828. Log.create(username=session['username'], action='Removed schedule for a %s day reminder.' % schedule.interval).save()
  829. return redirect(url_for('schedule'))
  830. @app.route('/admin/system/log')
  831. def system_log():
  832. # Check to see if user is logged in
  833. if not requires_auth():
  834. return redirect(url_for('login'))
  835. # Get all logs from the DB
  836. logs = Log.select().order_by(Log.id.desc()).execute()
  837. context = {
  838. 'logs': logs,
  839. }
  840. return render_template('system_log.html', context=context)
  841. @app.route('/admin/system/log/password/resets')
  842. def password_reset_log():
  843. # Check to see if user is logged in
  844. if not requires_auth():
  845. return redirect(url_for('login'))
  846. # Get all logs from the DB
  847. logs = PasswordResetLog.select().order_by(PasswordResetLog.id.desc()).execute()
  848. context = {
  849. 'logs': logs,
  850. }
  851. return render_template('password_reset_log.html', context=context)
  852. @app.route('/logout')
  853. def logout():
  854. if 'username' in session:
  855. username = session['username']
  856. user = User.get(User.username == username)
  857. user.logged_in = False
  858. Log.create(username=session['username'], action='Logged out').save()
  859. user.save()
  860. session.pop('username', None)
  861. return redirect(url_for('login'))
  862. @app.route('/login', methods=['GET', 'POST'])
  863. def login():
  864. if request.method == 'POST':
  865. username = request.form.get('username')
  866. password = encrypt_password(request.form.get('password'))
  867. try:
  868. user = User.filter(User.username == username and User.password == password).first()
  869. except Exception as e:
  870. print(e)
  871. user = None
  872. session.pop('username', None)
  873. if user:
  874. # Login user
  875. session['username'] = request.form.get('username')
  876. user.logged_in = True
  877. Log.create(username=session['username'], action='Logged in').save()
  878. user.save()
  879. return redirect(url_for('admin'))
  880. else:
  881. error = 'Invalid Credentials. Please try again.'
  882. context = {
  883. 'error': error
  884. }
  885. return render_template('login.html', context=context)
  886. context = {
  887. }
  888. return render_template('login.html', context=context)
  889. def clean_up():
  890. shutdown_scheduler.set()
  891. http_server.stop()
  892. if __name__ == "__main__":
  893. print("------------------------- Start up -----------------------------")
  894. print("Starting HTTP Service on port %s..." % http_port)
  895. if debug is True:
  896. print("Debug mode is enabled.")
  897. http_server = WSGIServer(('0.0.0.0', int(http_port)), app)
  898. else:
  899. http_server = WSGIServer(('0.0.0.0', int(http_port)), app, log=log, error_log=log)
  900. print("HTTP Service Started.")
  901. print("--------------------- Application Details ---------------------")
  902. print("Application started at %s" % datetime.datetime.now())
  903. print("System IP Address: %s" % get_ip_address())
  904. print("System Hostname: %s" % get_hostname())
  905. print("Access the Dashboard using a web browser using any of the following:")
  906. print("http://%s:%s or http://%s:%s" % (get_hostname(), http_port, get_ip_address(), http_port))
  907. print("---------------------------------------------------------------")
  908. print("To stop the application close this window.")
  909. print("---------------------------------------------------------------")
  910. http_server.serve_forever()
  911. atexit.register(clean_up)