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/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/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/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/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..264f1ef 100644 --- a/BeyondTrustConnector/Service/IngestionService.cs +++ b/BeyondTrustConnector/Service/IngestionService.cs @@ -1,7 +1,6 @@ using Azure.Identity; using Azure.Monitor.Ingestion; -using BeyondTrustConnector.Model; -using BeyondTrustConnector.Parser; +using BeyondTrustConnector.Model.Dto; using Microsoft.Extensions.Logging; namespace BeyondTrustConnector.Service; @@ -10,17 +9,17 @@ 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); } - internal async Task IngestAccessSessions(List sessions) + internal async Task IngestAccessSessions(List sessions) { 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), 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': {