mirror of
https://github.com/Cian-H/invenio-theme-iform.git
synced 2025-12-23 13:11:58 +00:00
doi plugin
* feature: encrypt doi password * bugfix: add URL rule for pages
This commit is contained in:
@@ -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>",
|
|
||||||
}
|
|
||||||
|
|||||||
88
invenio_theme_tugraz/crypto.py
Normal file
88
invenio_theme_tugraz/crypto.py
Normal 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)
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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={}),
|
||||||
|
)
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user