14 KiB
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
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
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
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:
# 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:
# 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:
# 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:
# 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:
# 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
- User invokes command with or without admin credentials
- If credentials not provided, system prompts for them
authenticate_admin()validates credentials:- Creates database session
- Retrieves user by username
- Verifies password using bcrypt
- Checks user has Admin role
- On success: Command executes with new database session
- On failure: Error message displayed, command aborts
Security Features
- Password Hashing: All passwords hashed with bcrypt before storage
- Hidden Input: Password prompts hide input from console
- Confirmation Prompts: Password confirmation on creation, deletion confirmation
- Role-Based Access: Only Admin role can manage users
- Self-Protection: Admins cannot delete their own accounts
- Secure Sessions: Database sessions properly closed after operations
- Clear Error Messages: Generic errors prevent information leakage
Test Coverage
Unit Tests (17 tests, all passing)
# 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)
# 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
# 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 frameworksqlalchemy==2.0.23- Database ORMpasslib[bcrypt]==1.7.4- Password hashingbcrypt==4.0.1- Bcrypt implementationpytest==8.4.2- Testing frameworkpytest-mock==3.15.1- Mocking support for tests
Next Steps
This CLI user management foundation is now ready for:
- Story 1.6: FQDN Bucket Mapping Management (CLI commands for domain mappings)
- 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
# 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:
# 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-userscommand 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
- Add-user command implemented
- Delete-user command implemented
- List-users command implemented (bonus)
- Admin authentication required
- Interactive prompts for credentials
- Password confirmation on creation
- Deletion confirmation prompt
- Self-deletion prevention
- Proper error handling
- Success/failure feedback
- Unit tests written and passing (17 tests)
- Integration tests written and passing (18 tests)
- Shared test fixtures in conftest.py
- 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