doi plugin

* feature: encrypt doi password
* bugfix: add URL rule for pages
This commit is contained in:
rekt-hard
2021-03-10 15:30:17 +01:00
committed by GitHub
parent daace427b9
commit 4094730481
5 changed files with 145 additions and 20 deletions

View File

@@ -105,22 +105,10 @@ DEPOSITS_HEADER_TEMPLATE = "invenio_theme_tugraz/header.html"
TUG_ROUTES = { TUG_ROUTES = {
"index": "/", "index": "/",
"comingsoon": "/comingsoon", "comingsoon": "/comingsoon",
"deposit_create": "/uploads/new",
} }
# Invenio-app-rdm INVENIO_DATACITE_URL = ""
# ============= INVENIO_DATACITE_UNAME = ""
# See https://invenio-app-rdm.readthedocs.io/en/latest/configuration.html INVENIO_DATACITE_PASS = "password"
# """override the default search page""" INVENIO_DATACITE_PREFIX = ""
# Keep this in sync
APP_RDM_ROUTES = {
"index": "/notvalid/notvalid/notvalid",
"help_search": "/help/search",
"record_search": "/search2",
"record_detail": "/records/<pid_value>",
"record_export": "/records/<pid_value>/export/<export_format>",
"record_file_preview": "/records/<pid_value>/preview/<path:filename>",
"record_file_download": "/records/<pid_value>/files/<path:filename>",
"deposit_search": "/uploads",
"deposit_create": "/uploads/new",
"deposit_edit": "/uploads/<pid_value>",
}

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python
"""crypto helper module. see https://gist.github.com/marcoslin/8026990."""
import binascii
from Crypto import Random
from Crypto.Cipher import AES
# ------------------------------
# DEFINE Encryption Class
class Cryptor(object):
"""Crypto class implementation.
Provide encryption and decryption function that works with crypto-js.
https://code.google.com/p/crypto-js/
Padding implemented as per RFC 2315: PKCS#7 page 21
http://www.ietf.org/rfc/rfc2315.txt
The key to make pycrypto work with crypto-js are:
1. Use MODE_CFB. For some reason, crypto-js decrypted result from MODE_CBC
gets truncated
2. Use Pkcs7 padding as per RFC 2315, the default padding used by CryptoJS
3. On the JS side, make sure to wrap ciphertext with CryptoJS.lib.CipherParams.create()
"""
# AES-256 key (32 bytes)
KEY = "01ab38d5e05c92aa098921d9d4626107133c7e2ab0e4849558921ebcc242bcb0"
BLOCK_SIZE = 16
@classmethod
def _pad_string(cls, in_string):
"""Pad an input string according to PKCS#7."""
in_len = len(in_string)
pad_size = cls.BLOCK_SIZE - (in_len % cls.BLOCK_SIZE)
return in_string.ljust(in_len + pad_size, chr(pad_size))
@classmethod
def _unpad_string(cls, in_string):
"""Remove the PKCS#7 padding from a text string."""
in_len = len(in_string)
pad_size = ord(in_string[-1])
if pad_size > cls.BLOCK_SIZE:
raise ValueError("Input is not padded or padding is corrupt")
return in_string[: in_len - pad_size]
@classmethod
def generate_iv(cls, size=16):
"""Generate initialization vector."""
return Random.get_random_bytes(size)
@classmethod
def encrypt(cls, in_string, in_key, in_iv=None):
"""Return encrypted string.
@in_string: Simple str to be encrypted
@key: hexified key
@iv: hexified iv
"""
key = binascii.a2b_hex(in_key)
if in_iv is None:
iv = cls.generate_iv()
in_iv = binascii.b2a_hex(iv)
else:
iv = binascii.a2b_hex(in_iv)
aes = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
padded = cls._pad_string(in_string).encode("utf-8")
encrypted = aes.encrypt(padded)
return in_iv, encrypted
@classmethod
def decrypt(cls, in_encrypted, in_key, in_iv):
"""Return encrypted string.
@in_encrypted: Base64 encoded
@key: hexified key
@iv: hexified iv
"""
key = binascii.a2b_hex(in_key)
iv = binascii.a2b_hex(in_iv)
aes = AES.new(key, AES.MODE_CFB, iv, segment_size=128)
decrypted = aes.decrypt(binascii.a2b_base64(in_encrypted).rstrip())
return cls._unpad_string(decrypted)

