Skip to content

Security API

The Security API provides endpoints for managing security features in the UBU Finance backend.

Overview

The Security API allows administrators to monitor and manage security features such as IP whitelisting, account lockout, and security status. These endpoints are protected by role-based access control and require admin privileges.

Authentication

All Security API endpoints require authentication with a valid JWT token. The token must be included in the Authorization header with the Bearer scheme.

Authorization: Bearer <token>

Endpoints

Get Security Status

Returns the current security status, including metrics for rate limit hits, IP whitelist blocks, and account lockouts.

Endpoint: GET /api/admin/security/status

Permissions Required: admin

Response:

{
  "security_status": {
    "rate_limit_hits": {
      "192.168.1.1": 5,
      "203.0.113.1": 10
    },
    "ip_whitelist_blocks": {
      "203.0.113.2": 3
    },
    "account_lockouts": {
      "user123": 2
    },
    "total_security_events": 20
  },
  "config": {
    "rate_limit": {
      "enabled": true,
      "max_requests": 100,
      "window_seconds": 60
    },
    "ip_whitelist": {
      "enabled": true,
      "allow_localhost": true
    },
    "account_lockout": {
      "enabled": true,
      "max_failed_attempts": 5,
      "lockout_duration_seconds": 1800
    }
  }
}

Test Security Components

Tests all security components and returns the results.

Endpoint: GET /api/admin/security/test

Permissions Required: admin

Response:

{
  "rate_limiter": {
    "status": "ok",
    "message": "Rate limiter is working correctly"
  },
  "ip_whitelist": {
    "status": "ok",
    "message": "IP whitelist is working correctly"
  },
  "account_lockout": {
    "status": "ok",
    "message": "Account lockout is working correctly"
  }
}

Add IP to Whitelist

Adds an IP address to the whitelist.

Endpoint: POST /api/admin/security/whitelist/add/{ip}

Permissions Required: admin

URL Parameters:

  • ip: The IP address to add to the whitelist. Can be a single IP (e.g., 192.168.1.1) or a CIDR range (e.g., 192.168.1.0/24).

Response:

{
  "status": "success",
  "message": "Added 192.168.1.1 to whitelist"
}

Error Responses:

  • 400 Bad Request: If the IP address format is invalid.
  • 403 Forbidden: If the user doesn't have admin privileges.
  • 500 Internal Server Error: If there's an error adding the IP to the whitelist.

Remove IP from Whitelist

Removes an IP address from the whitelist.

Endpoint: POST /api/admin/security/whitelist/remove/{ip}

Permissions Required: admin

URL Parameters:

  • ip: The IP address to remove from the whitelist.

Response:

{
  "status": "success",
  "message": "Removed 192.168.1.1 from whitelist"
}

Error Responses:

  • 400 Bad Request: If the IP address format is invalid.
  • 403 Forbidden: If the user doesn't have admin privileges.
  • 500 Internal Server Error: If there's an error removing the IP from the whitelist.

Unlock Account

Unlocks a user account that has been locked due to too many failed login attempts.

Endpoint: POST /api/admin/security/account/unlock/{username}

Permissions Required: admin

URL Parameters:

  • username: The username of the account to unlock.

Response:

{
  "status": "success",
  "message": "Unlocked account for user123"
}

Error Responses:

  • 403 Forbidden: If the user doesn't have admin privileges.
  • 404 Not Found: If the user doesn't exist.
  • 500 Internal Server Error: If there's an error unlocking the account.

Client Implementation Examples

Getting Security Status

import requests

def get_security_status(base_url, token):
    url = f"{base_url}/api/admin/security/status"
    headers = {
        "Authorization": f"Bearer {token}"
    }

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        return response.json()
    else:
        print(f"Failed to get security status: {response.json()}")
        return None
async function getSecurityStatus(baseUrl, token) {
  const url = `${baseUrl}/api/admin/security/status`;
  const headers = {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  };

  try {
    const response = await fetch(url, {
      method: 'GET',
      headers: headers
    });

    if (response.ok) {
      return await response.json();
    } else {
      const data = await response.json();
      console.error(`Failed to get security status: ${data.detail}`);
      return null;
    }
  } catch (error) {
    console.error(`Error getting security status: ${error}`);
    return null;
  }
}
#!/bin/bash

