From ec877c5b8aed1d43df38cf1c62a8239e12f2d313 Mon Sep 17 00:00:00 2001 From: B Date: Mon, 28 Oct 2024 22:46:59 -0500 Subject: [PATCH 01/10] =?UTF-8?q?=E2=9C=A8=20feat(core):=20added=20status?= =?UTF-8?q?=20verify=20on=20crypto=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/main.py | 120 ++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 12 deletions(-) diff --git a/crypto_controller/main.py b/crypto_controller/main.py index c2ae022..59acafc 100644 --- a/crypto_controller/main.py +++ b/crypto_controller/main.py @@ -2,7 +2,6 @@ import sys import argparse import logging -from enum import verify from logging.handlers import RotatingFileHandler import shutil import hashlib @@ -13,6 +12,7 @@ import warnings import smtplib from email.mime.text import MIMEText +import json # Added import for JSON handling from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa, padding @@ -310,25 +310,105 @@ def verify(self) -> bool: with open(self.key_pair_file, "r") as kp_file: encrypted_kp = kp_file.read() decrypted_kp = self.decrypt(encrypted_kp) - kp_content = decrypted_kp.split( - ":" - ) # Assuming kp_content was concatenated without JSON + kp_data = json.loads(decrypted_kp) + + # Required fields in kp_data + required_fields = { + "public_key_file", + "public_fp_sha1", + "public_fp_sha256", + "private_key_file", + "private_fp_sha1", + "private_fp_sha256", + "key_pair_file", + "creation_date", + "expiration_date", + } - # Assuming kp_content has specific order: public_key_name:public_key_footprint:private_key_name:private_key_footprint:key_pair_name:no_before_date:no_after_date - # TODO: Adjust this according to your actual key_pair_file format + missing_fields = required_fields - kp_data.keys() + if missing_fields: + logger.error(f"Missing fields in key pair data: {missing_fields}") + return False + + # Check if key files exist + if not os.path.exists(kp_data["public_key_file"]): + logger.error(f"Public key file does not exist: {kp_data['public_key_file']}") + return False + + if not os.path.exists(kp_data["private_key_file"]): + logger.error(f"Private key file does not exist: {kp_data['private_key_file']}") + return False + + # Verify public key footprint + current_public_fp = get_key_footprint(kp_data["public_key_file"], "public") + if ( + current_public_fp.sha1 != kp_data["public_fp_sha1"] + or current_public_fp.sha256 != kp_data["public_fp_sha256"] + ): + logger.error("Public key fingerprints do not match.") + return False + + # Verify private key footprint + current_private_fp = get_key_footprint(kp_data["private_key_file"], "private") + if ( + current_private_fp.sha1 != kp_data["private_fp_sha1"] + or current_private_fp.sha256 != kp_data["private_fp_sha256"] + ): + logger.error("Private key fingerprints do not match.") + return False + + # Optionally, check if the key pair file path matches + if kp_data["key_pair_file"] != self.key_pair_file: + logger.error("Key pair file path does not match the expected location.") + return False + + # Optionally, check if the current date is before expiration + expiration_date = datetime.strptime(kp_data["expiration_date"], "%d%m%Y%H%M%S") + if datetime.now() > expiration_date: + logger.error("The key pair has expired.") + return False - # For simplicity, we'll skip detailed verification here logger.info("Key verification successful.") return True except Exception as error: logger.error(f"Verification failed: {error}", exc_info=True) return False + def get_expiration(self) -> str: + """ + Retrieves the expiration date of the key pair. + + Returns: + str: Expiration date in ISO format, or 'Unknown' if not available. + """ + try: + with open(self.key_pair_file, "r") as kp_file: + encrypted_kp = kp_file.read() + decrypted_kp = self.decrypt(encrypted_kp) + logger.debug(f"Decrypted key pair content: {decrypted_kp}") + + # Parse JSON + kp_data = json.loads(decrypted_kp) + logger.debug(f"Key pair data: {kp_data}") + + expiration_str = kp_data.get("expiration_date") + if not expiration_str: + raise ValueError("Expiration date not found in key pair data.") + + # Parse the date string + expiration_date = datetime.strptime(expiration_str, "%d%m%Y%H%M%S") + # Return the date in ISO format + logger.info(f"Expiration date retrieved successfully: {expiration_date.isoformat()}") + return expiration_date.isoformat() + except Exception as error: + logger.error(f"Failed to get expiration date: {error}", exc_info=True) + return "Unknown" + def create_keys(self) -> None: """ Generates a new RSA key pair and stores them securely. """ - if verify(): + if self.verify(): logger.info("Keys validation successful, nothing to do.") return try: @@ -357,10 +437,22 @@ def create_keys(self) -> None: public_fp = get_key_footprint(self.public_key_file, "public") private_fp = get_key_footprint(self.private_key_file, "private") - # Create key pair content as concatenated string + # Create key pair content as JSON now = datetime.now() - expire = now + timedelta(days=365 * 6) # Expiration in 6 years - key_pair_content = f"{self.public_key_file}:{public_fp.sha1},{public_fp.sha256}:{self.private_key_file}:{private_fp.sha1},{private_fp.sha256}:{self.key_pair_file}:{now.strftime('%d%m%Y%H%M%S')}:{expire.strftime('%d%m%Y%H%M%S')}" + expire = now + timedelta(days=365 * int(os.getenv("EXPIRATION", "1"))) + key_pair_data = { + "public_key_file": self.public_key_file, + "public_fp_sha1": public_fp.sha1, + "public_fp_sha256": public_fp.sha256, + "private_key_file": self.private_key_file, + "private_fp_sha1": private_fp.sha1, + "private_fp_sha256": private_fp.sha256, + "key_pair_file": self.key_pair_file, + "creation_date": now.strftime("%d%m%Y%H%M%S"), + "expiration_date": expire.strftime("%d%m%Y%H%M%S"), + } + + key_pair_content = json.dumps(key_pair_data) encrypted_kp = self.encrypt(key_pair_content) # Uses hybrid encryption with open(self.key_pair_file, "w") as kp_file: @@ -436,10 +528,14 @@ def get_status(self) -> None: "Private Key Exists": os.path.exists(self.private_key_file), "Key Pair File Exists": os.path.exists(self.key_pair_file), "Key Verification": self.verify(), + "Expiration": self.get_expiration(), } print("CryptoController Status:") for key, value in status.items(): - print(f" - {key}: {'Yes' if value else 'No'}") + if isinstance(value, bool): + print(f" - {key}: {'Yes' if value else 'No'}") + else: + print(f" - {key}: {value}") except Exception as error: logger.error(f"Failed to retrieve status: {error}", exc_info=True) print("Failed to retrieve status. Check logs for more details.") From 05881591525c726b1f0d3e7650895ded4b757df2 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 14:50:10 -0500 Subject: [PATCH 02/10] =?UTF-8?q?=E2=9C=A8=20feat(core):=20added=20crypto?= =?UTF-8?q?=20controller=20validation=20status=20and=20timestamp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 + crypto_controller/README.md | 210 +++++++++++ crypto_controller/main.py | 10 +- crypto_controller/pyproject.toml | 32 ++ crypto_controller/requirements.txt | 6 + pyproject.toml | 8 + tests/__init__.py | 0 tests/test_crypto_controller.py | 537 +++++++++++++++++++++++++++++ 8 files changed, 803 insertions(+), 5 deletions(-) create mode 100644 crypto_controller/README.md create mode 100644 crypto_controller/pyproject.toml create mode 100644 crypto_controller/requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_crypto_controller.py diff --git a/README.md b/README.md index f6dbe30..496d63b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # 🗄️ Scripts Repository +![CI/CD](https://img.shields.io/badge/CI/CD-Pipeline-blue) +![Status](https://img.shields.io/badge/Status-Stable-green.svg) +![Python](https://img.shields.io/badge/Python-3.11%2B-blue.svg) +![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg) + This repository contains a collection of base Python scripts that are invoked by the CI/CD processes of other repositories, especially the template repository. These scripts are used for formatting, checking files, version control, and updating the year in file headers or documentation. ## 📚 Table of Contents diff --git a/crypto_controller/README.md b/crypto_controller/README.md new file mode 100644 index 0000000..8e0f343 --- /dev/null +++ b/crypto_controller/README.md @@ -0,0 +1,210 @@ +# 🔐 CryptoController + +![Status](https://img.shields.io/badge/Status-Stable-green.svg) +![Python](https://img.shields.io/badge/Python-3.11%2B-blue.svg) +![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg) + +CryptoController is a robust Python application designed for secure key management, encryption, and decryption operations. It leverages hybrid encryption (AES + RSA) to ensure data confidentiality and integrity, making it ideal for applications requiring strong cryptographic safeguards. + +## 📚 Table of Contents + +- [Features](#-features) +- [Installation](#-installation) +- [Configuration](#-configuration) +- [Usage](#-usage) + - [Initialization](#-initialization) + - [Renewing Keys](#-renewing-keys) + - [Encrypting Data](#-encrypting-data) + - [Decrypting Data](#-decrypting-data) + - [Checking Status](#-checking-status) +- [Environment Variables](#-environment-variables) +- [Logging](#-logging) +- [License](#-license) +- [Contact](#-contact) + +## ✨ Features + +- **Hybrid Encryption:** Combines AES (symmetric) and RSA (asymmetric) encryption for enhanced security. +- **Key Management:** Generates, verifies, and renews RSA key pairs securely. +- **Expiration Handling:** Tracks key expiration dates and sends email alerts before keys expire. +- **Status Reporting:** Provides detailed status reports of the cryptographic setup. +- **Secure Storage:** Stores keys in a protected certificate vault with appropriate permissions. +- **Logging:** Comprehensive logging with rotating file handlers for easy monitoring and debugging. + +## 🛠️ Installation + +1. **Clone the Repository:** + + ```bash + cd scripts/crypto_controller + ``` + +2. **Create a Virtual Environment** + + ```bash + python -m venv venv + ``` + +3. **Activate the Virtual Environment** + + On Unix or MacOS: + + ```bash + source venv/bin/activate + ``` + + On Windows: + + ```bash + .\venv\Scripts\activate + ``` + + - or + + ```bash + powershell.exe -ExecutionPolicy Bypass -File .\venv\Scripts\Activate.ps1 + ``` + +4. **Upgrade pip** + + ```bash + pip install --upgrade pip + ``` + +5. **Install Dependencies** + + ```bash + pip install -r requirements.txt + ``` + + - Deactivate the Virtual Environment + + When you're done, deactivate the environment: + + ```bash + deactivate + ``` + +## ⚙️ Configuration + +**Environment Variables**: + +Create a .env file in the project root directory and populate it with the following variables: + +```bash +CPU_USAGE_THRESHOLD=70.0 +MEMORY_USAGE_THRESHOLD=395.0 +DISK_SPACE_THRESHOLD=75.0 +EXPIRATION=1 +TOKEN_SECURITY=your_secure_token_here +SMTP_SERVER=smtp.example.com +SMTP_PORT=587 +SMTP_USER=your_email@example.com +SMTP_PASSWORD=your_email_password +ALERT_RECIPIENT=recipient@example.com +``` + +- Descriptions: + - CPU_USAGE_THRESHOLD: CPU usage percentage threshold. + - MEMORY_USAGE_THRESHOLD: Memory usage threshold in MB. + - DISK_SPACE_THRESHOLD: Disk space usage percentage threshold. + - EXPIRATION: Number of years before key expiration. + - TOKEN_SECURITY: Token for fetching the private key password securely. + - SMTP_SERVER: SMTP server address for sending emails. + - SMTP_PORT: SMTP server port. + - SMTP_USER: SMTP server username. + - SMTP_PASSWORD: SMTP server password. + - ALERT_RECIPIENT: Email address to receive expiration alerts. + +## 🚀 Usage + +CryptoController supports several operations: init, renew, encrypt, decrypt, and status. + +### 📦 Initialization + +Generates a new RSA key pair and sets up the certificate vault. + +```bash +python main.py init --log-level DEBUG +``` + + Options: + --cert-location: Directory to store certificates (default: certs in the current directory). + --key-pair-name: Name of the key pair (default: Crypto-Key-Pair-). + --log-level: Logging level (INFO or DEBUG). + +### 🔄 Renewing Keys + +Renews existing keys by cleaning the vault and generating new keys. + +```bash +python main.py renew --log-level DEBUG +``` + +## 🔒 Encrypting Data + +Encrypts plain text using hybrid encryption. + +```bash +python main.py encrypt "Your sensitive data here" --log-level DEBUG +``` + + Output: Encrypted Base64 string. + +### 🔓 Decrypting Data + +Decrypts previously encrypted data. + +```bash +python main.py decrypt "EncryptedBase64StringHere" --log-level DEBUG +``` + + Output: Decrypted plain text. + +### 📝 Checking Status + +Retrieves and displays the current status of the CryptoController. + +```bash +python main.py status --log-level DEBUG +``` + +### 📜 Environment Variables + +Ensure all required environment variables are set in the .env file: + + Resource Thresholds: + CPU_USAGE_THRESHOLD + MEMORY_USAGE_THRESHOLD + DISK_SPACE_THRESHOLD + + Key Management: + EXPIRATION: Number of years before key expiration. + + Security: + TOKEN_SECURITY: Token for secure API access. + + Email Notifications: + SMTP_SERVER + SMTP_PORT + SMTP_USER + SMTP_PASSWORD + ALERT_RECIPIENT + +## 📊 Logging + +Logs are maintained in crypto_controller.log with rotating file handlers to prevent excessive file sizes. + + Log Levels: + INFO: General operational messages. + DEBUG: Detailed diagnostic information. + +## 📫 Contact + +For any inquiries or support, please open an issue or contact [r6ty5r296it6tl4eg5m.constant214@passinbox.com](mailto:r6ty5r296it6tl4eg5m.constant214@passinbox.com). + +--- + +## 📜 License + +2024 - This project is licensed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html). You are free to use, modify, and distribute this software under the terms of the GPL-3.0 license. For more details, please refer to the [LICENSE](../LICENSE) file included in this repository. diff --git a/crypto_controller/main.py b/crypto_controller/main.py index 59acafc..0a22ab4 100644 --- a/crypto_controller/main.py +++ b/crypto_controller/main.py @@ -398,7 +398,7 @@ def get_expiration(self) -> str: # Parse the date string expiration_date = datetime.strptime(expiration_str, "%d%m%Y%H%M%S") # Return the date in ISO format - logger.info(f"Expiration date retrieved successfully: {expiration_date.isoformat()}") + logger.debug(f"Expiration date retrieved successfully: {expiration_date.isoformat()}") return expiration_date.isoformat() except Exception as error: logger.error(f"Failed to get expiration date: {error}", exc_info=True) @@ -439,7 +439,7 @@ def create_keys(self) -> None: # Create key pair content as JSON now = datetime.now() - expire = now + timedelta(days=365 * int(os.getenv("EXPIRATION", "1"))) + expire = now + timedelta(days=365 * int(os.getenv("CERT_EXPIRATION_YEARS", "1"))) key_pair_data = { "public_key_file": self.public_key_file, "public_fp_sha1": public_fp.sha1, @@ -586,15 +586,15 @@ def fetch_private_key_password() -> str: str: The private key password. """ try: - token_security = os.getenv("TOKEN_SECURITY") + token_security = os.getenv("API_TOKEN_SECURITY") headers = { "content-type": "application/json", "token_security": token_security, } response = requests.get( - "https://c2d81kn4r1.execute-api.us-east-1.amazonaws.com/security/private-key", + os.getenv("API_URI"), headers=headers, - timeout=10, # seconds + timeout=os.getenv("API_TIMEOUT"), ) response.raise_for_status() # Raises HTTPError for bad responses pk_key_pass = response.json().get("value") diff --git a/crypto_controller/pyproject.toml b/crypto_controller/pyproject.toml new file mode 100644 index 0000000..375c312 --- /dev/null +++ b/crypto_controller/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["setuptools>=42", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "crypto_controller" +version = "1.1.8" +description = "A robust Python application for secure key management, encryption, and decryption operations." +authors = [ + { name = "B", email = "g46327wsj1.marbling129@passinbox.com" } +] +readme = "README.md" +license = { text = "Apache-2.0" } +keywords = ["cryptography", "encryption", "decryption", "key management"] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", +] + +[project.dependencies] +cryptography = ">=3.4.7" +requests = ">=2.25.1" +python-dotenv = ">=0.19.0" +wheel = ">=0.36.2" + +[project.scripts] +crypto-controller = "main:main" + +[tool.setuptools.packages.find] +where = ["."] +exclude = ["tests*"] diff --git a/crypto_controller/requirements.txt b/crypto_controller/requirements.txt new file mode 100644 index 0000000..7c6a8fe --- /dev/null +++ b/crypto_controller/requirements.txt @@ -0,0 +1,6 @@ +cryptography>=3.4.7 +requests>=2.25.1 +python-dotenv>=0.19.0 +wheel>=0.36.2 +pytest>=7.0.0 +pytest-cov>=4.0.0 diff --git a/pyproject.toml b/pyproject.toml index d8245ea..f85adda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ bump2version = "^1.0.0" python-dotenv = "^1.0.0" cryptography = "^43.0.0" requests = "^2.32.3" +wheel = ">=0.36.2" [tool.poetry.group.dev.dependencies] pre-commit = "^4.0.0" @@ -57,6 +58,9 @@ exclude = ''' minversion = "6.0" addopts = "--cov=app --cov-report=xml:coverage.xml --cov-report=term" testpaths = ["tests"] +pythonpath = [ + ".", "crypto_controller" +] [tool.isort] profile = "black" @@ -73,3 +77,7 @@ rcfile = ".pylintrc" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.setuptools.packages.find] +where = ["."] +exclude = ["tests*"] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_crypto_controller.py b/tests/test_crypto_controller.py new file mode 100644 index 0000000..e4fb69e --- /dev/null +++ b/tests/test_crypto_controller.py @@ -0,0 +1,537 @@ +# tests/test_crypto_controller.py + +import os +import json +from unittest import mock + +import pytest +import shutil +import tempfile +import logging +from logging.handlers import RotatingFileHandler +from unittest.mock import patch, mock_open +from datetime import datetime, timedelta + +# Import CryptoController and related functions from main.py +from crypto_controller.main import CryptoController, get_key_footprint, Footprint + + +# Configure logger for the test module +def configure_test_logger(log_level: str = "DEBUG") -> logging.Logger: + """ + Configures the logger with rotating file handler and console handler for tests. + + Args: + log_level (str): Logging level (INFO, DEBUG, etc.). + + Returns: + logging.Logger: Configured logger instance. + """ + logger = logging.getLogger("test_crypto_controller") + numeric_level = getattr(logging, log_level.upper(), None) + if not isinstance(numeric_level, int): + raise ValueError(f"Invalid log level: {log_level}") + + logger.setLevel(numeric_level) + + # File handler with rotation + file_handler = RotatingFileHandler( + "test_crypto_controller.log", maxBytes=5 * 1024 * 1024, backupCount=5 + ) + # Console handler + console_handler = logging.StreamHandler() + + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") + file_handler.setFormatter(formatter) + console_handler.setFormatter(formatter) + + # Clear existing handlers to avoid duplicate logs + logger.handlers.clear() + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + return logger + + +# Initialize the test logger +logger = configure_test_logger("DEBUG") + + +# Helper function to create dummy PEM files +def create_dummy_pem(path: str, key_type: str = "public") -> None: + """ + Creates a dummy PEM file for testing purposes. + + Args: + path (str): Path where the PEM file will be created. + key_type (str): Type of the key ('public' or 'private'). + """ + if key_type == "public": + content = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn...\n" + "-----END PUBLIC KEY-----\n" + ) + else: + content = ( + "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" + "MIIE6TAbBgkqhkiG9w0BBQMwDgQIoY...\n" + "-----END ENCRYPTED PRIVATE KEY-----\n" + ) + with open(path, "w") as f: + f.write(content) + logger.debug(f"Created dummy PEM file at {path} as {key_type} key.") + + +# Fixtures + + +@pytest.fixture(autouse=True) +def mock_env_variables(): + """ + Automatically mock environment variables for all tests. + """ + env_vars = { + "API_URI": "https://api.mocked.com/get_password", + "API_TOKEN_SECURITY": "mocked_secure_token", + "API_TIMEOUT": "5", + "SMTP_SERVER": "smtp.mocked.com", + "SMTP_PORT": "587", + "SMTP_USER": "mocked_user@mocked.com", + "SMTP_PASSWORD": "mocked_password", + "ALERT_RECIPIENT": "admin@mocked.com", + } + with patch.dict(os.environ, env_vars, clear=True): + yield + + +@pytest.fixture +def mock_load_dotenv(): + """ + Mock the load_dotenv function to prevent actual loading of .env files. + """ + with patch("crypto_controller.main.load_dotenv", return_value=None) as mock_ld: + yield mock_ld + + +@pytest.fixture +def mock_requests_get(): + """ + Mock the requests.get method for API calls. + """ + with patch("crypto_controller.main.requests.get") as mock_get: + mock_response = mock.Mock() + mock_response.raise_for_status.return_value = None + mock_response.json.return_value = {"value": "secure_pass"} + mock_get.return_value = mock_response + yield mock_get + + +@pytest.fixture +def mock_smtp(): + """ + Mock the smtplib.SMTP class to prevent actual email sending. + """ + with patch("crypto_controller.main.smtplib.SMTP") as mock_smtp_class: + yield mock_smtp_class + + +@pytest.fixture +def temp_cert_vault() -> str: + """ + Creates a temporary directory to simulate the certificate vault. + Cleans up after the test. + + Yields: + str: Path to the temporary certificate vault. + """ + vault = tempfile.mkdtemp() + logger.debug(f"Created temporary certificate vault at {vault}") + yield vault + shutil.rmtree(vault) + logger.debug(f"Deleted temporary certificate vault at {vault}") + + +@pytest.fixture +def crypto_controller_fixture(temp_cert_vault: str) -> CryptoController: + """ + Initializes a CryptoController instance with a temporary vault. + + Args: + temp_cert_vault (str): Path to the temporary certificate vault. + + Returns: + CryptoController: Initialized CryptoController instance. + """ + logger.debug("Initializing CryptoController fixture.") + return CryptoController( + cert_location=temp_cert_vault, key_pair_name="test_key_pair", private_key_pass="test_pass" + ) + + +# Test Cases + + +def test_env_variables_loaded(mock_load_dotenv, crypto_controller_fixture): + """ + Test that environment variables are loaded correctly using load_dotenv. + """ + logger.debug("Testing environment variable loading.") + mock_load_dotenv.assert_called_once() + assert crypto_controller_fixture.cert_location is not None + assert crypto_controller_fixture.key_pair_name == "test_key_pair" + logger.debug("Environment variables loaded and CryptoController initialized correctly.") + + +def test_create_cert_vault(crypto_controller_fixture, temp_cert_vault): + """ + Test the creation of the certificate vault with correct permissions. + """ + logger.debug("Testing certificate vault creation.") + # Ensure the vault does not exist + shutil.rmtree(temp_cert_vault, ignore_errors=True) + logger.debug("Deleted existing certificate vault to test creation.") + assert not os.path.exists(temp_cert_vault) + + # Create the vault + crypto_controller_fixture.create_cert_vault() + logger.debug("Called create_cert_vault method.") + + # Verify the vault creation + assert os.path.exists(temp_cert_vault) + logger.debug("Certificate vault exists after creation.") + + # Check permissions (mode 700) + vault_mode = os.stat(temp_cert_vault).st_mode & 0o777 + logger.debug(f"Certificate vault permissions: {oct(vault_mode)}") + assert vault_mode == 0o700 + logger.debug("Certificate vault has correct permissions (700).") + + +def test_encrypt_decrypt(crypto_controller_fixture: CryptoController): + """ + Test the encrypt and decrypt functionality of CryptoController. + """ + logger.debug("Testing encrypt and decrypt functionality.") + # Mock the loaded keys + with patch.object( + crypto_controller_fixture, "load_keys", return_value=(mock.Mock(), mock.Mock()) + ) as mock_load_keys: + public_key_mock = mock.Mock() + private_key_mock = mock.Mock() + mock_load_keys.return_value = (public_key_mock, private_key_mock) + logger.debug("Mocked load_keys to return public and private key mocks.") + + # Mock encryption of AES key with RSA public key + public_key_mock.encrypt.return_value = b"encrypted_aes_key" + logger.debug("Mocked public_key.encrypt to return encrypted AES key.") + + # Mock decryption of AES key with RSA private key + private_key_mock.decrypt.return_value = b"a" * 32 # AES key + logger.debug("Mocked private_key.decrypt to return AES key.") + + # Create dummy PEM files + public_key_path = crypto_controller_fixture.public_key_file + private_key_path = crypto_controller_fixture.private_key_file + create_dummy_pem(public_key_path, "public") + create_dummy_pem(private_key_path, "private") + logger.debug(f"Created dummy PEM files at {public_key_path} and {private_key_path}.") + + # Define plaintext + plaintext = "Hello, World!" + logger.debug(f"Defined plaintext: '{plaintext}'") + + # Mock os.urandom to return predictable bytes + with patch("os.urandom", side_effect=[b"a" * 32, b"b" * 16]): + encrypted = crypto_controller_fixture.encrypt(plaintext) + logger.debug(f"Encrypted text: {encrypted}") + assert isinstance(encrypted, str) + parts = encrypted.split(":") + assert len(parts) == 3 # encrypted_aes_key:iv:ciphertext + logger.debug("Encryption resulted in three parts separated by ':'") + + # Mock base64 decoding + with patch( + "base64.b64decode", side_effect=[b"encrypted_aes_key", b"b" * 16, b"ciphertext"] + ): + # Mock Cipher for decryption + with patch("crypto_controller.main.Cipher") as mock_cipher: + mock_decryptor = mock.Mock() + mock_decryptor.update.return_value = b"decrypted_text" + mock_decryptor.finalize.return_value = b"" + mock_cipher.return_value.decryptor.return_value = mock_decryptor + logger.debug("Mocked Cipher for decryption to return decrypted text.") + + decrypted = crypto_controller_fixture.decrypt(encrypted) + logger.debug(f"Decrypted text: {decrypted}") + assert decrypted == "decrypted_text" + logger.debug("Decryption successful and matches expected output.") + + +def test_verify_success(crypto_controller_fixture: CryptoController, temp_cert_vault: str, mocker): + """ + Test that verify returns True when all conditions are met. + """ + logger.debug("Testing verify method for successful verification.") + # Create dummy key files + create_dummy_pem(crypto_controller_fixture.public_key_file, "public") + create_dummy_pem(crypto_controller_fixture.private_key_file, "private") + logger.debug( + f"Created dummy public and private key files at {crypto_controller_fixture.public_key_file} and {crypto_controller_fixture.private_key_file}." + ) + + # Create valid key pair data + key_pair_data = { + "public_key_file": crypto_controller_fixture.public_key_file, + "public_fp_sha1": "dummysha1", + "public_fp_sha256": "dummysha256", + "private_key_file": crypto_controller_fixture.private_key_file, + "private_fp_sha1": "dummysha1", + "private_fp_sha256": "dummysha256", + "key_pair_file": crypto_controller_fixture.key_pair_file, + "creation_date": datetime.now().strftime("%d%m%Y%H%M%S"), + "expiration_date": (datetime.now() + timedelta(days=365)).strftime("%d%m%Y%H%M%S"), + } + logger.debug(f"Created key pair data: {key_pair_data}") + + # Mock the encrypt method to return JSON string + mocker.patch.object( + crypto_controller_fixture, "encrypt", return_value=json.dumps(key_pair_data) + ) + # Mock get_key_footprint to return matching footprints + mocker.patch( + "crypto_controller.main.get_key_footprint", + return_value=Footprint("dummysha1", "dummysha256"), + ) + + # Write key pair data to key pair file + with open(crypto_controller_fixture.key_pair_file, "w") as kp_file: + kp_file.write(json.dumps(key_pair_data)) + logger.debug( + f"Wrote key pair data to key pair file at {crypto_controller_fixture.key_pair_file}." + ) + + # Execute verify + result = crypto_controller_fixture.verify() + logger.debug(f"Verification result: {result}") + assert result is True + logger.debug("Verification successful as expected.") + + +def test_verify_missing_fields( + crypto_controller_fixture: CryptoController, temp_cert_vault: str, mocker +): + """ + Test that verify returns False when key pair data is missing required fields. + """ + logger.debug("Testing verify method with missing key pair data fields.") + # Create dummy key files + create_dummy_pem(crypto_controller_fixture.public_key_file, "public") + create_dummy_pem(crypto_controller_fixture.private_key_file, "private") + logger.debug( + f"Created dummy public and private key files at {crypto_controller_fixture.public_key_file} and {crypto_controller_fixture.private_key_file}." + ) + + # Create incomplete key pair data (missing some fields) + key_pair_data = { + "public_key_file": crypto_controller_fixture.public_key_file, + # Missing 'public_fp_sha1' and other required fields + "private_key_file": crypto_controller_fixture.private_key_file, + "key_pair_file": crypto_controller_fixture.key_pair_file, + "creation_date": datetime.now().strftime("%d%m%Y%H%M%S"), + "expiration_date": (datetime.now() + timedelta(days=365)).strftime("%d%m%Y%H%M%S"), + } + logger.debug(f"Created incomplete key pair data: {key_pair_data}") + + # Mock the encrypt method to return JSON string + mocker.patch.object( + crypto_controller_fixture, "encrypt", return_value=json.dumps(key_pair_data) + ) + + # Write incomplete key pair data to key pair file + with open(crypto_controller_fixture.key_pair_file, "w") as kp_file: + kp_file.write(json.dumps(key_pair_data)) + logger.debug( + f"Wrote incomplete key pair data to key pair file at {crypto_controller_fixture.key_pair_file}." + ) + + # Execute verify + result = crypto_controller_fixture.verify() + logger.debug(f"Verification result with missing fields: {result}") + assert result is False + logger.debug("Verification correctly failed due to missing fields.") + + +def test_create_keys(crypto_controller_fixture: CryptoController, temp_cert_vault: str, mocker): + """ + Test the creation of RSA key pairs and the key pair file. + """ + logger.debug("Testing create_keys method.") + # Ensure key files do not exist + assert not os.path.exists(crypto_controller_fixture.public_key_file) + assert not os.path.exists(crypto_controller_fixture.private_key_file) + assert not os.path.exists(crypto_controller_fixture.key_pair_file) + logger.debug("Verified that key files do not exist before creation.") + + # Mock the key generation and serialization + mock_generate_key = mocker.patch("crypto_controller.main.rsa.generate_private_key") + mock_private_key = mock.Mock() + mock_public_key = mock.Mock() + mock_generate_key.return_value = mock_private_key + mock_private_key.public_key.return_value = mock_public_key + mock_private_key.private_bytes.return_value = b"encrypted_private_key" + mock_public_key.public_bytes.return_value = b"public_key" + logger.debug("Mocked rsa.generate_private_key and key serialization methods.") + + # Mock get_key_footprint to return dummy footprints + mocker.patch( + "crypto_controller.main.get_key_footprint", + return_value=Footprint("dummysha1", "dummysha256"), + ) + + # Mock json.dumps to return a JSON string + mocker.patch("crypto_controller.main.json.dumps", return_value='{"key": "value"}') + + # Mock the encrypt method to return an encrypted key pair + mocker.patch.object(crypto_controller_fixture, "encrypt", return_value="encrypted_kp") + + # Execute create_keys + crypto_controller_fixture.create_keys() + logger.debug("Called create_keys method.") + + # Verify that key files are created + assert os.path.exists(crypto_controller_fixture.public_key_file) + assert os.path.exists(crypto_controller_fixture.private_key_file) + assert os.path.exists(crypto_controller_fixture.key_pair_file) + logger.debug("Verified that key files were created successfully.") + + +def test_get_status(crypto_controller_fixture: CryptoController, mocker, capsys): + """ + Test the get_status method outputs the correct status information. + """ + logger.debug("Testing get_status method.") + # Mock the status methods to return known values + mocker.patch.object(crypto_controller_fixture, "check_cert_vault_exists", return_value=True) + mocker.patch.object(crypto_controller_fixture, "verify", return_value=True) + mocker.patch.object( + crypto_controller_fixture, "get_expiration", return_value="2025-12-31T23:59:59" + ) + + # Mock os.path.exists to return True for all key files + with patch("os.path.exists", return_value=True): + crypto_controller_fixture.get_status() + logger.debug("Called get_status method.") + captured = capsys.readouterr() + logger.debug(f"Captured output: {captured.out}") + assert "Certificate Vault Exists: Yes" in captured.out + assert "Public Key Exists: Yes" in captured.out + assert "Private Key Exists: Yes" in captured.out + assert "Key Pair File Exists: Yes" in captured.out + assert "Key Verification: True" in captured.out + assert "Expiration: 2025-12-31T23:59:59" in captured.out + logger.debug("Verified that get_status output is correct.") + + +def test_fetch_private_key_password(mock_requests_get, crypto_controller_fixture: CryptoController): + """ + Test fetching the private key password from a secure API endpoint. + """ + logger.debug("Testing fetch_private_key_password method.") + password = crypto_controller_fixture.fetch_private_key_password() + logger.debug(f"Fetched private key password: {password}") + assert password == "secure_pass" + mock_requests_get.assert_called_once_with( + "https://api.mocked.com/get_password", + headers={ + "content-type": "application/json", + "token_security": "mocked_secure_token", + }, + timeout=5, # Changed from "5" to 5 (integer) + ) + logger.debug("Verified that fetch_private_key_password fetched the correct password.") + + +def test_send_expiration_alert(mock_smtp, crypto_controller_fixture: CryptoController): + """ + Test sending an expiration alert email. + """ + logger.debug("Testing send_expiration_alert method.") + expiration_date = datetime.now() + timedelta(days=30) + crypto_controller_fixture.send_expiration_alert(expiration_date) + logger.debug("Called send_expiration_alert method.") + + # Verify SMTP interactions + mock_smtp.assert_called_with("smtp.mocked.com", 587) + instance = mock_smtp.return_value.__enter__.return_value + instance.starttls.assert_called_once() + instance.login.assert_called_with("mocked_user@mocked.com", "mocked_password") + instance.sendmail.assert_called_once() + logger.debug("Verified that SMTP methods were called correctly.") + + +def test_renew_keys_confirm_yes(crypto_controller_fixture: CryptoController, mocker): + """ + Test renewing keys when user confirms with 'yes'. + """ + logger.debug("Testing renew_keys method with user confirmation 'yes'.") + # Mock user input to return 'yes' + mock_input = mocker.patch("crypto_controller.main.input", return_value="yes") + # Mock clean_cert_vault and create_keys methods + mock_clean = mocker.patch.object(crypto_controller_fixture, "clean_cert_vault") + mock_create = mocker.patch.object(crypto_controller_fixture, "create_keys") + + crypto_controller_fixture.renew_keys() + logger.debug("Called renew_keys method.") + + mock_clean.assert_called_once() + mock_create.assert_called_once() + logger.debug("Verified that clean_cert_vault and create_keys were called.") + + +def test_renew_keys_confirm_no(crypto_controller_fixture: CryptoController, mocker): + """ + Test renewing keys when user declines with 'no'. + """ + logger.debug("Testing renew_keys method with user confirmation 'no'.") + # Mock user input to return 'no' + mock_input = mocker.patch("crypto_controller.main.input", return_value="no") + # Mock clean_cert_vault and create_keys methods + mock_clean = mocker.patch.object(crypto_controller_fixture, "clean_cert_vault") + mock_create = mocker.patch.object(crypto_controller_fixture, "create_keys") + # Mock sys.exit + mock_exit = mocker.patch("crypto_controller.main.sys.exit") + + crypto_controller_fixture.renew_keys() + logger.debug("Called renew_keys method.") + + mock_clean.assert_not_called() + mock_create.assert_not_called() + mock_exit.assert_called_once_with(0) + logger.debug( + "Verified that clean_cert_vault and create_keys were not called and sys.exit was called with 0." + ) + + +def test_renew_keys_invalid_confirmation(crypto_controller_fixture: CryptoController, mocker): + """ + Test renewing keys with invalid user input. + """ + logger.debug("Testing renew_keys method with invalid user confirmation input.") + # Mock user input to return 'invalid' + mock_input = mocker.patch("crypto_controller.main.input", return_value="invalid") + # Mock clean_cert_vault and create_keys methods + mock_clean = mocker.patch.object(crypto_controller_fixture, "clean_cert_vault") + mock_create = mocker.patch.object(crypto_controller_fixture, "create_keys") + # Mock sys.exit + mock_exit = mocker.patch("crypto_controller.main.sys.exit") + + crypto_controller_fixture.renew_keys() + logger.debug("Called renew_keys method with invalid input.") + + mock_clean.assert_not_called() + mock_create.assert_not_called() + mock_exit.assert_called_once_with(1) + logger.debug( + "Verified that clean_cert_vault and create_keys were not called and sys.exit was called with 1." + ) From 1648582c72620434f8224cff6875b48ff07bd1bc Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 14:58:02 -0500 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=90=9B=20fix(core):=20fixed=20crypt?= =?UTF-8?q?o=20controller=20expiration=20years?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_controller/main.py b/crypto_controller/main.py index 0a22ab4..0971bfe 100644 --- a/crypto_controller/main.py +++ b/crypto_controller/main.py @@ -594,7 +594,7 @@ def fetch_private_key_password() -> str: response = requests.get( os.getenv("API_URI"), headers=headers, - timeout=os.getenv("API_TIMEOUT"), + timeout=int(os.getenv("API_TIMEOUT")), ) response.raise_for_status() # Raises HTTPError for bad responses pk_key_pass = response.json().get("value") From ee4c1767be770b0fc48881612e32e393756e5b48 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 18:10:42 -0500 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=92=84=20style(core):=20visual=20ch?= =?UTF-8?q?anges=20on=20readme,=20local=20env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto_controller/README.md b/crypto_controller/README.md index 8e0f343..3663a7c 100644 --- a/crypto_controller/README.md +++ b/crypto_controller/README.md @@ -36,7 +36,7 @@ CryptoController is a robust Python application designed for secure key manageme 1. **Clone the Repository:** ```bash - cd scripts/crypto_controller + cd crypto_controller ``` 2. **Create a Virtual Environment** @@ -141,7 +141,7 @@ Renews existing keys by cleaning the vault and generating new keys. python main.py renew --log-level DEBUG ``` -## 🔒 Encrypting Data +### 🔒 Encrypting Data Encrypts plain text using hybrid encryption. From 90608154f725e059f2012311fef631f64cb706db Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 19:28:04 -0500 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=90=9B=20fix(core):=20fixed=20crypt?= =?UTF-8?q?o=20controller=20logging=20levels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/.env.example | 18 +++++++++++++ crypto_controller/README.md | 28 +++++++++++++-------- crypto_controller/main.py | 46 +++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 crypto_controller/.env.example diff --git a/crypto_controller/.env.example b/crypto_controller/.env.example new file mode 100644 index 0000000..4461b2c --- /dev/null +++ b/crypto_controller/.env.example @@ -0,0 +1,18 @@ +# Password KP Settings +## Password Key Pair (API-Token Mode) +# API_URI="https://tu.dominio.com/private-key" <- Uncomment and comment KP_PASSWORD +# API_TOKEN_SECURITY="api_token" <- Uncomment and comment KP_PASSWORD +# API_TIMEOUT=12 <- Uncomment and comment KP_PASSWORD +### OR +## Pasword Key Pair (Local Mode) +KP_PASSWORD="<28 (Chars)>" + +# Certificate Vault Settings +CERT_EXPIRATION_YEARS=6 + +# Expiration Notifications Settings +SMTP_SERVER=smtp.example.com +SMTP_PORT=587 +SMTP_USER=your_email@example.com +SMTP_PASSWORD=your_email_password +ALERT_RECIPIENT=recipient@example.com diff --git a/crypto_controller/README.md b/crypto_controller/README.md index 3663a7c..f53499a 100644 --- a/crypto_controller/README.md +++ b/crypto_controller/README.md @@ -92,11 +92,19 @@ CryptoController is a robust Python application designed for secure key manageme Create a .env file in the project root directory and populate it with the following variables: ```bash -CPU_USAGE_THRESHOLD=70.0 -MEMORY_USAGE_THRESHOLD=395.0 -DISK_SPACE_THRESHOLD=75.0 -EXPIRATION=1 -TOKEN_SECURITY=your_secure_token_here +# Password KP Settings +## Password Key Pair (API-Token Mode) +# API_URI="https://tu.dominio.com/private-key" <- Uncomment and comment KP_PASSWORD +# API_TOKEN_SECURITY="api_token" <- Uncomment and comment KP_PASSWORD +# API_TIMEOUT=12 <- Uncomment and comment KP_PASSWORD +### OR +## Password Key Pair (Local Mode) +KP_PASSWORD="<28 (Chars)>" + +# Certificate Vault Settings +CERT_EXPIRATION_YEARS=6 + +# Expiration Notifications Settings SMTP_SERVER=smtp.example.com SMTP_PORT=587 SMTP_USER=your_email@example.com @@ -105,11 +113,11 @@ ALERT_RECIPIENT=recipient@example.com ``` - Descriptions: - - CPU_USAGE_THRESHOLD: CPU usage percentage threshold. - - MEMORY_USAGE_THRESHOLD: Memory usage threshold in MB. - - DISK_SPACE_THRESHOLD: Disk space usage percentage threshold. - - EXPIRATION: Number of years before key expiration. - - TOKEN_SECURITY: Token for fetching the private key password securely. + - API_URI: Password API mode base URI. + - API_TOKEN_SECURITY: Password API mode token security. + - API_TIMEOUT: Password API mode timeout. + - KP*PASSWORD: Password plain mode, used it or API* vars. + - CERT_EXPIRATION_YEARS: Number of years before key expiration. - SMTP_SERVER: SMTP server address for sending emails. - SMTP_PORT: SMTP server port. - SMTP_USER: SMTP server username. diff --git a/crypto_controller/main.py b/crypto_controller/main.py index 0971bfe..bdadc09 100644 --- a/crypto_controller/main.py +++ b/crypto_controller/main.py @@ -27,17 +27,10 @@ # Load environment variables from .env file load_dotenv() -# Resource usage thresholds -CPU_USAGE_THRESHOLD = float(os.getenv("CPU_USAGE_THRESHOLD", "70.0")) -MEMORY_USAGE_THRESHOLD = float(os.getenv("MEMORY_USAGE_THRESHOLD", "395.0")) -DISK_SPACE_THRESHOLD = float(os.getenv("DISK_SPACE_THRESHOLD", "75.0")) +CERT_EXPIRATION_YEARS = os.getenv("CERT_EXPIRATION_YEARS", "1") # Verify that required environment variables are set -REQUIRED_ENV_VARS = [ - CPU_USAGE_THRESHOLD, - MEMORY_USAGE_THRESHOLD, - DISK_SPACE_THRESHOLD, -] +REQUIRED_ENV_VARS = [CERT_EXPIRATION_YEARS] if not all(REQUIRED_ENV_VARS): raise EnvironmentError("One or more required environment variables are missing.") @@ -213,7 +206,7 @@ def encrypt_hybrid(self, plain_text: str) -> str: # Concatenate with colon as delimiter encrypted_data = f"{encrypted_aes_key_b64}:{iv_b64}:{ciphertext_b64}" - logger.info("Hybrid encryption successful.") + logger.debug("Hybrid encryption successful.") return encrypted_data except Exception as error: @@ -263,12 +256,13 @@ def decrypt_hybrid(self, encrypted_data: str) -> str: decrypted_text = decryptor.update(ciphertext) + decryptor.finalize() decrypted_str = decrypted_text.decode("utf-8") - logger.info("Hybrid decryption successful.") + logger.debug("Hybrid decryption successful.") return decrypted_str except Exception as error: logger.error(f"Hybrid decryption failed: {error}", exc_info=True) - raise + logger.fatal("Can't decrypt encrypted data.") + sys.exit(1) def encrypt(self, plain_text: str) -> str: """ @@ -368,7 +362,7 @@ def verify(self) -> bool: logger.error("The key pair has expired.") return False - logger.info("Key verification successful.") + logger.debug("Key verification successful.") return True except Exception as error: logger.error(f"Verification failed: {error}", exc_info=True) @@ -439,7 +433,7 @@ def create_keys(self) -> None: # Create key pair content as JSON now = datetime.now() - expire = now + timedelta(days=365 * int(os.getenv("CERT_EXPIRATION_YEARS", "1"))) + expire = now + timedelta(days=365 * int(CERT_EXPIRATION_YEARS)) key_pair_data = { "public_key_file": self.public_key_file, "public_fp_sha1": public_fp.sha1, @@ -598,13 +592,25 @@ def fetch_private_key_password() -> str: ) response.raise_for_status() # Raises HTTPError for bad responses pk_key_pass = response.json().get("value") - if not pk_key_pass: - logger.error("The key 'value' was not found in the response.") - sys.exit(1) return pk_key_pass - except requests.exceptions.RequestException as e: - logger.error(f"Error fetching private key password: {e}", exc_info=True) - sys.exit(1) + except requests.exceptions.RequestException as e_requests_exception_fetch_password: + logger.error( + f"Error fetching private key password from api: {e_requests_exception_fetch_password}", + exc_info=True, + ) + try: + logger.debug("Trying using KP_PASSWORD value...") + pk_key_pass = os.getenv("KP_PASSWORD") + return pk_key_pass + except KeyError as e_key_error_fetch_password: + logger.error( + f"The key was not found in the environment: {e_key_error_fetch_password}", + exc_info=True, + ) + logger.error( + "STARTING USING DEFAULT PASSWORD WHICH IS NOT RECOMMENDED, CLEAN AND SET THIS ONE TO .env FILE AS KP_PASSWORD..." + ) + return "password123456789099ab5e7b9add0dc4e5" def send_expiration_alert(expiration_date: datetime) -> None: From 04f676e6b21d29fbc711efaa9322878467a65464 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 19:35:04 -0500 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=92=84=20style(core):=20fixed=20cry?= =?UTF-8?q?pto=5Fcontroller=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto_controller/README.md b/crypto_controller/README.md index f53499a..4144d66 100644 --- a/crypto_controller/README.md +++ b/crypto_controller/README.md @@ -116,7 +116,7 @@ ALERT_RECIPIENT=recipient@example.com - API_URI: Password API mode base URI. - API_TOKEN_SECURITY: Password API mode token security. - API_TIMEOUT: Password API mode timeout. - - KP*PASSWORD: Password plain mode, used it or API* vars. + - KP_PASSWORD: Password plain mode, used it or API vars. - CERT_EXPIRATION_YEARS: Number of years before key expiration. - SMTP_SERVER: SMTP server address for sending emails. - SMTP_PORT: SMTP server port. From d4562de35ae905a2b745b0b8df134f0af6410846 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 19:43:15 -0500 Subject: [PATCH 07/10] =?UTF-8?q?=F0=9F=92=84=20style(core):=20fixed=20cry?= =?UTF-8?q?pto=5Fcontroller=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/README.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/crypto_controller/README.md b/crypto_controller/README.md index 4144d66..55f225d 100644 --- a/crypto_controller/README.md +++ b/crypto_controller/README.md @@ -181,23 +181,24 @@ python main.py status --log-level DEBUG Ensure all required environment variables are set in the .env file: - Resource Thresholds: - CPU_USAGE_THRESHOLD - MEMORY_USAGE_THRESHOLD - DISK_SPACE_THRESHOLD - - Key Management: - EXPIRATION: Number of years before key expiration. - - Security: - TOKEN_SECURITY: Token for secure API access. - - Email Notifications: - SMTP_SERVER - SMTP_PORT - SMTP_USER - SMTP_PASSWORD - ALERT_RECIPIENT + Password KP Settings: + Password Key Pair (API-Token Mode): + API_URI: Password API mode base URI. + API_TOKEN_SECURITY: Password API mode token security. + API_TIMEOUT: Password API mode timeout. + + Pasword Key Pair (Local Mode): + KP_PASSWORD: Password plain mode, used it or API vars. + + Certificate Vault Settings: + CERT_EXPIRATION_YEARS: Number of years before key expiration. + + Expiration Notifications Settings: + SMTP_SERVER: SMTP server address for sending emails. + SMTP_PORT: SMTP server port. + SMTP_USER: SMTP server username. + SMTP_PASSWORD: SMTP server password. + ALERT_RECIPIENT: Email address to receive expiration alerts. ## 📊 Logging From e408067a6367c6eaacc9c54336f34277995f364d Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 20:05:14 -0500 Subject: [PATCH 08/10] =?UTF-8?q?=E2=9C=A8=20feat(core):=20added=20crypto?= =?UTF-8?q?=20controller=20times=20on=20debug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_controller/main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crypto_controller/main.py b/crypto_controller/main.py index bdadc09..c0ff3f4 100644 --- a/crypto_controller/main.py +++ b/crypto_controller/main.py @@ -2,6 +2,7 @@ import sys import argparse import logging +import time from logging.handlers import RotatingFileHandler import shutil import hashlib @@ -653,6 +654,8 @@ def main(): """ Main function to execute the Crypto Controller operations. """ + start_time = time.time() + args = parse_arguments() configure_logger(args.log_level) @@ -712,10 +715,12 @@ def main(): elif operation == "status": crypto.get_status() - except Exception as error: logger.error(f"Operation '{args.operation}' failed: {error}", exc_info=True) - sys.exit(1) + finally: + end_time = time.time() + time_elapsed = end_time - start_time + logger.debug(f"End. Took: {time_elapsed:.4f} seconds") if __name__ == "__main__": From 827bd495623853303ecbeec1eeab0de68ff759c5 Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 20:52:56 -0500 Subject: [PATCH 09/10] =?UTF-8?q?=E2=9C=A8=20feat(core):=20added=20crypto?= =?UTF-8?q?=20controller=20[patch=20candidate]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- control_commit/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/control_commit/main.py b/control_commit/main.py index 5fa0e9a..52f175c 100644 --- a/control_commit/main.py +++ b/control_commit/main.py @@ -94,7 +94,8 @@ def emit(self, record): ) self.stream.write(msg + self.terminator) self.flush() - except Exception: + except Exception as e_handle_emit: + logger.debug(f"SafeStreamHandler error: {e_handle_emit}") self.handleError(record) safe_console_handler = SafeStreamHandler() From 2aa2e187fcae39d867db64e873fd014dba85bf5b Mon Sep 17 00:00:00 2001 From: B Date: Tue, 29 Oct 2024 20:53:21 -0500 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=94=96=20Bump=20version:=201.1.8=20?= =?UTF-8?q?=E2=86=92=201.1.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 1289a03..5fcd1cb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.8 +current_version = 1.1.9 commit = True tag = False diff --git a/pyproject.toml b/pyproject.toml index f85adda..ca149be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "scripts" -version = "1.1.8" +version = "1.1.9" description = "CICD Core Scripts" authors = ["B "] license = "Apache 2.0"