private void Application_PreRequestHandlerExecute(object sender, EventArgs e) { // Check if access to resource is to be secured. string resource = GetResourceName(); if (!IsAccessSecured(resource)) { return; } SecurityProviderCache.ValidateCurrentProvider(); if (!m_application.User.Identity.IsAuthenticated) { // Failed to authenticate user. Redirect(HttpStatusCode.Unauthorized); } if (IsAccessRestricted() || !SecurityProviderUtility.IsResourceAccessible(resource)) { // User does not have access to the resource. Redirect(HttpStatusCode.Forbidden); } }
// Applies authentication for requests where credentials are passed directly in the HTTP headers. private SecurityPrincipal AuthenticateCachedCredentials(string authenticationToken) { string username, password; if ((object)authenticationToken == null) { return(null); } // Get the user's credentials from the credential cache if (!SessionHandler.TryGetCachedCredentials(authenticationToken, out username, out password)) { return(null); } // Create the security provider that will authenticate the user's credentials ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(username, autoRefresh: false); securityProvider.Password = password; securityProvider.Authenticate(); // Return the security principal that will be used for role-based authorization SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); return(new SecurityPrincipal(securityIdentity)); }
/// <summary> /// Provides an entry point for custom authorization checks. /// </summary> /// <param name="user">The <see cref="IPrincipal"/> for the client being authorize</param> /// <returns> /// <c>true</c> if the user is authorized, otherwise, <c>false</c>. /// </returns> protected override bool UserAuthorized(IPrincipal user) { // Get current user name string userName = user.Identity.Name; SecurityProviderCache.ValidateCurrentProvider(); // Setup the principal user = Thread.CurrentPrincipal; // Verify that the current thread principal has been authenticated. if (!Thread.CurrentPrincipal.Identity.IsAuthenticated) { throw new SecurityException($"Authentication failed for user '{userName}': {SecurityProviderCache.CurrentProvider.AuthenticationFailureReason}"); } if (AllowedRoles.Length > 0 && !AllowedRoles.Any(role => user.IsInRole(role))) { throw new SecurityException($"Access is denied for user '{userName}': minimum required roles = {AllowedRoles.ToDelimitedString(", ")}."); } // Make sure current user ID is cached if (!AuthorizationCache.UserIDs.ContainsKey(userName)) { using (AdoDataConnection connection = new AdoDataConnection(SettingsCategory)) { AuthorizationCache.UserIDs.TryAdd(userName, connection.ExecuteScalar <Guid?>("SELECT ID FROM UserAccount WHERE Name={0}", UserInfo.UserNameToSID(userName)) ?? Guid.Empty); } } return(true); }
private static void CachePrincipal(Guid sessionID, SecurityPrincipal principal) { if (s_authorizationCache.TryAdd(sessionID, principal)) { SecurityProviderCache.AutoRefresh(principal.Identity.Provider); } }
/// <summary> /// Sends a command request to the service. /// </summary> /// <param name="clientID">Client ID of sender.</param> /// <param name="userInput">Request string.</param> public void SendRequest(Guid clientID, string userInput) { ClientRequest request = ClientRequest.Parse(userInput); if ((object)request != null) { ClientRequestHandler requestHandler = ServiceHelper.FindClientRequestHandler(request.Command); SecurityProviderCache.ValidateCurrentProvider(); if (SecurityProviderUtility.IsResourceSecurable(request.Command) && !SecurityProviderUtility.IsResourceAccessible(request.Command)) { ServiceHelper.UpdateStatus(clientID, UpdateType.Alarm, $"Access to \"{request.Command}\" is denied.\r\n\r\n"); return; } if ((object)requestHandler != null) { requestHandler.HandlerMethod(new ClientRequestInfo(new ClientInfo { ClientID = clientID }, request)); } else { ServiceHelper.UpdateStatus(clientID, UpdateType.Alarm, $"Command \"{request.Command}\" is not supported.\r\n\r\n"); } } }
/// <summary> /// Called when authorization is required. /// </summary> /// <param name="filterContext">The filter context.</param> public void OnAuthorization(AuthorizationContext filterContext) { SecurityProviderCache.ValidateCurrentProvider(); // Setup the principal filterContext.HttpContext.User = Thread.CurrentPrincipal; // Get current user name string userName = Thread.CurrentPrincipal.Identity.Name; // Verify that the current thread principal has been authenticated. if (!Thread.CurrentPrincipal.Identity.IsAuthenticated) { throw new SecurityException($"Authentication failed for user '{userName}': {SecurityProviderCache.CurrentProvider.AuthenticationFailureReason}"); } if (AllowedRoles.Length > 0 && !AllowedRoles.Any(role => filterContext.HttpContext.User.IsInRole(role))) { throw new SecurityException($"Access is denied for user '{userName}': minimum required roles = {AllowedRoles.ToDelimitedString(", ")}."); } // Make sure current user ID is cached if (!AuthorizationCache.UserIDs.ContainsKey(userName)) { using (AdoDataConnection connection = new AdoDataConnection(SettingsCategory)) { Guid?userID = connection.ExecuteScalar <Guid?>("SELECT ID FROM UserAccount WHERE Name={0}", UserInfo.UserNameToSID(userName)); if ((object)userID != null) { AuthorizationCache.UserIDs.TryAdd(userName, userID.GetValueOrDefault()); } } } }
/// <summary> /// Evaluates the <paramref name="evaluationContext"/> and initializes security. /// </summary> /// <param name="evaluationContext">An <see cref="EvaluationContext"/> object.</param> /// <param name="state">Custom state of the <see cref="SecurityPolicy"/>.</param> /// <returns></returns> public virtual bool Evaluate(EvaluationContext evaluationContext, ref object state) { // In order for this to work properly security on the binding must be configured to use windows security. // When this is done the caller's windows identity is available to us here and can be used to derive from // it the security principal that can be used by WCF service code downstream for implementing security. object property; if (evaluationContext.Properties.TryGetValue("Identities", out property)) { // Extract and assign the caller's windows identity to current thread if available. IList <IIdentity> identities = property as List <IIdentity>; if ((object)identities == null) { throw new SecurityException(string.Format("Null Identities in Evaluation Context for '{0}'", Thread.CurrentPrincipal.Identity)); } foreach (IIdentity identity in identities) { if (identity is WindowsIdentity) { Thread.CurrentPrincipal = new WindowsPrincipal((WindowsIdentity)identity); break; } } } string resource = GetResourceName(); if (SecurityProviderUtility.IsResourceSecurable(resource)) { // Initialize the security principal from caller's windows identity if uninitialized. SecurityProviderCache.ValidateCurrentProvider(); // Setup the principal to be attached to the thread on which WCF service will execute. evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal; // Verify that the current thread principal has been authenticated. if (!Thread.CurrentPrincipal.Identity.IsAuthenticated) { throw new SecurityException(string.Format("Authentication failed for user '{0}'", Thread.CurrentPrincipal.Identity.Name)); } // Perform a top-level permission check on the resource being accessed. if (!SecurityProviderUtility.IsResourceAccessible(resource)) { throw new SecurityException(string.Format("Access to '{0}' is denied", resource)); } return(true); } // Setup the principal to be attached to the thread on which WCF service will execute. evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal; return(true); }
private void SecureForm_Load(object sender, EventArgs e) { try { // Don't proceed if the form is opened in design mode if (DesignMode) { return; } // Check if the resource is excluded from being secured string resource = GetResourceName(); if (!SecurityProviderUtility.IsResourceSecurable(resource)) { return; } // Set up security provider for passthrough authentication ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(WindowsIdentity.GetCurrent().Name); securityProvider.PassthroughPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); securityProvider.Authenticate(); // Setup the security principal for role-based security SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); SecurityPrincipal = new SecurityPrincipal(securityIdentity); // Verify that the current thread principal has been authenticated if (!SecurityPrincipal.Identity.IsAuthenticated) { throw new SecurityException($"Authentication failed for user '{SecurityPrincipal.Identity.Name}'"); } // Perform a top-level permission check on the resource being accessed if (!SecurityProviderUtility.IsResourceAccessible(resource, SecurityPrincipal)) { throw new SecurityException($"Access to '{resource}' is denied"); } // Set up the current thread principal // NOTE: Provided for backwards compatibility; // recommended to use the SecurityPrincipal instead Thread.CurrentPrincipal = SecurityPrincipal; } catch (Exception ex) { if (ExceptionHandler is null) { throw; } ExceptionHandler(ex); } }
/// <summary> /// Changes user password. /// </summary> /// <param name="oldPassword">User's current password.</param> /// <param name="newPassword">User's new password.</param> /// <returns>true if the password is changed, otherwise false.</returns> public bool ChangePassword(string oldPassword, string newPassword) { SecurityProviderCache.ValidateCurrentProvider(); if (!SecurityProviderCache.CurrentProvider.CanChangePassword) { return(false); } return(SecurityProviderCache.CurrentProvider.ChangePassword(oldPassword, newPassword)); }
/// <summary> /// Resets user password. /// </summary> /// <param name="securityAnswer">Answer to user's security question.</param> /// <returns>true if password is reset, otherwise false.</returns> public bool ResetPassword(string securityAnswer) { SecurityProviderCache.ValidateCurrentProvider(); if (!SecurityProviderCache.CurrentProvider.CanResetPassword) { return(false); } return(SecurityProviderCache.CurrentProvider.ResetPassword(securityAnswer)); }
/// <summary> /// Refreshes and returns information about the current user. /// </summary> /// <returns>An <see cref="UserData"/> object of the user if user's security context has been initialized, otherwise null.</returns> public UserData RefreshUserData() { SecurityProviderCache.ValidateCurrentProvider(); if (SecurityProviderCache.CurrentProvider.CanRefreshData) { SecurityProviderCache.CurrentProvider.RefreshData(); } return(SecurityProviderCache.CurrentProvider.UserData); }
private void ValidateCurrentProvider() { if (CurrentProvider == null) { ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(Thread.CurrentPrincipal.Identity.Name); securityProvider.PassthroughPrincipal = Thread.CurrentPrincipal; securityProvider.Authenticate(); SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); Thread.CurrentPrincipal = new SecurityPrincipal(securityIdentity); } }
/// <summary> /// Clears any cached authorizations for the specified <paramref name="sessionID"/>. /// </summary> /// <param name="sessionID">Identifier of session authorization to clear.</param> /// <returns><c>true</c> if session authorization was found and cleared; otherwise, <c>false</c>.</returns> public static bool ClearAuthorizationCache(Guid sessionID) { SecurityPrincipal securityPrincipal; bool removed = s_authorizationCache.TryRemove(sessionID, out securityPrincipal); if (removed) { SecurityProviderCache.DisableAutoRefresh(securityPrincipal.Identity.Provider); } return(removed); }
/// <summary> /// Evaluates if menu item should be visible to current user with access to <see cref="Roles"/>. /// </summary> /// <param name="parameter"> /// Data used by the <see cref="MenuCommand"/>. If the <see cref="MenuCommand"/> does not require /// data to be passed, this object can be set to <c>null</c>. /// </param> /// <returns><c>true</c> if this <see cref="MenuCommand"/> can be executed; otherwise, <c>false</c>.</returns> public bool CanExecute(object parameter) { SecurityPrincipal currentPrincipal = CommonFunctions.CurrentPrincipal; ISecurityProvider securityProvider; if (!SecurityProviderCache.TryGetCachedProvider(currentPrincipal.Identity.Name, out securityProvider)) { securityProvider = SecurityProviderCache.CurrentProvider; } return(((object)securityProvider != null) && currentPrincipal.Identity.IsAuthenticated && securityProvider.UserData.Roles.Any() && (string.IsNullOrEmpty(Roles) || Roles == "*" || currentPrincipal.IsInRole(Roles))); }
// Client-side script functionality #region [ Security Functions ] /// <summary> /// Resets the current provider cache. /// </summary> /// <param name="cookieSessionID">Session ID as it appears in the cookie header value.</param> /// <returns><c>true</c> if session was cleared; otherwise, <c>false</c>.</returns> public bool Logout(string cookieSessionID) { Guid sessionID; SecurityPrincipal securityPrincipal; if (!Guid.TryParse(cookieSessionID, out sessionID)) { return(false); } // Flush any cached information that has been saved for this user if (AuthenticationHandler.TryGetPrincipal(sessionID, out securityPrincipal)) { SecurityProviderCache.Flush(securityPrincipal.Identity.Name); } // Clear any cached session state for user (this also clears any cached authorizations) return(RazorView.ClearSessionCache(sessionID)); }
/// <summary> /// Enables processing of HTTP web requests by a custom handler that implements the <see cref="GSF.Web.Hosting.IHostedHttpHandler"/> interface. /// </summary> /// <param name="request">HTTP request message.</param> /// <param name="response">HTTP response message.</param> public Task ProcessRequestAsync(HttpRequestMessage request, HttpResponseMessage response, System.Threading.CancellationToken cancellationToken) { return(Task.Run(() => { SecurityProviderCache.ValidateCurrentProvider(); NameValueCollection parameters = request.RequestUri.ParseQueryString(); m_eventID = Convert.ToInt32(parameters["EventID"]); m_templateID = Convert.ToInt32(parameters["TemplateID"]); if ((object)parameters["chartID"] == null) { ProcessEmailRequest(request, response); } else { ProcessChartRequest(request, response); } })); }
// Applies authentication for requests where credentials are passed directly in the HTTP headers. private SecurityPrincipal AuthenticateBasic(string credentials) { string username, password; // Get the user's credentials from the HTTP headers if (!TryParseCredentials(credentials, out username, out password)) { return(null); } // Create the security provider that will authenticate the user's credentials ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(username, autoRefresh: false); securityProvider.Password = password; securityProvider.Authenticate(); // Return the security principal that will be used for role-based authorization SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); return(new SecurityPrincipal(securityIdentity)); }
// Applies authentication for requests using Windows pass-through authentication. public static SecurityPrincipal AuthenticatePassthrough(IPrincipal user) { string username = user?.Identity.Name; if ((object)username == null) { return(null); } // Get the principal used for verifying the user's pass-through authentication IPrincipal passthroughPrincipal = user; // Create the security provider that will verify the user's pass-through authentication ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(username, passthroughPrincipal, false); securityProvider.Authenticate(); // Return the security principal that will be used for role-based authorization SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); return(new SecurityPrincipal(securityIdentity)); }
private void Application_PreRequestHandlerExecute(object sender, EventArgs e) { // Check if access to resource is to be secured. string resource = GetResourceName(); if (!IsAccessSecured(resource)) { return; } SecurityPrincipal securityPrincipal = Thread.CurrentPrincipal as SecurityPrincipal; if ((object)securityPrincipal == null) { ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(Thread.CurrentPrincipal.Identity.Name); securityProvider.PassthroughPrincipal = Thread.CurrentPrincipal; securityProvider.Authenticate(); SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); securityPrincipal = new SecurityPrincipal(securityIdentity); Thread.CurrentPrincipal = securityPrincipal; } if (!m_application.User.Identity.IsAuthenticated) { // Failed to authenticate user. Redirect(HttpStatusCode.Unauthorized); } if (IsAccessRestricted() || !SecurityProviderUtility.IsResourceAccessible(resource, securityPrincipal)) { // User does not have access to the resource. Redirect(HttpStatusCode.Forbidden); } }
private void SecureForm_Load(object sender, EventArgs e) { // Don't proceed if the form is opened in design mode. if (DesignMode) { return; } // Check if the resource is excluded from being secured. string resource = GetResourceName(); if (!SecurityProviderUtility.IsResourceSecurable(resource)) { return; } // Setup thread principal to current windows principal. if (!(Thread.CurrentPrincipal is WindowsPrincipal)) { Thread.CurrentPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); } // Setup the security provider for role-based security. SecurityProviderCache.ValidateCurrentProvider(); // Verify that the current thread principal has been authenticated. if (!Thread.CurrentPrincipal.Identity.IsAuthenticated) { throw new SecurityException($"Authentication failed for user '{Thread.CurrentPrincipal.Identity.Name}'"); } // Perform a top-level permission check on the resource being accessed. if (!SecurityProviderUtility.IsResourceAccessible(resource)) { throw new SecurityException($"Access to '{resource}' is denied"); } }
/// <summary> /// Attempts to change user's password. /// </summary> /// <param name="sender">Source of this event.</param> /// <param name="e">Arguments of this event.</param> private void ButtonChange_Click(object sender, RoutedEventArgs e) { try { // Check if old and new password are different if (TextBoxOldPassword.Password == TextBoxNewPassword.Password) { throw new Exception("New password cannot be same as old password."); } // Check is new password and confirm password are same if (TextBoxNewPassword.Password != TextBoxConfirmPassword.Password) { throw new Exception("New password and confirm password should be same."); } ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(TextBoxChangePasswordUserName.Text); securityProvider.SecurePassword = TextBoxNewPassword.SecurePassword; if (securityProvider.CanChangePassword) { // Attempt to change password if (securityProvider.ChangePassword(TextBoxOldPassword.Password, TextBoxNewPassword.Password) && securityProvider.Authenticate()) { // Password changed and authenticated successfully DisplayErrorMessage("Password changed successfully."); // Setup security principal for subsequent uses SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); SecurityPrincipal = new SecurityPrincipal(securityIdentity); ClearErrorMessage(); ExitSuccess = true; } else { // Show why password change failed if (!ShowFailureReason(securityProvider)) { if (!securityProvider.IsUserAuthenticated) { DisplayErrorMessage("Authentication was not successful."); } else { DisplayErrorMessage("Password change was not successful."); } if (string.IsNullOrWhiteSpace(TextBoxChangePasswordUserName.Text)) { TextBoxChangePasswordUserName.Focus(); } else { TextBoxOldPassword.Focus(); } } } } else { DisplayErrorMessage("Account does not support password change."); } } catch (Exception ex) { DisplayErrorMessage("Change password failed: " + ex.Message); TextBoxOldPassword.Focus(); } }
/// <summary> /// Logins the user. /// </summary> /// <param name="sender">Source of this event.</param> /// <param name="e">Arguments of this event.</param> private void ButtonLogin_Click(object sender, RoutedEventArgs e) { UserInfo userInfo; WindowsImpersonationContext impersonationContext = null; ISecurityProvider securityProvider; try { // Determine whether we need to try impersonating the user userInfo = new UserInfo(TextBoxUserName.Text); // If the application is unable to access the domain, possibly because the local user // running the application does not have access to domain objects, it's possible that // the user logging in does have access to the domain. So we attempt to impersonate the // user logging in to allow authentication to proceed if (!userInfo.DomainRespondsForUser && TryImpersonate(userInfo.LoginID, TextBoxPassword.Password, out impersonationContext)) { try { // Working around a known issue - DirectorySearcher will often throw // an exception the first time it is used after impersonating another // user so we get that out of the way here userInfo.Initialize(); } catch (InitializationException) { // Exception is expected so we ignore it } } // Initialize the security provider securityProvider = SecurityProviderCache.CreateProvider(TextBoxUserName.Text); securityProvider.SecurePassword = TextBoxPassword.SecurePassword; // Attempt to authenticate user if (securityProvider.Authenticate()) { // Setup security principal for subsequent uses SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); SecurityPrincipal = new SecurityPrincipal(securityIdentity); ClearErrorMessage(); ExitSuccess = true; } else { // Verify their password hasn't expired if (securityProvider.UserData.IsDefined && securityProvider.UserData.PasswordChangeDateTime <= DateTime.UtcNow) { // Display password expired message DisplayErrorMessage(string.Format("Your password has expired. {0} You must change your password to continue.", securityProvider.AuthenticationFailureReason)); m_displayType = DisplayType.ChangePassword; ManageScreenVisualization(); TextBoxPassword.Password = ""; } else { // Display login failure message DisplayErrorMessage("The username or password is invalid. " + securityProvider.AuthenticationFailureReason); if (string.IsNullOrWhiteSpace(TextBoxUserName.Text)) { TextBoxUserName.Focus(); } else { TextBoxPassword.Focus(); } } } } catch (Exception ex) { DisplayErrorMessage("Login failed: " + ex.Message); if (string.IsNullOrWhiteSpace(TextBoxUserName.Text)) { TextBoxUserName.Focus(); } else { TextBoxPassword.Focus(); } } finally { if ((object)impersonationContext != null) { impersonationContext.Undo(); impersonationContext.Dispose(); } } }
private async Task CopyModelAsCsvToStreamAsync(NameValueCollection requestParameters, Stream responseStream, CancellationToken cancellationToken) { const int DefaultFrameRate = 30; SecurityProviderCache.ValidateCurrentProvider(); string dateTimeFormat = Program.Host.Model.Global.DateTimeFormat; // TODO: Improve operation for large point lists: // Pick-up "POST"ed parameters with a "genurl" param, then cache parameters // in a memory cache and return the unique URL (a string instead of a file) // with a "download" param and unique ID associated with cached parameters. // Then extract params based on unique ID and follow normal steps... string pointIDsParam = requestParameters["PointIDs"]; string startTimeParam = requestParameters["StartTime"]; string endTimeParam = requestParameters["EndTime"]; string frameRateParam = requestParameters["FrameRate"]; string alignTimestampsParam = requestParameters["AlignTimestamps"]; string missingAsNaNParam = requestParameters["MissingAsNaN"]; string fillMissingTimestampsParam = requestParameters["FillMissingTimestamps"]; string instanceName = requestParameters["InstanceName"]; ulong[] pointIDs; string headers; if (string.IsNullOrEmpty(pointIDsParam)) { throw new ArgumentNullException("PointIDs", "Cannot export data: no values were provided in \"PointIDs\" parameter."); } try { pointIDs = pointIDsParam.Split(',').Select(ulong.Parse).ToArray(); Array.Sort(pointIDs); } catch (Exception ex) { throw new ArgumentNullException("PointIDs", $"Cannot export data: failed to parse \"PointIDs\" parameter value \"{pointIDsParam}\": {ex.Message}"); } if (string.IsNullOrEmpty(startTimeParam)) { throw new ArgumentNullException("StartTime", "Cannot export data: no \"StartTime\" parameter value was specified."); } if (string.IsNullOrEmpty(pointIDsParam)) { throw new ArgumentNullException("EndTime", "Cannot export data: no \"EndTime\" parameter value was specified."); } DateTime startTime, endTime; try { startTime = DateTime.ParseExact(startTimeParam, dateTimeFormat, null, DateTimeStyles.AdjustToUniversal); } catch (Exception ex) { throw new ArgumentException($"Cannot export data: failed to parse \"StartTime\" parameter value \"{startTimeParam}\". Expected format is \"{dateTimeFormat}\". Error message: {ex.Message}", "StartTime", ex); } try { endTime = DateTime.ParseExact(endTimeParam, dateTimeFormat, null, DateTimeStyles.AdjustToUniversal); } catch (Exception ex) { throw new ArgumentException($"Cannot export data: failed to parse \"EndTime\" parameter value \"{endTimeParam}\". Expected format is \"{dateTimeFormat}\". Error message: {ex.Message}", "EndTime", ex); } if (startTime > endTime) { throw new ArgumentOutOfRangeException("StartTime", "Cannot export data: start time exceeds end time."); } using (DataContext dataContext = new DataContext()) { // Validate current user has access to requested data if (!dataContext.UserIsInRole(s_minimumRequiredRoles)) { throw new SecurityException($"Cannot export data: access is denied for user \"{Thread.CurrentPrincipal.Identity?.Name ?? "Undefined"}\", minimum required roles = {s_minimumRequiredRoles.ToDelimitedString(", ")}."); } headers = GetHeaders(dataContext, pointIDs.Select(id => (int)id)); } int frameRate; if (!int.TryParse(frameRateParam, out frameRate)) { frameRate = DefaultFrameRate; } bool alignTimestamps = alignTimestampsParam?.ParseBoolean() ?? true; bool missingAsNaN = missingAsNaNParam?.ParseBoolean() ?? true; bool fillMissingTimestamps = alignTimestamps && (fillMissingTimestampsParam?.ParseBoolean() ?? false); if (string.IsNullOrEmpty(instanceName)) { instanceName = TrendValueAPI.DefaultInstanceName; } LocalOutputAdapter adapter; LocalOutputAdapter.Instances.TryGetValue(instanceName, out adapter); HistorianServer serverInstance = adapter?.Server; if ((object)serverInstance == null) { throw new InvalidOperationException($"Cannot export data: failed to access internal historian server instance \"{instanceName}\"."); } const int TargetBufferSize = 524288; StringBuilder readBuffer = new StringBuilder(TargetBufferSize * 2); ManualResetEventSlim bufferReady = new ManualResetEventSlim(false); List <string> writeBuffer = new List <string>(); object writeBufferLock = new object(); bool readComplete = false; Task readTask = Task.Factory.StartNew(() => { try { using (SnapClient connection = SnapClient.Connect(serverInstance.Host)) { Dictionary <ulong, int> pointIDIndex = new Dictionary <ulong, int>(pointIDs.Length); float[] values = new float[pointIDs.Length]; for (int i = 0; i < pointIDs.Length; i++) { pointIDIndex.Add(pointIDs[i], i); } for (int i = 0; i < values.Length; i++) { values[i] = float.NaN; } Ticks[] subseconds = Ticks.SubsecondDistribution(frameRate); ulong interval = (ulong)(subseconds.Length > 1 ? subseconds[1].Value : Ticks.PerSecond); ulong lastTimestamp = 0; // Write data pages SeekFilterBase <HistorianKey> timeFilter = TimestampSeekFilter.CreateFromRange <HistorianKey>(startTime, endTime); MatchFilterBase <HistorianKey, HistorianValue> pointFilter = PointIdMatchFilter.CreateFromList <HistorianKey, HistorianValue>(pointIDs); HistorianKey historianKey = new HistorianKey(); HistorianValue historianValue = new HistorianValue(); // Write row values function Action bufferValues = () => { readBuffer.Append(missingAsNaN ? string.Join(",", values) : string.Join(",", values.Select(val => float.IsNaN(val) ? "" : $"{val}"))); if (readBuffer.Length < TargetBufferSize) { return; } lock (writeBufferLock) writeBuffer.Add(readBuffer.ToString()); readBuffer.Clear(); bufferReady.Set(); }; using (ClientDatabaseBase <HistorianKey, HistorianValue> database = connection.GetDatabase <HistorianKey, HistorianValue>(instanceName)) { // Start stream reader for the provided time window and selected points TreeStream <HistorianKey, HistorianValue> stream = database.Read(SortedTreeEngineReaderOptions.Default, timeFilter, pointFilter); ulong timestamp = 0; while (stream.Read(historianKey, historianValue) && !cancellationToken.IsCancellationRequested) { if (alignTimestamps) { timestamp = (ulong)Ticks.RoundToSubsecondDistribution((long)historianKey.Timestamp, frameRate).Value; } else { timestamp = historianKey.Timestamp; } // Start a new row for each encountered new timestamp if (timestamp != lastTimestamp) { if (lastTimestamp > 0) { bufferValues(); } for (int i = 0; i < values.Length; i++) { values[i] = float.NaN; } if (fillMissingTimestamps && lastTimestamp > 0 && timestamp > lastTimestamp) { ulong difference = timestamp - lastTimestamp; if (difference > interval) { ulong interpolated = lastTimestamp; for (ulong i = 1; i < difference / interval; i++) { interpolated = (ulong)Ticks.RoundToSubsecondDistribution((long)(interpolated + interval), frameRate).Value; readBuffer.Append($"{Environment.NewLine}{new DateTime((long)interpolated, DateTimeKind.Utc).ToString(dateTimeFormat)},"); bufferValues(); } } } readBuffer.Append($"{Environment.NewLine}{new DateTime((long)timestamp, DateTimeKind.Utc).ToString(dateTimeFormat)},"); lastTimestamp = timestamp; } // Save value to its column values[pointIDIndex[historianKey.PointID]] = historianValue.AsSingle; } if (timestamp > 0) { bufferValues(); } if (readBuffer.Length > 0) { lock (writeBufferLock) writeBuffer.Add(readBuffer.ToString()); } } } } finally { readComplete = true; bufferReady.Set(); } }, cancellationToken); Task writeTask = Task.Factory.StartNew(() => { using (StreamWriter writer = new StreamWriter(responseStream)) { //Ticks exportStart = DateTime.UtcNow.Ticks; string[] localBuffer; // Write column headers writer.Write(headers); while ((writeBuffer.Count > 0 || !readComplete) && !cancellationToken.IsCancellationRequested) { bufferReady.Wait(cancellationToken); bufferReady.Reset(); lock (writeBufferLock) { localBuffer = writeBuffer.ToArray(); writeBuffer.Clear(); } foreach (string buffer in localBuffer) { writer.Write(buffer); } } // Flush stream writer.Flush(); //Debug.WriteLine("Export time: " + (DateTime.UtcNow.Ticks - exportStart).ToElapsedTimeString(3)); } }, cancellationToken); await readTask; await writeTask; }
private void CopyModelAsCsvToStream(NameValueCollection requestParameters, Stream responseStream, Action flushResponse, CompatibleCancellationToken cancellationToken) { SecurityProviderCache.ValidateCurrentProvider(); string modelName = requestParameters["ModelName"]; string hubName = requestParameters["HubName"]; string connectionID = requestParameters["ConnectionID"]; string filterText = requestParameters["FilterText"]; string sortField = requestParameters["SortField"]; bool sortAscending = requestParameters["SortAscending"].ParseBoolean(); bool showDeleted = requestParameters["ShowDeleted"].ParseBoolean(); string[] parentKeys = requestParameters["ParentKeys"].Split(','); if (string.IsNullOrEmpty(modelName)) { throw new ArgumentNullException(nameof(modelName), "Cannot download CSV data: no model type name was specified."); } if (string.IsNullOrEmpty(hubName)) { throw new ArgumentNullException(nameof(hubName), "Cannot download CSV data: no hub type name was specified."); } Type modelType = AssemblyInfo.FindType(modelName); if ((object)modelType == null) { throw new InvalidOperationException($"Cannot download CSV data: failed to find model type \"{modelName}\" in loaded assemblies."); } Type hubType = AssemblyInfo.FindType(hubName); if ((object)hubType == null) { throw new InvalidOperationException($"Cannot download CSV data: failed to find hub type \"{hubName}\" in loaded assemblies."); } IRecordOperationsHub hub; // Record operation tuple defines method name and allowed roles Tuple <string, string> queryRecordCountOperation; Tuple <string, string> queryRecordsOperation; string queryRoles; try { // Create a local record operations hub instance so that CSV export can query same record set that is visible in active hub context hub = Activator.CreateInstance(hubType) as IRecordOperationsHub; if ((object)hub == null) { throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" is not a IRecordOperationsHub, access cannot be validated."); } // Assign provided connection ID from active hub context to our local hub instance so that any session based data will be available to query functions hub.ConnectionID = connectionID; Tuple <string, string>[] recordOperations; try { // Get any authorized query roles as defined in hub records operations for modeled table, default to read allowed for query recordOperations = hub.RecordOperationsCache.GetRecordOperations(modelType); if ((object)recordOperations == null) { throw new NullReferenceException(); } } catch (KeyNotFoundException ex) { throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" does not define record operations for \"{modelName}\", access cannot be validated.", ex); } // Get record operation for querying record count queryRecordCountOperation = recordOperations[(int)RecordOperation.QueryRecordCount]; if ((object)queryRecordCountOperation == null) { throw new NullReferenceException(); } // Get record operation for querying records queryRecordsOperation = recordOperations[(int)RecordOperation.QueryRecords]; if ((object)queryRecordsOperation == null) { throw new NullReferenceException(); } // Get any defined role restrictions for record query operation - access to CSV download will based on these roles queryRoles = string.IsNullOrEmpty(queryRecordsOperation.Item1) ? "*" : queryRecordsOperation.Item2 ?? "*"; } catch (Exception ex) { throw new SecurityException($"Cannot download CSV data: failed to instantiate hub type \"{hubName}\" or access record operations, access cannot be validated.", ex); } DataContext dataContext = hub.DataContext; // Validate current user has access to requested data if (!dataContext.UserIsInRole(queryRoles)) { throw new SecurityException($"Cannot download CSV data: access is denied for user \"{Thread.CurrentPrincipal.Identity?.Name ?? "Undefined"}\", minimum required roles = {queryRoles.ToDelimitedString(", ")}."); } const int TargetBufferSize = 524288; StringBuilder readBuffer = new StringBuilder(TargetBufferSize * 2); ManualResetEventSlim bufferReady = new ManualResetEventSlim(false); List <string> writeBuffer = new List <string>(); object writeBufferLock = new object(); bool readComplete = false; ITableOperations table; string[] fieldNames; bool hasDeletedField; table = dataContext.Table(modelType); fieldNames = table.GetFieldNames(false); hasDeletedField = !string.IsNullOrEmpty(dataContext.GetIsDeletedFlag(modelType)); Task readTask = Task.Factory.StartNew(() => { try { const int PageSize = 250; // Get query operation methods MethodInfo queryRecordCount = hubType.GetMethod(queryRecordCountOperation.Item1); MethodInfo queryRecords = hubType.GetMethod(queryRecordsOperation.Item1); // Setup query parameters List <object> queryRecordCountParameters = new List <object>(); List <object> queryRecordsParameters = new List <object>(); // Add current show deleted state parameter, if model defines a show deleted field if (hasDeletedField) { queryRecordCountParameters.Add(showDeleted); } // Add any parent key restriction parameters if (parentKeys.Length > 0 && parentKeys[0].Length > 0) { queryRecordCountParameters.AddRange(parentKeys.Select((s, i) => { Type type = queryRecordCount.GetParameters()[i].ParameterType; if (type == typeof(string)) { return((object)s); } else if (type == typeof(Guid)) { return((object)Guid.Parse(s)); } return(Convert.ChangeType(s, type)); })); } // Add parameters for query records from query record count parameters - they match up to this point queryRecordsParameters.AddRange(queryRecordCountParameters); // Add sort field parameter queryRecordsParameters.Add(sortField); // Add ascending sort order parameter queryRecordsParameters.Add(sortAscending); // Track parameter index for current page to query int pageParameterIndex = queryRecordsParameters.Count; // Add page index parameter queryRecordsParameters.Add(0); // Add page size parameter queryRecordsParameters.Add(PageSize); // Add filter text parameter queryRecordCountParameters.Add(filterText); queryRecordsParameters.Add(filterText); // Read queried records in page sets so there is not a memory burden and long initial query delay on very large data sets int recordCount = (int)queryRecordCount.Invoke(hub, queryRecordCountParameters.ToArray()); int totalPages = Math.Max((int)Math.Ceiling(recordCount / (double)PageSize), 1); // Read data pages for (int page = 0; page < totalPages && !cancellationToken.IsCancelled; page++) { // Update desired page to query queryRecordsParameters[pageParameterIndex] = page + 1; // Query page records IEnumerable records = queryRecords.Invoke(hub, queryRecordsParameters.ToArray()) as IEnumerable ?? Enumerable.Empty <object>(); int exportCount = 0; // Export page records foreach (object record in records) { // Periodically check for client cancellation if (exportCount++ % (PageSize / 4) == 0 && cancellationToken.IsCancelled) { break; } readBuffer.AppendLine(string.Join(",", fieldNames.Select(fieldName => $"\"{table.GetFieldValue(record, fieldName)}\""))); if (readBuffer.Length < TargetBufferSize) { continue; } lock (writeBufferLock) writeBuffer.Add(readBuffer.ToString()); readBuffer.Clear(); bufferReady.Set(); } } if (readBuffer.Length > 0) { lock (writeBufferLock) writeBuffer.Add(readBuffer.ToString()); } } finally { readComplete = true; bufferReady.Set(); } }, cancellationToken); Task writeTask = Task.Factory.StartNew(() => { using (StreamWriter writer = new StreamWriter(responseStream)) { //Ticks exportStart = DateTime.UtcNow.Ticks; string[] localBuffer; Action flushStream = () => { writer.Flush(); if ((object)flushResponse != null) { flushResponse(); } }; // Write column headers writer.WriteLine(string.Join(",", fieldNames.Select(fieldName => $"\"{fieldName}\""))); flushStream(); while ((writeBuffer.Count > 0 || !readComplete) && !cancellationToken.IsCancelled) { bufferReady.Wait(cancellationToken); bufferReady.Reset(); lock (writeBufferLock) { localBuffer = writeBuffer.ToArray(); writeBuffer.Clear(); } foreach (string buffer in localBuffer) { writer.Write(buffer); } } // Flush stream flushStream(); //Debug.WriteLine("Export time: " + (DateTime.UtcNow.Ticks - exportStart).ToElapsedTimeString(3)); } }, cancellationToken); Task.WaitAll(readTask, writeTask); }
private void SecureWindow_Initialized(object sender, EventArgs e) { // Don't proceed if the window is opened in design mode if (DesignerProperties.GetIsInDesignMode(this)) { return; } // Check if the resource is excluded from being secured string resource = GetResourceName(); if (ResourceAccessiblity != ResourceAccessiblityMode.AlwaysIncluded && (ResourceAccessiblity == ResourceAccessiblityMode.AlwaysExcluded || !SecurityProviderUtility.IsResourceSecurable(resource))) { return; } try { // Setup the security provider for role-based security ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(WindowsIdentity.GetCurrent().Name); securityProvider.PassthroughPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); securityProvider.Authenticate(); SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); SecurityPrincipal = new SecurityPrincipal(securityIdentity); } catch (Exception ex) { ShowSecurityDialog(DisplayType.AccessDenied, "Error loading security provider: " + ex.Message); return; } // Verify that the security principal has been authenticated if (!SecurityPrincipal.Identity.IsAuthenticated || ForceLoginDisplay) { ISecurityProvider securityProvider = SecurityPrincipal.Identity.Provider; // See if user's password has expired if (securityProvider.UserData.IsDefined && securityProvider.UserData.PasswordChangeDateTime <= DateTime.UtcNow) { ShowSecurityDialog(DisplayType.ChangePassword, string.Format("Your password has expired. {0} You must change your password to continue.", securityProvider.AuthenticationFailureReason)); } else { ShowSecurityDialog(DisplayType.Login); } } // Perform a top-level permission check on the resource being accessed if (!string.IsNullOrEmpty(resource)) { // Stay in a dialog display loop until either access to resource is available or user exits while (!m_shutdownRequested && !IsResourceAccessible(resource)) { // Access to resource is denied ShowSecurityDialog(DisplayType.AccessDenied); } } }
public void Configuration(IAppBuilder app) { app.Use((context, next) => { context.Response.Headers.Remove("Server"); return(next.Invoke()); }); app.UseStageMarker(PipelineStage.PostAcquireState); // Modify the JSON serializer to serialize dates as UTC - otherwise, timezone will not be appended // to date strings and browsers will select whatever timezone suits them JsonSerializerSettings settings = JsonUtility.CreateDefaultSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; JsonSerializer serializer = JsonSerializer.Create(settings); GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer); // Load security hub in application domain before establishing SignalR hub configuration using (new SecurityHub()) { } // Enable GSF role-based security authentication w/o Logon Page // Configuration Windows Authentication for self-hosted web service HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"]; listener.AuthenticationSchemeSelectorDelegate = AuthenticationSchemeForClient; app.Use((context, next) => { string username = context.Request.User?.Identity.Name; if ((object)username == null) { return(null); } // Get the principal used for verifying the user's pass-through authentication IPrincipal passthroughPrincipal = context.Request.User; // Create the security provider that will verify the user's pass-through authentication ISecurityProvider securityProvider = SecurityProviderCache.CreateProvider(username, passthroughPrincipal, false); securityProvider.Authenticate(); // Return the security principal that will be used for role-based authorization SecurityIdentity securityIdentity = new SecurityIdentity(securityProvider); context.Request.User = new SecurityPrincipal(securityIdentity); return(next.Invoke()); }); HubConfiguration hubConfig = new HubConfiguration(); HttpConfiguration httpConfig = new HttpConfiguration(); // Enabled detailed client errors hubConfig.EnableDetailedErrors = true; // Enable GSF session management //httpConfig.EnableSessions(AuthenticationOptions); // Enable GSF role-based security authentication with Logon Page //app.UseAuthentication(AuthenticationOptions); string allowedDomainList = ConfigurationFile.Current.Settings["systemSettings"]["AllowedDomainList"]?.Value; if (allowedDomainList == "*") { app.UseCors(CorsOptions.AllowAll); } else if ((object)allowedDomainList != null) { httpConfig.EnableCors(new System.Web.Http.Cors.EnableCorsAttribute(allowedDomainList, "*", "*")); } // Load ServiceHub SignalR class app.MapSignalR(hubConfig); // Set configuration to use reflection to setup routes httpConfig.MapHttpAttributeRoutes(new CustomDirectRouteProvider()); // Load the WebPageController class and assign its routes app.UseWebApi(httpConfig); // Setup resolver for web page controller instances app.UseWebPageController(WebServer.Default, Program.Host.DefaultWebPage, Program.Host.Model, typeof(AppModel) /*, AuthenticationOptions*/); httpConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; // Check for configuration issues before first request httpConfig.EnsureInitialized(); }
/// <summary> /// Returns information about the current user. /// </summary> /// <returns>An <see cref="UserData"/> object of the user if user's security context has been initialized, otherwise null.</returns> public UserData GetUserData() { SecurityProviderCache.ValidateCurrentProvider(); return(SecurityProviderCache.CurrentProvider.UserData); }
private async Task CopyModelAsCsvToStreamAsync(NameValueCollection requestParameters, Stream responseStream, Func <bool> isCancelled, Func <Task> flushResponseAsync = null) { SecurityProviderCache.ValidateCurrentProvider(); string modelName = requestParameters["ModelName"]; string hubName = requestParameters["HubName"]; string filterText = requestParameters["FilterText"]; string sortField = requestParameters["SortField"]; bool sortAscending = requestParameters["SortAscending"].ParseBoolean(); bool showDeleted = requestParameters["ShowDeleted"].ParseBoolean(); string[] parentKeys = requestParameters["ParentKeys"].Split(','); const int PageSize = 250; if (string.IsNullOrEmpty(modelName)) { throw new ArgumentNullException(nameof(modelName), "Cannot download CSV data: no model type name was specified."); } if (string.IsNullOrEmpty(hubName)) { throw new ArgumentNullException(nameof(hubName), "Cannot download CSV data: no hub type name was specified."); } Type modelType = AssemblyInfo.FindType(modelName); if ((object)modelType == null) { throw new InvalidOperationException($"Cannot download CSV data: failed to find model type \"{modelName}\" in loaded assemblies."); } Type hubType = AssemblyInfo.FindType(hubName); if ((object)hubType == null) { throw new InvalidOperationException($"Cannot download CSV data: failed to find hub type \"{hubName}\" in loaded assemblies."); } IRecordOperationsHub hub; // Record operation tuple defines method name and allowed roles Tuple <string, string> queryRecordCountOperation; Tuple <string, string> queryRecordsOperation; string queryRoles; try { hub = Activator.CreateInstance(hubType) as IRecordOperationsHub; if ((object)hub == null) { throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" is not a IRecordOperationsHub, access cannot be validated."); } Tuple <string, string>[] recordOperations; try { // Get any authorized query roles as defined in hub records operations for modeled table, default to read allowed for query recordOperations = hub.RecordOperationsCache.GetRecordOperations(modelType); if ((object)recordOperations == null) { throw new NullReferenceException(); } } catch (KeyNotFoundException ex) { throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" does not define record operations for \"{modelName}\", access cannot be validated.", ex); } // Get record operation for querying record count queryRecordCountOperation = recordOperations[(int)RecordOperation.QueryRecordCount]; if ((object)queryRecordCountOperation == null) { throw new NullReferenceException(); } // Get record operation for querying records queryRecordsOperation = recordOperations[(int)RecordOperation.QueryRecords]; if ((object)queryRecordsOperation == null) { throw new NullReferenceException(); } // Get any defined role restrictions for record query operation - access to CSV download will based on these roles queryRoles = string.IsNullOrEmpty(queryRecordsOperation.Item1) ? "*" : queryRecordsOperation.Item2 ?? "*"; } catch (Exception ex) { throw new SecurityException($"Cannot download CSV data: failed to instantiate hub type \"{hubName}\" or access record operations, access cannot be validated.", ex); } using (DataContext dataContext = new DataContext()) using (StreamWriter writer = new StreamWriter(responseStream)) { // Validate current user has access to requested data if (!dataContext.UserIsInRole(queryRoles)) { throw new SecurityException($"Cannot download CSV data: access is denied for user \"{Thread.CurrentPrincipal.Identity?.Name ?? "Undefined"}\", minimum required roles = {queryRoles.ToDelimitedString(", ")}."); } AdoDataConnection connection = dataContext.Connection; ITableOperations table = dataContext.Table(modelType); string[] fieldNames = table.GetFieldNames(false); Func <Task> flushAsync = async() => { // ReSharper disable once AccessToDisposedClosure await writer.FlushAsync(); if ((object)flushResponseAsync != null) { await flushResponseAsync(); } }; // Write column headers await writer.WriteLineAsync(string.Join(",", fieldNames.Select(fieldName => connection.EscapeIdentifier(fieldName, true)))); await flushAsync(); // See if modeled table has a flag field that represents a deleted row bool hasDeletedField = !string.IsNullOrEmpty(dataContext.GetIsDeletedFlag(modelType)); // Get query operation methods MethodInfo queryRecordCount = hubType.GetMethod(queryRecordCountOperation.Item1); MethodInfo queryRecords = hubType.GetMethod(queryRecordsOperation.Item1); // Setup query parameters List <object> queryRecordCountParameters = new List <object>(); List <object> queryRecordsParameters = new List <object>(); // Add current show deleted state parameter, if model defines a show deleted field if (hasDeletedField) { queryRecordCountParameters.Add(showDeleted); } // Add any parent key restriction parameters if (parentKeys.Length > 0 && parentKeys[0].Length > 0) { queryRecordCountParameters.AddRange(parentKeys); } // Add parameters for query records from query record count parameters - they match up to this point queryRecordsParameters.AddRange(queryRecordCountParameters); // Add sort field parameter queryRecordsParameters.Add(sortField); // Add ascending sort order parameter queryRecordsParameters.Add(sortAscending); // Track parameter index for current page to query int pageParameterIndex = queryRecordsParameters.Count; // Add page index parameter queryRecordsParameters.Add(0); // Add page size parameter queryRecordsParameters.Add(PageSize); // Add filter text parameter queryRecordCountParameters.Add(filterText); queryRecordsParameters.Add(filterText); // Read queried records in page sets so there is not a memory burden and long initial query delay on very large data sets int recordCount = (int)queryRecordCount.Invoke(hub, queryRecordCountParameters.ToArray()); int totalPages = Math.Max((int)Math.Ceiling(recordCount / (double)PageSize), 1); // Write data pages for (int page = 0; page < totalPages && !isCancelled(); page++) { // Update desired page to query queryRecordsParameters[pageParameterIndex] = page + 1; // Query page records IEnumerable records = queryRecords.Invoke(hub, queryRecordsParameters.ToArray()) as IEnumerable ?? Enumerable.Empty <object>(); int exportCount = 0; // Export page records foreach (object record in records) { // Periodically check for client cancellation if (exportCount++ % (PageSize / 4) == 0 && isCancelled()) { break; } await writer.WriteLineAsync(string.Join(",", fieldNames.Select(fieldName => $"\"{table.GetFieldValue(record, fieldName)}\""))); } await flushAsync(); } } }