// make a clone of all GraphNode except that only a few GraphEdge are kept // remove all uneeded GraphEdge to have only one GraphEdge between 2 GraphNodes (direct or indirect link) private GraphNode GenerateSimplifiedGraph(GraphNodeCollection nodes, GraphNode centralNode) { List <GraphNode> nodeAlreadyExamined = new List <GraphNode>(); GraphNode output = GraphNode.CloneWithoutTrusts(centralNode); Dictionary <DomainKey, GraphNode> graph = new Dictionary <DomainKey, GraphNode>(); graph.Add(output.Domain, output); List <GraphNode> nodesToExamine = new List <GraphNode>(); nodesToExamine.Add(centralNode); // proceed layer by layer for (int currentLevel = 0; ; currentLevel++) { List <GraphNode> nodesToExamineForNextLevel = new List <GraphNode>(); // this first iteration is important // it avoid a recursing exploration foreach (GraphNode nodeToExamine in nodesToExamine) { nodeAlreadyExamined.Add(nodeToExamine); } foreach (GraphNode nodeToExamine in nodesToExamine) { foreach (GraphEdge edge in nodeToExamine.Trusts.Values) { if (!nodeAlreadyExamined.Contains(edge.Destination) && !nodesToExamine.Contains(edge.Destination) && !nodesToExamineForNextLevel.Contains(edge.Destination)) { // make a clone and add one GraphEdge nodesToExamineForNextLevel.Add(edge.Destination); graph.Add(edge.Destination.Domain, GraphNode.CloneWithoutTrusts(edge.Destination)); GraphEdge newEdge = new GraphEdge(graph[nodeToExamine.Domain], graph[edge.Destination.Domain], null, false); graph[nodeToExamine.Domain].Trusts.Add(edge.Destination.Domain, newEdge); } } } if (nodesToExamineForNextLevel.Count == 0) { break; } nodesToExamine = nodesToExamineForNextLevel; } return(output); }
public bool IsEquivalentToReverseEdge(MigrationChecker migrationChecker) { if (TrustDirection != 3) { return(false); } // when there is a bidirectional trust, there are two GraphEdge // one starting from source and one starting from the other domain GraphEdge reverseEdge = Destination.Trusts[Source.Domain]; if (reverseEdge.TrustAttributes == TrustAttributes && GetSIDFilteringStatus(migrationChecker) == reverseEdge.GetSIDFilteringStatus(migrationChecker)) { return(true); } return(false); }
// authoritative data is considered as coming from the AD directly // non authoritative is deducted data private void Link(GraphNode destination, HealthCheckTrustData trust, bool isAuthoritative) { //Trace.WriteLine("Linking " + Domain + " to " + destination.Domain); if (!Trusts.ContainsKey(destination.Domain)) { GraphEdge edge = new GraphEdge(this, destination, trust, isAuthoritative); Trusts[destination.Domain] = edge; edge = new GraphEdge(destination, this, GetReverseTrust(trust), false); destination.Trusts[this.Domain] = edge; } else if (isAuthoritative) { Trusts[destination.Domain].SetAuthorativeTrustData(trust); } else { //Trace.WriteLine("non authoritative data & trust already exists"); } }
public string GenerateJsonFileFull(MigrationChecker migrationChecker) { Dictionary <int, int> idconversiontable = new Dictionary <int, int>(); StringBuilder sb = new StringBuilder(); sb.Append("{"); // START OF NODES sb.Append(" \"nodes\": ["); // it is important to put the root node as the first node for correct display int nodenumber = 0; bool firstnode = true; foreach (GraphNode node in Nodes) { if (!firstnode) { sb.Append(" },"); } else { firstnode = false; } sb.Append(" {"); sb.Append(" \"id\": " + nodenumber + ","); sb.Append(" \"shortname\": \"" + EscapeJsonString(node.Domain.DomainName.Split('.')[0]) + "\""); if (node.IsPartOfARealForest()) { sb.Append(" ,\"forest\": \"" + EscapeJsonString(node.Forest.DomainName) + "\""); } var entity = node.Entity; if (entity != null) { sb.Append(entity.GetJasonOutput()); } HealthcheckData data = node.HealthCheckData; sb.Append(" ,\"name\": \"" + EscapeJsonString(node.Domain.DomainName) + "\""); if (data != null) { sb.Append(" ,\"score\": " + data.GlobalScore); sb.Append(" ,\"staleObjectsScore\": " + data.StaleObjectsScore); sb.Append(" ,\"privilegiedGroupScore\": " + data.PrivilegiedGroupScore); sb.Append(" ,\"trustScore\": " + data.TrustScore); sb.Append(" ,\"anomalyScore\": " + data.AnomalyScore); if (data.UserAccountData != null) { sb.Append(" ,\"activeusers\": " + data.UserAccountData.NumberActive); } if (data.ComputerAccountData != null) { sb.Append(" ,\"activecomputers\": " + data.ComputerAccountData.NumberActive); } } sb.Append(" ,\"dist\": null"); idconversiontable[node.Id] = nodenumber++; } if (Nodes.Count > 0) { sb.Append(" }"); } sb.Append(" ],"); // END OF NODES // START LINKS sb.Append(" \"links\": ["); // avoid a final "," bool absenceOfLinks = true; // subtility: try to regroup 2 links at one if all the properties match // SkipLink contains the edge to ignore List <GraphEdge> SkipLink = new List <GraphEdge>(); // foreach edge foreach (GraphNode node in Nodes) { foreach (GraphEdge edge in node.Trusts.Values) { if (SkipLink.Contains(edge)) { continue; } // for unidirectional trusts // keep only the remote part of the trust. SID Filtering is unknown (avoid evaluating SID Filtering when no value is available) if (edge.TrustDirection == 2 && edge.IsAuthoritative == false) { continue; } // keep only the reception of the trust. SID Filtering status is sure if (edge.TrustDirection == 1 && edge.Destination.Trusts[edge.Source.Domain].IsAuthoritative == true) { continue; } // trying to simplify bidirectional trusts bool isBidirectional = false; if (edge.IsEquivalentToReverseEdge(migrationChecker)) { GraphEdge reverseEdge = edge.Destination.Trusts[edge.Source.Domain]; // keep only one of the two part of the bidirectional trust SkipLink.Add(reverseEdge); isBidirectional = true; } if (!absenceOfLinks) { sb.Append(" },"); } else { absenceOfLinks = false; } sb.Append(" {"); if (edge.TrustDirection == 2) { sb.Append(" \"source\": " + idconversiontable[edge.Destination.Id] + ","); sb.Append(" \"target\": " + idconversiontable[edge.Source.Id] + ","); } else { sb.Append(" \"source\": " + idconversiontable[edge.Source.Id] + ","); sb.Append(" \"target\": " + idconversiontable[edge.Destination.Id] + ","); } // blue: 25AEE4 // orange: FA9426 string sidFiltering = edge.GetSIDFilteringStatus(migrationChecker); if (!edge.IsActive) { // purple sb.Append(" \"color\": \"#A856AA\","); } else { switch (sidFiltering) { case "Remote": // yellow sb.Append(" \"color\": \"#FDC334\","); break; case "Migration": // blue sb.Append(" \"color\": \"#25AEE4\","); break; case "No": // red sb.Append(" \"color\": \"#E75351\","); break; case "Yes": // green sb.Append(" \"color\": \"#74C25C\","); break; } } if (isBidirectional) { sb.Append(" \"type\": \"double\","); } sb.Append(" \"rels\": [\""); sb.Append("Attributes=" + edge.GetTrustAttributes() + ","); if (edge.CreationDate != DateTime.MinValue) { sb.Append("CreationDate=" + edge.CreationDate.ToString("yyyy-MM-dd") + ","); } sb.Append("SIDFiltering=" + sidFiltering); sb.Append((edge.IsActive ? null : ",Inactive")); sb.Append("\"]"); } } if (!absenceOfLinks) { sb.Append(" }"); } sb.Append(" ]"); // END OF LINKS sb.Append("}"); return(sb.ToString()); }