# Admin API Implementation (Account Management)

This document describes the implementation of the Admin API endpoints for the MaplePress frontend, integrated with the MaplePress backend API.

## Overview

The Admin API endpoints provide administrative operations for managing user accounts, specifically handling account lockouts that occur due to failed login attempts. These endpoints implement CWE-307 protection (Improper Restriction of Excessive Authentication Attempts) by allowing administrators to check lock status and manually unlock accounts.

**⚠️ SECURITY**: These endpoints require admin authentication and should only be accessible to users with admin or root roles.

## Backend API Endpoints

### Check Account Lock Status
**Endpoint**: `GET /api/v1/admin/account-status`
**Authentication**: Required (JWT token with admin role)
**Query Parameters**: `email` (required)

### Unlock Locked Account
**Endpoint**: `POST /api/v1/admin/unlock-account`
**Authentication**: Required (JWT token with admin role)

**Source Files**:
- `cloud/maplepress-backend/internal/interface/http/handler/admin/account_status_handler.go`
- `cloud/maplepress-backend/internal/interface/http/handler/admin/unlock_account_handler.go`

## Request/Response Structures

### Check Account Status Request
```
GET /api/v1/admin/account-status?email=user@example.com
```

**Headers Required:**
- `Authorization: JWT {access_token}` (admin role required)

### Check Account Status Response
```json
{
  "email": "user@example.com",
  "is_locked": true,
  "failed_attempts": 5,
  "remaining_time": "5 minutes 30 seconds",
  "remaining_seconds": 330
}
```

**When Account Not Locked:**
```json
{
  "email": "user@example.com",
  "is_locked": false,
  "failed_attempts": 2
}
```

### Unlock Account Request
```json
{
  "email": "user@example.com"
}
```

**Headers Required:**
- `Content-Type: application/json`
- `Authorization: JWT {access_token}` (admin role required)

### Unlock Account Response
```json
{
  "success": true,
  "message": "Account unlocked successfully",
  "email": "user@example.com"
}
```

**When Account Not Locked:**
```json
{
  "success": true,
  "message": "Account is not locked",
  "email": "user@example.com"
}
```

## Frontend Implementation

### AdminService (`src/services/API/AdminService.js`)

Handles all admin operations for account management.

**Key Features:**
- Check account lock status with detailed information
- Unlock locked accounts (with security event logging)
- Helper to check if account needs unlocking
- Client-side email validation
- Remaining time formatting
- Admin role enforcement with clear error messages

**Methods:**

#### `getAccountStatus(email)`
Check if a user account is locked due to failed login attempts.

```javascript
import AdminService from './services/API/AdminService';

const status = await AdminService.getAccountStatus("user@example.com");
console.log(status);
// Output:
// {
//   email: "user@example.com",
//   isLocked: true,
//   failedAttempts: 5,
//   remainingTime: "5 minutes 30 seconds",
//   remainingSeconds: 330
// }
```

**Parameters:**
- `email` (string, required): User's email address to check

**Returns:**
```javascript
{
  email: string,              // User's email
  isLocked: boolean,          // Whether account is locked
  failedAttempts: number,     // Number of failed login attempts
  remainingTime: string,      // Human-readable time until unlock
  remainingSeconds: number    // Seconds until automatic unlock
}
```

**Throws:**
- "Email is required" - If email is missing
- "Email cannot be empty" - If email is empty after trimming
- "Invalid email format" - If email format is invalid
- "Admin authentication required. Please log in with admin credentials." - Missing/invalid admin token (401)
- "Access denied. Admin privileges required for this operation." - User is not admin (403)

#### `unlockAccount(email)`
Unlock a user account that has been locked due to failed login attempts.

```javascript
const result = await AdminService.unlockAccount("user@example.com");
console.log(result);
// Output:
// {
//   success: true,
//   message: "Account unlocked successfully",
//   email: "user@example.com"
// }
```

