Activities of "cangunaydin"

Hello, I didn't understand how i can pass the authorization header while i am redirecting the user from angular. Is there any sample code how to do that? I would be happy if you can share it. I fixed the problem by implementing another authentication schema, so in that way i can challenge the user to authenticate and when it is done i can sign in the user and redirect the user to the page. sth like this.

 context.Services.AddAuthentication(options =>
        {
            options.DefaultScheme = "DoohlinkCustomPolicy";
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddPolicyScheme("DoohlinkCustomPolicy", "DoohlinkCustomPolicy", options =>
        {
            options.ForwardDefaultSelector = context =>
            {
                string authorization = context.Request.Headers[HeaderNames.Authorization];
                if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
                    return JwtBearerDefaults.AuthenticationScheme;
                else
                    return CookieAuthenticationDefaults.AuthenticationScheme;
            };
        })
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
            options.Audience = "Doohlink";
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
         {
             options.ExpireTimeSpan = TimeSpan.FromDays(365);
         })
        .AddAbpOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            options.Authority = configuration["AuthServer:Authority"];
            options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
            options.ResponseType = OpenIdConnectResponseType.Code;

            options.ClientId = configuration["AuthServer:WebClientId"];
            options.ClientSecret = configuration["AuthServer:WebClientSecret"];

            options.UsePkce = true;
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("roles");
            options.Scope.Add("email");
            options.Scope.Add("phone");
            options.Scope.Add("Doohlink");
            options.Events.OnTicketReceived = async (TicketReceivedContext e) =>
            {
                await e.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
                    e.Principal,
                    new AuthenticationProperties
                    {
                        IsPersistent = true,
                        AllowRefresh = true,
                        ExpiresUtc = DateTime.UtcNow.AddDays(1)
                    });
            };
        });

and challenging the user to login in a controller.

public ActionResult Login()
    {
        return Challenge(new AuthenticationProperties { RedirectUri = "/hangfire" }, OpenIdConnectDefaults.AuthenticationScheme);
    }

this works for now. But I would happy to know how to do it from angular. Because as i know when you do redirect from client side like. window.open() you can not pass authorization header. Or maybe I am wrong?

Hello again, Thanks for the explanation, can i ask how can i do the same for hangfire dashboard? If i write a mvc controller action, how can i redirect the user to auth server log in page and then redirect to hangfire dashboard?

  • ABP Framework version: v7.1.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes

Hello, I am trying to implement hangfire to my project. I managed to do that, the only problem that i have is with hangfire dashboard. If i do not use authentication for my hangfire dashboard it works fine. And i can access it. But when i have enabled the filter, i can not access the page. In HttpContext i can not see the user claims, so it return unauthenticated inside AbpHangfireAuthorizationFilter.

I logged in with admin rights and when i call a method from swagger endpoint i can see that claims are there for user and CurrentUser.IsAuthenticated property returns true. I couldn't understand why current user is unauthenticated (or claims are not in the context) when AbpHangfireAuthorizationFilter is triggered. any tips about it?

here is the onApplicationInitialization

  public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAbpRequestLocalization();
        app.UseStaticFiles();
        app.UseAbpSecurityHeaders();
        app.UseRouting();
        app.UseCors();
        app.UseAuthentication();

        if (MultiTenancyConsts.IsEnabled)
        {
            app.UseMultiTenancy();
        }

        app.UseAuthorization();
        app.UseSwagger();
        app.UseAbpSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "Doohlink API");

            var configuration = context.GetConfiguration();
            options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
        });
        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseUnitOfWork();
        app.UseHangfireDashboard("/hangfire", new DashboardOptions
        {
            AsyncAuthorization = new[] { new DoohlinkAuthorizationFilter() }
        });
        app.UseConfiguredEndpoints();

    }

ps: to see the httpcontext I have changed AbpHangfireAuthorizationFilter to custom class inside my project here is DoohlinkAuthorizationFilter

public class DoohlinkAuthorizationFilter : IDashboardAsyncAuthorizationFilter
{
    private readonly bool _enableTenant;
    private readonly string _requiredPermissionName;

    public DoohlinkAuthorizationFilter(bool enableTenant = false, string requiredPermissionName = null)
    {
        _enableTenant = requiredPermissionName.IsNullOrWhiteSpace() ? enableTenant : true;
        _requiredPermissionName = requiredPermissionName;
    }

    public async Task<bool> AuthorizeAsync(DashboardContext context)
    {
        if (!IsLoggedIn(context, _enableTenant))
        {
            return false;
        }

        if (_requiredPermissionName.IsNullOrEmpty())
        {
            return true;
        }

        return await IsPermissionGrantedAsync(context, _requiredPermissionName);
    }

