changeset 12:4219d6fb4c38

Implement three kind of caches
author Benoît Allard <benoit.allard@greenbone.net>
date Tue, 30 Sep 2014 12:18:52 +0200
parents a32f9b86edb4
children d5265a0da13a
files CHANGES farol/cache.py farol/config.py farol/main.py farol/session.py farol/templates/about.j2 farol/templates/base.j2 farol/templates/cache/load.j2 farol/templates/cache/save.j2 farol/templates/document/edit_tracking.j2 farol/templates/load.j2 farol/templates/new.j2 farol/templates/producttree/edit_product.j2 farol/templates/save.j2
diffstat 14 files changed, 238 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- 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)
--- /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 <benoit.allard@greenbone.net>
+#
+# 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/<element>', 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'))
--- 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'
 
--- 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/<element>', 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)
--- 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)
--- 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 @@
   <p>This platform is meant as a way to review / create / edit / publish Security Advisories in an accessible way</p>
   <p>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.</p>
   {% if config.DEBUG and not config.DEBUG_SURE %}
+    <hr>
+    <h3 id="debug">Debug Mode</h3>
+    <p>Your application is Running in Debug mode. While this might be a choice you made, this is not suitable for Production deployment. The <a href="/console">console</a> is an example of unsafe debug facilities.</p>
+    <p>You can turn the Debug mode off by setting <code>DEBUG=False</code> in the configuration file located at the following path: <code>{{ instance_dir }}/farol.cfg</code>.</p>
+    <p>If you want to remove this message and the DEBUG Footer alert, but still want to keep the Debug mode on, just set <code>DEBUG_SURE=True</code> in your configuration file.</p>
+  {% endif %}
   <hr>
-  <h3 id="debug">Debug Mode</h3>
-  <p>Your application is Running in Debug mode. While this might be a choice you made, this is not suitable for Production deployment. The <a href="/console">console</a> is an example of unsafe debug facilities.</p>
-  <p>You can turn the Debug mode off by setting <code>DEBUG=False</code> in the configuration file located at the following path: <code>{{ instance_dir }}/farol.cfg</code>.</p>
-  <p>If you want to remove this message and the DEBUG Footer alert, but still want to keep the Debug mode on, just set <code>DEBUG_SURE=True</code> in your configuration file.</p>
+  <h3 id="caching">Document cache</h3>
+  {% if not caching %}
+    <p class="text-muted">Document cache is <strong>disabled</strong> by configuration.</p>
+  {% elif caching == 'global' %}
+    <p>All user share a <strong>global cache</strong>.</p>
+  {% else %}
+    <p>Each session have a <strong>private cache</strong>, sessions are bound to a browser <em>session</em>. They are usually reset when the browser is restarted.</p>
   {% endif %}
   <hr>
   <p><strong>Farol</strong> is published under GPLv2+, and is Copyright &copy; Greenbone Networks GmbH.</p>
--- 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') %}<li><a href="{{ url_for('render', format_=format)}}">as {{ format | upper }}</a></li>{% endfor %}
             </ul>
           </li>
+          {% if caching %}
           <li>
-            <form action="{{ url_for('save') }}">
+            <form action="{{ url_for('cache.save') }}">
               <button class="btn btn-success navbar-btn"{% if not has_current %} disabled="disabled"{% endif %}>Save</button>
             </form>
           </li>
           <li class="dropdown">
             <a href="#" class="dropdown-toggle" data-toggle="dropdown">Cache <span class="caret"></span></a>
             <ul class="dropdown-menu" role="menu">
-              {% for element in cache | sort %}<li><a href="{{ url_for('load', element=element)}}">{{ element }}</a></li>{% endfor %}
+              {% for element in cache | sort %}<li><a href="{{ url_for('cache.load', element=element)}}">{{ element }}</a></li>{% endfor %}
             </ul>
           </li>
+          {% endif %}
         </ul>
       </div>{# /.navbar-collapse #}
     </div>{# /.container-fluid #}
--- /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 <benoit.allard@greenbone.net>
+#
+# 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 %}
+<p>You asked to load {{ element }}, but you still have an unsaved document loaded. Do you want to <a href="{{ url_for('.save') }}">save</a> it first ?</p>
+<form role="form" method="POST">
+<button class="btn btn-primary" type="submit">Load "{{ element }}"</button>
+<a class="btn btn-danger" href="{{ url_for('welcome') }}">Cancel</a>
+</form>
+{% endblock %}
--- /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 <benoit.allard@greenbone.net>
+#
+# 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 %}
+<form role="form" method="POST">
+{{ textinput("fname", "File name", "doc1", id_, required=True) }}
+<button class="btn btn-primary" type="submit">Save</button>
+<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
+</form>
+{% endblock %}
--- 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 @@
 <form role="form" method="POST">
 
 {{ 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") }}
--- 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 <benoit.allard@greenbone.net>
-#
-# 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 %}
-<p>You asked to load {{ element }}, but you still have an unsaved document loaded. Do you want to <a href="{{ url_for('save') }}">save</a> it first ?</p>
-<form role="form" method="POST">
-<button class="btn btn-primary" type="submit">Load "{{ element }}"</button>
-<a class="btn btn-danger" href="{{ url_for('welcome') }}">Cancel</a>
-</form>
-{% endblock %}
--- 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 %}
-<p class="text-danger">You have an unsaved document, maybe you want to <a href={{ url_for('save') }}>save</a> it first ?</p>
+{% if has_document and caching %}
+<p class="text-danger">You have an unsaved document, maybe you want to <a href={{ url_for('cache.save') }}>save</a> it first ?</p>
 {% endif %}
 <h3>New Document</h3>
 
@@ -156,10 +156,12 @@
 
 </div>
 
-{% call panel(heading="Load a document from the cache", title=4, collapsible=False) %}
-<ul>
-{% for element in cache | sort %}<li><a href="{{ url_for('load', element=element)}}">{{ element }}</a></li>{% endfor %}
-</ul>
-{% endcall %}
+{% if caching %}
+  {% call panel(heading="Load a document from the cache", title=4, collapsible=False) %}
+  <ul>
+    {% for element in cache | sort %}<li><a href="{{ url_for('cache.load', element=element)}}">{{ element }}</a></li>{% endfor %}
+  </ul>
+  {% endcall %}
+{% endif %}
 
 {% endblock %}
--- 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) }}
 <hr>
 {{ selectinput2('parent_branch', "Parent Branch", orphaned_leaves , product.getParentPath()) }}
-<!-- {{ current_rel }} -->
 {{ selectinput2('parent_relationship', "Parent relationship", orphaned_relationships, current_rel) }}
 
 <button class="btn btn-primary" type="submit">{{ action }}</button>
--- 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 <benoit.allard@greenbone.net>
-#
-# 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 %}
-<form role="form" method="POST">
-{{ textinput("fname", "File name", "doc1", id_, required=True) }}
-<button class="btn btn-primary" type="submit">Save</button>
-<a class="btn btn-danger" href="{{ url_for('document.view') }}">Cancel</a>
-</form>
-{% endblock %}

http://farol.wald.intevation.org