get_security_status() {
  local base_url=$1
  local token=$2

  curl -s \
    -H "Authorization: Bearer $token" \
    "$base_url/api/admin/security/status"
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class SecurityClient
{
    private readonly HttpClient _client;
    private readonly string _baseUrl;

    public SecurityClient(string baseUrl)
    {
        _client = new HttpClient();
        _baseUrl = baseUrl;
    }

    public async Task<object> GetSecurityStatusAsync(string token)
    {
        string url = $"{_baseUrl}/api/admin/security/status";

        _client.DefaultRequestHeaders.Authorization = 
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

        try
        {
            HttpResponseMessage response = await _client.GetAsync(url);

            if (response.IsSuccessStatusCode)
            {
                string content = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject(content);
            }
            else
            {
                string content = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Failed to get security status: {content}");
                return null;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting security status: {ex.Message}");
            return null;
        }
    }
}

Testing Security Components

import requests

def test_security_components(base_url, token):
    url = f"{base_url}/api/admin/security/test"
    headers = {
        "Authorization": f"Bearer {token}"
    }

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        results = response.json()

        # Print results
        for feature, result in results.items():
            status = "✅" if result["status"] == "ok" else "❌"
            print(f"{status} {feature}: {result['message']}")

        return results
    else:
        print(f"Failed to test security components: {response.json()}")
        return None
async function testSecurityComponents(baseUrl, token) {
  const url = `${baseUrl}/api/admin/security/test`;
  const headers = {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  };

  try {
    const response = await fetch(url, {
      method: 'GET',
      headers: headers
    });

    if (response.ok) {
      const results = await response.json();

      // Print results
      for (const [feature, result] of Object.entries(results)) {
        const status = result.status === "ok" ? "✅" : "❌";
        console.log(`${status} ${feature}: ${result.message}`);
      }

      return results;
    } else {
      const data = await response.json();
      console.error(`Failed to test security components: ${data.detail}`);
      return null;
    }
  } catch (error) {
    console.error(`Error testing security components: ${error}`);
    return null;
  }
}
#!/bin/bash

test_security_components() {
  local base_url=$1
  local token=$2

  response=$(curl -s \
    -H "Authorization: Bearer $token" \
    "$base_url/api/admin/security/test")

  echo "$response" | jq
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Collections.Generic;

public class SecurityClient
{
    private readonly HttpClient _client;
    private readonly string _baseUrl;

    public SecurityClient(string baseUrl)
    {
        _client = new HttpClient();
        _baseUrl = baseUrl;
    }

    public class SecurityTestResult
    {
        public string status { get; set; }
        public string message { get; set; }
    }

    public async Task<Dictionary<string, SecurityTestResult>> TestSecurityComponentsAsync(string token)
    {
        string url = $"{_baseUrl}/api/admin/security/test";

        _client.DefaultRequestHeaders.Authorization = 
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

        try
        {
            HttpResponseMessage response = await _client.GetAsync(url);

            if (response.IsSuccessStatusCode)
            {
                string content = await response.Content.ReadAsStringAsync();
                var results = JsonConvert.DeserializeObject<Dictionary<string, SecurityTestResult>>(content);

                // Print results
                foreach (var kvp in results)
                {
                    string status = kvp.Value.status == "ok" ? "✅" : "❌";
                    Console.WriteLine($"{status} {kvp.Key}: {kvp.Value.message}");
                }

                return results;
            }
            else
            {
                string content = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Failed to test security components: {content}");
                return null;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error testing security components: {ex.Message}");
            return null;
        }
    }
}

Error Handling

The Security API returns standard HTTP status codes and JSON error responses:

  • 400 Bad Request: The request was invalid or malformed.
  • 401 Unauthorized: Authentication is required or the provided credentials are invalid.
  • 403 Forbidden: The authenticated user doesn't have permission to access the requested resource.
  • 404 Not Found: The requested resource doesn't exist.
  • 429 Too Many Requests: The client has sent too many requests in a given amount of time.
  • 500 Internal Server Error: An unexpected error occurred on the server.

Error responses have the following format:

{
  "detail": "Error message describing what went wrong"
}

Best Practices

  1. Use HTTPS: Always use HTTPS to encrypt API requests and responses.
  2. Secure API Keys: Store API keys securely and never expose them in client-side code.
  3. Implement Proper Authentication: Use the provided authentication mechanisms and never bypass them.
  4. Handle Rate Limiting: Implement exponential backoff when receiving rate limit responses.
  5. Monitor API Usage: Monitor API usage to detect unusual patterns that might indicate abuse.
  6. Validate Input: Always validate input to prevent injection attacks.
  7. Handle Errors Gracefully: Provide a good user experience when errors occur.