**⚠️ SECURITY EVENT**: This operation logs a security event (`ACCOUNT_UNLOCKED`) with the admin user ID who performed the unlock operation. This creates an audit trail for security compliance.

**Parameters:**
- `email` (string, required): User's email address to unlock

**Returns:**
```javascript
{
  success: boolean,
  message: string,
  email: string
}
```

**Throws:**
- "Email is required" - If email is missing
- "Email cannot be empty" - If email is empty after trimming
- "Invalid email format" - If email format is invalid
- "Account is not currently locked." - If account is not locked
- "Admin authentication required. Please log in with admin credentials." - Missing/invalid admin token (401)
- "Access denied. Admin privileges required for this operation." - User is not admin (403)

#### `needsUnlock(email)`
Check if an account needs unlocking (is locked with remaining time).

```javascript
const needs = await AdminService.needsUnlock("user@example.com");
console.log(needs); // true or false
```

**Parameters:**
- `email` (string, required): User's email address to check

**Returns:** `boolean` - True if account is locked and needs admin unlock

**Use Case:** Check before showing "Unlock Account" button in admin UI.

#### `validateEmail(email)`
Validate email format.

```javascript
const result = AdminService.validateEmail("user@example.com");
console.log(result); // { valid: true, error: null }

const invalid = AdminService.validateEmail("invalid-email");
console.log(invalid); // { valid: false, error: "Invalid email format" }
```

**Returns:** `{ valid: boolean, error: string|null }`

**Validation Rules:**
- Required (non-empty)
- Valid email format (`user@domain.com`)
- Maximum 255 characters

#### `formatRemainingTime(seconds)`
Format remaining time for display.

```javascript
const formatted = AdminService.formatRemainingTime(330);
console.log(formatted); // "5 minutes 30 seconds"

const oneHour = AdminService.formatRemainingTime(3661);
console.log(oneHour); // "1 hour 1 minute 1 second"
```

**Returns:** `string` (e.g., "5 minutes 30 seconds", "1 hour", "30 seconds")

## Data Flow

### Check Account Status Flow
```
Admin provides email to check
    ↓
AdminService.getAccountStatus()
    ↓
Validate email format (client-side)
    ↓
ApiClient.get() with JWT token (admin role)
    ↓
Token automatically refreshed if needed
    ↓
GET /api/v1/admin/account-status?email=...
    ↓
Backend checks Redis for lock status
    ↓
Backend returns lock info and failed attempts
    ↓
AdminService transforms response
    ↓
Component displays lock status
```

### Unlock Account Flow
```
Admin requests account unlock
    ↓
AdminService.unlockAccount()
    ↓
Validate email format (client-side)
    ↓
ApiClient.post() with JWT token (admin role)
    ↓
Token automatically refreshed if needed
    ↓
POST /api/v1/admin/unlock-account
    ↓
Backend checks if account is locked
    ↓
Backend clears lock status in Redis
    ↓
Backend logs security event (ACCOUNT_UNLOCKED)
    ↓
Backend returns success response
    ↓
AdminService transforms response
    ↓
Component displays unlock confirmation
```

## Error Handling

### Error Types

| Error Condition | Response | Frontend Behavior |
|----------------|----------|-------------------|
| Missing admin authentication | 401 Unauthorized | "Admin authentication required." |
| Insufficient privileges | 403 Forbidden | "Access denied. Admin privileges required." |
| Invalid email | 400 Bad Request | Specific validation error |
| Account not locked (unlock) | 200 OK | "Account is not locked" (success) |
| Server error | 500 Internal Server Error | Generic error message |

## Usage Examples

### Admin Panel - Check Account Status

