Add USB card reader authentication for edit feature
- Implemented CardReader class to read data from USB card readers - Added CardSwipePopup with 5-second timeout and visual feedback - Card data is captured and included in edit metadata - Card data sent to server when edited images are uploaded - Added evdev dependency for USB input device handling - Fallback mode when evdev not available (for development) - Created test utility (test_card_reader.py) for card reader testing - Added comprehensive documentation (CARD_READER_AUTHENTICATION.md) - Added access-card.png icon for authentication popup - Edit interface requires card swipe or times out after 5 seconds
This commit is contained in:
182
working_files/CARD_READER_AUTHENTICATION.md
Normal file
182
working_files/CARD_READER_AUTHENTICATION.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# USB Card Reader Authentication
|
||||
|
||||
This document describes the USB card reader authentication feature for the Kiwy Signage Player.
|
||||
|
||||
## Overview
|
||||
|
||||
The player now supports user authentication via USB card readers when accessing the edit/drawing interface. When a user clicks the edit button (pencil icon), they must swipe their card to authenticate before being allowed to edit the image.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Edit Button Click**: User clicks the pencil icon to edit the current image
|
||||
2. **Validation Checks**:
|
||||
- Verify current media is an image (not video)
|
||||
- Check if editing is allowed for this media (`edit_on_player` permission from server)
|
||||
3. **Card Reader Prompt**:
|
||||
- Display "Please swipe your card..." message
|
||||
- Wait for card swipe (5 second timeout)
|
||||
- Read card data from USB card reader
|
||||
- Store the card data (no validation required)
|
||||
4. **Open Edit Interface**: Edit interface opens with card data stored
|
||||
5. **Save & Upload**: When user saves the edited image:
|
||||
- Card data is included in the metadata JSON
|
||||
- Both image and metadata (with card data) are uploaded to server
|
||||
- Server receives `user_card_data` field for tracking who edited the image
|
||||
|
||||
## Card Reader Setup
|
||||
|
||||
### Hardware Requirements
|
||||
- USB card reader (HID/keyboard emulation type)
|
||||
- Compatible cards (magnetic stripe or RFID depending on reader)
|
||||
|
||||
### Software Requirements
|
||||
The player requires the `evdev` Python library to interface with USB input devices:
|
||||
|
||||
```bash
|
||||
# Install via apt (recommended for Raspberry Pi)
|
||||
sudo apt-get install python3-evdev
|
||||
|
||||
# Or via pip
|
||||
pip3 install evdev
|
||||
```
|
||||
|
||||
### Fallback Mode
|
||||
If `evdev` is not available, the player will:
|
||||
- Log a warning message
|
||||
- Use a default card value (`DEFAULT_USER_12345`) for testing
|
||||
- This allows development and testing without hardware
|
||||
|
||||
## Card Data Storage
|
||||
|
||||
The card data is captured as a raw string and stored without validation or mapping:
|
||||
|
||||
- **No preprocessing**: Card data is stored exactly as received from the reader
|
||||
- **Format**: Whatever the card reader sends (typically numeric or alphanumeric)
|
||||
- **Sent to server**: Raw card data is included in the `user_card_data` field of the metadata JSON
|
||||
- **Server-side processing**: The server can validate, map, or process the card data as needed
|
||||
|
||||
### Metadata JSON Format
|
||||
When an image is saved, the metadata includes:
|
||||
```json
|
||||
{
|
||||
"time_of_modification": "2025-12-08T10:30:00",
|
||||
"original_name": "image.jpg",
|
||||
"new_name": "image_e_v1.jpg",
|
||||
"original_path": "/path/to/image.jpg",
|
||||
"version": 1,
|
||||
"user_card_data": "123456789"
|
||||
}
|
||||
```
|
||||
|
||||
If no card is swiped (timeout), `user_card_data` will be `null`.
|
||||
|
||||
## Testing the Card Reader
|
||||
|
||||
A test utility is provided to verify card reader functionality:
|
||||
|
||||
```bash
|
||||
cd /home/pi/Desktop/Kiwy-Signage/working_files
|
||||
python3 test_card_reader.py
|
||||
```
|
||||
|
||||
The test tool will:
|
||||
1. List all available input devices
|
||||
2. Auto-detect the card reader (or let you select manually)
|
||||
3. Listen for card swipes and display the data received
|
||||
4. Show how the data will be processed
|
||||
|
||||
### Test Output Example
|
||||
```
|
||||
✓ Card data received: '123456789'
|
||||
Length: 9 characters
|
||||
Processed ID: card_123456789
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Main Components
|
||||
|
||||
1. **CardReader Class** (`main.py`)
|
||||
- Handles USB device detection
|
||||
- Reads input events from card reader
|
||||
- Provides async callback interface
|
||||
- Includes timeout handling (5 seconds)
|
||||
|
||||
2. **Card Read Flow** (`show_edit_interface()` method)
|
||||
- Validates media type and permissions
|
||||
- Initiates card read
|
||||
- Stores raw card data
|
||||
- Opens edit popup
|
||||
|
||||
3. **Metadata Creation** (`_save_metadata()` method)
|
||||
- Includes card data in metadata JSON
|
||||
- No processing or validation of card data
|
||||
- Sent to server as-is
|
||||
|
||||
### Card Data Format
|
||||
|
||||
Card readers typically send data as keyboard input:
|
||||
- Each character is sent as a key press event
|
||||
- Data ends with an ENTER key press
|
||||
- Reader format: `[CARD_DATA][ENTER]`
|
||||
|
||||
The CardReader class:
|
||||
- Captures key press events
|
||||
- Builds the card data string character by character
|
||||
- Completes reading when ENTER is detected
|
||||
- Returns the complete card data to the callback
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Server-Side Validation**: Card validation should be implemented on the server
|
||||
2. **Timeout**: 5-second timeout prevents infinite waiting for card swipe
|
||||
3. **Logging**: All card reads are logged with the raw card data
|
||||
4. **Permissions**: Edit permission must be enabled on the server (`edit_on_player`)
|
||||
5. **Raw Data**: Card data is sent as-is; server is responsible for validation and authorization
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Card Reader Not Detected
|
||||
- Check USB connection
|
||||
- Run `ls /dev/input/` to see available devices
|
||||
- Run the test script to verify detection
|
||||
- Check `evdev` is installed: `python3 -c "import evdev"`
|
||||
|
||||
### Card Swipes Not Recognized
|
||||
- Verify card reader sends keyboard events
|
||||
- Test with the `test_card_reader.py` utility
|
||||
- Check card format is compatible with reader
|
||||
- Ensure card is swiped smoothly at proper speed
|
||||
|
||||
### Card Data Not Captured
|
||||
- Check card data format in logs
|
||||
- Enable debug logging to see raw card data
|
||||
- Test in fallback mode (without evdev) to isolate hardware issues
|
||||
- Verify card swipe completes within 5-second timeout
|
||||
|
||||
### Permission Denied Errors
|
||||
- User may need to be in the `input` group:
|
||||
```bash
|
||||
sudo usermod -a -G input $USER
|
||||
```
|
||||
- Reboot after adding user to group
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements for the card reader system:
|
||||
|
||||
1. **Server Validation**: Server validates cards against database and returns authorization
|
||||
2. **Card Enrollment**: Server-side UI for registering new cards
|
||||
3. **Multiple Card Types**: Support for different card formats (barcode, RFID, magnetic)
|
||||
4. **Client-side Validation**: Add optional local card validation before opening edit
|
||||
5. **Audit Trail**: Server tracks all card usage with timestamps
|
||||
6. **RFID Support**: Test and optimize for RFID readers
|
||||
7. **Barcode Scanners**: Support USB barcode scanners as alternative
|
||||
8. **Retry Logic**: Allow re-swipe if card read fails
|
||||
|
||||
## Related Files
|
||||
|
||||
- `/src/main.py` - Main implementation (CardReader class, authentication flow)
|
||||
- `/src/edit_drowing.py` - Drawing/editing interface (uses authenticated user)
|
||||
- `/working_files/test_card_reader.py` - Card reader test utility
|
||||
- `/requirements.txt` - Dependencies (includes evdev)
|
||||
114
working_files/test_card_reader.py
Normal file
114
working_files/test_card_reader.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for USB card reader functionality
|
||||
"""
|
||||
|
||||
import evdev
|
||||
from evdev import InputDevice, categorize, ecodes
|
||||
import time
|
||||
|
||||
def list_input_devices():
|
||||
"""List all available input devices"""
|
||||
print("\n=== Available Input Devices ===")
|
||||
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
|
||||
|
||||
for i, device in enumerate(devices):
|
||||
print(f"\n[{i}] {device.path}")
|
||||
print(f" Name: {device.name}")
|
||||
print(f" Phys: {device.phys}")
|
||||
capabilities = device.capabilities()
|
||||
if ecodes.EV_KEY in capabilities:
|
||||
print(f" Type: Keyboard/HID Input Device")
|
||||
|
||||
return devices
|
||||
|
||||
def test_card_reader(device_index=None):
|
||||
"""Test reading from a card reader device"""
|
||||
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
|
||||
|
||||
if device_index is not None:
|
||||
if device_index >= len(devices):
|
||||
print(f"Error: Device index {device_index} out of range")
|
||||
return
|
||||
device = devices[device_index]
|
||||
else:
|
||||
# Try to find a card reader automatically
|
||||
device = None
|
||||
for dev in devices:
|
||||
if 'keyboard' in dev.name.lower() or 'card' in dev.name.lower() or 'reader' in dev.name.lower():
|
||||
device = dev
|
||||
print(f"Found potential card reader: {dev.name}")
|
||||
break
|
||||
|
||||
if not device and devices:
|
||||
# Use first keyboard device
|
||||
for dev in devices:
|
||||
capabilities = dev.capabilities()
|
||||
if ecodes.EV_KEY in capabilities:
|
||||
device = dev
|
||||
print(f"Using keyboard device: {dev.name}")
|
||||
break
|
||||
|
||||
if not device:
|
||||
print("No suitable input device found!")
|
||||
return
|
||||
|
||||
print(f"\n=== Testing Card Reader ===")
|
||||
print(f"Device: {device.name}")
|
||||
print(f"Path: {device.path}")
|
||||
print("\nSwipe your card now (press Ctrl+C to exit)...\n")
|
||||
|
||||
card_data = ""
|
||||
|
||||
try:
|
||||
for event in device.read_loop():
|
||||
if event.type == ecodes.EV_KEY:
|
||||
key_event = categorize(event)
|
||||
|
||||
if key_event.keystate == 1: # Key down
|
||||
key_code = key_event.keycode
|
||||
|
||||
# Handle Enter key (card read complete)
|
||||
if key_code == 'KEY_ENTER':
|
||||
print(f"\n✓ Card data received: '{card_data}'")
|
||||
print(f" Length: {len(card_data)} characters")
|
||||
print(f" Processed ID: card_{card_data.strip().upper()}")
|
||||
print("\nReady for next card swipe...")
|
||||
card_data = ""
|
||||
|
||||
# Build card data string
|
||||
elif key_code.startswith('KEY_'):
|
||||
char = key_code.replace('KEY_', '')
|
||||
if len(char) == 1: # Single character
|
||||
card_data += char
|
||||
print(f"Reading: {card_data}", end='\r', flush=True)
|
||||
elif char.isdigit(): # Handle numeric keys
|
||||
card_data += char
|
||||
print(f"Reading: {card_data}", end='\r', flush=True)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nTest stopped by user")
|
||||
except Exception as e:
|
||||
print(f"\nError: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("USB Card Reader Test Tool")
|
||||
print("=" * 50)
|
||||
|
||||
devices = list_input_devices()
|
||||
|
||||
if not devices:
|
||||
print("\nNo input devices found!")
|
||||
exit(1)
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
choice = input("\nEnter device number to test (or press Enter for auto-detect): ").strip()
|
||||
|
||||
if choice:
|
||||
try:
|
||||
device_index = int(choice)
|
||||
test_card_reader(device_index)
|
||||
except ValueError:
|
||||
print("Invalid device number!")
|
||||
else:
|
||||
test_card_reader()
|
||||
Reference in New Issue
Block a user