What is the MassTransit pattern for retrieving RabbitMQ password from a dependency? - asp.net-core

Using MassTransit.RabbitMq 8.0.9, in a .Net Core 3.1 project using AspNetCore 3.1.10 and IServiceContainer. The password for RabbitMq is stored in a secrets vault, accessible from a dependency-injected interface. All of the examples I've been able to find just get the password from configuration.
I'd like to do something like
var secrets = serviceProvider.GetRequiredService<ISecretRetrieval>();
var rabbitPassword = secrets.GetRabbitMqPassword();
and then hand that password to IRabbitHostConfigurator, but inside UseMassTransit...UseRabbitMq, there isn't an IServiceProvider instance that I've seen.
Alternatively, I could create a configuration object with a constructor-injected dependency on ISecreteRetrieval. I see examples for IConfiguration<MassTransitHostOptions> that show how to create and register my own class with its own constructor dependencies. Can I do that with IConfiguration<RabbitMqHostSettings> even though RabbitMqHostSettings is an interface, not a class like MassTransitHostOptions?

In the UsingRabbitMq block, the first parameter is a service provider.
x.UsingRabbitMq((context, cfg) =>
{
var secrets = context.GetRequiredService<ISecretRetrieval>();
cfg.Host("hostname", h =>
{
h.Password(secrets.GetRabbitMqPassword());
});
});

Related

Register dependent services on every request

I am working in Multi-tenant solution primarily there are 2 type of applications
WebAPI
Console app to process message from queue
I have implemented dependency injection to inject all services. I have crated TenantContext class where I am resolving tenant information from HTTP header and it's working fine for API, but console application getting tenant information with every message (tenant info is part of queue message) so I am calling dependency injection register method on every incoming message which is not correct, do you have any suggestion/solution here?
The way I am resolving ITenantContext in API
services.AddScoped<ITenantContext>(serviceProvider =>
{
//Get Tenant from JWT token
if (string.IsNullOrWhiteSpace(tenantId))
{
//1. Get HttpAccessor and processor settings
var httpContextAccessor =
serviceProvider.GetRequiredService<IHttpContextAccessor>();
//2. Get tenant information (temporary code, we will get token from JWT)
tenantId = httpContextAccessor?.HttpContext?.Request.Headers["tenant"]
.FirstOrDefault();
if (string.IsNullOrWhiteSpace(tenantId))
//throw bad request for api
throw new Exception($"Request header tenant is missing");
}
var tenantSettings =
serviceProvider.GetRequiredService<IOptionsMonitor<TenantSettings>>();
return new TenantContext(tenantId, tenantSettings );
});
Create two different ITenantContext implementations. One for your Web API, and one for your Console application.
Your Web API implementation than might look as follows:
public class WebApiTenantContext : ITenantContext
{
private readonly IHttpContextAccessor accessor;
private readonly IOptionsMonitor<TenantSettings> settings;
public WebApiTenantContext(
IHttpContextAccessor accessor,
IOptionsMonitor<TenantSettings> settings)
{
// Notice how the dependencies are not used in this ctor; this is a best
// practice. For more information about this, see Mark's blog:
// https://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/
this.accessor = accessor;
this.settings = settings;
}
// This property searches for the header each time its called. If needed,
// it can be optimized by using some caching, e.g. using Lazy<string>.
public string TenantId =>
this.accessor.HttpContext?.Request.Headers["tenant"].FirstOrDefault()
?? throw new Exception($"Request header tenant is missing");
}
Notice that this implementation might be a bit naive for your purposes, but hopefully you'll get the idea.
This class can be registered in the Composition Root of the Web API project as follows:
services.AddScoped<ITenantContext, WebApiTenantContext>();
Because the WebApiTenantContext has all its dependencies defined in the constructor, you can do a simple mapping between the ITenantContext abstraction and the WebApiTenantContext implementation.
For the Console application, however, you need a very different approach. The WebApiTenantContext, as shown above, is currently stateless. It is able to pull in the required data (i.e. TenantId) from its dependencies. This probably won't work for your Console application. In that case, you will likely need to manually wrap the execution of each message from the queue in a IServiceScope and initialize the ConsoleTenantContext at the beginning of that request. In that case, the ConsoleTenantContext would look merely as follows:
public class ConsoleTenantContext : ITentantContext
{
public string TenantId { get; set; }
}
Somewhere in the Console application's Composition Root, you will have to pull messages from the queue (logic that you likely already have), and that's the point where you do something as follows:
var envelope = PullInFromQueue();
using (var scope = this.serviceProvider.CreateScope())
{
// Initialize the tenant context
var context = scope.ServiceProvider.GetRequiredService<ConsoleTenantContext>();
content.TenantId = envelope.TenantId;
// Forward the call to the message handler
var handler = scope.ServiceProvider.GetRequiredService<IMessageHandler>();
handler.Handle(envelope.Message);
}
The Console application's Composition Root will how have the following registrations:
services.AddScoped<ConsoleTenantContext>();
services.AddScoped<ITenentContext>(
c => c.GetRequiredServices<ConsoleTenantContext>());
With the registrations above, you register the ConsoleTenantContext as scoped. This is needed, because the previous message infrastructure needs to pull in ConsoleTenantContext explicitly to configure it. But the rest of the application will depend instead on ITenantContext, which is why it needs to be registered as well. That registration just forwards itself to the registered ConsoleTenantContext to ensure that both registrations lead to the same instance within a single scope. This wouldn't work when there would be two instances.
Note that you could use the same approach for Web API as demonstrated here for the Console application, but in practice it's harder to intervene in the request lifecycle of Web API compared to doing that with your Console application, where you are in full control. That's why using an ITenantContext implementation that is itself responsible of retrieving the right values is in this case an easier solution for a Web API, compared to the ITenantContext that is initialized from the outside.
What you saw here was a demonstration of different composition models that you can use while configuring your application. I wrote extensively about this in my series on DI Composition Models on my blog.

