827 lines
22 KiB
Markdown
827 lines
22 KiB
Markdown
# 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
|