Skip to content

Commit

Permalink
Merge branch 'master' into bugfix/files_inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoooo committed May 17, 2019
2 parents 88e994b + f1a8d8d commit 0d9a17f
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 17 deletions.
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
language: python
python:
- "2.7"
env:
-DJANGO=1.11
install:
- pip install -r requirements.txt
- pip install coverage==4.5.1
script:
- coverage run --source='.' manage.py test waves
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGELOG
=========

Version 1.7.0 - 2019-02-28
--------------------------

- [Encryption] - MAJOR update on cryptography library in use within WAVES - https://github.com/pyca/cryptography see README


Version 1.6.x - 2018-03-10
----------------------------

Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/lirmm/waves-core.svg?branch=master)](https://travis-ci.org/lirmm/waves-core)

README
======

Expand Down Expand Up @@ -44,3 +46,16 @@ Support
-------

If you are having issues, (or just want to say hello): we have a mailing list located at: waves@lirmm.fr


-- UPDATE to 1.7.xx from 1.6.xxx --
-----------------------------------

If migrating from a version 1.6.x to any 1.7.x, you must patch you database encrypted password to a more reliable
encrypted technology (https://github.com/pyca/cryptography). This script must be run once and only once !
To migrate your data please do the following:
- retrieve the latest version 1.7.x
- stop any running service
- save your database first
- once the new version is installed, run `manage.py migrate_keys`
- if you don't have any error message: your keys are now more secured !
38 changes: 37 additions & 1 deletion docs/dev_doc/sample_code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,43 @@ Some WAVES API entries require to be authenticated (jobs list, job details, job
print(job_details['created'])
print(job_details['updated'])
Integrate a WAVES service form
------------------------------

You've got a website and you want your visitors could submit jobs? The better way for this is to add in your website using API.
Here, you're supposed to know there is a service named "sample_service" defined on demo WAVES instance (as you see in serviceList above).

.. code-block:: python
from coreapi import Client, auth
from coreapi.codecs import CoreJSONCodec, JSONCodec, TextCodec
decoders = [JSONCodec(), CoreJSONCodec(), TextCodec()]
client = Client(decoders=decoders)
document = client.get('http://waves.demo.atgc-montpellier.fr/waves/api/schema')
wavesform = client.action(document, ['services', 'form'], params={"service_app_name": 'sample_service'}, validate=False, encoding='multipart/form-data')
Now, you just render this form into your template (ex. in a django tpl).

.. warning::
Don't forget to add forms.css and services.js from your waves instance as in this sample.

.. code-block:: django
{% block head %}
{% addtoblock "waves_forms_css" %} <link rel="stylesheet" href="http://waves.demo.atgc-montpellier.fr/static/waves/css/forms.css">{% endaddtoblock %}
{% endblock %}
{% block main %}
<!-- Import the web form as is -->
{{ wavesform|safe }}
{% endblock main %}
{% block footer %}
{% addtoblock "js" %}
<script src="http://waves.demo.atgc-montpellier.fr/static/waves/js/services.js"></script>
{% endaddtoblock %}
{% endblock footer %}
Integrate a WAVES service form
------------------------------
Expand Down Expand Up @@ -129,4 +166,3 @@ Inputs are defined by expected inputs of the "sample_service". Be aware, "valida
job_list = client.action(document, ['jobs', 'list'])
print(job_list)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ djangorestframework==3.8.2
backports.ssl-match-hostname
inflection==0.3.1
psutil==5.4.5
pycrypto==2.6.1
cryptography==2.5
python-magic==0.4.15
saga-python==0.50.1
setproctitle==1.1.10
Expand Down
3 changes: 0 additions & 3 deletions waves/wcore/admin/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,6 @@ def display_run_params(self, obj):
display_run_params.short_description = "Runner initial params"

def add_view(self, request, form_url='', extra_context=None):
""" Override default behavior when creating a new service:
Do not show "save and add another button"
"""
context = extra_context or {}
context['show_save_as_new'] = False
context['show_save_and_add_another'] = False
Expand Down
78 changes: 78 additions & 0 deletions waves/wcore/management/commands/update_keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.core.management.base import BaseCommand
import base64
from Crypto.Cipher import XOR

from ...models import AdaptorInitParam
from ...models.base import WavesBaseModel
from ...settings import waves_settings
from ...utils.encrypt import Encrypt
from cryptography.fernet import InvalidToken


class ObsoleteEncrypt(object):
""" Encrypt values based on Django settings secret key substring """

def __init__(self):
raise RuntimeError('This class is intended to be used statically')

@staticmethod
def decrypt(to_decode):
""" Decrypt previously encoded 'to_decode'
:param to_decode: value to decode
:return: string initial value
"""
if len(waves_settings.SECRET_KEY) != 32:
raise RuntimeError('Encoded values must use a key at least a 32 chars length secret')
cipher = XOR.new(waves_settings.SECRET_KEY)
return cipher.decrypt(base64.b64decode(to_decode))


class TmpAdaptorInitParam(AdaptorInitParam):

class Meta:
app_label = "waves.wcore"
proxy = True

@classmethod
def from_db(cls, db, field_names, values):
return super(WavesBaseModel, cls).from_db(db, field_names, values)


class Command(BaseCommand):
help = 'Update the current encrypted values in WAVES database'

def handle(self, *args, **kwargs):
self.stdout.write("Starting patching ...")
all_encrypted = TmpAdaptorInitParam.objects.filter(crypt=True)
for encrypted in all_encrypted:
error = False
old_decrypted = ""
try:
Encrypt.decrypt(bytes(encrypted.value))
self.stdout.write("Value already encrypted with current algorithm %s" % encrypted.value)
except InvalidToken:
self.stdout.write("Updating encrypted value: %s - %s " % (encrypted, encrypted.value))

try:
old_decrypted = ObsoleteEncrypt.decrypt(encrypted.value)
except StandardError as e:
self.stderr.write("An error occured decrypting value with previous cryptography system ")
self.stderr.write("Related info [id:%s] [related object:%s] [related id:%s]" % (encrypted.id, encrypted.content_object, encrypted.content_object.id))
error = True
if not error:
new_value = Encrypt.encrypt(old_decrypted)
encrypted.value = new_value
self.stdout.write("Saving new updated value: %s " % encrypted.value)
encrypted.save()
self.stdout.write('Checking current values...')
try:
all_encrypted = AdaptorInitParam.objects.filter(crypt=True)
for decrypted in all_encrypted:
self.stdout.write('Successfully migrated %s for "%s"' % (decrypted, decrypted.content_object))
except StandardError as e:
self.stderr.write("Error occurred while controlling values see log ")
self.stderr.write(e)
self.stdout.write("All done ...")
1 change: 1 addition & 0 deletions waves/wcore/management/commands/waves.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from ..base import SubcommandDispatcher
Expand Down
2 changes: 1 addition & 1 deletion waves/wcore/management/commands/wpurge.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class Command(DaemonCommand):
"""
Dedicated command initialize daemon for old jobs purges
Dedicated command to summarize current WAVES specific settings
"""
help = 'Managing WAVES job queue states'
SLEEP_TIME = 2
Expand Down
2 changes: 1 addition & 1 deletion waves/wcore/templates/waves/admin/submit_line.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save"/>{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
{% url 'opts|admin_urlname:' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}"
class="deletelink">{% trans "Delete" %}</a></p>
{% endif %}
Expand Down
15 changes: 8 additions & 7 deletions waves/wcore/utils/encrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from __future__ import unicode_literals

from waves.wcore.settings import waves_settings
from Crypto.Cipher import XOR
from cryptography.fernet import Fernet

import base64


Expand All @@ -19,10 +20,10 @@ def encrypt(to_encode):
:param to_encode: value to encode
:return: base64 based encoded string
"""
if len(waves_settings.SECRET_KEY) != 32:
if len(waves_settings.SECRET_KEY) < 32:
raise RuntimeError('Encoded values must use a key at least a 32 chars length secret')
cipher = XOR.new(waves_settings.SECRET_KEY)
encoded = base64.b64encode(cipher.encrypt(to_encode))
encoder = Fernet(base64.urlsafe_b64encode(waves_settings.SECRET_KEY))
encoded = encoder.encrypt(bytes(to_encode))
return encoded

@staticmethod
Expand All @@ -31,7 +32,7 @@ def decrypt(to_decode):
:param to_decode: value to decode
:return: string initial value
"""
if len(waves_settings.SECRET_KEY) != 32:
if len(waves_settings.SECRET_KEY) < 32:
raise RuntimeError('Encoded values must use a key at least a 32 chars length secret')
cipher = XOR.new(waves_settings.SECRET_KEY)
return cipher.decrypt(base64.b64decode(to_decode))
encoder = Fernet(base64.urlsafe_b64encode(waves_settings.SECRET_KEY))
return encoder.decrypt(bytes(to_decode))
5 changes: 2 additions & 3 deletions waves/wcore/views/services.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from __future__ import unicode_literals

import logging
from uuid import UUID
import logging

from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import get_template
from django.urls import reverse
from django.views import generic
from django.core.exceptions import PermissionDenied

from waves.wcore.exceptions.jobs import JobException
from waves.wcore.forms.services import ServiceSubmissionForm
Expand All @@ -20,7 +20,6 @@
Service = get_service_model()
logger = logging.getLogger(__name__)


class SubmissionFormView(generic.FormView, generic.DetailView):
model = Service
context_object_name = 'service'
Expand Down

0 comments on commit 0d9a17f

Please sign in to comment.