ServiceStack - IAuthRepository vs IUserAuthRepository

I’ve to configure my web application to use the ServiceStack built-in ApiKeyAuthProvider. I’ve registered in the container the OrmLiteAuthRepository with the IAuthRepository interface but it throws an exception saying that I’ve not registered the IUserAuthRepository.
Could someone explain me the difference?
Thanks in advance
EDIT:
Sorry, i've made confusion
The error is
System.NotSupportedException: 'ApiKeyAuthProvider requires a registered IAuthRepository'
Our AppHost's Configure method is
public override void Configure(Container container)
{
var dbFactory = new OrmLiteConnectionFactory("connString", SqlServerDialect.Provider);
container.Register<IDbConnectionFactory>(dbFactory);
container.Register<IUserAuthRepository>(_ => new OrmLiteAuthRepository(dbFactory));
container.Resolve<IUserAuthRepository>().InitSchema();
var authProvider = new ApiKeyAuthProvider()
{
RequireSecureConnection = false
};
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[] {
authProvider
}
));
}
Could you explain me the difference between these two interfaces? we can't figure out (ServiceStack v.6.0.2)
Please refer to the Auth Repository docs for examples of correct usage, e.g:
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServer2012Dialect.Provider));
container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
container.Resolve<IAuthRepository>().InitSchema();
The IAuthRepository is the minimum interface all Auth Repositories have to implement whilst IUserAuthRepository is the extended interface to enable extended functionality to enabled additional features which all ServiceStack built-in Auth Repositories also implement. But you should never need to register or resolve a IUserAuthRepository, i.e. they should only be registered against the primary IAuthRepository interface.
Resolving Auth Repository
If you need to, the Auth Repository can be accessed from base.AuthRepository or base.AuthRepositoryAsync in your Service where you'll be able to use any IUserAuthRepository APIs since they're all available as extension methods on IAuthRepository, e.g. This example Service calls the IUserAuthRepository.GetUserAuth() method:
public class MyServices : Service
{
public object Get(MyRequest request) =>
AuthRepository.GetUserAuth(request.UserId);
}
Whilst here are the recommended APIs to access the Auth Repository outside of your Services:
var authRepo = HostContext.AppHost.GetAuthRepository();
var authRepoAsync = HostContext.AppHost.GetAuthRepositoryAsync();

Add Authentication inside AutoFac ConfigureTenant

