441 lines
14 KiB
Markdown
441 lines
14 KiB
Markdown
# 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
|
|
|