Base solution for your next web application

Activities of "hra"

Prerequisites

  • 10.0.0 (20210301)
  • Angular
  • .Net Core

I have integrated the DevExpress report server, and have found that my repository which supplies the data to the report, is not applying the ISoftDelete filter when a debugger is not attached.

Various scenarios:

  1. My machine, IIS Express, with debugger - ISoftDelete works
  2. My machine, IIS Express without debugger - ISoftDelete works
  3. Azure, with debugger - ISoftDelete works
  4. Azure, debugger NOT attached, ISoftDelete does not work - all entities are returned

Prerequisites

Asp.NET Core/Angular

I am adding a new area to my site which will use the PrimNG Table component. I have it working correctly, however, it is not styled the way it should be for a PrimeNG component.

I have taken the code sample from here: https://stackblitz.com/edit/primeng-tableedit-demo?file=angular.json

And it should look like this:

But instead it looks like this:

note how the primeicons font buttons have not loaded

What could I be missing for the styles to be picked up correctly?

Prerequisites

AspNetZero .NET Core/Angular 10.2

I have added numerous new tables to my schema, which must be initialized for each new tenant created.

Do you have a recommended hooking process/convention for "initializing" default state for a new tenant?

I have scanned the documentation, and the best I could find, was reference to seeding tables during migration - which doesnt address seeding/initializing when a host admin clicks "new tenant".

Thanks,

Prerequisites

10.0.0 (20210301) Angular .Net Core Multitenancy

I am receiving reports from my super users (those who have access to impersonate tenant accounts) that sometimes they lose their access to administrative menu pages, such as the "Tenants" and "Maintenance" menus. The menus disappears from the navigation tree, and even if the user types the administrative page address directly into the address bar, they are bounced as if they do not have permissions. I have traced this back to, when the user is impersonating a tenant account, the server application has been recycled. The user then "returns to my account", and they are still logged in, but they have lost their claims to administrative permissions. There is no way for the user to fix this - no amount of logging in and out, clearing cookies, etc, will fix it.

The only fix I have found, is to ask the user to log out, then recycle the application pool again - as you can imagine, this just raises the risk of it happening to another user.

Please advise,

thanks and regards,

I have found that while IMustHaveTenant ensures that newly inserted entities are stored against the active tenant, the same behavior is not applied to modified entities.

While I accept that it's possible to have a scenario where by one would want to move an entity from one tenant to another, I don't believe it's wise for this to be the default behavior.

Currently, if you instantiate an entity (IMustHaveTenant), and store it - it will receive the active tenant ID. If you then load that entity, change its tenant id (to that of a valid tenant), it will be moved to that tenant.

I feel there should be a filter in place, to mark the TenantId property as "unmodified" such that the EF context never updates it. It should then be the developer's responsibility to disable this filter if they want to move an entity between tenants.

This is the safer default behavior.

I discovered this, because I had a bad AutoMapper configuration, which reset the TenantId of an entity to 0 during an update, and it moved the entity to the host tenant.

The customer though he had lost data, fortunately it was possible to just move it back to the correct tenant, however it highlighted this security concern.

In the interim, my guard is implemented on my AbpDbContext as follows:

protected override void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, long? userId, EntityChangeReport changeReport)
{
    // prevent changes to TenantId
    if (entry.Entity is IMustHaveTenant || entry.Entity is IMayHaveTenant)
    {
        var tenantProperty = entry.Property(nameof(IMustHaveTenant.TenantId));
        if (tenantProperty.IsModified)
        {
            tenantProperty.IsModified = false;
            object[] keyParts = entry.Metadata.FindPrimaryKey()
             .Properties
             .Select(p => entry.Property(p.Name).CurrentValue)
             .ToArray();
            var keyAsString = String.Join(", ", Array.ConvertAll(keyParts, x => $"'{x}'"));
            this.Logger.Warn($"Blocked attempted change to {entry.Metadata.ClrType.Name}.{tenantProperty.Metadata.Name} for Key {keyAsString}, from {tenantProperty.OriginalValue} to {tenantProperty.CurrentValue}");
        }
    }

    base.ApplyAbpConceptsForModifiedEntity(entry, userId, changeReport);
}

While this is not strictly a bug, its an intersection of functionalities where the expected behavior has not been explicitly designed in a desireable way.

We have a set of permissions, some of which are dependent on specific features.

When disabling the feature for a tenant, all users in the tenant lose the permissions which depend on that feaure (this is expected). When enabling the feature for a tenant, none of the users have those permissions restored - this is the bit which is not clearly defined.

From a users perspective, if the permissions were granted through the assignment of a role (rather than explicit per-user grants) - one would expect the permissions to be restored.

Has the AspNetZero team considered and defined this behavior? If not, can we look at clearly defining this? What would be the best way to implement the behavior I defined above (where permission assignments received through roles are reassessed when necessary)?

Root cause is that when editing permissions for a user, hidden permissions (such as ones that the user cannot see because a missing feature is hiding them) will actually create a "isGranted = 0" record for those hidden permissions. Then, when switching ON the feature that will make that permission possible, it is initially explicitly disabled because of that "isGranted = 0" record.

Hi,

I need to include a custom '.well-known' folder in the root of my angular build output. My project looks like this (see screenshot) - note how I have a '.well-known' folder in my src folder.

I need this folder to be addressable via the url https://myangularsite/.well-known/

What script do I edit, and how do I edit it, to include it in the build output?

I'm setting up Azure A/D authentication integation to our AspNetZero Angular/Core application.

After reading this document: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant

I found that all I allegedly need to do is enable Microsoft (social) logins - which AspNetZero supports. https://docs.aspnetzero.com/en/aspnet-core-angular/latest/Features-Angular-Social-Logins

