JonPSmith / AuthPermissions.AspNetCore

This library provides extra authorization and multi-tenant features to an ASP.NET Core application.
https://www.thereformedprogrammer.net/finally-a-library-that-improves-role-authorization-in-asp-net-core/
MIT License
807 stars 161 forks source link

ShardingServices - value cannot be null due to update to 5.0.1 #90

Closed roos-robert closed 1 year ago

roos-robert commented 1 year ago

Hi!

I just upgraded AuthP from 5.0.0 to 5.0.1 and now my application throws the following error

System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Any[TSource](IEnumerable1 source) at AuthPermissions.AspNetCore.ShardingServices.ShardingConnectionsJsonFile.GetDatabaseInformation() at AuthPermissions.AspNetCore.ShardingServices.ShardingConnectionsJsonFile.FormConnectionString(String databaseInfoName) at AuthPermissions.AspNetCore.GetDataKeyCode.GetShardingDataUserNormal..ctor(IHttpContextAccessor accessor, IShardingConnections connectionService) at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope) at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired) at lambda_method433(Closure, IServiceProvider, Object[]) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at AuthPermissions.SupportCode.DownStatusCode.RedirectUsersViaStatusData.RedirectUserOnStatusesAsync(ClaimsPrincipal user, Action1 redirect, Func1 next) at AuthPermissions.SupportCode.DownStatusCode.RegisterDownForMaintenance.<>c__DisplayClass0_0.<<UseDownForMaintenance>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

I'm running a multi-tenancy application with sharding enabled, up until 5.0.1 it's been running perfect. I suspected it might be the lack of a new (?) file shardingsettings.json. But even after creating this and entering the correct data I'm getting the error above.

Would you have any pointers on what might be wrong and what broke my app going from 5.0.0 to 5.0.1?

JonPSmith commented 1 year ago

Hi @rr222cy,

Sorry to hear that 5.0.1 has a problem. I have tried Example6, which has sharding turned on, and it worked. Can you tell me:

roos-robert commented 1 year ago

Hi @JonPSmith,

Thank you for the quick reply and also a big thanks for your work on AuthP!

Your reply made me realize that I was missing the file "shardsettings.Development.json", the one I had was "shardsettings.json". So no problem at all with 5.0.1, the issue was I was lacking this file- which seems to have worked fine before, but now (correctly) triggered an exception.

Just as a reference and to answer your question, what made the exception trigger was when trying to read/manipulate any data at all, which makes perfect sense now since this file and contents were missing. And the type of sharding used is hybrid.

Thanks again and take care!

roos-robert commented 1 year ago

Hi again @JonPSmith,

It seems I was a bit to quick to put this issue as solved. While the issue is solved locally, I face the same issue now when trying to publish the application to my staging server (Windows Server, IIS). The error i get is the following:

Category: Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer EventId: 2 SpanId: 602148485bbcf686 TraceId: c54da5f16ec76b87d447a9b5270f86aa ParentId: 0000000000000000 RequestId: 40000090-0001-fd00-b63f-84710c7967bb RequestPath: /api/auth/login Connection ID "18230571296696041615", Request ID "40000090-0001-fd00-b63f-84710c7967bb": An unhandled exception was thrown by the application. Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Any[TSource](IEnumerable1 source) at AuthPermissions.AspNetCore.ShardingServices.ShardingConnectionsJsonFile.GetDatabaseInformation() at AuthPermissions.AspNetCore.ShardingServices.ShardingConnectionsJsonFile.FormConnectionString(String databaseInfoName) at AuthPermissions.AspNetCore.GetDataKeyCode.GetShardingDataUserNormal..ctor(IHttpContextAccessor accessor, IShardingConnections connectionService) at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope) at ResolveService(ILEmitResolverBuilderRuntimeContext, ServiceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired) at lambda_method440(Closure, IServiceProvider, Object[]) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at AuthPermissions.SupportCode.DownStatusCode.RedirectUsersViaStatusData.RedirectUserOnStatusesAsync(ClaimsPrincipal user, Action1 redirect, Func1 next) at AuthPermissions.SupportCode.DownStatusCode.RegisterDownForMaintenance.<>c__DisplayClass0_0.<<UseDownForMaintenance>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT1.ProcessRequestAsync()

I've checked on the server that the shardsettings file exists and has the correct values:

{
  "ShardingDatabases": [
    {
      "Name": "Default Database",
      "DatabaseName": null,
      "ConnectionName": "DefaultConnection",
      "DatabaseType": "SqlServer"
    }
  ]
}

The .json file is placed in the same root directory on the server as where the appsettings.json file is located. This works when running the MVC app locally (roslyn, Rider IDE) with the exact same setup. But breaks upon publish to IIS.

Has there been some changes in how this file is located perhaps that is causing this? Looking through the source code it seems (if I understand your comments correctly) that shardsettings should not even be necessary (like in 5.0.0), but instead if it does not exist, will default to the "DefaultConnection" value. Which is probably why my app before 5.0.1 was working, but after the changes not.

Thanks in advance for any hints on how to solve this! 😊

JonPSmith commented 1 year ago

I am pretty sure that your shardingsettings.Production.json file doesn't have the correct data in it.

One of the changes I made in version 5.0.1 to the ShardingConnectionsJsonFile service such that if you call the FormConnectionString and TestFormConnectionString methods and if the named DatabaseInformation isn't found you get a exception (previously it returned a null connection string which causes a problem later, and often harder to find).

So, if your shardingsettings.Production.json hasn't got a DatabaseInformation entry for a AuthP tenant, then you will get an exception. I should say that you would get an error with version 5.0.0, but it will something more subtle, like not getting tenant data or ITenantChangeService code doesn't work properly.

You can fix the mismatch by:

roos-robert commented 1 year ago

Hi again @JonPSmith,

Thank you once again for the very comprehensive answer and explanation! One of the things I love about AuthP, not just documentation/comments saying what to do- but also why and how stuff works.

I completely understand now the issue and why things were working before, but really should not have (causing issues later on).

I followed your recommendation on how to fix the mismatch, there was indeed a mismatch on the server in the shardsettings.Production.json file. After adding the missing information the application instantly came back online in my staging environment and everything is now working great again!

Thanks again for taking the time to answer, explain the issue and give a great solution. 😊

JonPSmith commented 1 year ago

Glad you could fix things. Notice that I have updated the issue name in case other people have these problems when upgrading to version 5.0.1