Activities of "themisoft"

Hi, switching to a Domain Service and getting a bit more guidance on the Authorization, i managed to fix my issues.

Thank you!

Test Project, but same issue:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Medallion.Threading;
using Medallion.Threading.Redis;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Authentication.Twitter;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Volo.Abp.PermissionManagement;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using HangfireAuthTest.EntityFrameworkCore;
using HangfireAuthTest.MultiTenancy;
using StackExchange.Redis;
using Microsoft.OpenApi.Models;
using HangfireAuthTest.HealthChecks;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Caching;
using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Modularity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Swashbuckle;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Volo.Abp.BackgroundJobs.Hangfire;
using Volo.Abp.Hangfire;
using Hangfire;
using Hangfire.PostgreSql;
using HangfireAuthTest.Permissions;

namespace HangfireAuthTest;

[DependsOn(
    typeof(HangfireAuthTestHttpApiModule),
    typeof(AbpAutofacModule),
    typeof(AbpCachingStackExchangeRedisModule),
    typeof(AbpDistributedLockingModule),
    typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
    typeof(AbpIdentityAspNetCoreModule),
    typeof(HangfireAuthTestApplicationModule),
    typeof(HangfireAuthTestEntityFrameworkCoreModule),
    typeof(AbpSwashbuckleModule),
    typeof(AbpAspNetCoreSerilogModule)
    )]

[DependsOn(
    typeof(AbpBackgroundJobsHangfireModule),
    typeof(AbpHangfireModule)
    )]
public class HangfireAuthTestHttpApiHostModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        if (!configuration.GetValue<bool>("App:DisablePII"))
        {
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
        }

        ConfigureHangfire(context, configuration);

        ConfigureUrls(configuration);
        ConfigureConventionalControllers();
        ConfigureAuthentication(context, configuration);
        ConfigureSwagger(context, configuration);
        ConfigureCache(configuration);
        ConfigureVirtualFileSystem(context);
        ConfigureDataProtection(context, configuration, hostingEnvironment);
        ConfigureDistributedLocking(context, configuration);
        ConfigureCors(context, configuration);
        ConfigureExternalProviders(context);
        ConfigureHealthChecks(context);

        Configure<PermissionManagementOptions>(options =>
        {
            options.IsDynamicPermissionStoreEnabled = true;
        });
    }


    private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddHangfire(config =>
        {
            config.UsePostgreSqlStorage(options =>
            {
                options.UseNpgsqlConnection(configuration.GetConnectionString("Default"));
            });
        });

        context.Services.AddHangfireServer(options =>
        {
            options.Queues = new[] { "jobs", "default" };
        });
    }


    private void ConfigureHealthChecks(ServiceConfigurationContext context)
    {
        context.Services.AddHangfireAuthTestHealthChecks();
    }

    private void ConfigureUrls(IConfiguration configuration)
    {
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
            options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
            options.Applications["Angular"].Urls[AccountUrlNames.EmailConfirmation] = "account/email-confirmation";
        });
    }

    private void ConfigureCache(IConfiguration configuration)
    {
        Configure<AbpDistributedCacheOptions>(options =>
        {
            options.KeyPrefix = "HangfireAuthTest:";
        });
    }

    private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        if (hostingEnvironment.IsDevelopment())
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
                options.FileSets.ReplaceEmbeddedByPhysical<HangfireAuthTestDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}HangfireAuthTest.Domain.Shared", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<HangfireAuthTestDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}HangfireAuthTest.Domain", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<HangfireAuthTestApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}HangfireAuthTest.Application.Contracts", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<HangfireAuthTestApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}HangfireAuthTest.Application", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<HangfireAuthTestHttpApiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}HangfireAuthTest.HttpApi", Path.DirectorySeparatorChar)));
            });
        }
    }

    private void ConfigureConventionalControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(HangfireAuthTestApplicationModule).Assembly);
        });
    }

    private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = configuration["AuthServer:Authority"];
                options.RequireHttpsMetadata = configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata");
                options.Audience = "HangfireAuthTest";
            });

        context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = true;
        });
    }

    private static void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddAbpSwaggerGenWithOidc(
            configuration["AuthServer:Authority"]!,
            ["HangfireAuthTest"],
            [AbpSwaggerOidcFlows.AuthorizationCode],
            configuration["AuthServer:MetaAddress"],
            options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "HangfireAuthTest API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            });
    }

    private void ConfigureDataProtection(
        ServiceConfigurationContext context,
        IConfiguration configuration,
        IWebHostEnvironment hostingEnvironment)
    {
        var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("HangfireAuthTest");
        if (!hostingEnvironment.IsDevelopment())
        {
            var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
            dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "HangfireAuthTest-Protection-Keys");
        }
    }

    private void ConfigureDistributedLocking(
            ServiceConfigurationContext context,
            IConfiguration configuration)
    {
        context.Services.AddSingleton<IDistributedLockProvider>(sp =>
        {
            var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
            return new RedisDistributedSynchronizationProvider(connection.GetDatabase());
        });
    }

    private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
            {
                builder
                    .WithOrigins(
                        configuration["App:CorsOrigins"]?
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.Trim().RemovePostFix("/"))
                            .ToArray() ?? Array.Empty<string>()
                    )
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });
    }

    private void ConfigureExternalProviders(ServiceConfigurationContext context)
    {
        context.Services
            .AddDynamicExternalLoginProviderOptions<GoogleOptions>(
                GoogleDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ClientId);
                    options.WithProperty(x => x.ClientSecret, isSecret: true);
                }
            )
            .AddDynamicExternalLoginProviderOptions<MicrosoftAccountOptions>(
                MicrosoftAccountDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ClientId);
                    options.WithProperty(x => x.ClientSecret, isSecret: true);
                }
            )
            .AddDynamicExternalLoginProviderOptions<TwitterOptions>(
                TwitterDefaults.AuthenticationScheme,
                options =>
                {
                    options.WithProperty(x => x.ConsumerKey);
                    options.WithProperty(x => x.ConsumerSecret, isSecret: true);
                }
            );
    }

    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.UseUnitOfWork();
        app.UseDynamicClaims();
        app.UseAuthorization();

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

            var configuration = context.GetConfiguration();
            options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
        });
        app.UseAuditing();
        app.UseAbpSerilogEnrichers();




        app.UseAbpHangfireDashboard("/hangfire", options =>
        {
            options.AsyncAuthorization = new[]
            {
                new AbpHangfireAuthorizationFilter(false, requiredPermissionName: HangfireAuthTestPermissions.Hangfire)
            };
        });


        app.UseConfiguredEndpoints();
    }
}