    private static bool IsLoggedIn(DashboardContext context, bool enableTenant)
    {
        var currentUser = context.GetHttpContext().RequestServices.GetRequiredService<ICurrentUser>();

        if (!enableTenant)
        {
            return currentUser.IsAuthenticated && !currentUser.TenantId.HasValue;
        }

        return currentUser.IsAuthenticated;
    }

    private static async Task<bool> IsPermissionGrantedAsync(DashboardContext context, string requiredPermissionName)
    {
        var permissionChecker = context.GetHttpContext().RequestServices.GetRequiredService<IPermissionChecker>();
        return await permissionChecker.IsGrantedAsync(requiredPermissionName);
    }
}

and this is httpcontext quickwatch

Thanks a lot, i have seen the fix now, I am closing the issue then.

Hello @liangshiwei. As you mention it works when you await, probably when i try to await i did sth wrong. It was late at night :) Thank you for sharing the issue link with me So it seems, collection has been updated from two threads. And collection that is updated in Volo.Abp.Uow.UnitOfWorkExtensions.GetOrAddItem is not thread safe. Maybe changing that to ConcurrentDictionary can help. https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

Thank you @mahmut.

Hello again, you can find the project in this github repo. https://github.com/cangunaydin/abpConcurrencyIssue to produce the same error you have to do the followings.

1- Clone the project.

2- Go to aspnet-core\etc\docker

3- execute run-docker-sideapps.ps1 this will create the redis and postgres(postgis) container.(stick to these images since appsettings has been configured according to those docker images and geolocation is involved)

4- Build project and run dbmigrator. This will seed a new tenant named "Tribulus" and one screen belongs to this tenant with id: "ad16f9e2-bf39-43cf-af69-47b7388ee9cd". You can check TenantDataSeeder class if you want.

5- abp install-libs inside AuthServer folder then Run Doohlink.AuthServer and Doohlink.HttpApi.Host project

6- Run angular app and get bearer token for Tribulus tenant with username: admin, password: 123qwe, note the bearer token and tenantid to somewhere, you will use them on step 8 7- Install Jmeter to do multiple http requests async. If you want you can use another tool but important part is you need bunch of async calls at the same time. https://jmeter.apache.org/download_jmeter.cgi Download zip file, unzip it then run jmeter.bat

8- After running the Jmeter, you need to open the TestJmeter.jmx file on the root folder of the project. This is a script that will do the call to the endpoint.

9- Change the bearer token and tenantid to what you got from step 6. You need to do that from Http Header Manager

10-Run the jmeter, it is gonna take 10 seconds, you can see if http requests are successful on treeview. Most of them will succeed around 5 of them will fail. you can look at the logs afterwards.

Hello @liangshiwei, is it okay if i share the original project? it is not a super complicated project i think you will understand.

  • ABP Framework version: v7.1.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes
  • Exception message and stack trace:

System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. at System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary2.set_Item(TKey key, TValue value) at Volo.Abp.Uow.UnitOfWorkExtensions.GetOrAddItem[TValue](IUnitOfWork unitOfWork, String key, Func2 factory) at Volo.Abp.Caching.DistributedCache2.GetAsync(TCacheKey key, Nullable1 hideErrors, Boolean considerUow, CancellationToken token) at Volo.Saas.Tenants.TenantStore.GetCacheItemAsync(Nullable1 id, String name) at Volo.Saas.Tenants.TenantStore.FindAsync(Guid id) at Volo.Abp.MultiTenancy.MultiTenantConnectionStringResolver.FindTenantConfigurationAsync(Guid tenantId) at Volo.Abp.MultiTenancy.MultiTenantConnectionStringResolver.ResolveAsync(String connectionStringName) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.ResolveConnectionStringAsync(String connectionStringName) at Volo.Abp.Uow.EntityFrameworkCore.UnitOfWorkDbContextProvider1.GetDbContextAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.GetDbSetAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.GetQueryableAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.WithDetailsAsync() at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository3.FindAsync(TKey id, Boolean includeDetails, CancellationToken cancellationToken) at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository3.GetAsync(TKey id, Boolean includeDetails, CancellationToken cancellationToken) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Doohlink.CampaignManagement.Screens.ScreenAppService.GetAsync(Guid id) in C:\Development\Projects\Examples\Doohlink\aspnet-core\modules\Doohlink.CampaignManagement\src\Doohlink.CampaignManagement.Application\Screens\ScreenAppService.cs:line 22 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope) at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Doohlink.Screens.ScreenAppService.GetAsync(Guid id) in C:\Development\Projects\Examples\Doohlink\aspnet-core\src\Doohlink.Application\Screens\ScreenAppService.cs:line 23

