# HG changeset patch # User Benoît Allard # Date 1412072332 -7200 # Node ID 4219d6fb4c3819cd06d115d2e25c8ca5ec9d7bb5 # Parent a32f9b86edb48ec905bdff75e2d13dc28580130a Implement three kind of caches diff -r a32f9b86edb4 -r 4219d6fb4c38 CHANGES --- a/CHANGES Mon Sep 29 14:19:07 2014 +0200 +++ b/CHANGES Tue Sep 30 12:18:52 2014 +0200 @@ -1,10 +1,20 @@ Farol 0.1.1 (2014-??-??) ======================== +This is the first patch release of Farol 0.1, it fixes various issues, and +improve usability. + + +Thanks to all the contributors: +Michael Wiegand and Benoît Allard. + Main changes since 0.1: ----------------------- -* Ease the import of documents +* Ease the import of documents, with various shortcuts * Add a Welcome page +* Add loging of the exceptions to a farol.log file (in the instance dir) +* Implement three different kind of 'caching': disableds, global or + session-based. Farol 0.1 (2014-09-24) diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/cache.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/farol/cache.py Tue Sep 30 12:18:52 2014 +0200 @@ -0,0 +1,117 @@ +# -*- encoding: utf-8 -*- +# Description: +# Web stuff related to the cache. +# +# Authors: +# Benoît Allard +# +# Copyright: +# Copyright (C) 2014 Greenbone Networks GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +"""\ +The caching stuff ... +""" + +import os + +from flask import (Blueprint, current_app, session, flash, redirect, url_for, + render_template, request) +from werkzeug import secure_filename + +from farolluz.parsers.cvrf import parse +from farolluz.renderer import render + +from .session import get_current, set_current, del_current, has_current, document_required + +mod = Blueprint('cache', __name__) + +def caching_type(): + """\ + Returns the type of caching we are using: + None, 'session' or 'global' + """ + c_type = current_app.config.get('CACHING', 'global') + return {'disabled': None}.get(c_type, c_type) + +def _caching_dir(): + """\ + Returns the current caching directory + """ + c_type = caching_type() + if c_type is None: return None + root_dir = current_app.config.get('CACHE_DIRECTORY', + os.path.join(current_app.instance_path, '_cache')) + if c_type == 'global': return root_dir + uid = session.get('id') + # No uid yet, so no cache ... + if uid is None: return None + return os.path.join(root_dir, uid.hex) + +def cache_content(): + """\ + The content of the cache + """ + dirname = _caching_dir() + if dirname is None: return [] + if not os.path.exists(dirname): + os.makedirs(dirname) + l = [] + for path in os.listdir(dirname): + name, ext = os.path.splitext(path) + if ext == '.xml': + l.append(name) + return l + +def cleanup_cache(): + """\ + Remove old documents ... maybe ... from time to time ... + """ + pass + +@mod.route('/save', methods=['GET', 'POST']) +@document_required +def save(): + if request.method != 'POST': + return render_template('cache/save.j2', + id_=get_current()._tracking._identification._id) + # Get some kind of filename, and save the cvrf on cache (disk) + path = secure_filename(request.form['fname']) + path, _ = os.path.splitext(path) + dirname = _caching_dir() + with open(os.path.join(dirname, path + '.xml'), 'wt') as f: + f.write(render(get_current(), 'cvrf.j2').encode('utf-8')) + flash('File saved as %s' % path) + del_current() + return redirect(url_for('new')) + +@mod.route('/load/', methods=['GET', 'POST']) +def load(element): + if request.method != 'POST': + if has_current(): + # Suggest to save first + return render_template('cache/load.j2', element=element) + + # Ouch, GET request changing state of the server ... + + dirname = _caching_dir() + fpath = os.path.join(dirname, element+'.xml') + with open(fpath, 'rt') as f: + set_current(parse(f)) + os.remove(fpath) + flash('"%s" has been removed from cache' % element) + # Get some kind of id, and load the file. + return redirect(url_for('document.view')) diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/config.py --- a/farol/config.py Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/config.py Tue Sep 30 12:18:52 2014 +0200 @@ -29,4 +29,5 @@ """ the default configuration """ DEBUG=True SECRET_KEY=os.urandom(24) +# CACHING='disabled' diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/main.py --- a/farol/main.py Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/main.py Tue Sep 30 12:18:52 2014 +0200 @@ -33,8 +33,9 @@ from flask import Flask, request, render_template, redirect, url_for, flash from werkzeug import secure_filename +from . import cache from .document import document -from .session import get_current, set_current, has_current, del_current, document_required +from .session import get_current, set_current, has_current, document_required from .vulnerability import vulnerability from .producttree import producttree @@ -42,6 +43,7 @@ app.config.from_object('farol.config.Config') app.config.from_pyfile('farol.cfg', silent=True) +app.register_blueprint(cache.mod, url_prefix='/cache') app.register_blueprint(document, url_prefix='/document') app.register_blueprint(vulnerability, url_prefix='/vulnerability') app.register_blueprint(producttree, url_prefix='/producttree') @@ -56,16 +58,8 @@ @app.context_processor def cache_content(): """ List the documents in cache """ - dirname = app.config.get('CACHE_DIRECTORY', - os.path.join(app.instance_path, '_cache')) - if not os.path.exists(dirname): - os.makedirs(dirname) - l = [] - for path in os.listdir(dirname): - name, ext = os.path.splitext(path) - if ext == '.xml': - l.append(name) - return dict(cache=l) + return dict(caching=cache.caching_type(), + cache=cache.cache_content()) @app.context_processor def doc_properties(): @@ -98,7 +92,7 @@ def new(): if request.method != 'POST': return render_template('new.j2', has_document=has_current(), now=utcnow()) - url = None + if 'rhsa' in request.form: year, index = request.form['id'].split(':') parse_url("https://www.redhat.com/security/data/cvrf/%(year)s/cvrf-rhsa-%(year)s-%(index)s.xml" % {'year': year, 'index': index}) @@ -136,39 +130,6 @@ doc = render_cvrf(cvrf, format_ + '.j2') return render_template('render.j2', format_=format_, title=cvrf._title, type_=cvrf._type, doc=doc ) -@app.route('/save', methods=['GET', 'POST']) -@document_required -def save(): - if request.method != 'POST': - return render_template('save.j2', id_=get_current()._tracking._identification._id) - # Get some kind of filename, and save the cvrf on cache (disk) - path = secure_filename(request.form['fname']) - path, _ = os.path.splitext(path) - dirname = app.config.get('CACHE_DIRECTORY', - os.path.join(app.instance_path, '_cache')) - with open(os.path.join(dirname, path + '.xml'), 'wt') as f: - f.write(render_cvrf(get_current(), 'cvrf.j2').encode('utf-8')) - flash('File saved as %s' % path) - del_current() - return redirect(url_for('new')) - -@app.route('/load/', methods=['GET', 'POST']) -def load(element): - if request.method != 'POST': - if has_current(): - # Suggest to save first - return render_template('load.j2', element=element) - - dirname = app.config.get('CACHE_DIRECTORY', - os.path.join(app.instance_path, '_cache')) - fpath = os.path.join(dirname, element+'.xml') - with open(fpath, 'rt') as f: - set_current(parse(f)) - os.remove(fpath) - flash('"%s" has been removed from cache' % element) - # Get some kind of id, and load the file. - return redirect(url_for('document.view')) - @app.route('/about') def about(): return render_template('about.j2', instance_dir=app.instance_path) diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/session.py --- a/farol/session.py Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/session.py Tue Sep 30 12:18:52 2014 +0200 @@ -34,7 +34,9 @@ CVRFs = {} def set_current(cvrf): - session['id'] = uuid.uuid4() + if 'id' not in session: + # New user. + session['id'] = uuid.uuid4() CVRFs[session['id']] = cvrf def get_current(): @@ -52,8 +54,6 @@ if 'id' in session: if session['id'] in CVRFs: del CVRFs[session['id']] - del session['id'] - def document_required(f): @wraps(f) diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/about.j2 --- a/farol/templates/about.j2 Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/templates/about.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -38,11 +38,20 @@

