Here's another suggestion:
If you can accept data sync delay, you can create a background worker to synchronize tenant and role permissions: https://docs.abp.io/en/abp/latest/Background-Workers#asyncperiodicbackgroundworkerbase
For example:
public class RolePermissionsSynchronizeWorker : AsyncPeriodicBackgroundWorkerBase
{
public RolePermissionsSynchronizeWorker(
AbpAsyncTimer timer,
IServiceScopeFactory serviceScopeFactory
) : base(
timer,
serviceScopeFactory)
{
Timer.Period = 600000; //10 minutes
}
protected async override Task DoWorkAsync(
PeriodicBackgroundWorkerContext workerContext)
{
Logger.LogInformation("Starting: Sync roles...");
.....
Logger.LogInformation("Completed: Sync roles...");
}
}
Hi,
I didn't see any invalid credentials message in the logs.
BTW, I see Redis errors in your logs: Wrong number of args calling Redis command From Lua script This is a known issue and we have fixed it: https://github.com/abpframework/abp/pull/13711
This is a temporary solution try to add this to your startup project:
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.5" />
Hi,
Open the ServiceBHttpClientModule class and change AddHttpClientProxies to AddStaticHttpClientProxies
You can search for AddHttpClientProxies globally and replace it with AddStaticHttpClientProxies in the MicroserviceB solution.
Hi,
You don't need to add it manually, it is already pre-added.
Can you share the full error logs? thanks.
Hi,
You can consider creating the same role with permissions for tenants 2 and 3 using domain events or override the application service interface of the module
Pseudocode:
public class MyIdentityRoleAppService: IdentityRoleAppService
{
public override async ....CreateAsync()
{
create role...
if(CurrentTenant.Name = "Tenant1")
{
using(CurrentTenant.Change("Tenant2"))
{
Create role...
}
}
}
}
Hi,
Shared.
Also, Instead of using my MainApp to create your suggested navigation, do you think, I can create in a ABP custom Module for above custom changes, then use it as plugin Module.
I think it would be ok, but you should not use my code 100%, because it is just a simple example to give you an idea
Request: Could you please explain how exactly your suggested code works?
I have explained it in detail in my previous answer : ), however, you can check the source code I shared.
Hi,
You can try :
MyGeneralSettings
@using Volo.Abp.DependencyInjection
@using Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components.ApplicationLayout.Common
@using System.Globalization
@using Microsoft.Extensions.Localization
@using Volo.Abp.LeptonX.Shared.Localization
@inject IStringLocalizer<LeptonXResource> L
@inherits GeneralSettings
@attribute [ExposeServices(typeof(GeneralSettings))]
@attribute [Dependency(ReplaceServices = true)]
<div class="lpx-settings" id="lpx-settings">
@if (HasMultipleStyles)
{
<div id="appearance" class="setting-icon" data-lpx-setting-icon="appearance" data-lpx-setting-id="settings-context-menu">
</div>
}
@if (HasContainerWidth)
{
<div id="containerWidth" class="setting-icon" data-lpx-setting-icon="containerWidth"
data-lpx-setting-id="settings-context-menu">
<div class="setting">
<i class="bi bi-layout-three-columns"></i>
</div>
</div>
}
<div id="language" class="setting-icon" data-lpx-setting-icon="language"
data-lpx-setting-id="settings-context-menu">
<div class="setting">
@CurrentLanguageTwoLetters
</div>
</div>
<div class="setting-icon">
<i class="bi bi-gear-wide-connected" aria-hidden="true" data-lpx-ctx-toggle="settings-context-menu"></i>
</div>
<div class="lpx-context-menu" data-lpx-context-menu="settings-context-menu">
<ul class="lpx-nav-menu" id="settings-routes">
<li class="outer-menu-item">
<a class="lpx-menu-item-link lpx-menu-item">
<span class="lpx-menu-item-icon">
<i class="lpx-icon outer-icon bi bi-gear-wide-connected" aria-hidden="true"></i>
</span>
<span class="lpx-menu-item-text">@L["GeneralSettings"]</span>
<span data-lpx-close="settings-context-menu">
<i class="lpx-icon bi bi-x outer-icon dd-icon" aria-hidden="true"></i>
</span>
</a>
</li>
@if (HasMultipleStyles)
{
<li class="outer-menu-item" hidden>
<a class="lpx-menu-item-link lpx-menu-item" data-lpx-setting-group="appearance">
</a>
</li>
}
@if (HasContainerWidth)
{
<li class="outer-menu-item">
<a class="lpx-menu-item-link lpx-menu-item" data-lpx-setting-group="containerWidth">
<span class="lpx-menu-item-icon">
<i class="lpx-icon bi bi-aspect-ratio" aria-hidden="true"></i>
</span>
<span class="lpx-menu-item-text hidden-in-hover-trigger">@L["ContainerWidth"]</span>
<i class="dd-icon hidden-in-hover-trigger lpx-caret bi-chevron-down" aria-hidden="true"></i>
</a>
<ul class="lpx-inner-menu hidden-in-hover-trigger collapsed" data-id="containerWidth">
<li class="lpx-inner-menu-item">
<a class="lpx-menu-item-link lpx-menu-item" data-lpx-setting="boxed">
<span class="lpx-menu-item-icon"><i class="lpx-icon bi bi-square"
aria-hidden="true"></i></span>
<span class="lpx-menu-item-text hidden-in-hover-trigger">@L["ContainerWidth:Boxed"]</span>
</a>
</li>
<li class="lpx-inner-menu-item">
<a class="lpx-menu-item-link lpx-menu-item selected" data-lpx-setting="full">
<span class="lpx-menu-item-icon">
<i class="lpx-icon bi bi-layout-three-columns" aria-hidden="true"></i>
</span>
<span
class="lpx-menu-item-text hidden-in-hover-trigger">@L["ContainerWidth:FullWidth"]</span>
</a>
</li>
</ul>
</li>
}
<li class="outer-menu-item">
<a class="lpx-menu-item-link lpx-menu-item" data-lpx-setting-group="language">
<span class="lpx-menu-item-icon"><i class="lpx-icon bi bi-globe" aria-hidden="true"></i></span>
<span class="lpx-menu-item-text hidden-in-hover-trigger">@L["Language"]</span>
<i class="dd-icon hidden-in-hover-trigger lpx-caret bi-chevron-down" aria-hidden="true"></i>
</a>
<ul class="lpx-inner-menu hidden-in-hover-trigger collapsed" data-id="language">
@if (HasLanguages)
{
@foreach (var language in Languages)
{
<li class="lpx-inner-menu-item">
<a class="lpx-menu-item-link@(language.CultureName == CurrentLanguage.CultureName ? " selected" : string.Empty)"
@onclick="() => ChangeLanguageAsync(language)">
<span class="lpx-menu-item-text hidden-in-hover-trigger">@language.DisplayName / @(new
CultureInfo(language.CultureName).TwoLetterISOLanguageName.ToUpper())</span>
</a>
</li>
}
}
</ul>
</li>
</ul>
</div>
</div>
Hi,
Ok, well, I think you're overcomplicating things, you don't even need to extend the entity.
Just using the ABP Suite to create an intermediate table :
implement the IOpenIddictProDbContext
Replace the IApplicationAppService service:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IApplicationAppService))]
public class MyApplicationAppService : ApplicationAppService
{
private readonly IRepository<OpenIddictUser, Guid> _openIddictUserRepository;
public MyApplicationAppService(
IOpenIddictApplicationManager applicationManager,
IOpenIddictApplicationRepository applicationRepository,
IOpenIddictUserRepository openIddictUserRepository) : base(
applicationManager, applicationRepository)
{
_openIddictUserRepository = openIddictUserRepository;
}
public override async Task<PagedResultDto<ApplicationDto>> GetListAsync(GetApplicationListInput input)
{
var list = await base.GetListAsync(input);
var resultList = new List<ApplicationDto>();
foreach (var item in list.Items)
{
if (await _openIddictUserRepository.AnyAsync(x => x.UserId == CurrentUser.Id && x.OpenIddictApplicationId == item.Id))
{
resultList.Add(item);
}
}
return new PagedResultDto<ApplicationDto>(resultList.Count, resultList);
}
}
Add automapper config
CreateMap<OpenIddictApplication, ApplicationDto>()
.ForMember(des=>des.Scopes,src =>src.Ignore())
.ForMember(des=>des.PostLogoutRedirectUris,src =>src.Ignore())
.ForMember(des=>des.RedirectUris,src =>src.Ignore());
How I to find the user and return TenantId??
For example:
private async Task<Guid?> FindTenantByUser()
{
var tenants = await _tenantRepository.GetListAsync();
foreach (var tenant in tenants)
{
using(CurrentTenant.Change(tenant.Id))
{
var user = await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress) ?? await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress);
if (user != null)
{
if (await UserManager.CheckPasswordAsync(user, LoginInput.Password))
{
return tenant.Id;
}
}
}
}
return null;
}
ABP Login Hiding Tenant + checking the user in which tenant to sign in Directly
This code does not work if users under different tenants have the same email and password According to your needs, I think this is unavoidable