//****************************************************************************************************** // SecurityHub.cs - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 03/03/2016 - Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Linq; using GSF.Data.Model; using GSF.Security; using GSF.Web.Hubs; using GSF.Web.Model; using GSF.Web.Security; using System.Threading.Tasks; using GSF.Collections; using GSF.Data; using GSF.Diagnostics; using GSF.TimeSeries.Statistics; using GSF.Web.Shared.Model; using Newtonsoft.Json.Linq; namespace GSF.Web.Shared { /// /// Defines a SignalR security hub for managing users, groups and SID management. /// [AuthorizeHubRole] public class SharedHub : RecordOperationsHub { #region [ Constructors ] /// /// Creates a new . /// public SharedHub() : this(null, null, null) { } /// /// Creates a new with the specified logging functions. /// /// Delegate to use to log status messages, if any. /// Delegate to use to log exceptions, if any. public SharedHub(Action logStatusMessageFunction, Action logExceptionFunction) : this(null, logStatusMessageFunction, logExceptionFunction) { } /// /// Creates a new with the specified and logging functions. /// /// Setting category that contains the connection settings. Defaults to "securityProvider". /// Delegate to use to log status messages, if any. /// Delegate to use to log exceptions, if any. public SharedHub(string settingsCategory, Action logStatusMessageFunction, Action logExceptionFunction) : this(settingsCategory, logStatusMessageFunction, logExceptionFunction, true) { // Capture initial defaults if ((object)logStatusMessageFunction != null && (object)s_logStatusMessageFunction == null) s_logStatusMessageFunction = logStatusMessageFunction; if ((object)logExceptionFunction != null && (object)s_logExceptionFunction == null) s_logExceptionFunction = logExceptionFunction; } // ReSharper disable once UnusedParameter.Local private SharedHub(string settingsCategory, Action logStatusMessageFunction, Action logExceptionFunction, bool overload) : base(settingsCategory ?? "systemSettings", logStatusMessageFunction ?? s_logStatusMessageFunction, logExceptionFunction ?? s_logExceptionFunction) { } #endregion #region [ Methods ] /// /// Overrides base OnConnected method to provide logging /// /// public override Task OnConnected() { LogStatusMessage($"SharedHub connect by {Context.User?.Identity?.Name ?? "Undefined User"} [{Context.ConnectionId}] - count = {ConnectionCount}", UpdateType.Information, false); return base.OnConnected(); } /// /// Overrides base OnDisconnected method to provide logging /// /// /// public override Task OnDisconnected(bool stopCalled) { if (stopCalled) { // Dispose any associated hub operations associated with current SignalR client LogStatusMessage($"SharedHub disconnect by {Context.User?.Identity?.Name ?? "Undefined User"} [{Context.ConnectionId}] - count = {ConnectionCount}", UpdateType.Information, false); } return base.OnDisconnected(stopCalled); } #endregion #region [ Static ] // Static Fields private static Action s_logStatusMessageFunction; private static Action s_logExceptionFunction; private static readonly LogPublisher s_log; private static readonly Dictionary s_statisticReferences; private static Statistic[] s_statistics; // Static Constructor static SharedHub() { s_log = Logger.CreatePublisher(typeof(SharedHub), MessageClass.Component); s_statisticReferences = new Dictionary(); } // Static Properties /// /// Gets current default Node ID for security. /// public static readonly Guid DefaultNodeID = AdoSecurityProvider.DefaultNodeID; // Static Methods private void InitializeStatistics() { if (s_statistics?.Length > 0) return; try { using (AdoDataConnection connection = new AdoDataConnection("systemSettings")) { TableOperations statistics = new TableOperations(connection); s_statistics = statistics.QueryRecords().ToArray(); } } catch (Exception ex) { s_log.Publish(MessageLevel.Error, "InitializeStatistics", exception: ex); } } #endregion // Client-side script functionality #region [ Company Table Operations ] /// /// Queries company table counts /// /// Text to filter search down to. /// The count of the rows in the table matching the query. [RecordOperation(typeof(Company), RecordOperation.QueryRecordCount)] public int QueryCompanyCount(string filterText) { return DataContext.Table().QueryRecordCount(filterText); } /// /// Queries company table. /// /// Table field label to sort on. /// Boolean denoting direction of sort. /// The page number to return. /// The size of the page. /// Text to filter search down to. /// The rows in the table matching the query. [RecordOperation(typeof(Company), RecordOperation.QueryRecords)] public IEnumerable QueryCompanies(string sortField, bool ascending, int page, int pageSize, string filterText) { return DataContext.Table().QueryRecords(sortField, ascending, page, pageSize, filterText); } /// /// Deletes record from company table. /// /// The id of the record to delete. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Company), RecordOperation.DeleteRecord)] public void DeleteCompany(int id) { DataContext.Table().DeleteRecord(id); } /// /// Creates a new Company object. /// /// A new company instance [RecordOperation(typeof(Company), RecordOperation.CreateNewRecord)] public Company NewCompany() { return DataContext.Table().NewRecord(); } /// /// Adds new record to company table. /// /// The record to add. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Company), RecordOperation.AddNewRecord)] public void AddNewCompany(Company company) { DataContext.Table().AddNewRecord(company); } /// /// Updates a record in company table. /// /// The record to update. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Company), RecordOperation.UpdateRecord)] public void UpdateCompany(Company company) { DataContext.Table().UpdateRecord(company); } #endregion #region [ Vendor Table Operations ] /// /// Queries vendor table count. /// /// Text to filter search down to. /// The count of the rows in the table matching the query. [RecordOperation(typeof(Vendor), RecordOperation.QueryRecordCount)] public int QueryVendorCount(string filterText) { return DataContext.Table().QueryRecordCount(filterText); } /// /// Queries vendor table records. /// /// Table field label to sort on. /// Boolean denoting direction of sort. /// The page number to return. /// The size of the page. /// Text to filter search down to. /// The rows in the table matching the query. [RecordOperation(typeof(Vendor), RecordOperation.QueryRecords)] public IEnumerable QueryVendors(string sortField, bool ascending, int page, int pageSize, string filterText) { return DataContext.Table().QueryRecords(sortField, ascending, page, pageSize, filterText); } /// /// Deletes vendor table record. /// /// The id of the record to delete. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Vendor), RecordOperation.DeleteRecord)] public void DeleteVendor(int id) { DataContext.Table().DeleteRecord(id); } /// /// Creates new vendor object. /// /// A new Vendor model instance. [RecordOperation(typeof(Vendor), RecordOperation.CreateNewRecord)] public Vendor NewVendor() { return DataContext.Table().NewRecord(); } /// /// Adds new vendor record to table. /// /// The record to add. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Vendor), RecordOperation.AddNewRecord)] public void AddNewVendor(Vendor vendor) { DataContext.Table().AddNewRecord(vendor); } /// /// Updates record in vendor table. /// /// The record to update. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(Vendor), RecordOperation.UpdateRecord)] public void UpdateVendor(Vendor vendor) { DataContext.Table().UpdateRecord(vendor); } #endregion #region [ VendorDevice Table Operations ] /// /// Queries vendor device table count. /// /// Text to filter search down to. /// The count of the rows in the table matching the query. [RecordOperation(typeof(VendorDevice), RecordOperation.QueryRecordCount)] public int QueryVendorDeviceCount(string filterText) { return DataContext.Table().QueryRecordCount(filterText); } /// /// Queries vendor device table records. /// /// Table field label to sort on. /// Boolean denoting direction of sort. /// The page number to return. /// The size of the page. /// Text to filter search down to. /// The rows in the table matching the query. [RecordOperation(typeof(VendorDevice), RecordOperation.QueryRecords)] public IEnumerable QueryVendorDevices(string sortField, bool ascending, int page, int pageSize, string filterText) { return DataContext.Table().QueryRecords(sortField, ascending, page, pageSize, filterText); } /// /// Deletes record from vendor device table. /// /// The id of the record to delete. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(VendorDevice), RecordOperation.DeleteRecord)] public void DeleteVendorDevice(int id) { DataContext.Table().DeleteRecord(id); } /// /// Creates new VendorDevice object. /// /// A new VendorDevice model instance. [RecordOperation(typeof(VendorDevice), RecordOperation.CreateNewRecord)] public VendorDevice NewVendorDevice() { return DataContext.Table().NewRecord(); } /// /// Adds new record to vendor device table. /// /// The record to add. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(VendorDevice), RecordOperation.AddNewRecord)] public void AddNewVendorDevice(VendorDevice vendorDevice) { DataContext.Table().AddNewRecord(vendorDevice); } /// /// Updates record in vendor device table. /// /// The record to update. [AuthorizeHubRole("Administrator, Editor")] [RecordOperation(typeof(VendorDevice), RecordOperation.UpdateRecord)] public void UpdateVendorDevice(VendorDevice vendorDevice) { DataContext.Table().UpdateRecord(vendorDevice); } #endregion #region [ Statistic Formatting Operations ] /// /// If measurement is a statistic, returns the associated Statistic record; otherwise, returns null. /// /// Record of measurement metadata used to lookup Statistic record. /// Associated Statistic record, if measurement is a statistic; otherwise, returns null. /// /// For best results, this function should be called after all statistic engine sources have been registered. /// object expected to contain a "signalid" and "signalreference" property. /// public Statistic GetStatistic(dynamic metadataRecord) { Guid signalID = metadataRecord.signalid; return s_statisticReferences.GetOrAdd(signalID, _ => { string signalReference = metadataRecord.signalreference; if (string.IsNullOrWhiteSpace(signalReference)) return null; if (!StatisticsEngine.TryLookupStatisticSource(signalReference, out string source, out int signalIndex)) return null; InitializeStatistics(); foreach (Statistic statistic in s_statistics) { if (statistic.Source.Equals(source, StringComparison.OrdinalIgnoreCase) && statistic.SignalIndex == signalIndex) return statistic; } return null; }); } /// /// Gets the format records for statistics associated with the . /// /// Collection of measurement metadata. /// Array of statistical records for the . /// /// This is usually used in conjunction with a call to /// or applied to the "FormatRecords" property of a Json subscriptionInfo object. /// Records in array expected to contain a "SignalID" and "SignalReference" property. /// public IEnumerable GetStatisticFormatRecords(IEnumerable metadata) { return metadata.Select(ToJsonFormatRecord).ToArray(); } private object ToJsonFormatRecord(dynamic metadataRecord) { Statistic statistic = GetStatistic(metadataRecord); Guid signalID = metadataRecord.signalid; dynamic obj = new JObject(); obj.signalID = signalID; obj.format = statistic?.DisplayFormat ?? "{0:N3}"; obj.dataType = statistic?.DataType; return obj; } #endregion } }