View File

@@ -9,6 +9,7 @@
"""invenio module for TUGRAZ theme.""" """invenio module for TUGRAZ theme."""
from . import config from . import config
from .views import deposit_create, index
class InvenioThemeTugraz(object): class InvenioThemeTugraz(object):
@@ -21,6 +22,10 @@ class InvenioThemeTugraz(object):
def init_app(self, app): def init_app(self, app):
"""Flask application initialization.""" """Flask application initialization."""
# add index route rule
# https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule
app.add_url_rule("/", "index", index)
app.add_url_rule("/uploads/new", "deposit_create", deposit_create)
self.init_config(app) self.init_config(app)
app.extensions["invenio-theme-tugraz"] = self app.extensions["invenio-theme-tugraz"] = self

View File

@@ -8,12 +8,20 @@
"""invenio module for TUGRAZ theme.""" """invenio module for TUGRAZ theme."""
import binascii
from typing import Dict from typing import Dict
from elasticsearch_dsl.utils import AttrDict from elasticsearch_dsl.utils import AttrDict
from flask import Blueprint, render_template from flask import Blueprint, current_app, render_template
from flask_login import login_required
from flask_menu import current_menu from flask_menu import current_menu
from invenio_app_rdm.records_ui.views.deposits import (
get_form_config,
get_search_url,
new_record,
)
from .crypto import Cryptor
from .search import FrontpageRecordsSearch from .search import FrontpageRecordsSearch
@@ -30,6 +38,7 @@ def ui_blueprint(app):
blueprint.add_url_rule(routes["index"], view_func=index) blueprint.add_url_rule(routes["index"], view_func=index)
blueprint.add_url_rule(routes["comingsoon"], view_func=comingsoon) blueprint.add_url_rule(routes["comingsoon"], view_func=comingsoon)
blueprint.add_url_rule(routes["deposit_create"], view_func=deposit_create)
@blueprint.app_template_filter("make_dict_like") @blueprint.app_template_filter("make_dict_like")
def make_dict_like(value: str, key: str) -> Dict[str, str]: def make_dict_like(value: str, key: str) -> Dict[str, str]:
@@ -51,9 +60,42 @@ def index():
"""Frontpage.""" """Frontpage."""
return render_template( return render_template(
"invenio_theme_tugraz/index.html", "invenio_theme_tugraz/index.html",
records=FrontpageRecordsSearch()[:5].sort("-created").execute()) records=FrontpageRecordsSearch()[:5].sort("-created").execute(),
)
def comingsoon(): def comingsoon():
"""Frontpage.""" """Frontpage."""
return render_template("invenio_theme_tugraz/comingsoon.html") return render_template("invenio_theme_tugraz/comingsoon.html")
def get_application_details():
"""Application credentials for DOI."""
url = current_app.config.get("invenio_datacite_url") or ""
username = current_app.config.get("INVENIO_DATACITE_UNAME") or ""
password = current_app.config.get("INVENIO_DATACITE_PASS") or ""
prefix = current_app.config.get("INVENIO_DATACITE_PREFIX") or ""
password_iv, encrypted_password = Cryptor.encrypt(password, Cryptor.KEY)
details = {
"datacite_url": url,
"datacite_uname": username,
"datacite_pass": binascii.b2a_base64(encrypted_password).rstrip(),
"datacite_prefix": prefix,
"datacite_password_iv": password_iv,
}
return details
@login_required
def deposit_create():
"""Create a new deposit."""
return render_template(
"invenio_app_rdm/records/deposit.html",
forms_config=get_form_config(createUrl=("/api/records")),
datacite_config=get_application_details(),
searchbar_config=dict(searchUrl=get_search_url()),
record=new_record(),
files=dict(default_preview=None, enabled=True, entries=[], links={}),
)

View File

@@ -59,7 +59,9 @@ install_requires = [
"elasticsearch_dsl>=7.2.1", "elasticsearch_dsl>=7.2.1",
"invenio_search>=1.4.0,<2.0.0", "invenio_search>=1.4.0,<2.0.0",
# keep this package updated. # keep this package updated.
"invenio_app_rdm>=0.18.8", "invenio_app_rdm<=1.0.0",
# needed for DOI credential encryption
"pycryptodome==3.10.1",
] ]
packages = find_packages() packages = find_packages()