diff --git a/roles/database/files/sql/idempotent/fworch-api-funcs.sql b/roles/database/files/sql/idempotent/fworch-api-funcs.sql index ae728f8093..ecaccf4fe8 100644 --- a/roles/database/files/sql/idempotent/fworch-api-funcs.sql +++ b/roles/database/files/sql/idempotent/fworch-api-funcs.sql @@ -214,11 +214,10 @@ RETURNS boolean AS $$ ELSE IF EXISTS ( -- ip of rule_from object is in tenant_network of tenant SELECT rf.obj_id FROM rule_from rf - LEFT JOIN rule r ON (rf.rule_id=r.rule_id) LEFT JOIN objgrp_flat ON (rf.obj_id=objgrp_flat.objgrp_flat_id) LEFT JOIN object ON (objgrp_flat.objgrp_flat_member_id=object.obj_id) LEFT JOIN tenant_network ON - (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rf.negated != r.rule_src_neg)) + (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rf.negated)) WHERE rule_from_id = rule_from.rule_from_id AND tenant_id = t_id ) THEN show := true; @@ -226,11 +225,10 @@ RETURNS boolean AS $$ FOR rule_to_obj IN SELECT rt.*, tenant_network.tenant_id FROM rule_to rt - LEFT JOIN rule r ON (rt.rule_id=r.rule_id) LEFT JOIN objgrp_flat ON (rt.obj_id=objgrp_flat_id) LEFT JOIN object ON (objgrp_flat_member_id=object.obj_id) LEFT JOIN tenant_network ON - (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rt.negated != r.rule_dst_neg)) + (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rt.negated)) WHERE rt.rule_id = rule_from.rule_id LOOP IF rule_to_obj.tenant_id = t_id THEN @@ -269,11 +267,10 @@ RETURNS boolean AS $$ ELSE IF EXISTS ( -- ip of rule_to object is in tenant_network of tenant SELECT rt.obj_id FROM rule_to rt - LEFT JOIN rule r ON (rt.rule_id=r.rule_id) LEFT JOIN objgrp_flat ON (rt.obj_id=objgrp_flat.objgrp_flat_id) LEFT JOIN object ON (objgrp_flat.objgrp_flat_member_id=object.obj_id) LEFT JOIN tenant_network ON - (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rt.negated != r.rule_dst_neg)) + (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rt.negated)) WHERE rule_to_id = rule_to.rule_to_id AND tenant_id = t_id ) THEN show := true; @@ -281,11 +278,10 @@ RETURNS boolean AS $$ FOR rule_from_obj IN SELECT rf.*, tenant_network.tenant_id FROM rule_from rf - LEFT JOIN rule r ON (rf.rule_id=r.rule_id) LEFT JOIN objgrp_flat ON (rf.obj_id=objgrp_flat_id) LEFT JOIN object ON (objgrp_flat.objgrp_flat_member_id=object.obj_id) LEFT JOIN tenant_network ON - (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rf.negated != r.rule_src_neg)) + (ip_ranges_overlap(obj_ip, obj_ip_end, tenant_net_ip, tenant_net_ip_end, rf.negated)) WHERE rf.rule_id = rule_to.rule_id LOOP IF rule_from_obj.tenant_id = t_id THEN diff --git a/roles/lib/files/FWO.Report/ReportAppRules.cs b/roles/lib/files/FWO.Report/ReportAppRules.cs index 19d7865911..647dffb852 100644 --- a/roles/lib/files/FWO.Report/ReportAppRules.cs +++ b/roles/lib/files/FWO.Report/ReportAppRules.cs @@ -45,14 +45,14 @@ public static async Task> PrepareAppRulesReport(List ownerIps = await GetAppServers(apiConnection, ownerId); List relevantData = []; - foreach(var mgt in managementData) + foreach (var mgt in managementData) { - ManagementReport relevantMgt = new(){ Name = mgt.Name, Id = mgt.Id, Import = mgt.Import }; - foreach(var dev in mgt.Devices) + ManagementReport relevantMgt = new() { Name = mgt.Name, Id = mgt.Id, Import = mgt.Import }; + foreach (var dev in mgt.Devices) { PrepareDevice(dev, modellingFilter, relevantMgt, ownerIps); } - if(relevantMgt.Devices.Length > 0) + if (relevantMgt.Devices.Length > 0) { relevantMgt.ReportedRuleIds = [.. relevantMgt.ReportedRuleIds.Distinct()]; relevantData.Add(relevantMgt); @@ -63,15 +63,15 @@ public static async Task> PrepareAppRulesReport(List ownerIps) { - DeviceReport relevantDevice = new(){ Name = dev.Name, Id = dev.Id }; - if(dev.Rules != null) + DeviceReport relevantDevice = new() { Name = dev.Name, Id = dev.Id }; + if (dev.Rules != null) { relevantDevice.Rules = []; - foreach(var rule in dev.Rules) + foreach (var rule in dev.Rules) { PrepareRule(rule, modellingFilter, relevantMgt, relevantDevice, ownerIps); } - if(relevantDevice.Rules.Length > 0) + if (relevantDevice.Rules.Length > 0) { relevantMgt.Devices = [.. relevantMgt.Devices, relevantDevice]; } @@ -120,78 +120,156 @@ private static (List, List) CheckNetworkObject { List relevantObjects = []; List disregardedObjects = []; - foreach(var obj in objList) + if (negated) { - if(obj.Object.IsAnyObject()) + bool isMatch = CheckNegatedOverlap(objList, ownerIps); + if (isMatch) { - if(modellingFilter.ShowAnyMatch) + relevantObjects.AddRange(objList); + } + else + { + disregardedObjects.AddRange(objList); + } + } + else + { + foreach (var obj in objList) + { + if (obj.Object.IsAnyObject()) { - relevantObjects.Add(obj); + if (!negated && modellingFilter.ShowAnyMatch) + { + relevantObjects.Add(obj); + } + else + { + disregardedObjects.Add(obj); + } } else { - disregardedObjects.Add(obj); + CheckSpecificObj(obj, ownerIps, relevantObjects, disregardedObjects); } } - else + } + return (relevantObjects, disregardedObjects); + } + + private static bool CheckNegatedOverlap(NetworkLocation[] objList, List ownerNetworks) + { + // match iff ip space covered by objList \cap ip space covered by ownerNetworks != \emptyset + List<(uint, uint)> ownerIpRanges = [.. ownerNetworks.Select(o => (IpOperations.IpToUint(o.Begin), IpOperations.IpToUint(o.End)))]; + ownerIpRanges.Sort((a, b) => a.Item1.CompareTo(b.Item1)); + for (int i = 0; i < ownerIpRanges.Count - 1; i++) + { + if (ownerIpRanges[i].Item2 >= ownerIpRanges[i + 1].Item1) { - CheckSpecificObj(obj, negated, ownerIps, relevantObjects, disregardedObjects); + // merge overlapping ranges + ownerIpRanges[i] = (ownerIpRanges[i].Item1, + Math.Max(ownerIpRanges[i].Item2, ownerIpRanges[i + 1].Item2)); + ownerIpRanges.RemoveAt(i + 1); + i--; } } - return (relevantObjects, disregardedObjects); + // subtract ip ranges of objects from owner ip ranges + // for negated object list to match, at least one ip must not be contained in owner ranges + foreach (var obj in objList) + { + List flatObjects = obj.Object.Type.Name == ObjectType.Group + ? [.. obj.Object.ObjectGroupFlats.Select(o => o.Object).Where(o => o != null).Cast()] + : [obj.Object]; + foreach (var flatObj in flatObjects) + { + if (flatObj.IP == null || flatObj.IP == "") + { + continue; // skip objects without IP + } + uint ipStart = IpOperations.IpToUint(IPAddress.Parse(flatObj.IP.StripOffNetmask())); + uint ipEnd = IpOperations.IpToUint(IPAddress.Parse((flatObj.IpEnd != null && flatObj.IpEnd != "" ? flatObj.IpEnd : flatObj.IP).StripOffNetmask())); + for (int i = 0; i < ownerIpRanges.Count; i++) + { + (uint ownerIpStart, uint ownerIpEnd) = ownerIpRanges[i]; + if (ownerIpStart <= ipEnd && ownerIpEnd >= ipStart) + { + // overlap exists, remove object ip range from owner ip ranges + if (ownerIpStart < ipStart) + { + // adjust start of range + ownerIpRanges[i] = (ownerIpStart, ipStart - 1); + } + else if (ownerIpEnd > ipEnd) + { + // adjust end of range + ownerIpRanges[i] = (ipEnd + 1, ownerIpEnd); + } + else + { + // remove this range completely + ownerIpRanges.RemoveAt(i); + i--; + } + } + } + } + } + // if there are any owner ip ranges left, then the negated objects do not cover the whole owner ip space + return ownerIpRanges.Count > 0; } - private static void CheckSpecificObj(NetworkLocation obj, bool negated, List ownerIps, List relevantObjects, List disregardedObjects) + private static void CheckSpecificObj(NetworkLocation obj, List ownerIps, List relevantObjects, List disregardedObjects) { - bool found = false; - if(obj.Object.Type.Name == ObjectType.Group) + if (obj.Object.Type.Name == ObjectType.Group) { - foreach(var grpobj in obj.Object.ObjectGroupFlats.Select(o => o.Object)) + foreach (var grpobj in obj.Object.ObjectGroupFlats.Select(o => o.Object)) { - if(grpobj != null && CheckObj(grpobj, negated, ownerIps)) + if (grpobj != null && CheckObj(grpobj, ownerIps)) { relevantObjects.Add(obj); - found = true; break; } } } - else if(CheckObj(obj.Object, negated, ownerIps)) + else if (CheckObj(obj.Object, ownerIps)) { relevantObjects.Add(obj); - found = true; } - if(!found) + else { disregardedObjects.Add(obj); } } - private static bool CheckObj(NetworkObject obj, bool negated, List ownerIps) + private static bool CheckObj(NetworkObject obj, List ownerIps, bool negated = false) { - foreach(var ownerIpRange in ownerIps) + if (obj.IP == null) { - if(obj.IP == null) - { - continue; - } + return false; + } - IPAddressRange objRange = new(IPAddress.Parse(obj.IP.StripOffNetmask()), - IPAddress.Parse((obj.IpEnd != null && obj.IpEnd != "" ? obj.IpEnd : obj.IP).StripOffNetmask())); + if (obj.IsAnyObject()) + { + return !negated; + } - if(negated) + foreach (var ownerIpRange in ownerIps) { - if (IpOperations.IpToUint(ownerIpRange.Begin) < IpOperations.IpToUint(objRange.Begin) || - (IpOperations.IpToUint(ownerIpRange.End) > IpOperations.IpToUint(objRange.End))) + IPAddressRange objRange = new(IPAddress.Parse(obj.IP.StripOffNetmask()), + IPAddress.Parse((obj.IpEnd != null && obj.IpEnd != "" ? obj.IpEnd : obj.IP).StripOffNetmask())); + + if (negated) + { + if (IpOperations.IpToUint(ownerIpRange.Begin) < IpOperations.IpToUint(objRange.Begin) || + (IpOperations.IpToUint(ownerIpRange.End) > IpOperations.IpToUint(objRange.End))) + { + return true; + } + } + else if (IpOperations.RangeOverlapExists(objRange, ownerIpRange)) { return true; } } - else if(IpOperations.RangeOverlapExists(objRange, ownerIpRange)) - { - return true; - } - } return false; } @@ -199,9 +277,9 @@ private static void PrepareFilter(ManagementReport mgt, List own { mgt.RelevantObjectIds = []; mgt.HighlightedObjectIds = []; - foreach(var dev in mgt.Devices.Where(d => d.Rules != null)) + foreach (var dev in mgt.Devices.Where(d => d.Rules != null)) { - foreach(var rule in dev.Rules!) + foreach (var rule in dev.Rules!) { PrepareObjects(rule.Froms, rule.SourceNegated, rule.DisregardedFroms, mgt, ownerIps); PrepareObjects(rule.Tos, rule.DestinationNegated, rule.DisregardedTos, mgt, ownerIps); @@ -213,41 +291,62 @@ private static void PrepareFilter(ManagementReport mgt, List own private static void PrepareObjects(NetworkLocation[] networkLocations, bool negated, NetworkLocation[] disregardedLocations, ManagementReport mgt, List ownerIps) { - foreach(var from in networkLocations.Select(f => f.Object)) + if (negated) { - mgt.RelevantObjectIds.Add(from.Id); - mgt.HighlightedObjectIds.Add(from.Id); - if(from.Type.Name == ObjectType.Group) + if (CheckNegatedOverlap(networkLocations, ownerIps)) { - foreach(var grpobj in from.ObjectGroupFlats.Select(g => g.Object).Where(gr => gr != null && CheckObj(gr, negated, ownerIps))) + foreach (var nwLoc in networkLocations) { - mgt.HighlightedObjectIds.Add(grpobj!.Id); + mgt.RelevantObjectIds.Add(nwLoc.Object.Id); + mgt.HighlightedObjectIds.Add(nwLoc.Object.Id); + } + } + else + { + foreach (var nwLoc in disregardedLocations) + { + mgt.RelevantObjectIds.Add(nwLoc.Object.Id); } } } - if(networkLocations.Length == 0) + else { - foreach(var from in disregardedLocations) + foreach (var nwObj in networkLocations.Select(nl => nl.Object)) + { + mgt.RelevantObjectIds.Add(nwObj.Id); + mgt.HighlightedObjectIds.Add(nwObj.Id); + if (nwObj.Type.Name == ObjectType.Group) + { + if (nwObj.ObjectGroupFlats.Any(g => g.Object != null && CheckObj(g.Object, ownerIps))) + { + mgt.HighlightedObjectIds.Add(nwObj.Id); + } + } + } + if (networkLocations.Length == 0) { - mgt.RelevantObjectIds.Add(from.Object.Id); + foreach (var nwLoc in disregardedLocations) + { + mgt.RelevantObjectIds.Add(nwLoc.Object.Id); + } } } } private static void PrepareRsbOutput(ManagementReport mgt) { - foreach(var obj in mgt.ReportObjects) + foreach (var obj in mgt.ReportObjects) { - obj.Highlighted = mgt.HighlightedObjectIds.Contains(obj.Id) || obj.IsAnyObject(); - if(obj.Type.Name == ObjectType.Group) + obj.Highlighted = mgt.HighlightedObjectIds.Contains(obj.Id); + if (obj.Type.Name == ObjectType.Group) { - foreach(var grpobj in obj.ObjectGroupFlats.Select(g => g.Object).Where(g => g != null)) + foreach (var grpobj in obj.ObjectGroupFlats.Select(g => g.Object).Where(g => g != null)) { - grpobj!.Highlighted = mgt.HighlightedObjectIds.Contains(grpobj.Id) || grpobj.IsAnyObject(); + grpobj!.Highlighted = mgt.HighlightedObjectIds.Contains(grpobj.Id); } - foreach(var grpobj in obj.ObjectGroups.Select(g => g.Object).Where(g => g != null)) + foreach (var grpobj in obj.ObjectGroups.Select(g => g.Object).Where(g => g != null)) { - grpobj!.Highlighted = mgt.HighlightedObjectIds.Contains(grpobj.Id) || grpobj.IsAnyObject(); + grpobj!.Highlighted = mgt.HighlightedObjectIds.Contains(grpobj.Id); } } } diff --git a/roles/ui/files/FWO.UI/Shared/AnchorNavToRSB.razor b/roles/ui/files/FWO.UI/Shared/AnchorNavToRSB.razor index 470a5c0911..0ec9351385 100644 --- a/roles/ui/files/FWO.UI/Shared/AnchorNavToRSB.razor +++ b/roles/ui/files/FWO.UI/Shared/AnchorNavToRSB.razor @@ -40,7 +40,15 @@ /// private async Task GetIdFromURI() { - var currentUrl = await JSRuntime.InvokeAsync("getCurrentUrl"); + string? currentUrl = null; + try + { + currentUrl = await JSRuntime.InvokeAsync("getCurrentUrl"); + } + catch (JSException) + { + return ""; + } var uri = new Uri(currentUrl, UriKind.Absolute); var fragment = uri.Fragment; if (fragment.StartsWith("#goto-")) @@ -88,12 +96,19 @@ CollapseState.Expand(elementId); - bool foundObj = await JSRuntime.InvokeAsync("scrollIntoRSBView", elementId); - - if (foundObj) + bool foundObj = false; + try + { + foundObj = await JSRuntime.InvokeAsync("scrollIntoRSBView", elementId); + if (foundObj) + { + // remove #goto-{obj-link} fragment from uri + await JSRuntime.InvokeVoidAsync("removeUrlFragment"); + } + } + catch (JSException) { - // remove #goto-{obj-link} fragment from uri - await JSRuntime.InvokeVoidAsync("removeUrlFragment"); + return; } } }