HTTP Error 401 when trying to access /hangfire

https://www.abp.io/support/questions/5167/Hangfire-Authorization-Problem

I want to Authorize the Dashboard, so it can only be accessed by Login. EngincanV mentioned in this ticket we have to redirect from Auth Server to "/hangfire" Right now i get Error 401

"Hi, you can do this easily by redirecting to auth server and specifying the ReturnUrl as /hangfire (https://localhost:5000/Account/Login?ReturnUrl=https://localhost:5001/hangfire), however, even if you do that, you should pass the authorization header from auth server to your host application somehow, so a simple redirection would not be enough."

But how would i do that?

This is the same code as in the ABP-Hangfire Documentation, but it does not work. As it was mentioned in old ticket, i dont know how to redirect from Auth Server to "/hangfire".

<br> HttpApiHostModule

[DependsOn(
    typeof(HangfireAuthTestHttpApiModule),
    typeof(AbpAutofacModule),
    typeof(AbpCachingStackExchangeRedisModule),
    typeof(AbpDistributedLockingModule),
    typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
    typeof(AbpIdentityAspNetCoreModule),
    typeof(HangfireAuthTestApplicationModule),
    typeof(HangfireAuthTestEntityFrameworkCoreModule),
    typeof(AbpSwashbuckleModule),
    typeof(AbpAspNetCoreSerilogModule)
    )]

[DependsOn(
    typeof(AbpBackgroundJobsHangfireModule),
    typeof(AbpHangfireModule)
    )]

...

public override void ConfigureServices(ServiceConfigurationContext context)
    {
        ...
        ConfigureHangfire(context, configuration);
        ...
    }


private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddHangfire(config =>
        {
            config.UsePostgreSqlStorage(options =>
            {
                options.UseNpgsqlConnection(configuration.GetConnectionString("Default"));
            });
        });

        context.Services.AddHangfireServer(options =>
        {
            options.Queues = new[] { "jobs", "default" };
        });
    }


public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        ...
        app.UseAbpHangfireDashboard("/hangfire", options =>
        {
            options.AsyncAuthorization = new[]
            {
                new AbpHangfireAuthorizationFilter(false, requiredPermissionName: HangfireAuthTestPermissions.Hangfire)
            };
        });

        app.UseConfiguredEndpoints();
    }

I did not think about that, since i was using a lot of DTOs. this solution helped, Thank you!

But what about the Dashboard Authorization?

  • ABP Framework version: v8.1.4
  • UI Type: Blazor Server
  • Database System: PostgreSQL
  • Tiered (Auth/Blazor/Host): yes
  • My Problem: I implemented a Recurring Job Scheduler into our Application. The Jobs are getting scheduled and executed. The Problem is that Hangfire is not able to execute Application Services because it has no Authorization (await _peopleAppService.GetAsync() etc.). How can i Authorize Hangfire?

The only issue i found similiar to this, was this Ticket: https://www.abp.io/support/questions/5167/Hangfire-Authorization-Problem

But the only issue we have in common is that we cannot Authorize the Dashboard too. It was explained that we should redirect from Auth Server to the /hangfire URL, i followed the Documentation and the ticket but found no solution, how do i redirect from Auth Server to /hangfire? To Authorize the Dashboard?

  • Steps to reproduce the issue:
  • I can share a simple test project to demonstrate the issue, tell me what email to send to.

