Esempio n. 1
0
class Crypt(CryptAbstract):
    def __init__(self, *args, **kwargs):
        # TODO Remove temporally backward compatibility on future versions
        crypt_file_key_env = self.__get_updated_crypt_file_key_env()  # Temporally backward compatibility
        self._loader = LoadFile(kwargs.get("path"), crypt_file_key_env, DEFAULT_KEY_FILENAME)
        super().__init__(*args, **kwargs)

    def generate_key(self, password: Text, write_to_file: bool = False) -> bytes:
        byte_password = password.encode()  # Convert to type bytes
        salt = os.urandom(16)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA512_256(), length=32, salt=salt, iterations=100000, backend=default_backend()
        )
        key = base64.urlsafe_b64encode(kdf.derive(byte_password))  # Can only use kdf once
        if write_to_file:
            self._loader.put_file(key, "wb")
        return key

    def read_key(self):
        key = self._loader.get_file()
        if not key:
            # TODO Remove temporally backward compatibility on future versions
            crypt_file_key_env = self.__get_updated_crypt_file_key_env()  # Temporally backward compatibility
            raise FileDoesNotExistException(
                "Decrypt key {} not exists. You must set a correct env var {} "
                "or run `pyms crypt create-key` command".format(self._loader.path, crypt_file_key_env)
            )
        return key

    def encrypt(self, message):
        key = self.read_key()
        message = message.encode()
        f = Fernet(key)
        encrypted = f.encrypt(message)
        return encrypted

    def decrypt(self, encrypted):
        key = self.read_key()
        encrypted = encrypted.encode()
        f = Fernet(key)
        decrypted = f.decrypt(encrypted)
        return str(decrypted, encoding="utf-8")

    def delete_key(self):
        os.remove(self._loader.get_path_from_env())

    @staticmethod
    def __get_updated_crypt_file_key_env() -> str:
        result = CRYPT_FILE_KEY_ENVIRONMENT
        if (os.getenv(CRYPT_FILE_KEY_ENVIRONMENT_LEGACY) is not None) and (
            os.getenv(CRYPT_FILE_KEY_ENVIRONMENT) is None
        ):
            result = CRYPT_FILE_KEY_ENVIRONMENT_LEGACY
        return result
Esempio n. 2
0
class Crypt(CryptAbstract):
    def __init__(self, *args, **kwargs):
        self._loader = LoadFile(kwargs.get("path"), CRYPT_FILE_KEY_ENVIRONMENT,
                                DEFAULT_KEY_FILENAME)
        super().__init__(*args, **kwargs)

    def generate_key(self, password: Text, write_to_file: bool = False):
        password = password.encode()  # Convert to type bytes
        salt = os.urandom(16)
        kdf = PBKDF2HMAC(algorithm=hashes.SHA512_256(),
                         length=32,
                         salt=salt,
                         iterations=100000,
                         backend=default_backend())
        key = base64.urlsafe_b64encode(
            kdf.derive(password))  # Can only use kdf once
        if write_to_file:
            self._loader.put_file(key, 'wb')
        return key

    def read_key(self):
        key = self._loader.get_file()
        if not key:
            raise FileDoesNotExistException(
                "Decrypt key {} not exists. You must set a correct env var {} "
                "or run `pyms crypt create-key` command".format(
                    self._loader.path, CRYPT_FILE_KEY_ENVIRONMENT))
        return key

    def encrypt(self, message):
        key = self.read_key()
        message = message.encode()
        f = Fernet(key)
        encrypted = f.encrypt(message)
        return encrypted

    def decrypt(self, encrypted):
        key = self.read_key()
        encrypted = encrypted.encode()
        f = Fernet(key)
        decrypted = f.decrypt(encrypted)
        return str(decrypted, encoding="utf-8")

    def delete_key(self):
        os.remove(self._loader.get_path_from_env())
Esempio n. 3
0
class ConfFile(dict):
    """Recursive get configuration from dictionary, a config file in JSON or YAML format from a path or
    `CONFIGMAP_FILE` environment variable.
    **Atributes:**
    * path: Path to find the `DEFAULT_CONFIGMAP_FILENAME` and `DEFAULT_KEY_FILENAME` if use encrypted vars
    * empty_init: Allow blank variables
    * config: Allow to pass a dictionary to ConfFile without use a file
    """
    _empty_init = False

    def __init__(self, *args, **kwargs):
        """
        Get configuration from a dictionary(variable `config`), from path (variable `path`) or from
        environment with the constant `CONFIGMAP_FILE`
        Set the configuration as upper case to inject the keys in flask config. Flask search for uppercase keys in
        `app.config.from_object`
        ```python
        if key.isupper():
            self[key] = getattr(obj, key)
        ```
        """
        self._loader = LoadFile(kwargs.get("path"), CONFIGMAP_FILE_ENVIRONMENT,
                                DEFAULT_CONFIGMAP_FILENAME)
        self._crypt = Crypt(path=kwargs.get("path"))
        self._empty_init = kwargs.get("empty_init", False)
        config = kwargs.get("config")
        if config is None:
            config = self._loader.get_file(anyconfig.load)
        if not config:
            if self._empty_init:
                config = {}
            else:
                path = self._loader.path if self._loader.path else ""
                raise ConfigDoesNotFoundException(
                    "Configuration file {}not found".format(path + " "))

        config = self.set_config(config)

        super(ConfFile, self).__init__(config)

    def to_flask(self) -> Dict:
        return ConfFile(config={k.upper(): v for k, v in self.items()})

    def set_config(self, config: Dict) -> Dict:
        """
        Set a dictionary as attributes of ConfFile. This attributes could be access as `ConfFile["attr"]` or
        ConfFile.attr
        :param config: a dictionary from `config.yml`
        :return:
        """
        config = dict(self.normalize_config(config))
        pop_encripted_keys = []
        for k, v in config.items():
            if k.lower().startswith("enc_"):
                k_not_crypt = re.compile(re.escape('enc_'), re.IGNORECASE)
                setattr(self, k_not_crypt.sub('', k), self._crypt.decrypt(v))
                pop_encripted_keys.append(k)
            else:
                setattr(self, k, v)

        # Delete encrypted keys to prevent decrypt multiple times a element
        for x in pop_encripted_keys:
            config.pop(x)

        return config

    def normalize_config(
            self,
            config: Dict) -> Iterable[Tuple[Text, Union[Dict, Text, bool]]]:
        for key, item in config.items():
            if isinstance(item, dict):
                item = ConfFile(config=item, empty_init=self._empty_init)
            yield self.normalize_keys(key), item

    @staticmethod
    def normalize_keys(key: Text) -> Text:
        """The keys will be transformed to a attribute. We need to replace the charactes not valid"""
        key = key.replace("-", "_")
        return key

    def __eq__(self, other):
        if not isinstance(other, ConfFile) and not isinstance(other, dict):
            return False
        return dict(self) == dict(other)

    def __getattr__(self, name, *args, **kwargs):
        try:
            keys = self.normalize_keys(name).split(".")
            aux_dict = self
            for k in keys:
                aux_dict = aux_dict[k]
            return aux_dict
        except KeyError:
            if self._empty_init:
                return ConfFile(config={}, empty_init=self._empty_init)
            raise AttrDoesNotExistException(
                "Variable {} not exist in the config file".format(name))

    def reload(self):
        """
        Remove file from memoize variable, return again the content of the file and set the configuration again
        :return: None
        """
        config_src = self._loader.reload(anyconfig.load)
        self.set_config(config_src)

    def __setattr__(self, name, value, *args, **kwargs):
        super(ConfFile, self).__setattr__(name, value)