I did so, and the "Microsoft" button appears on the tenant login page. Great. However, after using that button to log into my Microsoft account, the AspNetZero page shows an error popup "Could not validate external user login".

I'm guesing there is some step I am missing? But I don't know what it is. Something method I'm supposed to override in the .Net Core code?

The following documentation explains what should happen if an external account is and isn't identified for EXTERNAL authentication - but I'm not sure if that should apply to Microsoft auth as your documentation identifies that as "Social" authentication. https://aspnetboilerplate.com/Pages/Documents/Zero/User-Management

Anyway, the missing knowledge is... I ASSUME that one should only be able to "log in" with a Microsoft account that was used to first CREATE an AspNetZero user account... however, when I visit the tenant "create account" page, there is no "Create with Microsoft" button...

What am I missing?

Thanks,

--- UPDATE --- I've found something in the TokenAuthController ProviderKey comparison which looks likely to be a bug... confirm? Note how the provider key in the UserInfo object differs from the provider key in the Model by only the hypens being stripped (see watch window in screenshot). Bug?

--- UPDATE 2 --- Using the inspector, I updated the ProviderKey to match such that the comparison passes. The next issue is that by the time "UserManager.FindAsync" is called, no tenant id is provided by the code - which means the the query against UserLogin doesnt find the entry that is mapped to a specific tenant. I'm guessing this should have been populated somehow by now? How is the query to find the UserLogin supposed to know which tenant to search under?

--- UPDATE 3 ---

Clearly there are issues here. Status? https://github.com/aspnetzero/aspnet-zero-core/issues/3046

I guess these are the same issues. Broken for almost 2 years? I can get accounts to be created if I tweak the ProviderKey comparison to strip hyphens - however the activate user cannot log in (error "User name XXX is already taken") because the search for the user account is performed with a null tenancyName.

Prerequisites

  • AspNetZero 10

We have enabled Microsoft authentication, and have had users report that they get an error while attempting to associate their Microsoft account with our product.

We tracked it back to the fact that some of the users do not have a "Last Name" set in their Microsoft accounts.

This is the error in AspNetZero. You can clearly see that AspNetZero demands a last name, which is inherently incompatible with Microsoft accounts.

RROR 2021-10-19 03:01:25,045 [6    ] Mvc.ExceptionHandling.AbpExceptionFilter - An error occurred while updating the entries. See the inner exception for details.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
 ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Surname', table 'cloudrisk-staging.dbo.AbpUsers'; column does not allow nulls. INSERT fails.
The statement has been terminated.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
ClientConnectionId:a272394f-be75-4a1d-ab5b-4f6838ec0bdd
Error Number:515,State:2,Class:16
ClientConnectionId before routing:e3fa7b20-81ee-4edd-9aa2-c99dfb447e78
Routing Destination:b8b26ffad02d.tr1.australiacentral1-a.worker.database.windows.net,11020
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Abp.EntityFrameworkCore.AbpDbContext.SaveChangesAsync(CancellationToken cancellationToken)
   at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext`3.SaveChangesAsync(CancellationToken cancellationToken)
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContextAsync(DbContext dbContext)
   at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync()
   at Abp.Authorization.Users.AbpUserStore`2.CreateAsync(TUser user, CancellationToken cancellationToken)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
   at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user)
   at Abp.Authorization.Users.AbpUserManager`2.CreateAsync(TUser user)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
   at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user, String password)
   at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation)
   at HRA.Portal.Authorization.Users.UserRegistrationManager.RegisterAsync(String name, String surname, String emailAddress, String userName, String plainPassword, Boolean isEmailConfirmed, String emailActivationLink) in /home/vsts/work/1/s/aspnet-core/src/HRA.Portal.Core/Authorization/Users/UserRegistrationManager.cs:line 87
   at HRA.Portal.Web.Controllers.TokenAuthController.RegisterExternalUserAsync(ExternalAuthUserInfo externalLoginInfo) in /home/vsts/work/1/s/aspnet-core/src/HRA.Portal.Web.Core/Controllers/TokenAuthController.cs:line 572
   at HRA.Portal.Web.Controllers.TokenAuthController.ExternalAuthenticate(ExternalAuthenticateModel model) in /home/vsts/work/1/s/aspnet-core/src/HRA.Portal.Web.Core/Controllers/TokenAuthController.cs:line 490
   at lambda_method2047(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeActionMethodAsync&gt;g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

Prerequisites

AspNetZero 10

We are using Microsoft Authentication, and have noticed that after a user has provisioned their account using "Sign in with Microsoft", their name appears as: "John Smith Smith" - because their first and last names are squeezed into the AbpUser.FirstName in addition to their last name being placed in Abpuser.Surname.

After recently being given a code sample, we can now see why: in MicrosoftAuthProviderApi is the following code:

            var result = new ExternalAuthUserInfo
            {
                Name = MicrosoftAccountHelper.GetDisplayName(payload),
                EmailAddress = MicrosoftAccountHelper.GetEmail(payload),
                Surname = MicrosoftAccountHelper.GetSurname(payload),
                Provider = Name,
                ProviderKey = MicrosoftAccountHelper.GetId(payload)
            };

I believe It should be

            var result = new ExternalAuthUserInfo
            {
                Name = MicrosoftAccountHelper.GetGivenName(payload),
                EmailAddress = MicrosoftAccountHelper.GetEmail(payload),
                Surname = MicrosoftAccountHelper.GetSurname(payload),
                Provider = Name,
                ProviderKey = MicrosoftAccountHelper.GetId(payload)
            };
Showing 1 to 10 of 27 entries