Skip to content

Commit

Permalink
add API admin
Browse files Browse the repository at this point in the history
  • Loading branch information
cooperlarson committed Sep 6, 2024
1 parent a2ffdb7 commit 7c72110
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 52 deletions.
4 changes: 4 additions & 0 deletions include/forti_api/api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#include <utility>
#include <cstdlib>
#include <stdexcept>
#include <regex>

inline static std::regex ipv4("(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])");
inline static std::regex ipv6("((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}");

struct Response {
unsigned int size{}, matched_count{}, next_idx{}, http_status{}, build{};
Expand Down
260 changes: 208 additions & 52 deletions include/forti_api/system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

#include "api.hpp"
#include <string>
#include <utility>


// SYSTEM INTERFACE TYPES

struct SystemResponse {
unsigned int build{};
std::string http_method, revision, vdom, path, name, action, status, serial, version;
Expand Down Expand Up @@ -77,75 +80,228 @@ struct InterfacesGeneralResponse : public SystemResponse {
name, action, status, serial, version, results);
};

class System {
inline static std::string available_interfaces_endpoint = "/monitor/system/available-interfaces";

inline static std::vector<SystemInterface> physical_interfaces{},
tunnel_interfaces{},
hard_switch_vlan_interfaces{},
aggregate_interfaces{};

static void update_local_interface_data() {
physical_interfaces.clear();
tunnel_interfaces.clear();
hard_switch_vlan_interfaces.clear();
aggregate_interfaces.clear();

auto interfaces = FortiAPI::get<InterfacesGeneralResponse>(available_interfaces_endpoint);
for (const auto& interface : interfaces.results) {
if (!interface.contains("type")) continue;
auto type = interface["type"].get<std::string>();

if (type == "physical") physical_interfaces.emplace_back(interface);
else if (type == "tunnel") tunnel_interfaces.emplace_back(interface);
else if (type == "hard-switch-vlan") hard_switch_vlan_interfaces.emplace_back(interface);
else if (type == "aggregate") aggregate_interfaces.emplace_back(interface);
}
}

static unsigned int count_interfaces() {
return FortiAPI::get<GeneralResponse>(available_interfaces_endpoint).results.size();
}
// SYSTEM ADMIN TYPES

static nlohmann::json get(const std::string& name, const std::string& vdom = "root") {
std::string endpoint =
std::format("{}?vdom={}&mkey={}", available_interfaces_endpoint, vdom, name);
struct VDomEntry {
std::string name, q_origin_key;

return FortiAPI::get<std::vector<nlohmann::json>>(endpoint)[0];
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VDomEntry, name, q_origin_key)
};

enum class TrustHostType {
IPV4,
IPV6,
};

// Base class for TrustHostEntry
struct TrustHostEntry {
unsigned int id{}, q_origin_key{};
std::string type;

static SystemInterface get(const std::vector<SystemInterface>& interfaces,
const std::string& name, const std::string& vdom = "root") {
if (interfaces.empty()) update_local_interface_data();
for (const auto& interface : interfaces)
if (interface.name == name && interface.vdom == vdom) return interface;
throw std::runtime_error(std::format("No system interface found for: {}", name));
TrustHostEntry() = default;
explicit TrustHostEntry(std::string type) : type(std::move(type)) {}

[[nodiscard]] virtual TrustHostType get_type() const = 0;
[[nodiscard]] virtual std::string get_ip() const = 0;
virtual ~TrustHostEntry() = default;

[[nodiscard]] bool is_ipv4() const { return get_type() == TrustHostType::IPV4; }
[[nodiscard]] bool is_ipv6() const { return get_type() == TrustHostType::IPV6; }

friend void to_json(nlohmann::json& j, const TrustHostEntry& host) {
j = nlohmann::json{
{"id", host.id},
{"q_origin_key", host.q_origin_key},
{"type", host.type},
{host.is_ipv4() ? "ipv4-trusthost" : "ipv6-trusthost", host.get_ip()}
};
}
};

public:
static SystemInterface get_physical_interface(const std::string& name, const std::string& vdom = "root") {
return get(physical_interfaces, name, vdom);
// Derived class for IPv4 TrustHost
struct IPV4TrustHost : public TrustHostEntry {
std::string ipv4_trusthost;

[[nodiscard]] TrustHostType get_type() const override { return TrustHostType::IPV4; }
[[nodiscard]] std::string get_ip() const override { return ipv4_trusthost; }

IPV4TrustHost() = default;
explicit IPV4TrustHost(std::string ip_addr) : TrustHostEntry("ipv4-trusthost"), ipv4_trusthost(std::move(ip_addr)) {}

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IPV4TrustHost, id, q_origin_key, type, ipv4_trusthost)
};

// Derived class for IPv6 TrustHost
struct IPV6TrustHost : public TrustHostEntry {
std::string ipv6_trusthost;

[[nodiscard]] TrustHostType get_type() const override { return TrustHostType::IPV6; }
[[nodiscard]] std::string get_ip() const override { return ipv6_trusthost; }

IPV6TrustHost() = default;
explicit IPV6TrustHost(std::string ip_addr) : TrustHostEntry("ipv4-trusthost"), ipv6_trusthost(std::move(ip_addr)) {}

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IPV6TrustHost, id, q_origin_key, type, ipv6_trusthost)
};

struct TrustHost : public std::vector<std::shared_ptr<TrustHostEntry>> {
friend void from_json(const nlohmann::json& j, TrustHost& th) {
for (const auto& item : j) {
auto type = item.at("type").get<std::string>();
if (type == "ipv4-trusthost") th.push_back(std::make_shared<IPV4TrustHost>(item));
else th.push_back(std::make_shared<IPV6TrustHost>(item));
}
}

static SystemInterface get_tunnel_interface(const std::string& name, const std::string& vdom = "root") {
return get(tunnel_interfaces, name, vdom);
friend void to_json(nlohmann::json& j, const TrustHost& th) {
for (const auto& host : th) j.push_back(*host);
}
};

static SystemInterface get_hard_vlan_switch_interface(const std::string& name, const std::string& vdom = "root") {
return get(hard_switch_vlan_interfaces, name, vdom);
class APIUser {
void update_trusthost() {
FortiAPI::post(std::format("{}/{}/trusthost", api_user_endpoint, name), trusthost);
}

static SystemInterface get_aggregate_interface(const std::string& name, const std::string& vdom = "root") {
return get(aggregate_interfaces, name, vdom);
public:
inline static std::string api_user_endpoint = "/cmdb/system/api-user";
std::string name, q_origin_key, comments, api_key, accprofile, schedule, cors_allow_origin,
peer_auth, peer_group;
TrustHost trusthost;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(APIUser, name, q_origin_key, comments, api_key, accprofile,
schedule, cors_allow_origin, peer_auth, peer_group, trusthost)

void trust(const std::string& ip_addr) {
if (std::regex_match(ip_addr, ipv4)) trusthost.push_back(std::make_shared<IPV4TrustHost>(ip_addr));
else if (std::regex_match(ip_addr, ipv6)) trusthost.push_back(std::make_shared<IPV6TrustHost>(ip_addr));
update_trusthost();
}

static VirtualWANLink get_virtual_wan_link(const std::string& name = "virtual-wan-link", const std::string& vdom = "root") {
return get(name, vdom);
void distrust(const std::string& ip_addr) {
// Use std::remove_if to remove elements that match the condition
trusthost.erase(std::remove_if(trusthost.begin(), trusthost.end(),
[&ip_addr](const std::shared_ptr<TrustHostEntry>& entry) {
return entry->get_ip() == ip_addr; // Match the IP address
}), trusthost.end());
}

static std::string get_wan_ip(unsigned int wan_port = 1, const std::string& vdom = "root") {
return get_physical_interface(std::format("wan{}", wan_port), vdom).ipv4_addresses[0].ip;
void update() {
FortiAPI::post(std::format("{}/{}", api_user_endpoint, name), *this);
}
};

struct AllAPIUsersResponse : public Response {
std::vector<APIUser> results;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AllAPIUsersResponse, http_method, size, matched_count, next_idx,
revision, vdom, path, name, status, http_status, serial, version,
build, results)
};

namespace System {

class Interface {
inline static std::string available_interfaces_endpoint = "/monitor/system/available-interfaces";

inline static std::vector<SystemInterface> physical_interfaces{},
tunnel_interfaces{},
hard_switch_vlan_interfaces{},
aggregate_interfaces{};

static void update_local_interface_data() {
physical_interfaces.clear();
tunnel_interfaces.clear();
hard_switch_vlan_interfaces.clear();
aggregate_interfaces.clear();

auto interfaces = FortiAPI::get<InterfacesGeneralResponse>(available_interfaces_endpoint);
for (const auto& interface : interfaces.results) {
if (!interface.contains("type")) continue;
auto type = interface["type"].get<std::string>();

if (type == "physical") physical_interfaces.emplace_back(interface);
else if (type == "tunnel") tunnel_interfaces.emplace_back(interface);
else if (type == "hard-switch-vlan") hard_switch_vlan_interfaces.emplace_back(interface);
else if (type == "aggregate") aggregate_interfaces.emplace_back(interface);
}
}

static unsigned int count_interfaces() {
return FortiAPI::get<GeneralResponse>(available_interfaces_endpoint).results.size();
}

static nlohmann::json get(const std::string& name, const std::string& vdom = "root") {
std::string endpoint =
std::format("{}?vdom={}&mkey={}", available_interfaces_endpoint, vdom, name);

return FortiAPI::get<std::vector<nlohmann::json>>(endpoint)[0];
}

static SystemInterface get(const std::vector<SystemInterface>& interfaces,
const std::string& name, const std::string& vdom = "root") {
if (interfaces.empty()) update_local_interface_data();
for (const auto& interface : interfaces)
if (interface.name == name && interface.vdom == vdom) return interface;
throw std::runtime_error(std::format("No system interface found for: {}", name));
}

public:
static SystemInterface get_physical_interface(const std::string& name, const std::string& vdom = "root") {
return get(physical_interfaces, name, vdom);
}

static SystemInterface get_tunnel_interface(const std::string& name, const std::string& vdom = "root") {
return get(tunnel_interfaces, name, vdom);
}

static SystemInterface get_hard_vlan_switch_interface(const std::string& name, const std::string& vdom = "root") {
return get(hard_switch_vlan_interfaces, name, vdom);
}

static SystemInterface get_aggregate_interface(const std::string& name, const std::string& vdom = "root") {
return get(aggregate_interfaces, name, vdom);
}

static VirtualWANLink get_virtual_wan_link(const std::string& name = "virtual-wan-link", const std::string& vdom = "root") {
return get(name, vdom);
}

static std::string get_wan_ip(unsigned int wan_port = 1, const std::string& vdom = "root") {
return get_physical_interface(std::format("wan{}", wan_port), vdom).ipv4_addresses[0].ip;
}
}; // System::Interface

class Admin {
inline static std::string admin_endpoint = "/cmdb/system/admin";
inline static std::string admin_profiles_endpoint = "cmdb/system/accprofile";

public:

class API {
inline static std::string api_user_endpoint = "/cmdb/system/api-user";

static std::string get_trusthost_endpoint(const std::string& admin) {
return std::format("{}/{}/trusthost", api_user_endpoint, admin);
}

public:
static std::vector<APIUser> get() {
return FortiAPI::get<AllAPIUsersResponse>(api_user_endpoint).results;
}

static APIUser get_api_user(const std::string& api_admin_name) {
auto endpoint = std::format("{}/{}", api_user_endpoint, api_admin_name);
auto response = FortiAPI::get<AllAPIUsersResponse>(endpoint);
if (response.status == "success") return response.results[0];
else throw std::runtime_error("API Admin user " + api_admin_name + " not found...");
}
};
};

} // namespace System



#endif //FORTI_API_SYSTEM_H

0 comments on commit 7c72110

Please sign in to comment.