Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First steps to reduce coupling between lib level and flask #1313

Merged
merged 8 commits into from
Dec 3, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions privacyidea/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
from privacyidea.api.lib.prepolicy import is_remote_user_allowed
from privacyidea.api.lib.utils import getParam
from privacyidea.lib.utils import get_client_ip
from privacyidea.lib.config import get_from_config, SYSCONF, ConfigClass
from privacyidea.lib.config import get_from_config, SYSCONF, update_config_object
from privacyidea.lib import _
import logging

Expand All @@ -81,7 +81,7 @@ def before_request():
"""
This is executed before the request
"""
g.config_object = ConfigClass()
update_config_object()
request.all_data = get_all_params(request.values, request.data)
privacyidea_server = current_app.config.get("PI_AUDIT_SERVERNAME") or \
request.host
Expand Down
4 changes: 2 additions & 2 deletions privacyidea/api/before_after.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from privacyidea.lib.event import EventConfiguration
from privacyidea.lib.lifecycle import call_finalizers
from privacyidea.api.auth import (user_required, admin_required)
from privacyidea.lib.config import get_from_config, SYSCONF, ConfigClass
from privacyidea.lib.config import get_from_config, SYSCONF, update_config_object
from privacyidea.lib.token import get_token_type
from .resolver import resolver_blueprint
from .policy import policy_blueprint
Expand Down Expand Up @@ -139,7 +139,7 @@ def before_request():
"""
# remove session from param and gather all parameters, either
# from the Form data or from JSON in the request body.
g.config_object = ConfigClass()
update_config_object()
request.all_data = get_all_params(request.values, request.data)
try:
request.User = get_user_from_param(request.all_data)
Expand Down
13 changes: 4 additions & 9 deletions privacyidea/api/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
from privacyidea.lib.policy import PolicyClass, ACTION
from privacyidea.lib.auth import get_db_admins
from privacyidea.lib.error import HSMException
from privacyidea.lib.crypto import geturandom
from privacyidea.lib.crypto import geturandom, set_hsm_password, get_hsm
from privacyidea.lib.importotp import GPGImport
import base64
import binascii
Expand Down Expand Up @@ -291,12 +291,7 @@ def set_security_module():
Set the password for the security module
"""
password = getParam(request.all_data, "password", required)
HSM = current_app.config["pi_hsm"]
hsm = HSM.get("obj")
if hsm.is_ready:
raise HSMException("HSM already set up.")

is_ready = hsm.setup_module({"password": password})
is_ready = set_hsm_password(password)
res = {"is_ready": is_ready}
g.audit_object.log({'success': res})
return send_result(res)
Expand All @@ -309,8 +304,8 @@ def get_security_module():
"""
Get the status of the security module.
"""
HSM = current_app.config["pi_hsm"]
is_ready = HSM.get("obj").is_ready
hsm = get_hsm(require_ready=False)
is_ready = hsm.is_ready
res = {"is_ready": is_ready}
g.audit_object.log({'success': res})
return send_result(res)
Expand Down
4 changes: 2 additions & 2 deletions privacyidea/api/ttype.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from privacyidea.lib.policy import PolicyClass
from privacyidea.lib.audit import getAudit
from privacyidea.lib.config import (get_token_class, get_from_config,
SYSCONF, ConfigClass)
SYSCONF, update_config_object)
from privacyidea.lib.user import get_user_from_param
from privacyidea.api.lib.postpolicy import postrequest, sign_response
from privacyidea.lib.utils import get_client_ip
Expand All @@ -60,7 +60,7 @@ def before_request():
"""
This is executed before the request
"""
g.config_object = ConfigClass()
update_config_object()
request.all_data = get_all_params(request.values, request.data)
privacyidea_server = current_app.config.get("PI_AUDIT_SERVERNAME") or \
request.host
Expand Down
5 changes: 2 additions & 3 deletions privacyidea/api/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
from privacyidea.api.lib.utils import get_all_params
from privacyidea.lib.config import (return_saml_attributes, get_from_config,
return_saml_attributes_on_fail,
SYSCONF)
SYSCONF, update_config_object)
from privacyidea.lib.audit import getAudit
from privacyidea.api.lib.prepolicy import (prepolicy, set_realm,
api_key_required, mangle,
Expand All @@ -89,7 +89,6 @@
offline_info,
add_user_detail_to_response, construct_radius_response)
from privacyidea.lib.policy import PolicyClass
from privacyidea.lib.config import ConfigClass
from privacyidea.lib.event import EventConfiguration
import logging
from privacyidea.api.lib.postpolicy import postrequest, sign_response
Expand Down Expand Up @@ -118,7 +117,7 @@ def before_request():
"""
This is executed before the request
"""
g.config_object = ConfigClass()
update_config_object()
request.all_data = get_all_params(request.values, request.data)
request.User = get_user_from_param(request.all_data)
privacyidea_server = current_app.config.get("PI_AUDIT_SERVERNAME") or \
Expand Down
4 changes: 3 additions & 1 deletion privacyidea/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@
work with user.py
"""

from flask_babel import gettext as _
from .framework import _

__all__ = ['_']
46 changes: 31 additions & 15 deletions privacyidea/lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@
import sys
import logging
import inspect
from flask import current_app, g

from .log import log_with
from ..models import (Config, db, Resolver, Realm, PRIVACYIDEA_TIMESTAMP,
save_config_timestamp)

from .framework import get_request_local_store, get_app_config_value
from .crypto import encryptPassword
from .crypto import decryptPassword
from .resolvers.UserIdResolver import UserIdResolver
Expand Down Expand Up @@ -109,9 +108,9 @@ def reload_from_db(self):
the internal timestamp, then read the complete data
:return:
"""
check_reload_config = get_app_config_value("PI_CHECK_RELOAD_CONFIG", 0)
if not self.timestamp or \
self.timestamp + datetime.timedelta(seconds=current_app.config.get(
"PI_CHECK_RELOAD_CONFIG", 0)) < datetime.datetime.now():
self.timestamp + datetime.timedelta(seconds=check_reload_config) < datetime.datetime.now():
db_ts = Config.query.filter_by(Key=PRIVACYIDEA_TIMESTAMP).first()
if reload_db(self.timestamp, db_ts):
self.config = {}
Expand Down Expand Up @@ -219,6 +218,29 @@ def get_privacyidea_config():
return get_from_config()