```javascript
import React, { useState } from 'react';
import AdminService from '../../services/API/AdminService';

function AccountStatusChecker() {
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState(null);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const handleCheck = async (e) => {
    e.preventDefault();
    setError('');
    setStatus(null);
    setLoading(true);

    // Validate before sending
    const validation = AdminService.validateEmail(email);
    if (!validation.valid) {
      setError(validation.error);
      setLoading(false);
      return;
    }

    try {
      const accountStatus = await AdminService.getAccountStatus(email);
      setStatus(accountStatus);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="account-status-checker">
      <h2>Check Account Lock Status</h2>

      <form onSubmit={handleCheck}>
        <div>
          <label>Email Address:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="user@example.com"
            maxLength={255}
          />
        </div>

        {error && <p className="error">{error}</p>}

        <button type="submit" disabled={loading}>
          {loading ? 'Checking...' : 'Check Status'}
        </button>
      </form>

      {status && (
        <div className="status-result">
          <h3>Account Status for {status.email}</h3>

          {status.isLocked ? (
            <div className="locked-status">
              <p className="warning">🔒 Account is LOCKED</p>
              <p>Failed Attempts: {status.failedAttempts}</p>
              <p>Automatic Unlock In: {status.remainingTime}</p>
              <p>({status.remainingSeconds} seconds remaining)</p>
            </div>
          ) : (
            <div className="unlocked-status">
              <p className="success">✓ Account is NOT locked</p>
              <p>Failed Attempts: {status.failedAttempts}</p>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

export default AccountStatusChecker;
```

### Admin Panel - Unlock Account

```javascript
import React, { useState } from 'react';
import AdminService from '../../services/API/AdminService';

function AccountUnlocker() {
  const [email, setEmail] = useState('');
  const [result, setResult] = useState(null);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const handleUnlock = async (e) => {
    e.preventDefault();
    setError('');
    setResult(null);
    setLoading(true);

    // Validate before sending
    const validation = AdminService.validateEmail(email);
    if (!validation.valid) {
      setError(validation.error);
      setLoading(false);
      return;
    }

    // Confirm action
    const confirmed = confirm(
      `Are you sure you want to unlock the account for "${email}"?\n\n` +
      `This will:\n` +
      `- Clear all failed login attempts\n` +
      `- Remove the account lock immediately\n` +
      `- Log a security event with your admin ID\n\n` +
      `Continue?`
    );

    if (!confirmed) {
      setLoading(false);
      return;
    }

    try {
      const unlockResult = await AdminService.unlockAccount(email);
      setResult(unlockResult);

      // Clear form on success
      if (unlockResult.success) {
        setEmail('');
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="account-unlocker">
      <h2>Unlock User Account</h2>
      <p className="warning">
        ⚠️ Admin action: This operation will be logged for security audit.
      </p>

      <form onSubmit={handleUnlock}>
        <div>
          <label>Email Address:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="user@example.com"
            maxLength={255}
          />
        </div>

        {error && <p className="error">{error}</p>}

        {result && (
          <div className={result.success ? "success" : "error"}>
            <p>{result.message}</p>
            <p>Email: {result.email}</p>
          </div>
        )}

        <button type="submit" disabled={loading}>
          {loading ? 'Unlocking...' : 'Unlock Account'}
        </button>
      </form>
    </div>
  );
}

export default AccountUnlocker;
```

### Combined Admin Panel - Status + Unlock

