From d1754273bac79587a0a684e2cab4a38f9db3f4c6 Mon Sep 17 00:00:00 2001 From: Frode Hus Date: Fri, 20 Dec 2024 08:24:52 +0100 Subject: [PATCH 1/2] Refactor to use DTOs and enhance XML handling Refactored code to use Data Transfer Objects (DTOs) instead of XML elements, improving type safety and maintainability. Updated import statements and method signatures to reflect these changes. Removed XML-specific parsing logic and methods. Enhanced `BeyondTrustService` to deserialize XML responses into strongly-typed objects. Updated `IngestionService` to use new DTOs. Added new columns to `Custom_Table_BeyondTrustAccessSession_CL` and `dataCollectionRule` resources for detailed data collection. Introduced new namespace and classes for DTOs and XML schema definition. --- BeyondTrustConnector/AccessSessionUpdater.cs | 114 +- .../Model/BeyondTrustAccessSession.cs | 1047 ++++++++++++++++- .../Model/Dto/BeyondTrustAccessSessionDto.cs | 21 + .../Model/Dto/DtoHelperExtensions.cs | 64 + .../Service/BeyondTrustService.cs | 13 +- .../Service/IngestionService.cs | 3 +- .../schemas/BeyondTrustAccessSession.xsd | 173 +++ modules/datacollection.bicep | 32 + 8 files changed, 1340 insertions(+), 127 deletions(-) create mode 100644 BeyondTrustConnector/Model/Dto/BeyondTrustAccessSessionDto.cs create mode 100644 BeyondTrustConnector/Model/Dto/DtoHelperExtensions.cs create mode 100644 BeyondTrustConnector/schemas/BeyondTrustAccessSession.xsd diff --git a/BeyondTrustConnector/AccessSessionUpdater.cs b/BeyondTrustConnector/AccessSessionUpdater.cs index 3228511..09a12ef 100644 --- a/BeyondTrustConnector/AccessSessionUpdater.cs +++ b/BeyondTrustConnector/AccessSessionUpdater.cs @@ -1,6 +1,6 @@ using System.Globalization; using System.Xml.Linq; -using BeyondTrustConnector.Model; +using BeyondTrustConnector.Model.Dto; using BeyondTrustConnector.Service; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; @@ -10,34 +10,16 @@ namespace BeyondTrustConnector; public class AccessSessionUpdater(BeyondTrustService beyondTrustService, IngestionService ingestionService, QueryService queryService, ILogger logger) { [Function(nameof(AccessSessionUpdater))] - public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = false)] TimerInfo myTimer) + public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = true)] TimerInfo myTimer) { DateTime? lastEventTime = await GetLastUpdatedTime(); lastEventTime ??= DateTime.Now.AddDays(-5); - var ns = "http://www.beyondtrust.com/sra/namespaces/API/reporting"; var report = await beyondTrustService.GetAccessSessionReport(lastEventTime.Value); - var sessions = report.Root!.Descendants(XName.Get("session", ns)); + var sessions = report.Items.Select(i => i.ToDto()).ToList(); var existingSessions = await CheckIfSessionsAlreadyExists(queryService, sessions); - var accessSessions = new List(); - foreach (var session in sessions) - { - if (session is null) continue; - - var sessionId = session.Attribute("lsid")!.Value; - if (existingSessions.Contains(sessionId)) - { - logger.LogInformation("Session {SessionId} already exists in the workspace", sessionId); - continue; - } - - var sessionData = CreateSessionData(session, ns); - if (sessionData != null) - { - accessSessions.Add(sessionData); - } - } + var accessSessions = sessions.Where(s => !existingSessions.Contains(s.SessionId)).ToList(); if (accessSessions.Count != 0) { @@ -45,66 +27,9 @@ public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = false)] Tim } } - private BeyondTrustAccessSession? CreateSessionData(XElement session, string ns) - { - try - { - var sessionId = session.Attribute("lsid")!.Value; - var startTime = DateTimeOffset.Parse(session.Element(XName.Get("start_time", ns))!.Value); - DateTimeOffset? endTime = null; - var endTimeValue = session.Element(XName.Get("end_time", ns)); - if (!string.IsNullOrEmpty(endTimeValue?.Value)) - { - endTime = DateTimeOffset.Parse(endTimeValue!.Value); - } - - var jumpPoint = session.Element(XName.Get("jumpoint", ns))!.Value; - var jumpItem = session.Element(XName.Get("primary_customer", ns)); - var jumpItemValue = jumpItem!.Value; - var jumpItemId = jumpItem!.Attribute("gsnumber")!.Value; - var jumpGroup = session.Element(XName.Get("jump_group", ns))!.Value; - var sessionType = session.Element(XName.Get("session_type", ns))!.Value; - - var sessionData = new BeyondTrustAccessSession - { - StartTime = startTime.UtcDateTime, - EndTime = endTime?.UtcDateTime, - SessionId = sessionId, - Jumpoint = jumpPoint, - JumpItemAddress = jumpItemValue, - JumpItemId = int.Parse(jumpItemId), - JumpGroup = jumpGroup, - SessionType = sessionType - }; - - if (int.TryParse(session.Element(XName.Get("file_transfer_count", ns))?.Value, out var fileTransferCount)) - { - sessionData.FileTransferCount = fileTransferCount; - } - - if (int.TryParse(session.Element(XName.Get("file_move_count", ns))?.Value, out var fileMoveCount)) - { - sessionData.FileMoveCount = fileMoveCount; - } - - if (int.TryParse(session.Element(XName.Get("file_move_count", ns))?.Value, out var fileDeleteCount)) - { - sessionData.FileDeleteCount = fileDeleteCount; - } - - sessionData.UserDetails = GetUserDetails(session, ns); - return sessionData; - } - catch (Exception ex) - { - logger.LogError("Error creating session data: {ErrorMessage}", ex.Message); - return null; - } - } - - private static async Task> CheckIfSessionsAlreadyExists(QueryService queryService, IEnumerable sessions) + private static async Task> CheckIfSessionsAlreadyExists(QueryService queryService, IEnumerable sessions) { - var sessionIds = sessions.Select(s => s.Attribute("lsid")!.Value).Distinct().ToList(); + var sessionIds = sessions.Select(s => s.SessionId).Distinct().ToList(); var existingSessions = new List(); const int batchSize = 20; @@ -123,33 +48,6 @@ public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = false)] Tim return existingSessions.Distinct().ToList(); } - private static List> GetUserDetails(XElement session, string ns) - { - var users = new List>(); - var userList = session.Element(XName.Get("rep_list", ns)); - if (userList is null) return users; - - foreach (var userElement in userList.Elements(XName.Get("representative", ns))) - { - var userDetails = new Dictionary(); - if (userElement is not null) - { - userDetails.Add("Username", userElement.Element(XName.Get("username", ns))?.Value ?? "Unknown"); - userDetails.Add("PublicIP", userElement.Element(XName.Get("public_ip", ns))?.Value ?? "Unknown"); - userDetails.Add("PrivateIP", userElement.Element(XName.Get("private_ip", ns))?.Value ?? "Unknown"); - userDetails.Add("Hostname", userElement.Element(XName.Get("hostname", ns))?.Value ?? "Unknown"); - userDetails.Add("OS", userElement.Element(XName.Get("os", ns))?.Value ?? "Unknown"); - var sessionOwnerData = userElement.Element(XName.Get("session_owner", ns))?.Value; - if (!string.IsNullOrEmpty(sessionOwnerData)) - { - userDetails.Add("SessionOwner", sessionOwnerData == "1"); - } - } - users.Add(userDetails); - } - return users; - } - private async Task GetLastUpdatedTime(int offsetSeconds = 1) { var result = await queryService.QueryWorkspace("BeyondTrustAccessSession_CL | summarize arg_max(TimeGenerated,*) | project TimeGenerated"); diff --git a/BeyondTrustConnector/Model/BeyondTrustAccessSession.cs b/BeyondTrustConnector/Model/BeyondTrustAccessSession.cs index 478b070..1e98040 100644 --- a/BeyondTrustConnector/Model/BeyondTrustAccessSession.cs +++ b/BeyondTrustConnector/Model/BeyondTrustAccessSession.cs @@ -1,17 +1,1034 @@ -namespace BeyondTrustConnector.Model; +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ -public class BeyondTrustAccessSession -{ - public DateTime StartTime { get; set; } - public DateTime? EndTime { get; set; } - public required string SessionId { get; set; } - public required string Jumpoint { get; set; } - public required string SessionType { get; set; } - public required string JumpGroup { get; set; } - public required string JumpItemAddress { get; set; } - public int JumpItemId { get; set; } - public IEnumerable> UserDetails { get; set; } = []; - public int? FileTransferCount { get; set; } - public int? FileMoveCount { get; set; } - public int? FileDeleteCount { get; set; } +// +// This source code was auto-generated by xsd, Version=4.8.3928.0. +// +namespace BeyondTrustConnector.Model { + using System.Xml.Serialization; + + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + [System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting", IsNullable=false)] + public partial class session_list { + + private session_listSession[] itemsField; + + /// + [System.Xml.Serialization.XmlElementAttribute("session")] + public session_listSession[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSession { + + private string session_typeField; + + private string lseqField; + + private string durationField; + + private string external_keyField; + + private string custom_attributesField; + + private string session_chat_view_urlField; + + private string session_chat_download_urlField; + + private string file_transfer_countField; + + private string file_move_countField; + + private string file_delete_countField; + + private session_listSessionStart_time start_timeField; + + private session_listSessionEnd_time end_timeField; + + private session_listSessionJumpoint jumpointField; + + private session_listSessionPrimary_customer primary_customerField; + + private session_listSessionJump_group jump_groupField; + + private session_listSessionPrimary_rep primary_repField; + + private session_listSessionCustomer[] customer_listField; + + private session_listSessionRepresentative[] rep_listField; + + private session_listSessionEvent[] session_detailsField; + + private string lsidField; + + /// + public string session_type { + get { + return this.session_typeField; + } + set { + this.session_typeField = value; + } + } + + /// + public string lseq { + get { + return this.lseqField; + } + set { + this.lseqField = value; + } + } + + /// + public string duration { + get { + return this.durationField; + } + set { + this.durationField = value; + } + } + + /// + public string external_key { + get { + return this.external_keyField; + } + set { + this.external_keyField = value; + } + } + + /// + public string custom_attributes { + get { + return this.custom_attributesField; + } + set { + this.custom_attributesField = value; + } + } + + /// + public string session_chat_view_url { + get { + return this.session_chat_view_urlField; + } + set { + this.session_chat_view_urlField = value; + } + } + + /// + public string session_chat_download_url { + get { + return this.session_chat_download_urlField; + } + set { + this.session_chat_download_urlField = value; + } + } + + /// + public string file_transfer_count { + get { + return this.file_transfer_countField; + } + set { + this.file_transfer_countField = value; + } + } + + /// + public string file_move_count { + get { + return this.file_move_countField; + } + set { + this.file_move_countField = value; + } + } + + /// + public string file_delete_count { + get { + return this.file_delete_countField; + } + set { + this.file_delete_countField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionStart_time start_time { + get { + return this.start_timeField; + } + set { + this.start_timeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionEnd_time end_time { + get { + return this.end_timeField; + } + set { + this.end_timeField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionJumpoint jumpoint { + get { + return this.jumpointField; + } + set { + this.jumpointField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionPrimary_customer primary_customer { + get { + return this.primary_customerField; + } + set { + this.primary_customerField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionJump_group jump_group { + get { + return this.jump_groupField; + } + set { + this.jump_groupField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionPrimary_rep primary_rep { + get { + return this.primary_repField; + } + set { + this.primary_repField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("customer", IsNullable=false)] + public session_listSessionCustomer[] customer_list { + get { + return this.customer_listField; + } + set { + this.customer_listField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("representative", IsNullable=false)] + public session_listSessionRepresentative[] rep_list { + get { + return this.rep_listField; + } + set { + this.rep_listField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("event", IsNullable=false)] + public session_listSessionEvent[] session_details { + get { + return this.session_detailsField; + } + set { + this.session_detailsField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string lsid { + get { + return this.lsidField; + } + set { + this.lsidField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionStart_time { + + private string timestampField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string timestamp { + get { + return this.timestampField; + } + set { + this.timestampField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEnd_time { + + private string timestampField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string timestamp { + get { + return this.timestampField; + } + set { + this.timestampField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionJumpoint { + + private string idField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionPrimary_customer { + + private string gsnumberField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionJump_group { + + private string typeField; + + private string idField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionPrimary_rep { + + private string gsnumberField; + + private string idField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionCustomer { + + private string usernameField; + + private string public_ipField; + + private string private_ipField; + + private string hostnameField; + + private string osField; + + private string gsnumberField; + + /// + public string username { + get { + return this.usernameField; + } + set { + this.usernameField = value; + } + } + + /// + public string public_ip { + get { + return this.public_ipField; + } + set { + this.public_ipField = value; + } + } + + /// + public string private_ip { + get { + return this.private_ipField; + } + set { + this.private_ipField = value; + } + } + + /// + public string hostname { + get { + return this.hostnameField; + } + set { + this.hostnameField = value; + } + } + + /// + public string os { + get { + return this.osField; + } + set { + this.osField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionRepresentative { + + private string usernameField; + + private string display_nameField; + + private string public_display_nameField; + + private string private_display_nameField; + + private string public_ipField; + + private string private_ipField; + + private string hostnameField; + + private string osField; + + private string session_ownerField; + + private string seconds_involvedField; + + private string gsnumberField; + + private string idField; + + /// + public string username { + get { + return this.usernameField; + } + set { + this.usernameField = value; + } + } + + /// + public string display_name { + get { + return this.display_nameField; + } + set { + this.display_nameField = value; + } + } + + /// + public string public_display_name { + get { + return this.public_display_nameField; + } + set { + this.public_display_nameField = value; + } + } + + /// + public string private_display_name { + get { + return this.private_display_nameField; + } + set { + this.private_display_nameField = value; + } + } + + /// + public string public_ip { + get { + return this.public_ipField; + } + set { + this.public_ipField = value; + } + } + + /// + public string private_ip { + get { + return this.private_ipField; + } + set { + this.private_ipField = value; + } + } + + /// + public string hostname { + get { + return this.hostnameField; + } + set { + this.hostnameField = value; + } + } + + /// + public string os { + get { + return this.osField; + } + set { + this.osField = value; + } + } + + /// + public string session_owner { + get { + return this.session_ownerField; + } + set { + this.session_ownerField = value; + } + } + + /// + public string seconds_involved { + get { + return this.seconds_involvedField; + } + set { + this.seconds_involvedField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEvent { + + private string filenameField; + + private string filesizeField; + + private string bodyField; + + private session_listSessionEventPerformed_by performed_byField; + + private session_listSessionEventDestination destinationField; + + private session_listSessionEventData[] dataField; + + private string timestampField; + + private string event_typeField; + + /// + public string filename { + get { + return this.filenameField; + } + set { + this.filenameField = value; + } + } + + /// + public string filesize { + get { + return this.filesizeField; + } + set { + this.filesizeField = value; + } + } + + /// + public string body { + get { + return this.bodyField; + } + set { + this.bodyField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionEventPerformed_by performed_by { + get { + return this.performed_byField; + } + set { + this.performed_byField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] + public session_listSessionEventDestination destination { + get { + return this.destinationField; + } + set { + this.destinationField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("data")] + public session_listSessionEventData[] data { + get { + return this.dataField; + } + set { + this.dataField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string timestamp { + get { + return this.timestampField; + } + set { + this.timestampField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string event_type { + get { + return this.event_typeField; + } + set { + this.event_typeField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEventPerformed_by { + + private string typeField; + + private string gsnumberField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEventDestination { + + private string typeField; + + private string gsnumberField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string gsnumber { + get { + return this.gsnumberField; + } + set { + this.gsnumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlTextAttribute()] + public string Value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEventData { + + private session_listSessionEventDataValue valueField; + + /// + public session_listSessionEventDataValue value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.beyondtrust.com/sra/namespaces/API/reporting")] + public partial class session_listSessionEventDataValue { + + private string nameField; + + private string valueField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string value { + get { + return this.valueField; + } + set { + this.valueField = value; + } + } + } } diff --git a/BeyondTrustConnector/Model/Dto/BeyondTrustAccessSessionDto.cs b/BeyondTrustConnector/Model/Dto/BeyondTrustAccessSessionDto.cs new file mode 100644 index 0000000..eeadc2a --- /dev/null +++ b/BeyondTrustConnector/Model/Dto/BeyondTrustAccessSessionDto.cs @@ -0,0 +1,21 @@ +namespace BeyondTrustConnector.Model.Dto; + +public class BeyondTrustAccessSessionDto +{ + public DateTime StartTime { get; set; } + public DateTime? EndTime { get; set; } + public required string SessionId { get; set; } + public required string Jumpoint { get; set; } + public required string SessionType { get; set; } + public required string JumpGroup { get; set; } + public required string JumpItemAddress { get; set; } + public int JumpItemId { get; set; } + public IEnumerable> UserDetails { get; set; } = []; + public int? FileTransferCount { get; set; } + public int? FileMoveCount { get; set; } + public int? FileDeleteCount { get; set; } + public IEnumerable> Events { get; internal set; } = []; + public string? ChatDownloadUrl { get; internal set; } + public string? ChatViewUrl { get; internal set; } + public int JumpGroupId { get; internal set; } +} diff --git a/BeyondTrustConnector/Model/Dto/DtoHelperExtensions.cs b/BeyondTrustConnector/Model/Dto/DtoHelperExtensions.cs new file mode 100644 index 0000000..6e84dba --- /dev/null +++ b/BeyondTrustConnector/Model/Dto/DtoHelperExtensions.cs @@ -0,0 +1,64 @@ +namespace BeyondTrustConnector.Model.Dto +{ + internal static class DtoHelperExtensions + { + internal static BeyondTrustAccessSessionDto ToDto(this session_listSession session) + { + var jumpGroup = session.jump_group.Value; + var sessionId = session.lsid; + var startTime = DateTimeOffset.Parse(session.start_time.Value); + DateTimeOffset? endTime = null; + var endTimeValue = session.end_time?.Value; + if (!string.IsNullOrEmpty(endTimeValue)) + { + endTime = DateTimeOffset.Parse(endTimeValue); + } + + var jumpoint = session.jumpoint.Value; + var jumpItem = session.primary_customer.Value; + var sessionType = session.session_type; + return new BeyondTrustAccessSessionDto + { + SessionId = sessionId, + SessionType = sessionType, + StartTime = startTime.UtcDateTime, + EndTime = endTime?.UtcDateTime, + Jumpoint = jumpoint, + JumpItemId = int.Parse(session.primary_customer.gsnumber), + JumpItemAddress = jumpItem, + JumpGroup = jumpGroup, + JumpGroupId = int.Parse(session.jump_group.id), + FileDeleteCount = int.Parse(session.file_delete_count), + FileMoveCount = int.Parse(session.file_move_count), + FileTransferCount = int.Parse(session.file_transfer_count), + ChatDownloadUrl = session.session_chat_download_url, + ChatViewUrl = session.session_chat_view_url, + UserDetails = session.rep_list.Select(userDetails => new Dictionary + { + { "Username", userDetails.username }, + { "PublicIP", userDetails.public_ip }, + { "PrivateIP", userDetails.private_ip }, + { "Hostname", userDetails.hostname }, + { "OS", userDetails.os }, + { "SessionOwner", userDetails.session_owner == "1" }, + { "SecondsInvolved", userDetails.seconds_involved } + } + ).ToList(), + Events = session.session_details.Select(sessionDetails => new Dictionary + { + { "Event", sessionDetails.event_type }, + { "When", sessionDetails.timestamp }, + { "Who", sessionDetails.performed_by?.Value ?? "Unknown" }, + { "Destination", sessionDetails.destination?.Value ?? "Unknown" }, + { "Details", sessionDetails.data?.Select(detail => new Dictionary + { + { "Key", detail.value.name }, + { "Value", detail.value.value } + } + ).ToList() ?? []} + } + ).ToList() + }; + } + } +} diff --git a/BeyondTrustConnector/Service/BeyondTrustService.cs b/BeyondTrustConnector/Service/BeyondTrustService.cs index da1007c..6f9ff9d 100644 --- a/BeyondTrustConnector/Service/BeyondTrustService.cs +++ b/BeyondTrustConnector/Service/BeyondTrustService.cs @@ -1,13 +1,15 @@ using BeyondTrustConnector.Model; using Microsoft.Extensions.Logging; using System.Net.Http.Json; +using System.Xml; using System.Xml.Linq; +using System.Xml.Serialization; namespace BeyondTrustConnector.Service { public class BeyondTrustService(IHttpClientFactory httpClientFactory, ILogger logger) { - public async Task GetAccessSessionReport(DateTime start, int reportPeriod = 0) + public async Task GetAccessSessionReport(DateTime start, int reportPeriod = 0) { var client = httpClientFactory.CreateClient(nameof(BeyondTrustConnector)); var unixTime = ((DateTimeOffset)start).ToUnixTimeSeconds(); @@ -20,8 +22,13 @@ public async Task GetAccessSessionReport(DateTime start, int reportPe throw new Exception("Failed to generate report"); } var reportContent = await response.Content.ReadAsStringAsync(); - - return XDocument.Parse(reportContent); + if (string.IsNullOrEmpty(reportContent)) + { + throw new Exception("Failed to generate report"); + } + var serializer = new XmlSerializer(typeof(session_list)); + var sessionList = serializer.Deserialize(new StringReader(reportContent)) as session_list; + return sessionList ?? throw new Exception("Failed to deserialize report"); } public async Task GetVaultActivityReport(DateTime start, int reportPeriod = 0) diff --git a/BeyondTrustConnector/Service/IngestionService.cs b/BeyondTrustConnector/Service/IngestionService.cs index bbd08eb..c291833 100644 --- a/BeyondTrustConnector/Service/IngestionService.cs +++ b/BeyondTrustConnector/Service/IngestionService.cs @@ -1,6 +1,7 @@ using Azure.Identity; using Azure.Monitor.Ingestion; using BeyondTrustConnector.Model; +using BeyondTrustConnector.Model.Dto; using BeyondTrustConnector.Parser; using Microsoft.Extensions.Logging; @@ -15,7 +16,7 @@ public async Task IngestSyslog(List events) await Ingest("BeyondTrustEvents_CL", events); } - internal async Task IngestAccessSessions(List sessions) + internal async Task IngestAccessSessions(List sessions) { await Ingest("BeyondTrustAccessSession_CL", sessions); } diff --git a/BeyondTrustConnector/schemas/BeyondTrustAccessSession.xsd b/BeyondTrustConnector/schemas/BeyondTrustAccessSession.xsd new file mode 100644 index 0000000..8fab92a --- /dev/null +++ b/BeyondTrustConnector/schemas/BeyondTrustAccessSession.xsd @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/datacollection.bicep b/modules/datacollection.bicep index 52fd74b..b691ec2 100644 --- a/modules/datacollection.bicep +++ b/modules/datacollection.bicep @@ -136,6 +136,10 @@ resource Custom_Table_BeyondTrustAccessSession_CL 'Microsoft.OperationalInsights name: 'JumpGroup' type: 'string' } + { + name: 'JumpGroupId' + type: 'int' + } { name: 'Jumpoint' type: 'string' @@ -144,6 +148,10 @@ resource Custom_Table_BeyondTrustAccessSession_CL 'Microsoft.OperationalInsights name: 'UserDetails' type: 'dynamic' } + { + name: 'Events' + type: 'dynamic' + } { name: 'FileTransferCount' type: 'int' @@ -156,6 +164,14 @@ resource Custom_Table_BeyondTrustAccessSession_CL 'Microsoft.OperationalInsights name: 'FileDeleteCount' type: 'int' } + { + name: 'ChatDownloadUrl' + type: 'string' + } + { + name: 'ChatViewUrl' + type: 'string' + } ] } retentionInDays: 30 @@ -226,6 +242,10 @@ resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2023-03-11' name: 'JumpGroup' type: 'string' } + { + name: 'JumpGroupId' + type: 'int' + } { name: 'Jumpoint' type: 'string' @@ -234,6 +254,18 @@ resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2023-03-11' name: 'UserDetails' type: 'dynamic' } + { + name: 'Events' + type: 'dynamic' + } + { + name: 'ChatDownloadUrl' + type: 'string' + } + { + name: 'ChatViewUrl' + type: 'string' + } ] } 'Custom-BeyondTrustVaultActivity_CL': { From fc7cb7c5db36898d74203db3112355baba3dca83 Mon Sep 17 00:00:00 2001 From: Frode Hus Date: Fri, 20 Dec 2024 08:26:11 +0100 Subject: [PATCH 2/2] Refactor BeyondTrust classes to use DTOs Replaced `BeyondTrustVaultActivity` with `BeyondTrustVaultActivityDto` and `BeyondTrustLogEntry` with `BeyondTrustLogEntryDto` in the `BeyondTrustConnector.Model.Dto` namespace. Updated `BeyondTrustLogParser` to use `BeyondTrustLogEntryDto` in the `Parse` method. Modified `IngestionService` to use DTOs in `IngestSyslog` and `IngestVaultActivity` methods. Updated `VaultActivityUpdater` to use `BeyondTrustVaultActivityDto`. Added new files `BeyondTrustLogEntryDto.cs` and `BeyondTrustVaultActivityDto.cs` to define the DTO classes. --- .../Dto/BeyondTrustLogEntryDto.cs} | 4 ++-- .../BeyondTrustVaultActivityDto.cs} | 6 +++--- BeyondTrustConnector/Parser/BeyondTrustLogParser.cs | 8 +++++--- BeyondTrustConnector/Service/IngestionService.cs | 6 ++---- BeyondTrustConnector/VaultActivityUpdater.cs | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) rename BeyondTrustConnector/{Parser/BeyondTrustLogEntry.cs => Model/Dto/BeyondTrustLogEntryDto.cs} (82%) rename BeyondTrustConnector/Model/{BeyondTrustVaultActivity.cs => Dto/BeyondTrustVaultActivityDto.cs} (63%) diff --git a/BeyondTrustConnector/Parser/BeyondTrustLogEntry.cs b/BeyondTrustConnector/Model/Dto/BeyondTrustLogEntryDto.cs similarity index 82% rename from BeyondTrustConnector/Parser/BeyondTrustLogEntry.cs rename to BeyondTrustConnector/Model/Dto/BeyondTrustLogEntryDto.cs index 1fb50c4..16736de 100644 --- a/BeyondTrustConnector/Parser/BeyondTrustLogEntry.cs +++ b/BeyondTrustConnector/Model/Dto/BeyondTrustLogEntryDto.cs @@ -1,6 +1,6 @@ -namespace BeyondTrustConnector.Parser; +namespace BeyondTrustConnector.Model.Dto; -public class BeyondTrustLogEntry +public class BeyondTrustLogEntryDto { public DateTime Timestamp { get; set; } public int CorrelationId { get; set; } diff --git a/BeyondTrustConnector/Model/BeyondTrustVaultActivity.cs b/BeyondTrustConnector/Model/Dto/BeyondTrustVaultActivityDto.cs similarity index 63% rename from BeyondTrustConnector/Model/BeyondTrustVaultActivity.cs rename to BeyondTrustConnector/Model/Dto/BeyondTrustVaultActivityDto.cs index e6c495e..e6c3c7e 100644 --- a/BeyondTrustConnector/Model/BeyondTrustVaultActivity.cs +++ b/BeyondTrustConnector/Model/Dto/BeyondTrustVaultActivityDto.cs @@ -1,11 +1,11 @@ -namespace BeyondTrustConnector.Model; +namespace BeyondTrustConnector.Model.Dto; -internal class BeyondTrustVaultActivity +internal class BeyondTrustVaultActivityDto { public DateTime Timestamp { get; set; } public required string EventType { get; set; } public string? SessionId { get; set; } - public string? User { get; set; } + public string? User { get; set; } public int UserId { get; set; } public int VaultAccountId { get; set; } } diff --git a/BeyondTrustConnector/Parser/BeyondTrustLogParser.cs b/BeyondTrustConnector/Parser/BeyondTrustLogParser.cs index 6c2c181..ea8eaa2 100644 --- a/BeyondTrustConnector/Parser/BeyondTrustLogParser.cs +++ b/BeyondTrustConnector/Parser/BeyondTrustLogParser.cs @@ -1,16 +1,18 @@  +using BeyondTrustConnector.Model.Dto; + namespace BeyondTrustConnector.Parser; internal class BeyondTrustLogParser(string log) { - public List Parse() + public List Parse() { - var entries = new List(); + var entries = new List(); var parser = new SyslogParser(log); foreach(var entry in parser.Entries) { var details = ParsePayload(entry.Payload); - var beyondTrustLogEntry = new BeyondTrustLogEntry + var beyondTrustLogEntry = new BeyondTrustLogEntryDto { Timestamp = UnixTimeStampToDateTimeUTC(int.Parse(details["when"])), CorrelationId = entry.CorrelationId, diff --git a/BeyondTrustConnector/Service/IngestionService.cs b/BeyondTrustConnector/Service/IngestionService.cs index c291833..264f1ef 100644 --- a/BeyondTrustConnector/Service/IngestionService.cs +++ b/BeyondTrustConnector/Service/IngestionService.cs @@ -1,8 +1,6 @@ using Azure.Identity; using Azure.Monitor.Ingestion; -using BeyondTrustConnector.Model; using BeyondTrustConnector.Model.Dto; -using BeyondTrustConnector.Parser; using Microsoft.Extensions.Logging; namespace BeyondTrustConnector.Service; @@ -11,7 +9,7 @@ public class IngestionService(ILogger logger) { private readonly ILogger _logger = logger; - public async Task IngestSyslog(List events) + public async Task IngestSyslog(List events) { await Ingest("BeyondTrustEvents_CL", events); } @@ -21,7 +19,7 @@ internal async Task IngestAccessSessions(List sessi await Ingest("BeyondTrustAccessSession_CL", sessions); } - internal async Task IngestVaultActivity(List vaultActivities) + internal async Task IngestVaultActivity(List vaultActivities) { await Ingest("BeyondTrustVaultActivity_CL", vaultActivities); } diff --git a/BeyondTrustConnector/VaultActivityUpdater.cs b/BeyondTrustConnector/VaultActivityUpdater.cs index 8f1b93c..2acc7e0 100644 --- a/BeyondTrustConnector/VaultActivityUpdater.cs +++ b/BeyondTrustConnector/VaultActivityUpdater.cs @@ -1,7 +1,7 @@ using System; using System.Globalization; using System.Xml.Linq; -using BeyondTrustConnector.Model; +using BeyondTrustConnector.Model.Dto; using BeyondTrustConnector.Service; using Microsoft.Azure.Functions.Worker; using Microsoft.Extensions.Logging; @@ -21,7 +21,7 @@ public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = false)] Tim var activities = report.Root!.Descendants(XName.Get("vault_account_activity", ns)); var sessionBasedActivities = new string[] { "Credentials Used" }; - var vaultActivities = new List(); + var vaultActivities = new List(); foreach (var activity in activities) { var timestamp = int.Parse(activity.Attribute("timestamp")!.Value); @@ -35,7 +35,7 @@ public async Task Run([TimerTrigger("0 */15 * * * *", RunOnStartup = false)] Tim var performedBy = activity.Element(XName.Get("performed_by", ns)); int.TryParse(performedBy?.Attribute("id")?.Value, out var userId); var userName = performedBy?.Value; - var vaultActivity = new BeyondTrustVaultActivity + var vaultActivity = new BeyondTrustVaultActivityDto { EventType = eventType, Timestamp = UnixTimeStampToDateTimeUTC(timestamp),