I would like to have Tenant Based Authentication on .NET Core App. I'm using AutoFac to build Tenant based Containers.
I was able to create a ServiceCollection and Populate the authentication services. However Authentication fails and getting Unauthorized response for the Tenant.
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
multitenantContainer.ConfigureTenant("80fdb3c0-5888-4295-bf40-ebee0e3cd8f3", containerBuilder =>
{
containerBuilder.RegisterType<DataService>().As<IDataService>().InstancePerDependency();
containerBuilder.RegisterInstance(new OperationIdService()).SingleInstance();
ServiceCollection tenantServices = new();
tenantServices.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "https://key-cloak.cloudapp.azure.com:8443/auth/realms/test";
options.Audience = "test";
});
containerBuilder.Populate(tenantServices);
});
return multitenantContainer;
}
I was able to fix it myself with the help of this article.
MultiTenant Authentication by Michael McKenna
By default handlers aren’t registered using the default “.UseAuthentication” middleware. The schemes are registered in the middleware constructor before you have a valid tenant context. Since it doesn’t support registering schemes dynamically OOTB we will need to slightly modify it.
We’re going to take the existing AuthenticationMiddleware.cs and just move the IAuthenticationSchemeProvider injection point from the constructor to the Invoke method. Since the invoke method is called after we’ve registered our tenant services it will have all the tenant specific authentication services available to it now.

Shared service provider NServiceBus and ASPNET Core

I'm creating a way to publish integration events via NServiceBus that are published from within an operation executed in a handler. The path I've chosen is bridge the IIntegrationEventProvider with IEventCollectionPublisher to get the published events from domain layer.
public sealed class Bridge : IIntegrationEventProvider /* Infrastructure */,
IEventCollectionPublisher /* Domain */
{
private readonly List<object> _events = new List<object>();
void IEventCollectionPublisher.Publish(object domainEvent) { _events.Add(domainEvent): }
IReadOnlyCollection IIntegrationEventProvider.GetEvents() => _events;
}
Since NServiceBus has its own service provider (IBuilder) I need to resolve the class doing the application operation from the IServiceProvider that is made available to pipeline in ServiceScopedBehavior. Doing this I can get the bridge instance that contains the events published from domain layer and publish them as integration events using NServiceBus.
I published a Gist with (hopefully) the code pieces needed to grasp what I'm trying to achieve.
The question is: can I instruct NServiceBus to just delegate calls to the application service provider instead of building it and copy all instructions in endpoint.UserContainer<ServiceBuilder>()? Below is an example
internal sealed class Handler : IHandleMessages<Command>
{
public async Task Handle(Command message, IMessageHandlerContext context)
{
// Resolved from ASPNET DI
var useCase = context.GetService<CommandUseCase>();
// _useCase is resolved NSB DI since injected from constructor
Debug.Assert(ReferenceEquals(useCase, _useCase), "");
await useCase.Execute().ConfigureAwait(false);
}
}
This way I could inject to correct scoped application class in the handler constructor instead of resolving it from the scope provided by IServiceProvider that is made available from context.Extensions.Get<IServiceScope>().ServiceProvider.
Thanks for help
Regards
I think ASP.NET Core integration sample could be useful. Starting from version 7.2 sharing of the DI infrastructure between ASP.NET and NServiceBus is much simpler. There is also a specialized NServiceBus.Extensions.Hosting adapter package that adds UseNServiceBus API.

Autofac PerRequest WebApi 2 + Owin

Trying to implement autofac with my WebApi ... but having some issues with lifetime for my objects...
My startup webapi class:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
container.RegisterType<MyConcreteClass>().As<IMyInterface>().InstancePerRequest();
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
But not works
Unable to resolve the type 'IMyInterface' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
- IMyInterface
Details ---> No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. (See inner exception for details.)
Removing this part .InstancePerRequest(); , then works, but the object is not disposing.
What am i doing wrong ?
Thanks!
I strongly suspect the problem lies with the following code:
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
The error message indicates you must resolve dependencies using the dependency resolver, but this is bypassing that and using the container itself.
On a side note, using a service locator is anti-pattern. You should be injecting dependencies into your controllers and other MVC extension points rather than using this approach.