```javascript
import React, { useState, useEffect } from 'react';
import AdminService from '../../services/API/AdminService';

function AccountManager() {
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState(null);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const [unlocking, setUnlocking] = useState(false);

  const checkStatus = async () => {
    if (!email) return;

    setError('');
    setLoading(true);

    try {
      const accountStatus = await AdminService.getAccountStatus(email);
      setStatus(accountStatus);
    } catch (err) {
      setError(err.message);
      setStatus(null);
    } finally {
      setLoading(false);
    }
  };

  const handleUnlock = async () => {
    if (!confirm(`Unlock account for "${email}"?`)) {
      return;
    }

    setError('');
    setUnlocking(true);

    try {
      await AdminService.unlockAccount(email);
      // Refresh status after unlock
      await checkStatus();
      alert(`Account unlocked successfully for ${email}`);
    } catch (err) {
      setError(err.message);
    } finally {
      setUnlocking(false);
    }
  };

  return (
    <div className="account-manager">
      <h2>Account Management</h2>

      <div className="search-form">
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Enter email address"
          maxLength={255}
        />
        <button onClick={checkStatus} disabled={loading || !email}>
          {loading ? 'Checking...' : 'Check Status'}
        </button>
      </div>

      {error && <p className="error">{error}</p>}

      {status && (
        <div className="account-info">
          <h3>{status.email}</h3>

          <div className="status-details">
            <p>
              Status:{" "}
              <strong className={status.isLocked ? "text-red" : "text-green"}>
                {status.isLocked ? "🔒 LOCKED" : "✓ Not Locked"}
              </strong>
            </p>
            <p>Failed Attempts: {status.failedAttempts}</p>

            {status.isLocked && (
              <>
                <p>Automatic Unlock In: {status.remainingTime}</p>
                <p className="text-muted">
                  ({status.remainingSeconds} seconds)
                </p>

                <button
                  onClick={handleUnlock}
                  disabled={unlocking}
                  className="btn-danger"
                >
                  {unlocking ? 'Unlocking...' : 'Unlock Account Now'}
                </button>
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

export default AccountManager;
```

### Locked Users Dashboard

```javascript
import React, { useState, useEffect } from 'react';
import AdminService from '../../services/API/AdminService';

function LockedUsersDashboard({ suspectedEmails }) {
  const [lockedUsers, setLockedUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkAllUsers = async () => {
      setLoading(true);
      const locked = [];

      for (const email of suspectedEmails) {
        try {
          const status = await AdminService.getAccountStatus(email);
          if (status.isLocked) {
            locked.push(status);
          }
        } catch (err) {
          console.error(`Failed to check ${email}:`, err);
        }
      }

      setLockedUsers(locked);
      setLoading(false);
    };

    checkAllUsers();
  }, [suspectedEmails]);

  const unlockUser = async (email) => {
    try {
      await AdminService.unlockAccount(email);
      // Remove from locked users list
      setLockedUsers(prev => prev.filter(user => user.email !== email));
    } catch (err) {
      alert(`Failed to unlock ${email}: ${err.message}`);
    }
  };

  if (loading) return <div>Checking locked accounts...</div>;

  if (lockedUsers.length === 0) {
    return <div>No locked accounts found.</div>;
  }

  return (
    <div className="locked-users-dashboard">
      <h2>Locked User Accounts ({lockedUsers.length})</h2>

      <table>
        <thead>
          <tr>
            <th>Email</th>
            <th>Failed Attempts</th>
            <th>Unlocks In</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {lockedUsers.map(user => (
            <tr key={user.email}>
              <td>{user.email}</td>
              <td>{user.failedAttempts}</td>
              <td>{user.remainingTime}</td>
              <td>
                <button onClick={() => unlockUser(user.email)}>
                  Unlock Now
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default LockedUsersDashboard;
```

## Testing

### Test Check Account Status

```javascript
// In browser console after admin login

import AdminService from './services/API/AdminService';

// Validate email
const validation = AdminService.validateEmail("user@example.com");
console.log("Email valid:", validation.valid);

// Check account status
const status = await AdminService.getAccountStatus("user@example.com");
console.log("Account status:", status);
console.log("Is locked:", status.isLocked);
console.log("Failed attempts:", status.failedAttempts);

if (status.isLocked) {
  console.log("Remaining time:", status.remainingTime);
  console.log("Remaining seconds:", status.remainingSeconds);
}
```

### Test Unlock Account

```javascript
// Check if needs unlock
const needs = await AdminService.needsUnlock("user@example.com");
console.log("Needs unlock:", needs);

// Unlock account
if (needs) {
  const result = await AdminService.unlockAccount("user@example.com");
  console.log("Unlock result:", result);
  console.log("Success:", result.success);
  console.log("Message:", result.message);
}
```

### Test with curl