def update_config_object():
"""
Ensure that the request-local store contains a config object.
If it already contains one, check for updated configuration.
:return: a ConfigClass object
"""
store = get_request_local_store()
config_object = store['config_object'] = ConfigClass()
return config_object


def get_config_object():
"""
Return the request-local config object. If it does not exist yet, create it.
Currently, the config object is a singleton, so it is shared among threads.
:return: a ConfigClass object
"""
store = get_request_local_store()
if 'config_object' not in store:
store['config_object'] = update_config_object()
return store['config_object']


@log_with(log)
#@cache.cached(key_prefix="singleConfig")
def get_from_config(key=None, default=None, role="admin", return_bool=False):
Expand All @@ -235,9 +257,9 @@ def get_from_config(key=None, default=None, role="admin", return_bool=False):
:return: If key is None, then a dictionary is returned. If a certain key
is given a string/bool is returned.
"""
g.config_object = ConfigClass()
return g.config_object.get_config(key=key, default=default, role=role,
return_bool=return_bool)
config_object = update_config_object()
return config_object.get_config(key=key, default=default, role=role,
return_bool=return_bool)


#@cache.cached(key_prefix="resolver")
Expand Down Expand Up @@ -831,7 +853,6 @@ def get_prepend_pin():
:return: True or False
:rtype: bool
"""
from flask import g
r = get_from_config(key="PrependPin", default=False, return_bool=True)
return r

Expand Down Expand Up @@ -864,9 +885,7 @@ def get_privacyidea_node():
If it does not exist, the PI_AUDIT_SERVERNAME is used.
:return: the destinct node name
"""
node_name = current_app.config.get("PI_NODE",
current_app.config.get("PI_AUDIT_SERVERNAME",
"localnode"))
node_name = get_app_config_value("PI_NODE", get_app_config_value("PI_AUDIT_SERVERNAME", "localnode"))
return node_name


Expand All @@ -876,11 +895,8 @@ def get_privacyidea_nodes():
:return: list of nodes
"""
own_node_name = get_privacyidea_node()
nodes = current_app.config.get("PI_NODES", [])[:]
nodes = get_app_config_value("PI_NODES", [])[:]
if own_node_name not in nodes:
nodes.append(own_node_name)
return nodes


def get_app_config(key, default=None):
return current_app.config.get(key, default)
58 changes: 39 additions & 19 deletions privacyidea/lib/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
from .error import HSMException
import binascii
import ctypes
from flask import current_app
from Crypto.Hash import SHA as SHA1
from Crypto.Hash import SHA256 as HashFunc
from Crypto.Cipher import AES
Expand All @@ -63,6 +62,7 @@
import sys
import traceback

from privacyidea.lib.framework import get_app_local_store, get_app_config_value, get_app_config

FAILED_TO_DECRYPT_PASSWORD = "FAILED TO DECRYPT PASSWORD!"

Expand Down Expand Up @@ -270,7 +270,7 @@ def hash_with_pepper(password, rounds=10023, salt_size=10):

:return: Hash string
"""
key = current_app.config.get("PI_PEPPER", "missing")
key = get_app_config_value("PI_PEPPER", "missing")
pw_dig = passlib.hash.pbkdf2_sha512.encrypt(key + password, rounds=rounds,
salt_size=salt_size)
return pw_dig
Expand All @@ -279,42 +279,62 @@ def hash_with_pepper(password, rounds=10023, salt_size=10):
def verify_with_pepper(passwordhash, password):
# get the password pepper
password = password or ""
key = current_app.config.get("PI_PEPPER", "missing")
key = get_app_config_value("PI_PEPPER", "missing")
success = passlib.hash.pbkdf2_sha512.verify(key + password, passwordhash)
return success


def init_hsm():
"""
Initialize the HSM in the current_app config
Initialize the HSM in the app-local store

