Base solution for your next web application

Activities of "sms.environmental"

Hi,

We are planning to introduce parent permissions into our existing permissions tree. Our goal is to implement this change without impacting our existing clients. When adding a parent permission to an existing permission, how can we ensure that the system automatically selects the parent permission for users/roles that already have the child permission selected?

(please see the "Organization" permission below remains unselected although one of the child permissions is selected)

Hi,

Thank you for confirming that and providing a link - we figured out that must be the problem as we realised we only experienced the issue we described while the app scaled up to 4 instances and not at all while running 1 instance.

I guess we'll find out more from that link but is everything we need to implement and use a Distributed Cache built-into the framework and/or Azure hosting?

Hi,

Thanks for your your response. During the hours we experiencing these issues the app is scaled out to 4 different instances. Could this be related?

Hi ismcagdas,

This is our the API method that our Role Switching feature calls from Client Mode and Exit Client Mode buttons in the front-end (it is also called where necessary during login to automatically exit Client Mode for users when they have had to re-enter login credentials):

        public async Task<long> SwitchUserRole(bool isClientRole, long userId)
        {
            User user = UserManager.Users.Include(x => x.Roles).Where(x => x.Id == userId).FirstOrDefault();
            if (isClientRole)
            {
                // save current roles and switch to Client role only (direclty-assigned user permissions are not added/removed)
                foreach (var item in user.Roles)
                {
                    var userRoleHistory = new UserRoleSwitchHistory();
                    userRoleHistory.UserId = AbpSession.UserId;
                    userRoleHistory.PreviousRoleId = item.RoleId;
                    await _userRoleSwitchHistory.InsertAndGetIdAsync(userRoleHistory);
                }
                string[] roles = { "Client" };
                await UserManager.SetRolesAsync(user, roles);
                user.IsSwitchedToClientRoleForViewing = true;

                CheckErrors(await UserManager.UpdateAsync(user));

                await LogClientAdminModePermissions(true, false, "SwitchUserRole - 1 - AFTER setting client user role");

                // NB removed for moment as may not be needed
                //// NOD-2141 NB this should cause the user info cookie to be re-ussed to the browser updating the permissions information
                //await _signInManager.SignOutAsync();
                //await _signInManager.SignInAsync(user, false);

                //// NOD-2141 Added to clear cached user permissions
                await ForceRefresh();

                await LogClientAdminModePermissions(true, false, "SwitchUserRole - 2 - AFTER ForceRefresh() call");

                // ensure some cached user information affecting SessionAppService.IsClient/IsContract etc. is correctly updated for the revised role(s) representing Client mode i.e. ensures IsClient returns true
                // NB we're not doing anything with the results in output - just calling the function rebuilds the cached user information
                var output = await _sessionAppService.GetCurrentLoginInformations();

                return user.Id;
            }
            else
            {
                // remove current role(s) (probably Client one) and restore previous stored roles (direclty-assigned user permissions are not added/removed)
                var usersRole = _userRoleSwitchHistory.GetAll().Where(x=>x.UserId==user.Id).ToList();
                var roles=  _roleManager.Roles.Where(x => usersRole.Select(xx => xx.PreviousRoleId).Contains(x.Id)).Select(x=>x.Name);
                await UserManager.SetRolesAsync(user, roles.ToArray());
                await _userRoleSwitchHistory.HardDeleteAsync(x => x.UserId == user.Id);

                // ensure some cached user information affecting SessionAppService.IsClient/IsContract is correctly updated for the restored previous role(s)
                // NB we're not doing anything with the results in output - just calling the function rebuilds the cached user information
                var output = await _sessionAppService.GetCurrentLoginInformations();
                user.IsSwitchedToClientRoleForViewing = false;

                CheckErrors(await UserManager.UpdateAsync(user));

                await LogClientAdminModePermissions(false, true, "SwitchUserRole - 1 - AFTER restoring previous user roles");

                // NB removed for moment as may not be needed
                //// NOD-2141 NB this should cause the user info cookie to be re-ussed to the browser updating the permissions information
                //await _signInManager.SignOutAsync();
                //await _signInManager.SignInAsync(user, false);

                //// NOD-2141 Added to clear cached user permissions
                await ForceRefresh();

                await LogClientAdminModePermissions(false, true, "SwitchUserRole - 2 - AFTER ForceRefresh() call");

                return user.Id;
            }
        }

And this is the ForceRefresh() method we tried to clear the relevant parts of the cache in an attempt to try and get the cached permissions in the front-end to refresh:

// NOD 2141 - Added based on https://stackoverflow.com/questions/49769936/refresh-permissions-from-in-memory-cache-with-asp-net-boilerplate
        //          - Intended for use in SwitchUserRole() above to force a refresh of cached used permissions in the back-end
        //            and hopefully in the front-end too
        private async Task <bool> ForceRefresh()
        {
            // Clear the user permission cache to force a refresh of it's contents when next used
            await _cacheManager.GetUserPermissionCache().ClearAsync();

            // lets try this too
            await _cacheManager.GetRolePermissionCache().ClearAsync();

            return true;
        }