Wow, it was a checkbox all along, anyways thank you!

I have sent the project via. WeTransfer.

The things i added are:

  • your code from the 3 year old post
  • my custom setting UI for testing MyEmailSettingService

Hello, i tried clearing the cache multiple times and restarting but i did not change anything. I have still no access to email-settings or the option to give permission on tenant.

ABP Framework version: v8.1.4 UI type: Blazor Server DB provider: PostgresSQL Tiered: yes

I found a similar post, but it's three years old, and the suggested code changes didn't help.: https://abp.io/support/questions/1622/Email-settings-not-visible-for-tenant

Steps to reproduce the issue:

  • Give a Tenant the Permission to Edit/Create his own E-Mail Settings. (We need this functionality because our future clients will need to add their own SMTP settings.)

I basically copied the code that liangshiwei provided in his old answer, i implemented the service and tested it on the Host Side and it works fine, i also edited the Permission:

 public class YourProjectNamePermissionDefinitionProvider : PermissionDefinitionProvider
{
    public override void Define(IPermissionDefinitionContext context)
    {
        ......
        var settingManagement = context.GetPermissionOrNull(SettingManagementPermissions.Emailing);
        settingManagement.MultiTenancySide = MultiTenancySides.Both;
        .....
    }
    .....
}

But when im on a tenant as an admin, I do not have the option to grant permission for the email settings, there is no checkbox. Is the old method outdated? If so, what is a better method to do this?

I also tried creating my own Setting-UI just to check if i could enter and im getting this Error:

2024-07-26 17:16:53.806 +02:00 [INF] Executing endpoint 'Volo.Abp.SettingManagement.EmailSettingsController.GetAsync (Volo.Abp.SettingManagement.HttpApi)' 2024-07-26 17:16:53.808 +02:00 [INF] Route matched with {area = "settingManagement", action = "Get", controller = "EmailSettings", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[Volo.Abp.SettingManagement.EmailSettingsDto] GetAsync() on controller Volo.Abp.SettingManagement.EmailSettingsController (Volo.Abp.SettingManagement.HttpApi).
2024-07-26 17:16:53.850 +02:00 [INF] Authorization failed. These requirements were not met:
PermissionRequirement: SettingManagement.Emailing
2024-07-26 17:16:53.864 +02:00 [WRN] ---------- RemoteServiceErrorInfo ----------
{
"code": "Volo.Authorization:010001",
"message": "Autorisation fehlgeschlagen! Die entsprechende Richtlinie wurde nicht erfüllt.",
"details": null,
"data": {},
"validationErrors": null
}

2024-07-26 17:16:53.864 +02:00 [WRN] Exception of type 'Volo.Abp.Authorization.AbpAuthorizationException' was thrown.
Volo.Abp.Authorization.AbpAuthorizationException: Exception of type 'Volo.Abp.Authorization.AbpAuthorizationException' was thrown.
at Microsoft.AspNetCore.Authorization.AbpAuthorizationServiceExtensions.CheckAsync(IAuthorizationService authorizationService, AuthorizationPolicy policy)
at Volo.Abp.Authorization.MethodInvocationAuthorizationService.CheckAsync(MethodInvocationAuthorizationContext context)
at Volo.Abp.Authorization.AuthorizationInterceptor.AuthorizeAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle\.DynamicProxy\.AsyncInterceptorBase\.ProceedAsynchronous\[TResult\]\(IInvocation invocation\, IInvocationProceedInfo proceedInfo\)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync() at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.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.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle\.DynamicProxy\.AsyncInterceptorBase\.ProceedAsynchronous\[TResult\]\(IInvocation invocation\, IInvocationProceedInfo proceedInfo\)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at lambda\_method3516(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft\.AspNetCore\.Mvc\.Infrastructure\.ControllerActionInvoker\.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|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) 2024-07-26 17:16:53.865 +02:00 [WRN] Code:Volo.Authorization:010001 2024-07-26 17:16:53.869 +02:00 [INF] AuthenticationScheme: Bearer was forbidden. 2024-07-26 17:16:53.869 +02:00 [INF] Executed action Volo.Abp.SettingManagement.EmailSettingsController.GetAsync (Volo.Abp.SettingManagement.HttpApi) in 61.7528ms 2024-07-26 17:16:53.870 +02:00 [INF] Executed endpoint 'Volo.Abp.SettingManagement.EmailSettingsController.GetAsync (Volo.Abp.SettingManagement.HttpApi)' 2024-07-26 17:16:54.057 +02:00 [DBG] Added 0 entity changes to the current audit log 2024-07-26 17:16:54.064 +02:00 [DBG] Added 0 entity changes to the current audit log 2024-07-26 17:16:54.065 +02:00 [INF] Request finished HTTP/1.1 GET https://localhost:44392/api/setting-management/emailing?api-version=1.0 - 403 null null 266.8626ms
Showing 1 to 10 of 14 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 19, 2024, 10:13