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.

300 lines
9.3 KiB

3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
  1. import ldap as l
  2. from ldap3 import Server, Connection, ALL, MODIFY_REPLACE
  3. from ldap3.core.exceptions import LDAPBindError
  4. from flask import Flask, g, request, session, redirect, url_for, render_template, flash
  5. from flask_bs4 import Bootstrap
  6. from flask_sqlalchemy import SQLAlchemy
  7. from flask_login import LoginManager, login_manager, current_user, login_user, \
  8. logout_user, login_required
  9. from flask_wtf import FlaskForm
  10. from flask_cache_buster import CacheBuster
  11. from wtforms import StringField, PasswordField, BooleanField, SubmitField
  12. from wtforms.validators import DataRequired
  13. import yaml
  14. import datetime as dt
  15. import pytz
  16. import os
  17. import sqlite3
  18. app = Flask(__name__)
  19. Bootstrap(app)
  20. app.secret_key = 'asdf'
  21. app.debug = True
  22. app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
  23. app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
  24. app.config['WTF_CSRF_SECRET_KEY'] = 'asdf'
  25. # Base
  26. app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
  27. app.config['LDAP_HOST'] = os.environ.get('LDAP_HOST')
  28. app.config['LDAP_BASE_DN'] = os.environ.get('LDAP_BASE_DN')
  29. app.config['LDAP_USERNAME'] = os.environ.get('LDAP_USERNAME')
  30. app.config['LDAP_PASSWORD'] = os.environ.get('LDAP_PASSWORD')
  31. # OpenLDAP
  32. app.config['LDAP_OBJECTS_DN'] = 'dn'
  33. app.config['LDAP_OPENLDAP'] = True
  34. app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=posixAccount)(uid=%s))'
  35. # Login cookies
  36. app.config['SESSION_COOKIE_DOMAIN'] = os.environ.get('COOKIE_DOMAIN')
  37. app.config['REMEMBER_COOKIE_DOMAIN'] = os.environ.get('COOKIE_DOMAIN')
  38. db = SQLAlchemy(app)
  39. login_manager = LoginManager(app)
  40. login_manager.init_app(app)
  41. login_manager.login_view = 'login'
  42. db.create_all()
  43. config = {
  44. 'extensions': ['.js', '.css', '.csv'],
  45. 'hash_size': 10
  46. }
  47. cache_buster = CacheBuster(config=config)
  48. cache_buster.register_cache_buster(app)
  49. eastern = pytz.timezone('US/Eastern')
  50. with open('config/config.yaml') as f:
  51. yaml_data = yaml.load(f, Loader=yaml.SafeLoader)
  52. search = yaml_data['search']
  53. account_url = yaml_data['accounts']['account_url']
  54. description = yaml_data['description']
  55. game_description = yaml_data['game_description']
  56. countdown_data = None
  57. if yaml_data['countdown']['active'] == True:
  58. countdown_data = yaml_data['countdown']
  59. final_countdown_data = None
  60. final_time = None
  61. if yaml_data['final_countdown']['active'] == True:
  62. final_countdown_data = yaml_data['final_countdown']
  63. final_time = eastern.localize(dt.datetime.strptime(final_countdown_data['timestamp'], '%B %d %Y %H:%M:%S%z').replace(tzinfo=None))
  64. apps = []
  65. for itm in yaml_data['apps'].items():
  66. apps.append(itm[1])
  67. games = []
  68. for itm in yaml_data['games'].items():
  69. games.append(itm[1])
  70. server = Server(app.config['LDAP_HOST'])
  71. conn = Connection(server, app.config['LDAP_USERNAME'], app.config['LDAP_PASSWORD'], auto_bind=True)
  72. class User(db.Model):
  73. __tablename__ = 'user'
  74. id = db.Column(db.Integer, primary_key=True)
  75. username = db.Column(db.String(100))
  76. password = db.Column(db.String(128))
  77. authenticated = db.Column(db.Boolean, default=False)
  78. def __init__(self, username, password):
  79. self.username = username
  80. self.password = password
  81. @staticmethod
  82. def try_login(username, password):
  83. conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % username, attributes=['*'])
  84. if len(conn.entries) > 0:
  85. Connection(app.config['LDAP_HOST'], conn.entries[0].entry_dn, password, auto_bind=True)
  86. return
  87. raise LDAPBindError
  88. def is_authenticated(self):
  89. return self.authenticated
  90. def is_active(self):
  91. return True
  92. def is_anonymous(self):
  93. return False
  94. def get_id(self):
  95. return self.id
  96. def get_user_dict(self):
  97. user = {'dn': '',
  98. 'firstName': '',
  99. 'lastName': '',
  100. 'email': '',
  101. 'userName': self.username,
  102. }
  103. conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % self.username, attributes=['*'])
  104. user['dn'] = conn.entries[0].entry_dn
  105. user['firstName'] = conn.entries[0].givenName.value
  106. user['lastName'] = conn.entries[0].sn.value
  107. user['email'] = conn.entries[0].mail.value
  108. return user
  109. class LoginForm(FlaskForm):
  110. username = StringField('Username', validators=[DataRequired()])
  111. password = PasswordField('Password', validators=[DataRequired()])
  112. remember_me = BooleanField('Remember Me')
  113. submit = SubmitField('Sign In')
  114. @login_manager.user_loader
  115. def load_user(id):
  116. return User.query.get(int(id))
  117. @app.before_request
  118. def get_current_user():
  119. g.user = current_user
  120. @app.route('/frame')
  121. def frame():
  122. return render_template('frame.j2', apps = apps, games = games)
  123. @app.route('/')
  124. def index():
  125. current_time = eastern.localize(dt.datetime.now())
  126. if final_countdown_data != None:
  127. if (final_time - current_time).days > -1:
  128. return render_template('final_countdown.j2', final_countdown = final_countdown_data)
  129. if countdown_data != None:
  130. return render_template('index.j2', apps = apps, search = search, account_url = account_url, description = description, countdown = countdown_data)
  131. return render_template('index.j2', apps = apps, search = search, account_url = account_url, description = description)
  132. @app.route('/games')
  133. def game():
  134. current_time = eastern.localize(dt.datetime.now())
  135. if final_countdown_data != None:
  136. if (final_time - current_time).days > -1:
  137. return render_template('final_countdown.j2', final_countdown = final_countdown_data)
  138. if countdown_data != None:
  139. if current_user.is_authenticated:
  140. return render_template('games.j2', games = games, search = search, account_url = account_url, description = game_description, countdown = countdown_data, game_list = generate_game_list(current_user.username))
  141. return render_template('games.j2', games = games, search = search, account_url = account_url, description = game_description, countdown = countdown_data)
  142. if current_user.is_authenticated:
  143. return render_template('games.j2', games = games, search = search, account_url = account_url, description = game_description, game_list = generate_game_list(current_user.username))
  144. return render_template('games.j2', games = games, search = search, account_url = account_url, description = game_description)
  145. @app.route('/login', methods=['GET', 'POST'])
  146. def login():
  147. if current_user.is_authenticated:
  148. flash('You are already logged in.')
  149. return redirect(url_for('index'))
  150. form = LoginForm(request.form)
  151. print(form)
  152. print(request.method)
  153. if request.method == 'POST' and form.validate():
  154. username = request.form.get('username')
  155. password = request.form.get('password')
  156. print(username)
  157. print(password)
  158. try:
  159. User.try_login(username, password)
  160. except LDAPBindError:
  161. flash(
  162. 'Invalid username or password. Please try again.',
  163. 'danger')
  164. return render_template('login.j2', form=form)
  165. user = User.query.filter(User.username == username).first()
  166. print(user)
  167. if user is None:
  168. user = User(username, password)
  169. db.session.add(user)
  170. user.authenticated = True
  171. db.session.commit()
  172. login_user(user, remember=form.remember_me.data)
  173. print('You have successfully logged in.')
  174. return redirect('/games')
  175. if form.errors:
  176. flash(form.errors, 'danger')
  177. return render_template('login.j2', form=form)
  178. @app.route('/add', methods=['POST'])
  179. @login_required
  180. def add_game():
  181. if request.method == 'POST':
  182. game_title = request.form['game_title']
  183. game_link = request.form['game_link']
  184. games_conn = sqlite3.connect('config/games_in_progress.db')
  185. c = games_conn.cursor()
  186. if game_title is not None and len(game_title) > 0 and game_link is not None and len(game_link) > 0:
  187. c.execute("INSERT INTO games (user_id, game_title, game_link) VALUES (?, ?, ?)", (current_user.username, game_title, game_link,))
  188. games_conn.commit()
  189. games_conn.close()
  190. return 'Success'
  191. games_conn.commit()
  192. games_conn.close()
  193. return 'Error'
  194. @app.route('/delete', methods=['POST'])
  195. @login_required
  196. def delete_game():
  197. if request.method == 'POST':
  198. game_id = request.form['game_id']
  199. games_conn = sqlite3.connect('config/games_in_progress.db')
  200. c = games_conn.cursor()
  201. if game_id is not None and len(game_id) > 0:
  202. c.execute("DELETE FROM games WHERE id=? AND user_id=?", (game_id, current_user.username,))
  203. games_conn.commit()
  204. games_conn.close()
  205. return 'Success'
  206. games_conn.commit()
  207. games_conn.close()
  208. return 'Error'
  209. def generate_game_list(username):
  210. games_conn = sqlite3.connect('config/games_in_progress.db')
  211. c = games_conn.cursor()
  212. c.execute('SELECT * FROM games WHERE user_id=?', (username, ))
  213. rows = c.fetchall()
  214. games_conn.close()
  215. return rows
  216. @app.route('/logout')
  217. @login_required
  218. def logout():
  219. user = current_user
  220. user.authenticated = False
  221. db.session.add(user)
  222. db.session.commit()
  223. logout_user()
  224. return redirect(url_for('game'))
  225. if __name__ == '__main__':
  226. app.run(extra_files="config/config.yaml")