app.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #!/usr/bin/env python3
  2. # -*- coding:utf-8 -*
  3. #
  4. # Skia < lordbanana25 AT mailoo DOT org >
  5. #
  6. # MIT - 2015-2016
  7. #
  8. from flask import Flask, render_template, url_for, redirect, request, jsonify, session, send_from_directory, send_file, json
  9. from werkzeug.contrib.cache import SimpleCache
  10. from werkzeug import secure_filename
  11. import logging
  12. import argparse
  13. import uuid
  14. import os
  15. import io
  16. from flask_wtf.csrf import CsrfProtect
  17. from inventory.base import SappinDB
  18. from inventory.models import Module, Env, Host
  19. from settings import *
  20. cache = SimpleCache()
  21. app = Flask(__name__)
  22. app.secret_key = SECRET_KEY
  23. app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
  24. app.jinja_env.globals['config']['bases'] = SOURCE_BASE
  25. app.jinja_env.globals['config']['default_base'] = DEFAULT_BASE
  26. app.jinja_env.globals['config']['locations'] = LOCATIONS
  27. app.jinja_env.globals['config']['useful_urls'] = USEFUL_URLS
  28. app.jinja_env.globals['source_base'] = SOURCE_BASE[DEFAULT_BASE]
  29. CsrfProtect(app)
  30. @app.route('/getadmin/')
  31. @app.route('/getadmin/<path:url>')
  32. def get_admin_mode(url="/"):
  33. logging.debug("Setting admin mode")
  34. session['admin'] = True
  35. return redirect(url)
  36. @app.route('/leaveadmin/')
  37. @app.route('/leaveadmin/<path:url>')
  38. def leave_admin_mode(url="/"):
  39. logging.debug("Unsetting admin mode")
  40. session['admin'] = False
  41. return redirect(url)
  42. def get_source():
  43. if 'source_base' not in session.keys() or session['source_base'] not in SOURCE_BASE.keys():
  44. session['source_base'] = DEFAULT_BASE
  45. base = SOURCE_BASE[session['source_base']]
  46. base['key'] = session['source_base']
  47. app.jinja_env.globals['source_base'] = base
  48. return base
  49. @app.route('/<string:source>/')
  50. @app.route('/<string:source>/<path:url>')
  51. def set_source(source=None, url="/"):
  52. logging.debug("Setting source: "+source)
  53. if source is not None and source in SOURCE_BASE.keys():
  54. session['source_base'] = source
  55. return redirect(url+'?'+request.query_string.decode('utf-8'))
  56. @app.route('/')
  57. def main_page():
  58. source_base = get_source()
  59. base = SappinDB(source_base['key'])
  60. cts = cache.get('list-'+source_base['key'])
  61. if cts is None:
  62. cts = []
  63. for m in sorted(base.get_all_modules(None), key=lambda m: m.name):
  64. cts.append(m.as_dict())
  65. cache.set('list-'+source_base['key'], cts, timeout=15*60)
  66. logging.debug("Setting cache")
  67. else:
  68. logging.debug("Using cache for module list")
  69. return render_template("module_list.html", cts=cts, weird_host_list=base.get_weird_hosts(),
  70. weird_env_list=base.get_weird_env(), old_host_list=base.get_old_hosts())
  71. @app.route('/rebuild/')
  72. @app.route('/rebuild/<path:url>')
  73. def rebuild(url='/'):
  74. source_base = get_source()
  75. base = SappinDB(source_base['key'])
  76. base.make_base()
  77. logging.debug("Expiring cache")
  78. cache.set('list-'+source_base['key'], None)
  79. return redirect(url)
  80. @app.route('/admin_panel')
  81. @app.route('/admin_panel/')
  82. def admin_panel():
  83. set_source(source=DEFAULT_BASE)
  84. rebuild()
  85. source_base = get_source()
  86. base = SappinDB(source_base['key'])
  87. mods = []
  88. for m in base.get_all_modules():
  89. for v in json.loads(m._json_extra).values():
  90. if v == "":
  91. mods.append(m.as_dict())
  92. break
  93. envs = []
  94. for e in base.get_all_env():
  95. for v in json.loads(e._json_extra).values():
  96. if v == "":
  97. envs.append(e.as_dict())
  98. break
  99. hosts = []
  100. for h in base.get_all_hosts():
  101. for v in json.loads(h._json_extra).values():
  102. if v == "":
  103. hosts.append(h.as_dict())
  104. break
  105. return render_template("admin_panel.html", modules=mods, envs=envs, hosts=hosts)
  106. @app.route('/README')
  107. def readme():
  108. with open('README.md') as readme:
  109. try:
  110. import mistune
  111. return render_template('readme.html', readme=mistune.markdown(readme.read()))
  112. except:
  113. return render_template('readme.html', readme=readme.read().replace('\n', '<br>'))
  114. @app.route('/dump.json')
  115. def json_dump():
  116. source_base = get_source()
  117. base = SappinDB(source_base['key'])
  118. d = {}
  119. for m in base.get_all_modules():
  120. d[m.name] = {}
  121. d[m.name]['extra'] = m._json_extra
  122. envs = {}
  123. for e in m.env:
  124. envs[e.name] = {}
  125. envs[e.name]['extra'] = e._json_extra
  126. hosts = {}
  127. for h in e.host:
  128. hosts[h.name] = {}
  129. hosts[h.name]['extra'] = h._json_extra
  130. envs[e.name]['hosts'] = hosts
  131. d[m.name]['envs'] = envs
  132. string = io.BytesIO()
  133. string.write(json.dumps(d).encode())
  134. string.seek(0)
  135. return send_file(string, mimetype='text/json', attachment_filename="dump.json",
  136. as_attachment=True)
  137. @app.route('/json_load/', methods=['POST'])
  138. def json_load():
  139. if 'admin' not in session.keys() or session['admin'] == False:
  140. return "Operation not permitted", 500
  141. source_base = get_source()
  142. base = SappinDB(source_base['key'])
  143. file = request.files['json_import'] if 'json_import' in request.files.keys() else None
  144. if file:
  145. try:
  146. d = json.load(file)
  147. for k,v in d.items():
  148. base.get_mod_by_name(k)._json_extra = v['extra']
  149. for k2,v2 in v['envs'].items():
  150. base.get_env_by_name(k2)._json_extra = v2['extra']
  151. for k3,v3 in v2['hosts'].items():
  152. base.get_host_by_name(k3)._json_extra = v3['extra']
  153. base.session.commit()
  154. except Exception as e:
  155. logging.debug(e)
  156. return "Bad file format",500
  157. return redirect(url_for('admin_panel'))
  158. @app.route('/mod/<path:mod_name>')
  159. def detailed_mod(mod_name=""):
  160. source_base = get_source()
  161. base = SappinDB(source_base['key'])
  162. mod = base.get_mod_by_name(mod_name)
  163. if mod is None:
  164. return redirect(url_for('main_page'))
  165. return render_template("mod_detail.html", mod=mod.as_dict())
  166. @app.route('/env/<path:env_name>')
  167. def detailed_env(env_name=""):
  168. source_base = get_source()
  169. base = SappinDB(source_base['key'])
  170. env = base.get_env_by_name(env_name)
  171. if env is None:
  172. return redirect(url_for('main_page'))
  173. return render_template("env_detail.html", env=env.as_dict())
  174. @app.route('/host/<path:host_name>')
  175. def detailed_host(host_name=""):
  176. source_base = get_source()
  177. base = SappinDB(source_base['key'])
  178. host = base.get_host_by_name(host_name)
  179. if host is None:
  180. return redirect(url_for('main_page'))
  181. return render_template("host_detail.html", host=host.as_dict())
  182. @app.route('/delete_host', methods=['GET'])
  183. def delete_host():
  184. if 'admin' not in session.keys() or session['admin'] == False:
  185. return "Operation not permitted", 500
  186. source_base = get_source()
  187. id = request.args.get('id')
  188. if id == None:
  189. return redirect(url_for('main_page'))
  190. try:
  191. base = SappinDB(source_base['key'], True)
  192. base.del_host(id)
  193. cache.set('list-'+source_base['key'], None)
  194. logging.debug("Sucessfully deleted host")
  195. return "OK"
  196. except:
  197. logging.debug("There was a failure deleting host")
  198. return "DB handling error", 500
  199. def allowed_file(filename):
  200. return '.' in filename and \
  201. filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
  202. @app.route('/save_extra', methods=['POST'])
  203. def save_extra():
  204. if 'admin' not in session.keys() or session['admin'] == False:
  205. return "Operation not permitted", 500
  206. try:
  207. extra = {}
  208. # Handling file upload
  209. file = request.files['new_field_file'] if 'new_field_file' in request.files.keys() else None
  210. if file:
  211. if not allowed_file(file.filename):
  212. return "This file extension is not allowed",500
  213. if request.form['new_field_label'] == "" or request.form['new_field_label'] == "Field name":
  214. return "You need to put a name in the field name",500
  215. filename = str(uuid.uuid4()).split('-')[0]+'-'+secure_filename(file.filename)
  216. file.save(os.path.join(UPLOAD_FOLDER, filename))
  217. extra["FILE__"+request.form['new_field_label']] = filename
  218. logging.debug("File saved")
  219. # New value which is not a file
  220. elif request.form['new_field_label'] != "" and request.form['new_field_label'] != "Field name":
  221. extra[request.form['new_field_label']] = request.form['new_field_value']
  222. elif request.form['new_field_value'] != "" and request.form['new_field_value'] != "Value":
  223. return "You need to put a name in the field name",500
  224. # Updating all the old keys with the new values
  225. for k,v in request.form.items():
  226. if k[0:6] == "field_":
  227. extra[k[6:]] = v
  228. # Load the object for saving
  229. source_base = get_source()
  230. base = SappinDB(source_base['key'])
  231. obj = base.get_host_by_name(request.form['__field_name'])
  232. if obj is None:
  233. obj = base.get_env_by_name(request.form['__field_name'])
  234. if obj is None:
  235. obj = base.get_mod_by_name(request.form['__field_name'])
  236. # Clean up unused files
  237. old = json.loads(obj._json_extra)
  238. for k in old.keys():
  239. if k[0:6] == "FILE__" and k not in extra.keys():
  240. os.remove(UPLOAD_FOLDER+old[k])
  241. logging.debug("Deleted unused file: "+old[k])
  242. # Saving to database
  243. obj._json_extra = json.dumps(extra)
  244. base.session.commit()
  245. except Exception as e:
  246. return e,500
  247. return render_template("form.html", obj=obj.as_dict()),200
  248. @app.route('/files/<path:filename>')
  249. def download_file(filename):
  250. return send_from_directory(UPLOAD_FOLDER, filename, as_attachment=True)
  251. @app.route('/search')
  252. def search():
  253. source_base = get_source()
  254. base = SappinDB(source_base['key'])
  255. q = request.args.get('q')
  256. if q == None: return redirect(url_for('main_page'))
  257. envs = base.search_env_by_alias(q)
  258. hosts = base.search_host_by_name(q)
  259. envs = [e.as_dict() for e in envs]
  260. hosts = [e.as_dict() for e in hosts]
  261. return render_template("search.html", env_list=envs, host_list=hosts)
  262. def main():
  263. parser = argparse.ArgumentParser()
  264. parser.add_argument("--debug", "-d", help="Run with debugging informations", action="store_true")
  265. args = parser.parse_args()
  266. DEBUG=logging.INFO
  267. if args.debug == True: DEBUG=logging.DEBUG
  268. logging.basicConfig(level=DEBUG)
  269. logging.debug("Starting with debug mode")
  270. app.run(host=INTERFACE, port=PORT, debug=args.debug, use_reloader=True)
  271. if __name__ == '__main__':
  272. main()