This platform is meant as a way to review / create / edit / publish Security Advisories in an accessible way

In the current version, Advisories not currently saved are kept in memory of the running process. If the process terminates, and they are not saved, documents are lost.

{% if config.DEBUG and not config.DEBUG_SURE %} +
+

Debug Mode

+

Your application is Running in Debug mode. While this might be a choice you made, this is not suitable for Production deployment. The console is an example of unsafe debug facilities.

+

You can turn the Debug mode off by setting DEBUG=False in the configuration file located at the following path: {{ instance_dir }}/farol.cfg.

+

If you want to remove this message and the DEBUG Footer alert, but still want to keep the Debug mode on, just set DEBUG_SURE=True in your configuration file.

+ {% endif %}
-

Debug Mode

-

Your application is Running in Debug mode. While this might be a choice you made, this is not suitable for Production deployment. The console is an example of unsafe debug facilities.

-

You can turn the Debug mode off by setting DEBUG=False in the configuration file located at the following path: {{ instance_dir }}/farol.cfg.

-

If you want to remove this message and the DEBUG Footer alert, but still want to keep the Debug mode on, just set DEBUG_SURE=True in your configuration file.

+

Document cache

+ {% if not caching %} +

Document cache is disabled by configuration.

+ {% elif caching == 'global' %} +

All user share a global cache.

+ {% else %} +

Each session have a private cache, sessions are bound to a browser session. They are usually reset when the browser is restarted.

