Example #1
0
        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);
            }
        }
Example #2
0
        // 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));
        }
Example #3
0
        /// <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);
        }
Example #4
0
 private static void CachePrincipal(Guid sessionID, SecurityPrincipal principal)
 {
     if (s_authorizationCache.TryAdd(sessionID, principal))
     {
         SecurityProviderCache.AutoRefresh(principal.Identity.Provider);
     }
 }
Example #5
0
        /// <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");
                }
            }
        }
Example #6
0
        /// <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());
                    }
                }
            }
        }
Example #7
0
        /// <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);
        }
Example #8
0
        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);
            }
        }
Example #9
0
        /// <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));
        }
Example #10
0
        /// <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));
        }
Example #11
0
        /// <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);
        }
Example #12
0
        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);
            }
        }
Example #13
0
        /// <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);
        }
Example #14
0
        /// <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)));
        }
Example #15
0
        // 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));
        }
Example #16
0
        /// <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);
                }
            }));
        }
Example #17
0
        // 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));
        }
Example #18
0
        // 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));
        }
Example #19
0
        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);
            }
        }
Example #20
0
        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");
            }
        }
Example #21
0
        /// <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();
            }
        }
Example #22
0
        /// <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();
                }
            }
        }
Example #23
0
        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;
        }
Example #24
0
        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);
        }
Example #25
0
        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);
                }
            }
        }
Example #26
0
        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();
        }
Example #27
0
        /// <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();
                    }
                }
        }