Whats the current recommended way of impersonating users?
Haven't thought about this.
This is definitely a must in any real production environment. There issues that creep up in production that the current user might see, that the administrator may not. Being able to impersonate a user almost always results in being able to "see" the problem. I say this with experience in a large corporate environment.
"Impersonate User" should mean I can select from a list of Tenant/User and then from the system perspective, I AM that user. Of course, this means not needing a password, just the fact that the system now thinks that I am that particular user.
I agree that is is an important feature (and we have implemented it for aspnet zero before). However, we currently have more prioritized items to work on. We will think on that later.
Now that v1.0 has been released, I wondered if this might be reconsidered?
I've seen similar issues raised before:
Labeled as a high priority item in the backlog. If that's urgent for you, I suggest you to implement it yourself.
For anyone who is interested... My current working implementation can transparently impersonate a Tenant if you are logged in as a HOST user.
Because we have access to ICurrentUser.TenantId but also ICurrentTenant we can exploit this to impersonate other tenants...
First we create a request middleware to configure the 'ICurrentTenant' if we find a custom __tenant header or query parameter:
class CustomCurrentUserTenantResolveContributor : CurrentUserTenantResolveContributor
{
public override void Resolve(ITenantResolveContext context)
{
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser.IsAuthenticated != true)
{
return;
}
context.Handled = true;
context.TenantIdOrName = currentUser.TenantId?.ToString();
// If is host, then check from custom tenantId in request header and query...
// See "HeaderTenantResolveContributor" and "QueryStringTenantResolveContributor"
var httpContext = Volo.Abp.ServiceProviderAccessorExtensions.GetHttpContext(context);
if (currentUser.TenantId == null)
{
if (httpContext.Request != null)
{
var tenantIdKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey;
Guid tenantId = default;
// Look in header
if (httpContext.Request.Headers[tenantIdKey].Count >= 1)
{
if (!Guid.TryParse(httpContext.Request.Headers[tenantIdKey].Last(), out tenantId)) return;
}
// Look in query string
else if (httpContext.Request.QueryString.HasValue)
{
if (!Guid.TryParse(httpContext.Request.Query[tenantIdKey], out tenantId)) return;
}
if (tenantId == default) return;
var _settingManager = context.ServiceProvider.GetRequiredService<ISettingManager>();
// todo: should we be getting the current tenant inside the tenant resolver? Will this break things?
var _currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
using (_currentTenant.Change(tenantId))
{
if (Convert.ToBoolean(
// todo: will this cause deadlocks or reduce available threads? We probably need to find an alternative!
AsyncHelper.RunSync(() => _settingManager.GetOrNullForCurrentTenantAsync("App.AllowSupportAccess"))
))
{
context.TenantIdOrName = tenantId.ToString();
}
}
}
}
}
}
Within the ConfigureServices method of the HttpApiHostModule:
// Allows hosts to impersonate tenants
Configure<AbpTenantResolveOptions>(options =>
{
options.TenantResolvers.ReplaceOne(
x => x.GetType() == typeof(CurrentUserTenantResolveContributor),
new CustomCurrentUserTenantResolveContributor());
});
Then simply add the __tenant header with a valid TenantId to each request. The negative of this approach is that the audit logs aren't correct as the ImpersonatorUserId and ImpersonatorTenantId aren't populated.
Also @hikalkan can I get your opinion on this approach? Any improvements or recommendations?
@hikalkan. What is the timeline for this feature to be added?
Hi @hikalkan , could you give me some information about what is the correct way to set the CurrentUser in a process that is executed as a task?
For example, when running an Azure backgroundJob or WebJob that calls an AppService, it needs to be done with credentials.
I can't find the correct way to do it, I have searched the code, tests and documentation without success.
From already thank you very much.
hi @LuisPignataro
Changing the Current Principle
https://docs.abp.io/en/abp/latest/CurrentUser#changing-the-current-principle
Is this still on track for a 4.1-preview release? We are currently evaluating ABP Framework and time is limited. :)
hi @jogoertzen-stantec
Because there may be some limitations in tiered projects. I can't guarantee that it will work in 4.1, but I will try my best.
We are waiting this feature also
We are new but waiting to for this feature, we need a dead line!
@DmezaSpeed https://github.com/abpframework/abp/issues/1082#issuecomment-731579375
hi @LuisPignataro
Changing the Current Principle
https://docs.abp.io/en/abp/latest/CurrentUser#changing-the-current-principle
@maliming this sample code creates a new user. How about if the user already exists? How can one use it ?..
Most helpful comment
hi @jogoertzen-stantec
Because there may be some limitations in tiered projects. I can't guarantee that it will work in 4.1, but I will try my best.