{% endif %}

Farol is published under GPLv2+, and is Copyright © Greenbone Networks GmbH.

diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/base.j2 --- a/farol/templates/base.j2 Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/templates/base.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -90,17 +90,19 @@ {% for format in ('cvrf', 'nasl', 'oval') %}
  • as {{ format | upper }}
  • {% endfor %} + {% if caching %}
  • -
    +
  • + {% endif %} {# /.navbar-collapse #} {# /.container-fluid #} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/cache/load.j2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/farol/templates/cache/load.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -0,0 +1,36 @@ +{# +# Description: +# Web Template used in Farol Design +# +# Authors: +# Benoît Allard +# +# Copyright: +# Copyright (C) 2014 Greenbone Networks GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +-#} + +{% extends "base.j2" %} +{% from "macros.j2" import textinput %} +{% block title %}Edit the title{% endblock %} + +{% block content %} +

    You asked to load {{ element }}, but you still have an unsaved document loaded. Do you want to save it first ?

    +
    + +Cancel +
    +{% endblock %} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/cache/save.j2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/farol/templates/cache/save.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -0,0 +1,36 @@ +{# +# Description: +# Web Template used in Farol Design +# +# Authors: +# Benoît Allard +# +# Copyright: +# Copyright (C) 2014 Greenbone Networks GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +-#} + +{% extends "base.j2" %} +{% from "macros.j2" import textinput %} +{% block title %}Save{% endblock %} + +{% block content %} +
    +{{ textinput("fname", "File name", "doc1", id_, required=True) }} + +Cancel +
    +{% endblock %} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/document/edit_tracking.j2 --- a/farol/templates/document/edit_tracking.j2 Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/templates/document/edit_tracking.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -31,7 +31,8 @@
    {{ textinput("id", "ID", value=tracking._identification._id, required=True) }} -{{ textinput("id_aliases", "Aliases", value=', '.join(tracking._identification._aliases)) }} +{{ textinput("id_aliases", "Aliases", value=', '.join(tracking._identification._aliases), + help="Multiple aliases should be separated by commas.") }} {{ selectinput("status", "Status", statuses, tracking._status) }} {{ textinput("version", "Version", value=version, required=True) }} {{ textinput("initial", "Initial Release Date", value=tracking._initialDate.isoformat(), required=True, type="datetime") }} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/load.j2 --- a/farol/templates/load.j2 Mon Sep 29 14:19:07 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -{# -# Description: -# Web Template used in Farol Design -# -# Authors: -# Benoît Allard -# -# Copyright: -# Copyright (C) 2014 Greenbone Networks GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. --#} - -{% extends "base.j2" %} -{% from "macros.j2" import textinput %} -{% block title %}Edit the title{% endblock %} - -{% block content %} -

    You asked to load {{ element }}, but you still have an unsaved document loaded. Do you want to save it first ?

    - - -Cancel - -{% endblock %} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/new.j2 --- a/farol/templates/new.j2 Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/templates/new.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -31,8 +31,8 @@ {% block title %}New{% endblock %} {% block content %} -{% if has_document %} -

    You have an unsaved document, maybe you want to save it first ?

    +{% if has_document and caching %} +

    You have an unsaved document, maybe you want to save it first ?

    {% endif %}

    New Document

    @@ -156,10 +156,12 @@ -{% call panel(heading="Load a document from the cache", title=4, collapsible=False) %} - -{% endcall %} +{% if caching %} + {% call panel(heading="Load a document from the cache", title=4, collapsible=False) %} + + {% endcall %} +{% endif %} {% endblock %} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/producttree/edit_product.j2 --- a/farol/templates/producttree/edit_product.j2 Mon Sep 29 14:19:07 2014 +0200 +++ b/farol/templates/producttree/edit_product.j2 Tue Sep 30 12:18:52 2014 +0200 @@ -37,7 +37,6 @@ {{ textinput('cpe', "CPE", placeholder="cpe:/a:...", value=product._cpe) }}
    {{ selectinput2('parent_branch', "Parent Branch", orphaned_leaves , product.getParentPath()) }} - {{ selectinput2('parent_relationship', "Parent relationship", orphaned_relationships, current_rel) }} diff -r a32f9b86edb4 -r 4219d6fb4c38 farol/templates/save.j2 --- a/farol/templates/save.j2 Mon Sep 29 14:19:07 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -{# -# Description: -# Web Template used in Farol Design -# -# Authors: -# Benoît Allard -# -# Copyright: -# Copyright (C) 2014 Greenbone Networks GmbH -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. --#} - -{% extends "base.j2" %} -{% from "macros.j2" import textinput %} -{% block title %}Save{% endblock %} - -{% block content %} -
    -{{ textinput("fname", "File name", "doc1", id_, required=True) }} - -Cancel -
    -{% endblock %}