Hello, I have an application which i created two different modules and from the main app i am calling application services of those modules async methods in parallel. sth like this.

 public async Task&lt;ScreenDto&gt; GetAsync(Guid id)
    {
        var inventory = _inventoryScreenAppService.GetAsync(id);
        var campaign = _campaignScreenAppService.GetAsync(id);
        await Task.WhenAll(inventory, campaign);

        var screenDto = new ScreenDto();
        ObjectMapper.Map(inventory.Result, screenDto);
        ObjectMapper.Map(campaign.Result, screenDto);

        return screenDto;
    }

not always but sometimes I am getting this exception on runtime. It is very straightforward what happens inside those 2 appservices.

Inventory ScreenAppService

   public async Task&lt;ScreenDto&gt; GetAsync(Guid id)
    {
        var screen = await _screenRepository.GetAsync(id);
        return ObjectMapper.Map&lt;Screen, ScreenDto&gt;(screen);
    }

Campaign ScreenAppService

 public async Task&lt;ScreenDto&gt; GetAsync(Guid id)
    {
        var screen = await _screenRepository.GetAsync(id);
        return ObjectMapper.Map&lt;Screen, ScreenDto&gt;(screen);
    }

two modules have different dbcontexts(2 seperate databases). and storing different information about screen. it seems it is the same method but they are in different namespaces and different properties they hold for screen and screendto.

Campaign ScreenDto

using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;

namespace Doohlink.CampaignManagement.Screens;

public class ScreenDto : EntityDto&lt;Guid&gt;
{
    public ResolutionDto Resolution { get; set; }

    public List&lt;OpeningHoursDto&gt; OpeningHours { get; set; }

    public List&lt;double&gt; Durations { get; set; }

    public List&lt;string&gt; MimeTypes { get; set; }
}

Inventory ScreenDto

using System;
using Volo.Abp.Application.Dtos;

namespace Doohlink.InventoryManagement.Screens;

public class ScreenDto : EntityDto&lt;Guid&gt;
{
    public string Name { get; set; }

    public string MacAddress { get; set; }

    public LocationDto Location { get; set; }
}

as I see from the exception it is trying to get the tenant and resolve the configuration

at System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary2.set_Item(TKey key, TValue value) at Volo.Abp.Uow.UnitOfWorkExtensions.GetOrAddItem[TValue](IUnitOfWork unitOfWork, String key, Func2 factory) at Volo.Abp.Caching.DistributedCache2.GetAsync(TCacheKey key, Nullable1 hideErrors, Boolean considerUow, CancellationToken token) at Volo.Saas.Tenants.TenantStore.GetCacheItemAsync(Nullable1 id, String name) at Volo.Saas.Tenants.TenantStore.FindAsync(Guid id) at Volo.Abp.MultiTenancy.MultiTenantConnectionStringResolver.FindTenantConfigurationAsync(Guid tenantId)

it seems it is a concurrency issue while tenant has been fetched from the cache or db. Any suggestion to fix this issue?

Ps: I have tried to change the method in main app

 public async Task&lt;ScreenDto&gt; GetAsync(Guid id)
    {
        var inventory = _inventoryScreenAppService.GetAsync(id);
        var campaign = _campaignScreenAppService.GetAsync(id);
        await Task.WhenAll(inventory, campaign);

        var screenDto = new ScreenDto();
        ObjectMapper.Map(inventory.Result, screenDto);
        ObjectMapper.Map(campaign.Result, screenDto);

        return screenDto;
    }

to

 public async Task&lt;ScreenDto&gt; GetAsync(Guid id)
    {
        var inventory =await _inventoryScreenAppService.GetAsync(id);
        var campaign =await _campaignScreenAppService.GetAsync(id);
        

        var screenDto = new ScreenDto();
        ObjectMapper.Map(inventory, screenDto);
        ObjectMapper.Map(campaign, screenDto);

        return screenDto;
    }

it didn't fix the issue.

Hello again, thanks for pointing out the code now it is more clear. What i couldn't understand is if it is in the cache, it shouldn't give any error since it is gonna return it from the cache. Anyway i am gonna close this issue, thanks for your help @liangshiwei

Showing 81 to 90 of 111 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 19, 2024, 10:13