- ABP Framework version: v3.0.5
- UI type: Angular
- Tiered (MVC) or Identity Server Seperated (Angular): Identity Server Seperated
Hi, we are using Tenants functionality and noticed it's only possible to create a tenant in UI by a user logged in under a 'null'-tenant (super tenant?) We need to create tenants by a user, logged in under ANOTHER tenant - we have a custom tree-like structure of tenants in our system.
5 Answer(s)
- 
    0Hi, This is by design. A tenant must be under the host. But you can do this in the following ways: - Change permissions about tenants scope:
 var tenantPermission = context.GetPermissionOrNull(SaasHostPermissions.Tenants.Default); tenantPermission.MultiTenancySide = MultiTenancySides.Both; foreach (var tenantPermissionChild in tenantPermission.Children) { tenantPermissionChild.MultiTenancySide = MultiTenancySides.Both; } var editionPermission = context.GetPermissionOrNull(SaasHostPermissions.Editions.Default); editionPermission.MultiTenancySide = MultiTenancySides.Both; foreach (var editionPermissionChild in editionPermission.Children) { editionPermissionChild.MultiTenancySide = MultiTenancySides.Both; }- Create a table to store information about tenants under the tenant. Example:
 public class TenantInfo : AggregateRoot<Guid>, IMultiTenant { public Guid? TenantId { get; } public Guid RelatedTenantId { get; set; } public string Name { get; set; } ...... }- Override the tenant creation method:
 [Dependency(ReplaceServices = true)] [Authorize(SaasHostPermissions.Tenants.Default)] public class MyTenantService : TenantAppService { private readonly IRepository<TenantInfo, Guid> _tenantInfoRepository; public MyTenantService( ITenantRepository tenantRepository, IEditionRepository editionRepository, ITenantManager tenantManager, IDataSeeder dataSeeder, IRepository<TenantInfo, Guid> tenantInfoRepository) : base(tenantRepository, editionRepository, tenantManager, dataSeeder) { _tenantInfoRepository = tenantInfoRepository; } [Authorize(SaasHostPermissions.Tenants.Create)] public override async Task<SaasTenantDto> CreateAsync(SaasTenantCreateDto input) { SaasTenantDto result = null; using (CurrentTenant.Change(null)) { result = await base.CreateAsync(input); } await _tenantInfoRepository.InsertAsync(new TenantInfo() { Name = result.Name, RelatedTenantId = result.Id }); return result; } }
- 
    0Hi, thank you, would give it a try, but two questions right away: Change permissions about tenants scope where this needs to be placed? public class TenantInfo : AggregateRoot, IMultiTenant we already use the table for our Tenantentity (it hasIdandMasterIdfor tenant id and master tenant id correspondingly). It also has 1:1 ABP tenant entity:public class Tenant : SoftDeleteLogEntity<int> { ... public int? MasterId { get; set; } public Tenant MasterTenant { get; set; } public Guid AbpId { get; set; } // it is ABP tenant's id public AbpTenant AbpTenant { get; set; } // it is ABP tenant ... } public abstract class SoftDeleteLogEntity<TKey> : LogEntity<TKey>, IMayHaveDeleterName, ISoftDelete, IDeletionAuditedObject {...} public abstract class LogEntity<TKey> : AuditedEntity<TKey>, IMayHaveCreatorName, IMayHaveLastModifierName {...}but we did not implement IMultiTenant. Now, when I'd like to do that - I can seeTenantIdhas to beGuid, but in our tableId(MasterIdtoo, accordingly) isInteger(it was customer's decision)... How to overcome this problem? Is it OK just to implement a 'fake'GuidTenantIdfield and not use it?
- 
    0- Add to PermissionDefinitionProviderderived class, Usually under thePermissionsfolder of the.Application.Contractsproject.
- No problem, you can use the inttype as the primary key And you don’t need to implementIMultiTenant. You should always switch to the host when operating your own tenant entity.
 [Authorize(SaasHostPermissions.Tenants.Create)] public override async Task<SaasTenantDto> CreateAsync(SaasTenantCreateDto input) { var currentMyTenant = await _myTenantRepository.FindAsync(x => x.AbpTenantId == CurrentTenant.Id); using (CurrentTenant.Change(null)) { var result = await base.CreateAsync(input); await _myTenantRepository.InsertAsync(new MyTenant() { AbpTenantId = result.Id, MasterTenant = currentMyTenant, MasterId = currentMyTenant.Id }); return result; } }
- Add to 
- 
    0Thank you. I will give it a try later on, because requirements have changed. Could you please explain why I cannot reopen another ticket (which I closed) or add answer? When I'm trying to do that, I receive the message saying I am not authorized to do that (I AM logged in). 
- 
    0Please try one more. 
 
                                