namespace PS.IPAM.Helpers; using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PS.IPAM; public static class RequestHelper { // Handler for testing - allows injecting a mock HTTP handler public static HttpMessageHandler? TestHttpHandler { get; set; } public static async Task InvokeRequest( string method, controllers controller, types? type = null, subcontrollers? subController = null, object? parameters = null, string[]? identifiers = null, bool ignoreSsl = false) { var tokenStatus = SessionManager.TestSession(); if (tokenStatus == "NoToken") { throw new Exception("No session available!"); } if (tokenStatus == "Expired") { await UpdateSession(); } var session = SessionManager.CurrentSession; if (session == null) { throw new Exception("No session available!"); } var uri = $"{session.URL}/api/{session.AppID}/{controller}"; if (subController != null) { uri += $"/{subController}"; } if (identifiers != null && identifiers.Length > 0) { uri += $"/{string.Join("/", identifiers)}/"; } using var client = SessionManager.CreateHttpClient(ignoreSsl, TestHttpHandler); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); switch (session.AuthType) { case AuthType.credentials: client.DefaultRequestHeaders.Add("token", session.Token); break; case AuthType.token: client.DefaultRequestHeaders.Add("phpipam-token", session.Token); break; } HttpResponseMessage? response = null; try { if (method == "GET") { response = await client.GetAsync(uri); } else if (method == "POST") { var jsonContent = parameters != null ? JsonConvert.SerializeObject(parameters) : "{}"; var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); response = await client.PostAsync(uri, content); } else if (method == "PATCH") { var jsonContent = parameters != null ? JsonConvert.SerializeObject(parameters) : "{}"; var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); var request = new HttpRequestMessage(new HttpMethod("PATCH"), uri) { Content = content }; response = await client.SendAsync(request); } else if (method == "DELETE") { response = await client.DeleteAsync(uri); } if (response == null) { return null; } var responseContent = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { if (response.StatusCode == System.Net.HttpStatusCode.NotFound) { return null; } throw new HttpRequestException($"Request failed with status {response.StatusCode}"); } if (string.IsNullOrEmpty(responseContent)) { return null; } var jsonResponse = JsonConvert.DeserializeObject(responseContent); if (jsonResponse == null) { return null; } if (type.HasValue) { return ConvertToTypedObjects(jsonResponse, type.Value); } return jsonResponse.data; } catch (HttpRequestException ex) { if (ex.Message.Contains("404")) { return null; } throw; } } private static object? ConvertToTypedObjects(dynamic jsonResponse, types type) { if (jsonResponse?.data == null) { return null; } var data = jsonResponse.data; if (data is JArray array) { return array.Select(item => ConvertSingleObject(item, type)).ToList(); } else { return ConvertSingleObject(data, type); } } private static object? ConvertSingleObject(dynamic item, types type) { var jobject = item as JObject; if (jobject == null) { return null; } var customFields = new Dictionary(); foreach (var prop in jobject.Properties()) { if (prop.Name.StartsWith("custom_")) { customFields[prop.Name] = prop.Value?.ToObject() ?? new object(); } } switch (type) { case types.Address: return CreateAddress(jobject, customFields); case types.Vlan: return CreateVlan(jobject, customFields); case types.Subnetwork: return CreateSubnetwork(jobject, customFields); case types.Vrf: return CreateVrf(jobject, customFields); case types.Section: return CreateSection(jobject); case types.Tag: return CreateTag(jobject); case types.Nameserver: return CreateNameserver(jobject); case types.Domain: return CreateDomain(jobject); default: return jobject.ToObject(); } } private static Address CreateAddress(JObject jobject, Dictionary customFields) { return new Address( jobject["id"]?.ToObject() ?? 0, jobject["subnetId"]?.ToObject() ?? 0, jobject["ip"]?.ToString() ?? "", jobject["is_gateway"]?.ToObject() ?? false, jobject["description"]?.ToString() ?? "", jobject["hostname"]?.ToString() ?? "", jobject["mac"]?.ToString() ?? "", jobject["owner"]?.ToString() ?? "", jobject["tag"]?.ToObject() ?? 0, jobject["deviceId"]?.ToObject() ?? 0, jobject["location"]?.ToString() ?? "", jobject["port"]?.ToString() ?? "", jobject["note"]?.ToString() ?? "", jobject["lastSeen"]?.ToObject(), jobject["excludePing"]?.ToObject() ?? false, jobject["PTRignore"]?.ToObject() ?? false, jobject["PTR"]?.ToObject() ?? 0, jobject["firewallAddressObject"]?.ToString() ?? "", jobject["editDate"]?.ToObject(), jobject["customer_id"]?.ToObject() ?? 0, customFields.Count > 0 ? customFields : null ); } private static Vlan CreateVlan(JObject jobject, Dictionary customFields) { return new Vlan( jobject["vlanId"]?.ToObject() ?? 0, jobject["domainId"]?.ToObject() ?? 0, jobject["name"]?.ToString() ?? "", jobject["number"]?.ToObject() ?? 0, jobject["description"]?.ToString() ?? "", jobject["editDate"]?.ToObject(), jobject["customer_id"]?.ToObject() ?? 0, customFields.Count > 0 ? customFields : null ); } private static Subnetwork CreateSubnetwork(JObject jobject, Dictionary customFields) { var props = jobject.Properties().ToList(); return new Subnetwork( jobject["id"]?.ToObject() ?? 0, jobject["subnet"]?.ToString() ?? "", jobject["mask"]?.ToObject() ?? 0, jobject["sectionId"]?.ToObject() ?? 0, jobject["description"]?.ToString() ?? "", jobject["linked_subnet"]?.ToString() ?? "", jobject["firewallAddressObject"]?.ToString() ?? "", jobject["vrfId"]?.ToObject() ?? 0, jobject["masterSubnetId"]?.ToObject() ?? 0, jobject["allowRequests"]?.ToObject() ?? false, jobject["vlanId"]?.ToObject() ?? 0, jobject["showName"]?.ToObject() ?? false, jobject["deviceId"]?.ToObject() ?? 0, jobject["permissions"]?.ToString() ?? "", jobject["pingSubnet"]?.ToObject() ?? false, jobject["discoverSubnet"]?.ToObject() ?? false, jobject["resolveDNS"]?.ToObject() ?? false, jobject["DNSrecursive"]?.ToObject() ?? false, jobject["DNSrecords"]?.ToObject() ?? false, jobject["nameserverId"]?.ToObject() ?? 0, jobject["scanAgent"]?.ToObject() ?? false, jobject["isFolder"]?.ToObject() ?? false, jobject["isFull"]?.ToObject() ?? false, jobject["isPool"]?.ToObject() ?? false, jobject["state"]?.ToObject() ?? 0, jobject["threshold"]?.ToObject() ?? 0, jobject["location"]?.ToObject() ?? 0, jobject["editDate"]?.ToObject(), jobject["lastScan"]?.ToObject(), jobject["lastDiscovery"]?.ToObject(), jobject["calculation"]?.ToObject() ?? new object(), customFields.Count > 0 ? customFields : null ); } private static Vrf CreateVrf(JObject jobject, Dictionary customFields) { return new Vrf( jobject["id"]?.ToObject() ?? 0, jobject["name"]?.ToString() ?? "", jobject["rd"]?.ToString() ?? "", jobject["description"]?.ToString() ?? "", jobject["sections"]?.ToString() ?? "", jobject["editDate"]?.ToObject(), customFields.Count > 0 ? customFields : null ); } private static Section CreateSection(JObject jobject) { return new Section( jobject["id"]?.ToObject() ?? 0, jobject["name"]?.ToString() ?? "", jobject["description"]?.ToString() ?? "", jobject["masterSection"]?.ToObject() ?? 0, jobject["permissions"]?.ToString() ?? "", jobject["strictMode"]?.ToObject() ?? false, jobject["subnetOrdering"]?.ToString() ?? "", jobject["order"]?.ToObject() ?? 0, jobject["editDate"]?.ToObject(), jobject["showSubnet"]?.ToObject() ?? false, jobject["showVlan"]?.ToObject() ?? false, jobject["showVRF"]?.ToObject() ?? false, jobject["showSupernetOnly"]?.ToObject() ?? false, jobject["DNS"]?.ToObject() ?? 0 ); } private static Tag CreateTag(JObject jobject) { return new Tag( jobject["id"]?.ToObject() ?? 0, jobject["type"]?.ToString() ?? "", jobject["showtag"]?.ToObject() ?? false, jobject["bgcolor"]?.ToString() ?? "", jobject["fgcolor"]?.ToString() ?? "", jobject["compress"]?.ToString() ?? "", jobject["locked"]?.ToString() ?? "", jobject["updateTag"]?.ToObject() ?? false ); } private static Nameserver CreateNameserver(JObject jobject) { return new Nameserver( jobject["id"]?.ToObject() ?? 0, jobject["name"]?.ToString() ?? "", jobject["nameservers"]?.ToString() ?? "", jobject["description"]?.ToString() ?? "", jobject["permissions"]?.ToString() ?? "", jobject["editDate"]?.ToObject() ); } private static Domain CreateDomain(JObject jobject) { return new Domain( jobject["id"]?.ToObject() ?? 0, jobject["name"]?.ToString() ?? "", jobject["description"]?.ToString() ?? "", jobject["sections"]?.ToString() ?? "" ); } private static async Task UpdateSession() { var session = SessionManager.CurrentSession; if (session == null) { throw new Exception("No session available!"); } var tokenStatus = SessionManager.TestSession(); if (tokenStatus == "Valid") { // Just refresh the token var result = await InvokeRequest("PATCH", controllers.user, null, null, null, null); // Token refresh doesn't return expires in the same format, so we'll skip updating expires return; } if (tokenStatus == "Expired" && session.Credentials is PSCredential creds) { await SessionManager.CreateSessionWithCredentials( session.URL, session.AppID, creds, false ); } } }