From bbb63d5dce882c2329ead92996fb43011dfb959a Mon Sep 17 00:00:00 2001 From: phact Date: Wed, 8 Oct 2025 15:40:24 -0400 Subject: [PATCH] os password validator --- src/tui/screens/config.py | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/tui/screens/config.py b/src/tui/screens/config.py index 64fa4da7..42694f82 100644 --- a/src/tui/screens/config.py +++ b/src/tui/screens/config.py @@ -1,5 +1,6 @@ """Configuration screen for OpenRAG TUI.""" +import re from textual.app import ComposeResult from textual.containers import Container, Vertical, Horizontal, ScrollableContainer from textual.screen import Screen @@ -50,6 +51,40 @@ class DocumentsPathValidator(Validator): return self.failure(error_msg) +class PasswordValidator(Validator): + """Validator for OpenSearch admin password.""" + + def validate(self, value: str) -> ValidationResult: + # Allow empty value (will be auto-generated) + if not value: + return self.success() + + # Minimum length: 8 characters + if len(value) < 8: + return self.failure("Password must be at least 8 characters long") + + # Check for required character types + has_uppercase = bool(re.search(r"[A-Z]", value)) + has_lowercase = bool(re.search(r"[a-z]", value)) + has_digit = bool(re.search(r"[0-9]", value)) + has_special = bool(re.search(r"[!@#$%^&*()_+\-=\[\]{};':\"\\|,.<>/?]", value)) + + missing = [] + if not has_uppercase: + missing.append("uppercase letter") + if not has_lowercase: + missing.append("lowercase letter") + if not has_digit: + missing.append("digit") + if not has_special: + missing.append("special character") + + if missing: + return self.failure(f"Password must contain: {', '.join(missing)}") + + return self.success() + + class ConfigScreen(Screen): """Configuration screen for environment setup.""" @@ -118,9 +153,14 @@ class ConfigScreen(Screen): value=current_value, password=True, id="input-opensearch_password", + validators=[PasswordValidator()], ) yield input_widget self.inputs["opensearch_password"] = input_widget + yield Static( + "Min 8 chars with uppercase, lowercase, digit, and special character", + classes="helper-text", + ) yield Static(" ") # Langflow Admin Username @@ -488,6 +528,22 @@ class ConfigScreen(Screen): def action_save(self) -> None: """Save the configuration.""" + # First, check Textual input validators + validation_errors = [] + for field_name, input_widget in self.inputs.items(): + if hasattr(input_widget, "validate") and input_widget.value: + result = input_widget.validate(input_widget.value) + if result and not result.is_valid: + for failure in result.failures: + validation_errors.append(f"{field_name}: {failure.description}") + + if validation_errors: + self.notify( + f"Validation failed:\n" + "\n".join(validation_errors[:3]), + severity="error", + ) + return + # Update config from input fields for field_name, input_widget in self.inputs.items(): setattr(self.env_manager.config, field_name, input_widget.value)