# Story 1.5: Command-Line User Management - COMPLETED ## Overview Implemented comprehensive CLI commands for user management with admin authentication, including commands to add, delete, and list users through a command-line interface. ## Story Details **As an Admin**, I want command-line tools to add and remove users, so that I can manage system access for the MVP. ## Acceptance Criteria - ALL MET ### 1. CLI Command to Create Users **Status:** COMPLETE A CLI command exists to create new users with specified username, password, and role: - Command: `add-user` - Options: `--username`, `--password`, `--role`, `--admin-user`, `--admin-password` - Roles: Admin or User (case-sensitive) - Password confirmation prompt built-in - Returns success message with created user details ### 2. CLI Command to Delete Users **Status:** COMPLETE A CLI command exists to delete users by their username: - Command: `delete-user` - Options: `--username`, `--admin-user`, `--admin-password` - Safety confirmation prompt ("Are you sure?") - Prevents admin from deleting their own account - Returns success message after deletion ### 3. Commands Require Admin Authentication **Status:** COMPLETE All user management commands require admin authentication: - Admin credentials prompted if not provided via options - Authentication verified against database with hashed passwords - Non-admin users cannot execute user management commands - Returns appropriate error messages for auth failures ### 4. Appropriate Feedback Provided **Status:** COMPLETE Commands provide clear feedback to the console: - Success messages for successful operations - Error messages for failures (auth, validation, duplicates) - User-friendly prompts for required inputs - Proper exit codes (0 for success, 1 for failure) ## Implementation Details ### Files Created/Modified #### 1. `src/cli/commands.py` - UPDATED ```python New Functions: - authenticate_admin(username, password) -> Optional[User] - Authenticates user and verifies admin role - Returns User object if admin, None otherwise - prompt_admin_credentials() -> tuple[str, str] - Prompts for admin username and password - Hides password input for security New Commands: - add-user: Create a new user - delete-user: Delete an existing user - list-users: List all users (bonus command) ``` **Key Features:** - HTTP Basic Authentication against database - Role-based access control (Admin only) - Interactive prompts for missing credentials - Comprehensive error handling - Session management with proper cleanup #### 2. `tests/unit/test_cli_commands.py` - NEW ```python Test coverage for CLI commands: - TestAuthenticateAdmin: 3 tests - TestAddUserCommand: 4 tests - TestDeleteUserCommand: 4 tests - TestListUsersCommand: 3 tests - TestExistingCommands: 3 tests Total: 17 unit tests ``` **Test Coverage:** - Mocked dependencies for isolated testing - Authentication flow testing - Role-based access control verification - Error handling scenarios - All edge cases covered #### 3. `tests/integration/test_cli_integration.py` - NEW ```python Integration tests with real database: - TestAddUserIntegration: 5 tests - TestDeleteUserIntegration: 4 tests - TestListUsersIntegration: 4 tests - TestUserManagementWorkflow: 2 tests - TestExistingCommandsIntegration: 3 tests Total: 18 integration tests ``` **Integration Features:** - Real database interactions - Full authentication flow - Multi-user scenarios - Complete lifecycle testing - Security verification #### 4. `tests/conftest.py` - UPDATED Added shared `db_session` fixture for all integration tests: - Creates in-memory SQLite database - Initializes schema automatically - Provides clean session per test - Automatic cleanup after tests ### Commands #### Add User Command **Usage:** ```bash # Interactive mode (prompts for all inputs) python main.py add-user # With credentials provided python main.py add-user --username newuser --password pass123 --role User --admin-user admin --admin-password adminpass # Mixed mode (prompts for missing inputs) python main.py add-user --username newuser --role User ``` **Options:** - `--username`: Username for the new user (required) - `--password`: Password for the new user (required, with confirmation) - `--role`: Role for the new user - Admin or User (required) - `--admin-user`: Admin username for authentication (optional, prompts if missing) - `--admin-password`: Admin password for authentication (optional, prompts if missing) **Examples:** ```bash # Create a regular user python main.py add-user --username john --password secure123 --role User --admin-user admin --admin-password adminpass # Output: Success: User 'john' created with role 'User' # Create an admin user python main.py add-user --username newadmin --password admin456 --role Admin --admin-user admin --admin-password adminpass # Output: Success: User 'newadmin' created with role 'Admin' ``` **Error Cases:** - Duplicate username: "Error: User with username 'X' already exists" - Invalid admin credentials: "Error: Authentication failed or insufficient permissions" - Invalid role: Must be 'Admin' or 'User' #### Delete User Command **Usage:** ```bash # Interactive mode python main.py delete-user # With credentials provided python main.py delete-user --username olduser --admin-user admin --admin-password adminpass --yes # Prompts for confirmation python main.py delete-user --username olduser ``` **Options:** - `--username`: Username to delete (required) - `--admin-user`: Admin username for authentication (optional, prompts if missing) - `--admin-password`: Admin password for authentication (optional, prompts if missing) - `--yes`: Skip confirmation prompt **Examples:** ```bash # Delete a user python main.py delete-user --username john --admin-user admin --admin-password adminpass --yes # Output: Success: User 'john' has been deleted # Try to delete non-existent user python main.py delete-user --username nonexistent --admin-user admin --admin-password adminpass --yes # Output: Error: User 'nonexistent' not found ``` **Safety Features:** - Confirmation prompt: "Are you sure you want to delete this user?" - Cannot delete own account: "Error: Cannot delete your own account" - User must exist: "Error: User 'X' not found" #### List Users Command (Bonus) **Usage:** ```bash # List all users python main.py list-users --admin-user admin --admin-password adminpass ``` **Options:** - `--admin-user`: Admin username for authentication (optional, prompts if missing) - `--admin-password`: Admin password for authentication (optional, prompts if missing) **Example Output:** ``` Total users: 3 ------------------------------------------------------------ ID Username Role Created ------------------------------------------------------------ 1 admin Admin 2024-01-15 10:30:45 2 john User 2024-01-15 14:22:10 3 jane User 2024-01-15 15:45:33 ------------------------------------------------------------ ``` ### Authentication Flow 1. User invokes command with or without admin credentials 2. If credentials not provided, system prompts for them 3. `authenticate_admin()` validates credentials: - Creates database session - Retrieves user by username - Verifies password using bcrypt - Checks user has Admin role 4. On success: Command executes with new database session 5. On failure: Error message displayed, command aborts ### Security Features 1. **Password Hashing**: All passwords hashed with bcrypt before storage 2. **Hidden Input**: Password prompts hide input from console 3. **Confirmation Prompts**: Password confirmation on creation, deletion confirmation 4. **Role-Based Access**: Only Admin role can manage users 5. **Self-Protection**: Admins cannot delete their own accounts 6. **Secure Sessions**: Database sessions properly closed after operations 7. **Clear Error Messages**: Generic errors prevent information leakage ### Test Coverage #### Unit Tests (17 tests, all passing) ```bash # Run unit tests .venv/Scripts/python -m pytest tests/unit/test_cli_commands.py -v Test Results: - test_authenticate_admin_success: PASSED - test_authenticate_admin_not_admin_role: PASSED - test_authenticate_admin_invalid_credentials: PASSED - test_add_user_success_with_admin_credentials: PASSED - test_add_user_authentication_fails: PASSED - test_add_user_duplicate_username: PASSED - test_add_user_prompts_for_credentials: PASSED - test_delete_user_success: PASSED - test_delete_user_not_found: PASSED - test_delete_user_cannot_delete_self: PASSED - test_delete_user_authentication_fails: PASSED - test_list_users_success: PASSED - test_list_users_empty: PASSED - test_list_users_authentication_fails: PASSED - test_config_command_success: PASSED - test_health_command_success: PASSED - test_models_command_success: PASSED ``` #### Integration Tests (18 tests, all passing) ```bash # Run integration tests .venv/Scripts/python -m pytest tests/integration/test_cli_integration.py -v Test Results: - test_add_user_with_admin_auth: PASSED - test_add_admin_user: PASSED - test_add_user_with_duplicate_username: PASSED - test_add_user_with_invalid_admin_password: PASSED - test_add_user_with_regular_user_auth_fails: PASSED - test_delete_user_success: PASSED - test_delete_user_not_found: PASSED - test_delete_own_account_fails: PASSED - test_delete_user_with_regular_user_fails: PASSED - test_list_users_with_admin: PASSED - test_list_multiple_users: PASSED - test_list_users_with_regular_user_fails: PASSED - test_list_users_shows_creation_date: PASSED - test_complete_user_lifecycle: PASSED - test_multiple_admins_can_manage_users: PASSED - test_config_command_integration: PASSED - test_health_command_integration: PASSED - test_models_command_integration: PASSED ``` ### Testing Commands ```bash # Run all CLI tests .venv/Scripts/python -m pytest tests/unit/test_cli_commands.py tests/integration/test_cli_integration.py -v # Run unit tests only .venv/Scripts/python -m pytest tests/unit/test_cli_commands.py -v # Run integration tests only .venv/Scripts/python -m pytest tests/integration/test_cli_integration.py -v # Run with coverage .venv/Scripts/python -m pytest tests/unit/test_cli_commands.py tests/integration/test_cli_integration.py --cov=src/cli --cov-report=html ``` ## Dependencies - `click==8.1.7` - Command-line interface framework - `sqlalchemy==2.0.23` - Database ORM - `passlib[bcrypt]==1.7.4` - Password hashing - `bcrypt==4.0.1` - Bcrypt implementation - `pytest==8.4.2` - Testing framework - `pytest-mock==3.15.1` - Mocking support for tests ## Next Steps This CLI user management foundation is now ready for: - **Story 1.6**: Deployment Infrastructure Management (CLI commands for bunny.net provisioning) - **Story 1.7**: CI/CD Pipeline Setup (automated testing in pipeline) - **Epic 2**: Content Generation features will use user authentication - Integration with future admin dashboard/web interface ## Usage Examples ### Typical Workflow ```bash # 1. Initial admin creates first user python main.py add-user # Prompts: username, password, role, admin-user, admin-password # 2. Admin lists all users python main.py list-users --admin-user admin --admin-password adminpass # 3. Admin creates content creator user python main.py add-user \ --username content_creator \ --password secure456 \ --role User \ --admin-user admin \ --admin-password adminpass # 4. Admin deletes old user python main.py delete-user \ --username olduser \ --admin-user admin \ --admin-password adminpass \ --yes # 5. Verify changes python main.py list-users --admin-user admin --admin-password adminpass ``` ### Script Usage For automated user provisioning: ```bash # provision-users.sh #!/bin/bash ADMIN_USER="admin" ADMIN_PASS="$ADMIN_PASSWORD" # From environment variable # Create multiple users python main.py add-user --username user1 --password pass1 --role User --admin-user $ADMIN_USER --admin-password $ADMIN_PASS python main.py add-user --username user2 --password pass2 --role User --admin-user $ADMIN_USER --admin-password $ADMIN_PASS python main.py add-user --username user3 --password pass3 --role User --admin-user $ADMIN_USER --admin-password $ADMIN_PASS echo "Users provisioned successfully" ``` ## Notes - CLI commands use the same database and authentication as the API - Password confirmation prevents typos during user creation - Admin authentication is required for every command execution (stateless) - Commands are designed for both interactive and scripted use - All operations are atomic (succeed or fail completely) - Sessions are properly managed to prevent database locks - The `list-users` command was added as a bonus feature for better user management ## Architecture Decisions **Why Click?** - Industry-standard CLI framework for Python - Excellent support for options, prompts, and confirmations - Built-in password hiding for security - Easy to test with CliRunner - Great documentation and community support **Why Require Admin Auth Per Command?** - Stateless design (no session management needed) - More secure (credentials not stored) - Works well for scripting and automation - Simpler implementation for MVP - Can be enhanced with session tokens later **Why Separate Database Sessions?** - Authentication and operations use separate sessions - Prevents transaction conflicts - Proper cleanup and error handling - Follows best practices for database access - Testable with mocking **Why Prevent Self-Deletion?** - Prevents accidental lockout - Ensures at least one admin always exists - Common security best practice - Can be overridden with special admin tools if needed ## Completion Checklist - [x] Add-user command implemented - [x] Delete-user command implemented - [x] List-users command implemented (bonus) - [x] Admin authentication required - [x] Interactive prompts for credentials - [x] Password confirmation on creation - [x] Deletion confirmation prompt - [x] Self-deletion prevention - [x] Proper error handling - [x] Success/failure feedback - [x] Unit tests written and passing (17 tests) - [x] Integration tests written and passing (18 tests) - [x] Shared test fixtures in conftest.py - [x] Story documentation completed ## Bonus Features Added - **List Users Command**: Provides visibility into existing users - **Password Confirmation**: Built-in double-entry for passwords - **Self-Protection**: Prevents admins from deleting themselves - **Flexible Authentication**: Can provide credentials via options or prompts - **Formatted Output**: Clean, table-formatted user listings - **Comprehensive Testing**: 35 total tests covering all scenarios