Skip to content

Commit

Permalink
Merge pull request #39 from ctreffe/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jobrachem authored Apr 14, 2020
2 parents da28089 + 384439a commit 19c6a12
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 100 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ alfred.conf
exp/
.vscode/settings.json
test.py
test_copy.py
test_copy2.py
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Mortimer v0.4.4

## Encryption Keys

* Every user now has a unique fernet encryption key, generated via `cryptography.fernet.Fernet.generate_key()`. The key is passed as an entry in the `config` dictionary (key: `encryption_key`) to the `generate_experiment()` function in `alfredo.py`.
* The key itself is saved to the mortimer database in encrypted form.
* Usage: See [here](https://github.com/ctreffe/alfred/releases/tag/v1.0.6)

## Bugfixes

* Fixed a bug that prevent the deletion of single files in the resources pane to work properly.

# Mortimer v0.4.3

## Bugfixes
Expand Down
2 changes: 1 addition & 1 deletion mortimer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from flask_dropzone import Dropzone
import pymongo

__version__ = '0.4.3'
__version__ = '0.4.4'

# the EnvironSetter sets enviroment variables for the current session
# It is not included in the GitHub repository, because it contains sensitive
Expand Down
13 changes: 9 additions & 4 deletions mortimer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ class Config:
ssl_ca_certs = None
ssl_ca_path = None

SECRET_KEY = os.environ.get(
"SECRET_KEY"
) # secret key of flask app (e.g. for encrypted session data)
# secret key of app (e.g. for encrypted session data)
SECRET_KEY = os.environ.get("SECRET_KEY")

# URL-safe base64-encoded 32-byte key for fernet encryption
FERNET_KEY = os.environ.get("FERNET_KEY")

PAROLE = os.environ.get("PAROLE") # Parole/Passphrase for registration
EXP_PER_PAGE = 10 # number of experiments displayed per page

Expand Down Expand Up @@ -65,7 +68,9 @@ class Config:

# Flask-Dropzone settings:
DROPZONE_ALLOWED_FILE_CUSTOM = True
DROPZONE_ALLOWED_FILE_TYPE = ".pdf, image/*, .txt, .xml, .pem, .mp3, .mp4, .ogg, .csv"
DROPZONE_ALLOWED_FILE_TYPE = (
".pdf, image/*, .txt, .xml, .pem, .mp3, .mp4, .ogg, .csv"
)
DROPZONE_MAX_FILE_SIZE = 10
DROPZONE_MAX_FILES = 100
DROPZONE_UPLOAD_ON_CLICK = True
Expand Down
10 changes: 10 additions & 0 deletions mortimer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

from flask import current_app
from mortimer import db, login_manager
from mortimer.utils import create_fernet
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from datetime import datetime
from flask_login import UserMixin
from cryptography.fernet import Fernet


@login_manager.user_loader
Expand All @@ -16,13 +18,21 @@ class User(db.Document, UserMixin):
username = db.StringField(required=True, unique=True, max_length=20)
email = db.EmailField(required=True, unique=True)
role = db.StringField(required=True, default="user")
encryption_key = db.BinaryField()
password = db.StringField(required=True)
experiments = db.ListField(db.ObjectIdField())

def get_reset_token(self, expires_sec=1800):
# methode for creating a token for password reset
s = Serializer(current_app.config["SECRET_KEY"], expires_sec)
return s.dumps({"user_id": str(self.id)}).decode("utf-8")

@staticmethod
def generate_encryption_key():
key = Fernet.generate_key()
f = create_fernet()
encrypted_key = f.encrypt(key)
return encrypted_key

@staticmethod
def verify_reset_token(token):
Expand Down
11 changes: 8 additions & 3 deletions mortimer/templates/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
{% block content %}

<div class="content-section">
<div class="media">

</div>
<form method="POST" action="" class="needs-validation" novalidate>
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Account Info</legend>
<!-- Encryption key status -->
{% if current_user.encryption_key %}
<p><i class="fas fa-check-circle"></i> Your account is ready for using encryption in your alfred
experiments.</p>
{% else %}
<p><i class="fas fa-times-circle"></i> Your account is not ready for using encryption in your alfred
experiments. Please log out and log back in again to get ready!</p>
{% endif %}
<div class="form-group">
{{ form.username.label(class="form-control-label") }}

Expand Down
62 changes: 62 additions & 0 deletions mortimer/templates/additional/display_dir_controls.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<div>
<p>
<span><code style="color:black">{{path}}</code></span>
<span class="float-md-right">
<button class="btn btn-outline-primary btn-sm ml-2" type="button" data-toggle="collapse"
data-target="#collapse_{{directory}}_{{call_id}}" aria-expanded="false" aria-controls="collapseExample">
Show Files
</button>
<button class="btn btn-outline-primary btn-sm" type="button" data-toggle="collapse"
data-target="#NewDirectory_{{directory}}_{{call_id}}" aria-expanded="false"
aria-controls="collapseExample">
New Subdirectory
</button>
<a class="btn btn-outline-success btn-sm" href="{{upload_url}}">Upload Files</a>
<button type="button" class="btn btn-outline-danger btn-sm mt-1 mb-1" data-toggle="modal"
data-target="#deleteModal_{{directory}}_{{call_id}}">Delete Directory</button>
</span>
</p>

<div class="collapse" id="NewDirectory_{{directory}}_{{call_id}}">
<form action="{{new_subdirectory_url}}" , method="POST">
<div><input class="form-control form-control-lg mr-3 mb-3 mt-3" id="new_directory" name="new_directory"
required type="text" value="" placeholder="Name">
<input class="btn btn-outline-primary btn-sm" type="submit" value="Create">
</div>


</form>
</div>

<div class="collapse pt-3" id="collapse_{{directory}}_{{call_id}}">
{{file_display|safe}} <br>
</div>


{{subdirectory_display}}

</div>

<div class="modal fade" id="deleteModal_{{directory}}_{{call_id}}" tabindex="-1" role="dialog"
aria-labelledby="deleteModal_{{directory}}Label_{{call_id}}" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModal_{{directory}}Label_{{call_id}}">Delete All Files</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Are you sure that you want to delete the directory <code>{{path}}</code>? The data stored in Mortimer
will be lost!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form method="POST" action="{{delete_directory_url}}">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
</div>
</div>
</div>
35 changes: 35 additions & 0 deletions mortimer/templates/additional/display_one_file.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!-- Test -->
<div class="row m-1">
<div class="col-md-10 float-left">
<span class="ml-2">- {{f}}</span>
</div>
<div class="col-md-2 text-right">
<button type="button" class="btn btn-outline-danger btn-sm" data-toggle="modal"
data-target="#deleteModal_{{ fileid }}">Delete</button>
</div>
</div>

<!-- Modal for delete confirmation -->
<div class="modal fade" id="deleteModal_{{ fileid }}" tabindex="-1" role="dialog" aria-labelledby="deleteModal_{{ fileid }}Label"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModal_{{ fileid }}">Delete File</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Are you sure that you want to delete the file <code>{{f}}</code>? The data stored in Mortimer will be
lost!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form method="POST" action="{{url}}">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
</div>
</div>
</div>
9 changes: 8 additions & 1 deletion mortimer/users/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def register():
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode("utf-8")
user = User(username=form.username.data, email=form.email.data, password=hashed_password)
user.encryption_key = User.generate_encryption_key()
user.save()
flash("Account created for %s." % form.username.data, "success")
return redirect(url_for('users.login'))
Expand Down Expand Up @@ -52,6 +53,12 @@ def login():
login_user(user, remember=form.remember.data)
next_page = request.args.get("next")
flash("Login Successful.", "success")

# for backwards compatibility, generate encryption key if there isnt already one
if not user.encryption_key:
user.encryption_key = User.generate_encryption_key()
user.save()

return redirect(next_page) if next_page else redirect(url_for('main.home'))
else:
flash("Login Unsuccessful. Please check username and password", "danger")
Expand Down Expand Up @@ -84,7 +91,7 @@ def account():
form.username.data = current_user.username
form.email.data = current_user.email

return render_template("account.html", title="Account", form=form)
return render_template("account.html", title="Account", form=form, user = current_user)


@users.route("/reset_password", methods=["GET", "POST"])
Expand Down
101 changes: 10 additions & 91 deletions mortimer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@
from flask import current_app
from mortimer import mail
from flask_mail import Message
from flask import url_for
from flask import url_for, current_app, render_template
from uuid import uuid4
from jinja2 import Template
from cryptography.fernet import Fernet

def create_fernet():
app_fernet_key = current_app.config["FERNET_KEY"].encode()
return Fernet(app_fernet_key)


def set_experiment_settings(title, version, author, exp_id, path):
Expand Down Expand Up @@ -110,39 +115,9 @@ def display_files(file_list: list, path: str,
experiment_title=experiment_title,
username=experiment_author,
relative_path=filepath)
display_one = Template('''<div class=\"row m-1\">
<div class="col-md-10 float-left">
<span class=\"ml-2\">- {{f}}</span>
</div>
<div class="col-md-2 btn btn-outline-danger btn-sm"
<button type="button" data-toggle="modal" data-target="#deleteModal_{{filepath}}">Delete</button>
</div>
</div>
<div class="modal fade" id="deleteModal_{{filepath}}" tabindex="-1" role="dialog" aria-labelledby="deleteModal_{{filepath}}Label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModal_{{filepath}}Label">Delete All Files</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Are you sure that you want to delete the file <code>{{f}}</code>? The data stored in Mortimer will be lost!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form method="POST" action="{{url}}">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
</div>
</div>
</div>
''')

out.append(display_one.render(f=f, filepath=filepath, url=url))
fileid = "{}_{}".format(f.replace(".", ""), str(uuid4()))
single_file = render_template("additional/display_one_file.html", f=f, fileid=fileid, url=url)
out.append(single_file)
return "".join(out)

def display_directory_controls(directory: str,
Expand All @@ -168,63 +143,7 @@ def display_directory_controls(directory: str,
username=experiment_author,
relative_path=path)

out = Template('''<div>
<p>
<span><code style="color:black">{{path}}</code></span>
<span class="float-md-right">
<button class=\"btn btn-outline-primary btn-sm ml-2\" type=\"button\" data-toggle=\"collapse\" data-target=\"#collapse_{{directory}}_{{call_id}}\" aria-expanded=\"false\" aria-controls=\"collapseExample\">
Show Files
</button>
<button class=\"btn btn-outline-primary btn-sm\" type=\"button\" data-toggle=\"collapse\" data-target=\"#NewDirectory_{{directory}}_{{call_id}}\" aria-expanded=\"false\" aria-controls=\"collapseExample\">
New Subdirectory
</button>
<a class=\"btn btn-outline-success btn-sm\" href=\"{{upload_url}}\">Upload Files</a>
<button type="button" class="btn btn-outline-danger btn-sm mt-1 mb-1" data-toggle="modal" data-target="#deleteModal_{{directory}}_{{call_id}}">Delete Directory</button>
</span>
</p>
<div class="collapse" id="NewDirectory_{{directory}}_{{call_id}}">
<form action="{{new_subdirectory_url}}", method="POST">
<div><input class="form-control form-control-lg mr-3 mb-3 mt-3" id="new_directory" name="new_directory" required type="text" value="" placeholder="Name">
<input class="btn btn-outline-primary btn-sm" type="submit" value="Create"></div>
</form>
</div>
<div class=\"collapse pt-3\" id=\"collapse_{{directory}}_{{call_id}}\">
{{file_display}} <br>
</div>
{{subdirectory_display}}
</div>
<div class="modal fade" id="deleteModal_{{directory}}_{{call_id}}" tabindex="-1" role="dialog" aria-labelledby="deleteModal_{{directory}}Label_{{call_id}}" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModal_{{directory}}Label_{{call_id}}">Delete All Files</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Are you sure that you want to delete the directory <code>{{path}}</code>? The data stored in Mortimer will be lost!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form method="POST" action="{{delete_directory_url}}">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
</div>
</div>
</div>
''')

return out.render(path=path,
return render_template("additional/display_dir_controls.html", path=path,
directory=directory,
upload_url=upload_url,
call_id=call_id,
Expand Down
8 changes: 8 additions & 0 deletions mortimer/web_experiments/alfredo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from time import time
from uuid import uuid4
from mortimer.models import WebExperiment
from mortimer.utils import create_fernet
from bson.objectid import ObjectId
from alfred.alfredlog import init_logging, getLogger
import re
Expand Down Expand Up @@ -160,6 +161,13 @@ def start(expid):

custom_settings = experiment.settings
custom_settings['mortimer_specific']['session_id'] = sid

if current_user.encryption_key:
f = create_fernet()
key = f.decrypt(current_user.encryption_key)
custom_settings["encryption_key"] = key
else:
flash("Please log out and back in again to generate your personal encryption key.", "warning")

try:
if number_of_func_params(module.generate_experiment) > 2:
Expand Down

0 comments on commit 19c6a12

Please sign in to comment.