# User Management System

## Overview
A secure user management front-end that allows you to create and manage users. Each user record contains:
- **Username**: Unique identifier
- **Password**: Securely hashed using PBKDF2-SHA256 (industry-standard)
- **Default Location**: Primary location (required) - one of: Lindsborg, Iola, KC, or BMD
- **Active Status**: Whether the user account is active or inactive (toggleable in user list)
- **Location Settings**: Per-location configuration with:
  - **Accessible**: Whether the user can access this location

## User Interface Features

### Table-Based Location Configuration
The form uses an intuitive table layout with:
- **Default Location Column**: Radio buttons to select ONE primary location (required)
- **Accessible Column**: Checkboxes to mark which locations the user can access

### User List with Active Toggle
Each user in the list shows:
- **Username** and **Location Information**
- **Active/Inactive Toggle**: Click to enable or disable the user account
  - Active users have full border color
  - Inactive users are dimmed with reduced opacity
- **Delete Button**: Remove the user permanently

### Smart Header Checkboxes
The accessible column has a header checkbox that:
- Shows three states: checked ✓, unchecked ☐, or indeterminate ⊟ (mixed)
- **Clicking cycles**: If off or mixed → all on, if on → all off
- **Auto-updates**: When you check/uncheck individual rows, the header shows:
  - ✓ if all are checked
  - ☐ if none are checked
  - ⊟ if some are checked (mixed state)

### Extensible Design
The table structure is designed to easily add more columns in the future:
- Permission columns (Permission 1, Permission 2, etc.)
- Custom attributes
- Feature flags
- Any other per-location settings

Each new column can have the same header checkbox behavior.

## Security Features
- **Password Hashing**: Passwords are hashed using `pbkdf2:sha256` algorithm
- **Not Plain Text**: Passwords are never stored in plain text
- **Cryptographically Secure**: Uses Werkzeug's secure password hashing
- **Cannot Be Decoded**: Hashed passwords cannot be reversed back to plain text

## How to Use

### 1. Access the User Manager
Navigate to: `http://localhost:8080/users`

### 2. Add New Users
- Fill in the form with username, password, and location
- Click "Add User" button
- User will be added with a securely hashed password

### 3. View Users
- All users are displayed in a list showing username and location
- Password hashes are NOT displayed for security

### 4. Download Users JSON
- Click "📥 Download Users JSON" button
- Downloads a `users.json` file containing all users
- Password field contains the secure hash (not plain text)

### 5. Delete Users
- Click "Delete" next to any user to remove them
- Click "🗑️ Clear All Users" to remove all users at once

## API Endpoints

### GET /users
Displays the user management interface.

### GET /api/users
Returns all users in JSON format.

**Response:**
```json
{
  "status": "success",
  "users": [
    {
      "username": "john_doe",
      "password": "pbkdf2:sha256:600000$...",
      "defaultLocation": "LINDS",
      "active": true,
      "locationSettings": {
        "LINDS": { "accessible": true },
        "IOLA": { "accessible": true },
        "KC": { "accessible": false },
        "BMD": { "accessible": false }
      }
    }
  ]
}
```

### POST /api/users
Adds a new user.

**Request Body:**
```json
{
  "username": "jane_smith",
  "password": "mySecurePassword123",
  "defaultLocation": "KC",
  "active": true,
  "locationSettings": {
    "LINDS": { "accessible": true },
    "IOLA": { "accessible": true },
    "KC": { "accessible": true },
    "BMD": { "accessible": true }
  }
}
```

**Response:**
```json
{
  "status": "success",
  "message": "User added successfully",
  "users": [...]
}
```

### DELETE /api/users/{index}
Deletes a user by their index position.

### PATCH /api/users/{index}/active
Toggle user active status.

**Request Body:**
```json
{
  "active": true
}
```

### DELETE /api/users/clear
Clears all users from the system.

### GET /api/users/download
Downloads all users as a JSON file.

## Password Security

### How Passwords Are Stored
Passwords are hashed using PBKDF2-SHA256 with the following properties:
- **Algorithm**: PBKDF2 (Password-Based Key Derivation Function 2)
- **Hash Function**: SHA-256
- **Iterations**: 600,000+ (computationally expensive for attackers)
- **Salt**: Automatically generated unique salt per password

### Example Hash Format
```
pbkdf2:sha256:600000$AbCdEfGh$1234567890abcdef...
```

Components:
- `pbkdf2:sha256` - Algorithm identifier
- `600000` - Number of iterations
- `$AbCdEfGh` - Random salt
- `$1234567890abcdef...` - Actual hash

### Password Verification
To verify a password, use Werkzeug's `check_password_hash()`:

```python
from werkzeug.security import check_password_hash

# user['password'] contains the hash
if check_password_hash(user['password'], provided_password):
    print("Password is correct!")
```

## Data Storage
Users are stored in: `app/data/users.json`

**Example users.json:**
```json
[
  {
    "username": "admin",
    "password": "pbkdf2:sha256:600000$r7K8L9M0$a1b2c3d4e5f6...",
    "defaultLocation": "LINDS",
    "active": true,
    "locationSettings": {
      "LINDS": { "accessible": true },
      "IOLA": { "accessible": true },
      "KC": { "accessible": false },
      "BMD": { "accessible": false }
    }
  },
  {
    "username": "user1",
    "password": "pbkdf2:sha256:600000$n5O6P7Q8$x9y8z7w6v5u4...",
    "defaultLocation": "KC",
    "active": false,
    "locationSettings": {
      "LINDS": { "accessible": true },
      "IOLA": { "accessible": true },
      "KC": { "accessible": true },
      "BMD": { "accessible": true }
    }
  }
]
```

### Location Codes
- **LINDS** = Lindsborg
- **IOLA** = Iola
- **KC** = KC
- **BMD** = BMD

## Integration Example

### Authenticate and Get User Info

```python
from werkzeug.security import check_password_hash
import json

def authenticate_user(username, password):
    """Authenticate a user by username and password"""
    with open('app/data/users.json', 'r') as f:
        users = json.load(f)
    
    # Find user
    user = next((u for u in users if u['username'] == username), None)
    
    if user and check_password_hash(user['password'], password):
        return True, user
    return False, None

# Usage
success, user_data = authenticate_user('john_doe', 'password123')
if success:
    print(f"Welcome {user_data['username']}!")  
    print(f"Default location: {user_data['defaultLocation']}")
    print(f"Account active: {user_data.get('active', True)}")
    
    # Check if user can access a location
    if user_data['locationSettings']['KC']['accessible']:
        print("User can access KC")
```

### Check User Permissions for a Location

```python
def can_access_location(user_data, location_code):
    """Check if user can access a specific location"""
    return user_data.get('locationSettings', {}).get(location_code, {}).get('accessible', False)

def is_user_active(user_data):
    """Check if user account is active"""
    return user_data.get('active', True)

# Usage
if not is_user_active(user_data):
    print("User account is inactive")
    return

if can_access_location(user_data, 'LINDS'):
    print("User can access Lindsborg")
```

### Get All Accessible Locations for a User

```python
def get_accessible_locations(user_data):
    """Get all locations where user has access"""
    accessible_locations = []
    for location_code, settings in user_data.get('locationSettings', {}).items():
        if settings.get('accessible', False):
            accessible_locations.append(location_code)
    return accessible_locations

# Usage
accessible = get_accessible_locations(user_data)
print(f"User can access: {', '.join(accessible)}")
```

## Notes
- Username must be unique
- Minimum password length: 6 characters
- Default location is required (one of: Lindsborg, Iola, KC, BMD)
- **Default location is automatically marked as accessible** when creating a user
- Accessible checkboxes are optional for other locations
- New users are **active by default**
- Toggle active status in the user list section
- Users are stored locally in JSON format
- This is a separate endpoint from the main Product Finder app

## Adding New Permissions/Columns

The system is designed to be easily extensible. To add new permission columns:

### 1. Update the HTML table
Add a new column header and cells in `user_manager.html`:

```html
<!-- In the table header -->
<th class="checkbox-header" onclick="toggleHeaderCheckbox('newPermission')">
    <input type="checkbox" id="headerNewPermission" 
           onclick="event.stopPropagation(); toggleAllCheckboxes('newPermission')">
    Permission Name
</th>

<!-- In each table row -->
<td class="checkbox-cell">
    <input type="checkbox" class="newPermission-checkbox" 
           data-location="LINDS" onchange="updateHeaderCheckbox('newPermission')">
</td>
```

### 2. Update the JavaScript form submission
Modify the form submission handler to collect the new permission:

```javascript
locationSettings[loc.code] = {
    active: activeCheckbox ? activeCheckbox.checked : false,
    accessible: accessibleCheckbox ? accessibleCheckbox.checked : false,
    newPermission: newPermCheckbox ? newPermCheckbox.checked : false  // Add this
};
```

### 3. Update the backend (optional)
The backend already handles any properties in `locationSettings`, so no changes are required unless you want validation.

### 4. Reset header checkbox on form submit
Add to the form reset section:

```javascript
document.getElementById('headerNewPermission').checked = false;
document.getElementById('headerNewPermission').indeterminate = false;
```

That's it! The system will automatically save and load the new permission data.