```bash
# 1. Login as admin and get access token
ACCESS_TOKEN=$(curl -X POST http://localhost:8000/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@example.com",
    "password": "AdminPass123!"
  }' | jq -r '.access_token')

# 2. Check account status
curl -X GET "http://localhost:8000/api/v1/admin/account-status?email=user@example.com" \
  -H "Authorization: JWT $ACCESS_TOKEN" | jq

# Example response:
# {
#   "email": "user@example.com",
#   "is_locked": true,
#   "failed_attempts": 5,
#   "remaining_time": "5 minutes 30 seconds",
#   "remaining_seconds": 330
# }

# 3. Unlock account
curl -X POST http://localhost:8000/api/v1/admin/unlock-account \
  -H "Content-Type: application/json" \
  -H "Authorization: JWT $ACCESS_TOKEN" \
  -d '{
    "email": "user@example.com"
  }' | jq

# Example response:
# {
#   "success": true,
#   "message": "Account unlocked successfully",
#   "email": "user@example.com"
# }

# 4. Verify account is unlocked
curl -X GET "http://localhost:8000/api/v1/admin/account-status?email=user@example.com" \
  -H "Authorization: JWT $ACCESS_TOKEN" | jq

# Should show:
# {
#   "email": "user@example.com",
#   "is_locked": false,
#   "failed_attempts": 0
# }
```

## Important Notes

### Security and Authorization

1. **Admin Role Required**: Both endpoints require admin authentication
2. **Security Event Logging**: Unlock operations log security events for audit trail
3. **Admin User ID**: The admin who performs unlock is logged (from JWT)
4. **Rate Limiting**: Generic rate limiting applied to prevent abuse

### Account Lockout Mechanism

- Accounts are locked after **excessive failed login attempts** (configurable)
- Lock duration is typically **15-30 minutes** (configurable)
- Failed attempts counter resets after successful login
- Automatic unlock occurs when lock time expires
- Admin can unlock immediately without waiting

### Integration with CWE-307 Protection

These endpoints are part of the security system that protects against:
- **CWE-307**: Improper Restriction of Excessive Authentication Attempts
- **CWE-770**: Allocation of Resources Without Limits or Throttling

The login rate limiter tracks:
- Failed login attempts per email
- Account lock status and expiry
- Grace period for automatic unlock

### Best Practices

1. **Verify Admin Role**: Always check user has admin role before showing UI
2. **Confirm Before Unlock**: Always require confirmation before unlocking
3. **Audit Trail**: Log all admin actions for security compliance
4. **User Notification**: Consider notifying users when their account is unlocked by admin
5. **Regular Review**: Periodically review locked accounts dashboard

## Related Files

### Created Files
```
src/services/API/AdminService.js
docs/ADMIN_API.md
```

### Backend Reference Files
```
cloud/maplepress-backend/internal/interface/http/handler/admin/account_status_handler.go
cloud/maplepress-backend/internal/interface/http/handler/admin/unlock_account_handler.go
cloud/maplepress-backend/internal/interface/http/server.go (routes)
```

## Related Documentation

- [LOGIN_API.md](./LOGIN_API.md) - User login with rate limiting
- [ME_API.md](./ME_API.md) - Current user profile includes role checking
- [USER_API.md](./USER_API.md) - User management operations
- [FRONTEND_ARCHITECTURE.md](./FRONTEND_ARCHITECTURE.md) - Architecture overview
- [README.md](./README.md) - Documentation index

## Summary

The Admin API implementation provides:

1. **Account Status Checking**: View lock status, failed attempts, and remaining time
2. **Account Unlocking**: Manually unlock accounts with security event logging
3. **Helper Functions**: Email validation and time formatting
4. **Security Compliance**: Admin role enforcement and audit trail
5. **Error Handling**: Clear error messages and graceful failures

Essential for managing user account security and providing admin support for locked-out users while maintaining security compliance (CWE-307 protection).

---

**Last Updated**: October 30, 2024
**Frontend Version**: 0.0.0
**Documentation Version**: 1.0.0