And this is just some just some custom logging to Logs.txt that we added to try and understand what is happening in the back-end role switching on Azure:

        // NOD 2141 - Added for logging permission checks so we can confirm that they are as expected in the back-end
        //            in dev environment or on Azure irrespective of what is happenning in the front-end
        private async Task <bool> LogClientAdminModePermissions(bool clientMode, bool adminMode, string title)
        {
            if (clientMode)
            {
                Logger.Info(String.Empty);
                Logger.Info($"CLIENT MODE - {title}");

                bool b1 = await PermissionChecker.IsGrantedAsync(AppPermissions.Data_AlwaysRestrictContractsAndBuildings);
                bool b2 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Administration);
                bool b3 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Settings_General);
                bool b4 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_SalesInvoicing);

                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Data_AlwaysRestrictContractsAndBuildings) = {b1}  (TRUE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Administration) = {b2}  (FALSE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Settings_General) = {b3}  (FALSE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_SalesInvoicing) = {b4}  (FALSE expected)");

                Logger.Info(String.Empty);
            }

            if (adminMode)
            {
                Logger.Info(String.Empty);
                Logger.Info($"ADMIN MODE - {title}");

                bool b1 = await PermissionChecker.IsGrantedAsync(AppPermissions.Data_AlwaysRestrictContractsAndBuildings);
                bool b2 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Administration);
                bool b3 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Settings_General);
                bool b4 = await PermissionChecker.IsGrantedAsync(AppPermissions.Pages_SalesInvoicing);

                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Data_AlwaysRestrictContractsAndBuildings) = {b1}  (FALSE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Administration) = {b2}  (TRUE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_Settings_General) = {b3}  (TRUE expected)");
                Logger.Info($"PermissionChecker.IsGrantedAsync(AppPermissions.Pages_SalesInvoicing) = {b4}  (TRUE expected)");

                Logger.Info(String.Empty);
            }

            return true;
        }

So above we are just logging some checks on a few permissions (that we expect to change with role changes) a couple of times for each role switch/toggle - once after swapping the roles and again after forcing cache refreshes.

Please let us know if you need any more information - we are still experiencing the same behaviour where the permission changes are picked up instantly by the browser when running in the development-environment but when running from Azure the permission changes seem to be cached in the browser and don't get picked until after a few menu clicks and/or page reloads. NB the role switch buttons in the front-end do navigate to the home/dashboard page in he. application after a role switch

Hi,

We are encountering an unusual issue with our production application hosted on Azure App Service. Our application includes a functionality where administrative users can toggle between 'Admin' and 'Client' modes. This feature is designed to switch user roles: 'Admin', with full permissions, and 'Client', with restricted permissions. The process involves temporarily replacing the 'Admin' role with the 'Client' role for the user, and vice versa. To facilitate this, we maintain a snapshot of the user's original roles, ensuring they can revert back seamlessly.

However, in the production environment, we've observed inconsistent behavior when switching from 'Client' back to 'Admin' mode. Specifically, the full set of 'Admin' permissions and menu options do not always appear immediately. Even after refreshing the browser, the application sometimes continues to display only 'Client' menu options. After several refresh attempts, the correct 'Admin' options appear, but the issue can recur unpredictably. Additionally, we've encountered scenarios where, upon switching to 'Admin' mode, navigating to an admin-only page results in an error message about insufficient permissions, despite the page loading in the background.

This erratic behavior seems to suggest a potential caching issue, but it's perplexing as this problem does not occur in our local environment. We've tried executing _cacheManager.GetUserPermissionCache().Clear(); when we switch between the modes but this didn't help. We also tried _signInManager.SignInAsync(user, false); to force the user information cookie to be re-issued to the browser but that did not change anything either.

We don't use Redis.

Do you have any suggestions on what could be causing this?

Hi ismcagdas,

I hope you had a good weekend. We host the aspnet core app and the Angular app under the same website on IIS. To deploy the application we have followed the instructions outlined on your website. Please note that we have been running older version of the Zero project for a few years and we didn't have such a problem with it.

Arranging a meeting would be greatly appriciated. We are available any day this week. Just let us know what day and time works best for you.

Thanks, SMS Environmental Dev Team

Hi ismcagdas,

Thank you for offering to look further into this.

The public URL of the app is https://opuz.co.uk/

Thanks, SMS Environmental Dev Team

Hi ismcagdas,

Thank you for your reply. We have tried copying this web.config file and using just one rewrite rule however we get the same result. It goes intoa an infinite redirect loop. index.html redirects to index.html. If we remove this rule the app runs fine but when you refresh it goes to 404

We haven't changed anything on the base project. The Angular routing is just like it's in the base ASPNET Zero repository. Is there anything else you would recommoned we should try ?

Thanks, SMS Environmental Dev Team

Hi @ismcagdas,

Thank you for your response. We are following the deployment documnation available on your website. We are trying to overcome the Angular refresh issue. This is what the documentation on your website suggests: "If you refresh a page (F5) then IIS will handle the request and will not find the requested path and returns a HTTP 404 error. We should configure IIS to redirect all requests to the index.html page (or, to the root path). At the same time, IIS needs to install the URL Rewrite module, please refer to https://www.iis.net/downloads/microsoft/url-rewrite"

We tried using the rules available in the web.config file in the client app folder. You can see the result from the screenshot above. We also tried redirecting to index.html but that didn't work too.

We look forward to hearing from you.

Thanks, SMS Env Dev team

  • What is your product version? - 10.0.0
  • What is your product type (Angular or MVC)? ASP.NET Core Backend & Angular UI
  • What is product framework type (.net framework or .net core)? .net core

Hi ASP Zero team,

We applied the required URL rewrite rules (please see the screenshot) as outlined in the documentation however we end up in an infinite redirect loop.

Would you be able to advice on this please?

Many thanks, SMS Environmental Dev Team

Showing 1 to 10 of 10 entries