The config file pi.cfg may contain PI_HSM_MODULE and parameters like:
PI_HSM_MODULE_MODULE
PI_HSM_MODULE_SLOT_ID...

:return: hsm object
"""
config = current_app.config
if "pi_hsm" not in config or not isinstance(config["pi_hsm"], dict):
app_store = get_app_local_store()
if "pi_hsm" not in app_store or not isinstance(app_store["pi_hsm"], dict):
config = get_app_config()
HSM_config = {"obj": create_hsm_object(config)}
current_app.config["pi_hsm"] = HSM_config
app_store["pi_hsm"] = HSM_config
log.info("Initialized HSM object {0}".format(HSM_config))
hsm = current_app.config.get("pi_hsm").get('obj')
return hsm
return app_store["pi_hsm"]["obj"]


def _get_hsm():
def get_hsm(require_ready=True):
"""
Check that the HSM has been set up properly and return it.
If it is None, raise a HSMException.
If it is not ready, raise a HSMException. Optionally, the ready check can be disabled.
:param require_ready: Check whether the HSM is ready
:return: a HSM module object
"""
hsm = init_hsm()
if hsm is None or not hsm.is_ready: # pragma: no cover
if hsm is None:
raise HSMException('hsm is None!')
if require_ready and not hsm.is_ready:
raise HSMException('hsm not ready!')

return hsm


def set_hsm_password(password):
"""
Set the password for the HSM. Raises an exception if the HSM is already set up.
:param password: password string
:return: boolean flag indicating whether the HSM is ready now
"""
hsm = init_hsm()
if hsm.is_ready:
raise HSMException("HSM already set up.")
return hsm.setup_module({"password": password})


@log_with(log, log_entry=False)
def encryptPassword(password):
from privacyidea.lib.utils import to_utf8
hsm = _get_hsm()
hsm = get_hsm()
try:
ret = hsm.encrypt_password(to_utf8(password))
except Exception as exx: # pragma: no cover
Expand All @@ -325,7 +345,7 @@ def encryptPassword(password):

@log_with(log, log_entry=False)
def encryptPin(cryptPin):
hsm = _get_hsm()
hsm = get_hsm()
ret = hsm.encrypt_pin(cryptPin)
return ret

Expand All @@ -349,7 +369,7 @@ def decryptPassword(cryptPass, convert_unicode=False):
# other call sites of ``decryptPassword``. So we add the
# keyword argument to avoid breaking compatibility.
from privacyidea.lib.utils import to_unicode
hsm = _get_hsm()
hsm = get_hsm()
try:
ret = hsm.decrypt_password(cryptPass)
except Exception as exx: # pragma: no cover
Expand All @@ -366,7 +386,7 @@ def decryptPassword(cryptPass, convert_unicode=False):

@log_with(log, log_exit=False)
def decryptPin(cryptPin):
hsm = _get_hsm()
hsm = get_hsm()
ret = hsm.decrypt_pin(cryptPin)
return ret

Expand All @@ -386,7 +406,7 @@ def encrypt(data, iv, id=0):


'''
hsm = _get_hsm()
hsm = get_hsm()
ret = hsm.encrypt(data, iv, id)
return ret

Expand All @@ -405,7 +425,7 @@ def decrypt(input, iv, id=0):
:return: decryted buffer

'''
hsm = _get_hsm()
hsm = get_hsm()
ret = hsm.decrypt(input, iv, id)
return ret

Expand Down Expand Up @@ -495,7 +515,7 @@ def geturandom(length=20, hex=False):
:return: buffer of bytes

'''
hsm = _get_hsm()
hsm = get_hsm()
ret = hsm.random(length)

if hex:
Expand Down
4 changes: 2 additions & 2 deletions privacyidea/lib/eventhandler/scripthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"""
from privacyidea.lib.eventhandler.base import BaseEventHandler
from privacyidea.lib.utils import is_true
from privacyidea.lib.config import get_app_config
from privacyidea.lib.framework import get_app_config_value
from privacyidea.lib.error import ServerError
from privacyidea.lib import _
import logging
Expand All @@ -60,7 +60,7 @@ class ScriptEventHandler(BaseEventHandler):
def __init__(self, script_directory=None):
if not script_directory:
try:
self.script_directory = get_app_config("PI_SCRIPT_HANDLER_DIRECTORY",
self.script_directory = get_app_config_value("PI_SCRIPT_HANDLER_DIRECTORY",
"/etc/privacyidea/scripts")
except RuntimeError as e:
# In case of the tests we are outside of the application context
Expand Down
Loading