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.

407 lines
13 KiB

4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. from flask.helpers import send_from_directory
  2. import ldap as l
  3. from ldap3 import Server, Connection
  4. from ldap3.core.exceptions import LDAPBindError
  5. from flask import Flask, g, request, redirect, url_for, render_template, flash
  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. from werkzeug.utils import secure_filename
  14. from flask_bootstrap import Bootstrap
  15. import short_url
  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. # Uploads
  32. app.config['UPLOAD_FOLDER'] = 'links/images'
  33. # OpenLDAP
  34. app.config['LDAP_OBJECTS_DN'] = 'dn'
  35. app.config['LDAP_OPENLDAP'] = True
  36. app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=posixAccount)(uid=%s))'
  37. # Login cookies
  38. #app.config['SESSION_COOKIE_DOMAIN'] = os.environ.get('COOKIE_DOMAIN')
  39. #app.config['REMEMBER_COOKIE_DOMAIN'] = os.environ.get('COOKIE_DOMAIN')
  40. short_domain = os.environ.get('SHORT_DOMAIN')
  41. ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
  42. db = SQLAlchemy(app)
  43. login_manager = LoginManager(app)
  44. login_manager.init_app(app)
  45. login_manager.login_view = 'login'
  46. db.create_all()
  47. config = {
  48. 'extensions': ['.js', '.css', '.csv'],
  49. 'hash_size': 10
  50. }
  51. cache_buster = CacheBuster(config=config)
  52. cache_buster.register_cache_buster(app)
  53. server = Server(app.config['LDAP_HOST'])
  54. conn = Connection(server, app.config['LDAP_USERNAME'], app.config['LDAP_PASSWORD'], auto_bind=True)
  55. class User(db.Model):
  56. __tablename__ = 'user'
  57. id = db.Column(db.Integer, primary_key=True)
  58. username = db.Column(db.String(100))
  59. authenticated = db.Column(db.Boolean, default=False)
  60. def __init__(self, username):
  61. self.username = username
  62. @staticmethod
  63. def try_login(username, password):
  64. conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % username, attributes=['*'])
  65. if len(conn.entries) > 0:
  66. Connection(app.config['LDAP_HOST'], conn.entries[0].entry_dn, password, auto_bind=True)
  67. return
  68. raise LDAPBindError
  69. def is_authenticated(self):
  70. return self.authenticated
  71. def is_active(self):
  72. return True
  73. def is_anonymous(self):
  74. return False
  75. def get_id(self):
  76. return self.id
  77. def get_user_dict(self):
  78. user = {'dn': '',
  79. 'firstName': '',
  80. 'lastName': '',
  81. 'email': '',
  82. 'userName': self.username,
  83. }
  84. conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % self.username, attributes=['*'])
  85. user['dn'] = conn.entries[0].entry_dn
  86. user['firstName'] = conn.entries[0].givenName.value
  87. user['lastName'] = conn.entries[0].sn.value
  88. user['email'] = conn.entries[0].mail.value
  89. return user
  90. class LoginForm(FlaskForm):
  91. username = StringField('Username', validators=[DataRequired()])
  92. password = PasswordField('Password', validators=[DataRequired()])
  93. remember_me = BooleanField('Remember Me')
  94. submit = SubmitField('Sign In')
  95. @login_manager.user_loader
  96. def load_user(id):
  97. return User.query.get(int(id))
  98. @app.before_request
  99. def get_current_user():
  100. g.user = current_user
  101. @app.route('/')
  102. @login_required
  103. def index():
  104. pastes = get_pastes_for_user()
  105. links = get_links_for_user()
  106. images = get_images_for_user()
  107. return render_template('profile.j2', user = current_user.get_user_dict(), short_domain = short_domain, links = links, pastes = pastes, images = images)
  108. @app.route('/link')
  109. @login_required
  110. def link():
  111. return render_template('link.j2', user = current_user.get_user_dict(), short_domain = short_domain)
  112. @app.route('/paste')
  113. @login_required
  114. def text():
  115. return render_template('paste.j2', user = current_user.get_user_dict())
  116. @app.route('/image', methods=['GET', 'POST'])
  117. @login_required
  118. def image():
  119. if request.method == 'POST':
  120. print(request.files)
  121. # check if the post request has the file part
  122. if 'file' not in request.files:
  123. return render_template('image.j2', user = current_user.get_user_dict(), short_domain = short_domain, error_msg = "No file part.")
  124. file = request.files['file']
  125. # if user does not select file, browser also
  126. # submit an empty part without filename
  127. if file.filename == '':
  128. return render_template('image.j2', user = current_user.get_user_dict(), short_domain = short_domain, error_msg = "No selected file.")
  129. if file and allowed_file(file.filename):
  130. conn = sqlite3.connect('links/links.db')
  131. c = conn.cursor()
  132. filename = secure_filename(file.filename)
  133. file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
  134. c.execute("SELECT * FROM images WHERE filename=? AND user_id=?", (filename, current_user.get_user_dict()['dn']))
  135. row = c.fetchone()
  136. if row == None:
  137. c.execute("INSERT INTO images (filename, user_id) VALUES (?, ?)", (filename, current_user.get_user_dict()['dn']))
  138. c.execute("SELECT * FROM images WHERE filename=? AND user_id=?", (filename, current_user.get_user_dict()['dn']))
  139. row = c.fetchone()
  140. print(row[0])
  141. conn.commit()
  142. conn.close()
  143. url_fragment = short_url.encode_url(row[0])
  144. return render_template('image.j2', user = current_user.get_user_dict(), short_domain = short_domain, success_msg = "Your image link is <a target='_blank' href='{}/i/{}'>{}/i/{}</a>".format(short_domain, url_fragment, short_domain, url_fragment))
  145. return render_template('image.j2', user = current_user.get_user_dict(), short_domain = short_domain)
  146. @app.route('/login', methods=['GET', 'POST'])
  147. def login():
  148. if current_user.is_authenticated:
  149. flash('You are already logged in.')
  150. return redirect(url_for('index'))
  151. form = LoginForm(request.form)
  152. print(form)
  153. print(request.method)
  154. if request.method == 'POST' and form.validate():
  155. username = request.form.get('username')
  156. password = request.form.get('password')
  157. print(username)
  158. print(password)
  159. try:
  160. User.try_login(username, password)
  161. except LDAPBindError:
  162. flash(
  163. 'Invalid username or password. Please try again.',
  164. 'danger')
  165. return render_template('login.j2', form=form)
  166. user = User.query.filter(User.username == username).first()
  167. print(user)
  168. if user is None:
  169. user = User(username, password)
  170. db.session.add(user)
  171. user.authenticated = True
  172. db.session.commit()
  173. login_user(user, remember=form.remember_me.data)
  174. print('You have successfully logged in.')
  175. return redirect(url_for('index'))
  176. if form.errors:
  177. flash(form.errors, 'danger')
  178. return render_template('login.j2', form=form)
  179. @app.route('/shorten', methods=['POST'])
  180. @login_required
  181. def shorten_url():
  182. if request.method == 'POST':
  183. url = request.form['url']
  184. conn = sqlite3.connect('links/links.db')
  185. c = conn.cursor()
  186. if url is not None and len(url) > 0:
  187. c.execute("SELECT * FROM links WHERE url=? AND user_id=?", (url, current_user.get_user_dict()['dn']))
  188. row = c.fetchone()
  189. if row == None:
  190. c.execute("INSERT INTO links (url, user_id) VALUES (?, ?)", (url, current_user.get_user_dict()['dn']))
  191. c.execute("SELECT * FROM links WHERE url=? AND user_id=?", (url, current_user.get_user_dict()['dn']))
  192. row = c.fetchone()
  193. print(row[0])
  194. conn.commit()
  195. conn.close()
  196. url_fragment = short_url.encode_url(row[0])
  197. return "Your shortened link is <a target='_blank' href='{}/l/{}'>{}/l/{}</a>".format(short_domain, url_fragment, short_domain, url_fragment)
  198. conn.commit()
  199. conn.close()
  200. return 'Error'
  201. @app.route('/save', methods=['POST'])
  202. @login_required
  203. def save_paste():
  204. if request.method == 'POST':
  205. paste = request.data
  206. conn = sqlite3.connect('links/links.db')
  207. c = conn.cursor()
  208. if paste is not None and len(paste) > 0:
  209. c.execute("SELECT * FROM pastes WHERE paste=? AND user_id=?", (paste, current_user.get_user_dict()['dn']))
  210. row = c.fetchone()
  211. if row == None:
  212. c.execute("INSERT INTO pastes (paste, user_id) VALUES (?, ?)", (paste, current_user.get_user_dict()['dn']))
  213. c.execute("SELECT * FROM pastes WHERE paste=? AND user_id=?", (paste, current_user.get_user_dict()['dn']))
  214. row = c.fetchone()
  215. print(row[0])
  216. conn.commit()
  217. conn.close()
  218. url_fragment = short_url.encode_url(row[0])
  219. return {"success": True, "msg": "Your paste link is <a target='_blank' href='{}/p/{}'>{}/p/{}</a>".format(short_domain, url_fragment, short_domain, url_fragment)}
  220. conn.commit()
  221. conn.close()
  222. return {'success': False}
  223. @app.route('/delete', methods=['POST'])
  224. @login_required
  225. def delete():
  226. if request.method == 'POST':
  227. data = request.json
  228. table = data['table']
  229. conn = sqlite3.connect('links/links.db')
  230. c = conn.cursor()
  231. if table == 'links':
  232. c.execute("DELETE FROM links WHERE id=? AND user_id=?", (data['id'], current_user.get_user_dict()['dn']))
  233. elif table == 'pastes':
  234. c.execute("DELETE FROM pastes WHERE id=? AND user_id=?", (data['id'], current_user.get_user_dict()['dn']))
  235. elif table == 'images':
  236. c.execute("DELETE FROM images WHERE id=? AND user_id=?", (data['id'], current_user.get_user_dict()['dn']))
  237. else:
  238. return {'success': False, 'msg': 'This table doesn\'t exist!'}
  239. conn.commit()
  240. conn.close()
  241. return {'success': True, 'msg': 'Deleted successfully!'}
  242. return {'success': False, 'msg': 'An error occurred.'}
  243. @app.route('/l/<url>')
  244. def expand_url(url):
  245. idx = short_url.decode_url(url)
  246. conn = sqlite3.connect('links/links.db')
  247. c = conn.cursor()
  248. c.execute("SELECT * FROM links WHERE id=?", (idx,))
  249. out = c.fetchone()
  250. if out != None:
  251. out_link = out[1]
  252. return redirect(out_link)
  253. return render_template('404.j2')
  254. @app.route('/p/<url>')
  255. def show_paste(url):
  256. idx = short_url.decode_url(url)
  257. conn = sqlite3.connect('links/links.db')
  258. c = conn.cursor()
  259. c.execute("SELECT * FROM pastes WHERE id=?", (idx,))
  260. out = c.fetchone()
  261. if out != None:
  262. out_paste = str(out[1], 'utf-8')
  263. return render_template('public_paste.j2', paste = out_paste)
  264. return render_template('404.j2')
  265. @app.route('/i/<url>')
  266. def show_image(url):
  267. idx = short_url.decode_url(url)
  268. conn = sqlite3.connect('links/links.db')
  269. c = conn.cursor()
  270. c.execute("SELECT * FROM images WHERE id=?", (idx,))
  271. out = c.fetchone()
  272. if out != None:
  273. filename = out[1]
  274. return send_from_directory(app.config['UPLOAD_FOLDER'], filename=filename, as_attachment=False)
  275. return render_template('404.j2')
  276. def get_pastes_for_user():
  277. conn = sqlite3.connect('links/links.db')
  278. c = conn.cursor()
  279. c.execute("SELECT * FROM pastes WHERE user_id=?", (current_user.get_user_dict()["dn"],))
  280. out = []
  281. for row in c.fetchall():
  282. a = "{}/p/{}<span class='faded'> - {}</span>".format(short_domain, short_url.encode_url(row[0]), str(row[1], 'utf-8')[:80])
  283. b = "{}/p/{}".format(short_domain, short_url.encode_url(row[0]))
  284. out.append((row[0], a, b))
  285. return out
  286. def get_links_for_user():
  287. conn = sqlite3.connect('links/links.db')
  288. c = conn.cursor()
  289. c.execute("SELECT * FROM links WHERE user_id=?", (current_user.get_user_dict()["dn"],))
  290. out = []
  291. for row in c.fetchall():
  292. a = "{}/l/{}<span class='faded'> - {}</span>".format(short_domain, short_url.encode_url(row[0]), row[1])
  293. b = "{}/l/{}".format(short_domain, short_url.encode_url(row[0]))
  294. out.append((row[0], a, b))
  295. return out
  296. def get_images_for_user():
  297. conn = sqlite3.connect('links/links.db')
  298. c = conn.cursor()
  299. c.execute("SELECT * FROM images WHERE user_id=?", (current_user.get_user_dict()["dn"],))
  300. out = []
  301. for row in c.fetchall():
  302. a = "{}/i/{}".format(short_domain, short_url.encode_url(row[0]))
  303. out.append((row[0], a, a))
  304. return out
  305. def allowed_file(filename):
  306. return '.' in filename and \
  307. filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
  308. @app.route('/logout')
  309. @login_required
  310. def logout():
  311. user = current_user
  312. user.authenticated = False
  313. db.session.add(user)
  314. db.session.commit()
  315. logout_user()
  316. return redirect(url_for('game'))
  317. if __name__ == '__main__':
  318. app.run()