I use Masstransit in multi-tenant application. On Web Api part I use Owin middleware to resolve tenant ID from DNS. I can access it globaly from services using OWIN environment.
I've created a Masstransit middleware to intecept the message and get tenant ID. In some cases I need to access it.
Since the bus is singleton-scoped, how can I access it?
I tried with IConsumeObserver.PreConsume, in debug I see private properties from the consumer, but I can't access them.
UPDATE:
Each message has TenantId property. We use Entity Framework global filter to filter all queries by TenantId. It is set on Unit of Work that we inject into consumer:
public class MyConsumer : IConsumer<IMyCommand>
{
private readonly ITenantConfiguration _tenantConfiguration;
private readonly IUnitOfWork _uow;
public MyConsumer(ITenantConfiguration tenantConfiguration, IUnitOfWork uow)
{
_tenantConfiguration = tenantConfiguration;
_uow = uow;
}
public Task Consume(ConsumeContext<IMyCommand> context)
{
return Task.FromResult(true);
}
}
The problem is that Unit of work is instantiated before I get TenantId from the message. I use Ninject and if I set unit of work to .InTransientScope() it solves my problems.
If I use Web Api, I get tenantId from Owin Middleware startup class:
var domain = context.Request.Host.Value.ToLower();
context.Set<object>("tenantId", tenantId);
Later, I can access it from services:
var owinContext = HttpContext.Current.Request.GetOwinContext();
var owinEnvVars = owinContext.Environment;
var currentTenantInfo = owinEnvVars["tenantId"]
Is it possible to have something like that when Masstransit messages?
Related
we have implemented multi-tenant option in our application. Each tenant have each separate DB. using application filter i can manage or assign the each tenant from the request. same how can we do it in the spring boot scheduler?
#component
public class scheduler{
#Scheduled(fixedRate = 5000)
public void reminderEmail() {
//how can we fetch the exact data from exact tenant DB?
//since there is no request how can we get the tenant name for
fetching exact tenant db?
}
}
Please let me know how can we achieve this?
Something like:
...
public class TenantContext {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
CONTEXT.set(tenantId);
}
public static String getTenantId() {
return CONTEXT.get();
}
...
}
then your Filter or Spring MVC interceptor could do this just before chaining the request:
String tenantId = request.getHeader(TENANT_HEADER_NAME);
TenantContext.setTenantId(tenantId);
and reset it on the way back:
TenantContext.setTenantId(null);
To use it in a thread not related to an http request you could just do:
TenantContext.setTenantId("tenant_1");
More could be found in my blog post Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres
If you are using a multitenant setup similar to the one at this link: https://www.ricston.com/blog/multitenancy-jpa-spring-hibernate-part-1/ and/or you have a default tenant. The easiest way to accomplish this is to add a static method to your CurrentTenantIdentifierResolverImpl class that changes the default tenant for asynchronous tasks that have no session. This is because that the scheduled task will always use the default tenant.
CurrentTenantIdentifierResolverImpl.java
private static String DEFAULT_TENANTID = "tenantId1";
public static void setDefaultTenantForScheduledTasks(String tenant) {
DEFAULT_TENANT = tenant;
}
ScheduledTask.java
#Scheduled(fixedRate=20000)
public void runTasks() {
CurrentTenantIdentifierResolverImpl.setDefaultTenantForScheduledTasks("tenantId2");
//do something
CurrentTenantIdentifierResolverImpl.setDefaultTenantForScheduledTasks("tenantId1");
}
Then after the scheduled task is complete change it back. That is how we accomplished it and it works for our needs.
If you're using a request to determine which tenant is currently active and using tenant to determine database connections, then it's impossible to do anything involving the database from a scheduled task since the scheduled task has no tenant id
After creating a service for a model, how do I tell the model to use that service?
Within the sample Tesla app, there exists constructors which call for a service as an argument:
private readonly IClimateService _service;
public ClimateModel(IExrinContainer exrinContainer, IAuthModel authModel, IClimateService service)
: base(exrinContainer, new ClimateModelState())
{ _service = service; }
I searched but never found where the model receives the service, but I did find this:
protected override void InitServices()
{
RegisterTypeAssembly(typeof(IService), new AssemblyName(nameof(TeslaService)));
base.InitServices();
}
Exrin automatically loads Services, via reflection, if they inherit from
Exrin.Abstraction.IService
I have a webapi project and a repositories project.
I have configured to use oauth, which uses owin middleware bearer token authentication.
I have a unitofwork with multiple repositories.
Inside the repositories I want to filter data based on the logged on user.
I would like all repositories to get the logged in user via dependency injection.
I can access the logged on user in the webapi action, but I am struggling to work out if/how I can inject the current user using DI; because the authorization is happening via the webapi Authorize?:
[Authorize(Roles = "User")]
public IQueryable<Folder> Folders()
{
// return UnitOfWork.FolderRepository.All().OrderBy(o=>o.FolderId).Skip(10).Take(50);
var test = HttpContext.Current.GetOwinContext().Authentication;
//test is populated with the logged on user here, but I don't want to set the user details of the UOW in every action in my controllers
return UnitOfWork.FolderRepository.All();
}
So in the action Folders the Authorize annotation logs the user on. But I have already instantiated the unit of work in the controller constructor with DI:
public FolderController(IUnitOfWork uow, UserManager<IdentityUser,int> usermanager)
{
UnitOfWork = uow;
UserManager = usermanager;
}
IOC container:
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.For<HttpContextBase>()
.HybridHttpOrThreadLocalScoped()
.Use(() => new HttpContextWrapper(HttpContext.Current));
x.For<IUnitOfWork>().HttpContextScoped().Use(
() => new UnitOfWork(new BreezeValidator
(new UserManager<AspNet.Identity.SQLServer.IdentityUser, int>(new UserStore(new SqlDatabase()))))
);
}
}
I had tried to pass in HttpContext.Current.GetOwinContext(), but at that point the authorization hasn't taken place and so the Principal has not been set.
I have looked at actionfilters (which are run after the authorization filter), but can't figure out how I would return a new unit of work instance with the logged on user set, back to the controller.
...Or whether I can set a property on the controller from an action filter?
So the question is really, how can I set the user details in all my controller's unitofwork, without lots of duplication?
Thanks
EDIT: I have a working solution, but still not sure it's the right way to go:
I created an action filter and then from there get the controller and set a UserPrincipal property on the controller's unitOfWork property.
using Project1.Web.Controllers;
using System;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Project1.Web.Filters
{
public class InjectUserAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var action = actionContext.ControllerContext.Controller;
UowApiController ctrl = (UowApiController)actionContext.ControllerContext.Controller;
ctrl.UnitOfWork.UserPrincipal = actionContext.RequestContext.Principal;
}
}
Then, in my UnitOfWork setter of the UserPrincipal I set the UserPrincipal in the contained repositories:
public IPrincipal UserPrincipal
{
get
{
return this.userPrincipal;
}
set
{
this.userPrincipal = value;
((Repository<Folder>)FolderRepository).UserPrincipal = value;
}
}
This works now, but it doesn't achieve dependency injection.
Also I would like to know if this is a "right" way to do it, or what would be a better approach?
I was searching for the same thing and decided on this. I think this answer will be relevant to you as well.
Proper way to dependency inject authenticated user to my repository class
I've just added a getter to the service classes that accesses the user identity at request time.
public class MyService
{
//ctor...
public IEnumerable<Results> GetResults()
{
return _ResultRepository.GetResultsByUser(UserIdentity);
}
IIdentity UserIdentity
{
get { return Thread.CurrentPrincipal.Identity; }
}
}
I have a problem implementing forms authentication with an IOC container in my ASP.NET MVC 3 project. We have stored our user information in the database and has a lot of custom properties.
I have an interface of my user definition registrated to the IOC container for development purposes. This interface is given to each controller so the controllers has current user information.
This al works fine until i remove the dummy user registration in the Application_Start
I receive this error:
The current type, ...CurrentUserInformation.IUserInformation, is an interface and cannot be constructed. Are you missing a type mapping?
I don't want to work with a dummy user object because I think this is not the best practice.
Can sombody help me or is there a better way to do this custom authentication?
edit added some code
BaseController
public class BaseController : Controller
{
private readonly IUserInformation _userInformation;
public BaseController(IUserInformation userInformation)
{
_userInformation = userInformation
}
}
Bootstrapper Initialize called from Application_Start
public static void Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
//register all services en repositories
//here i put my dummy user wich i want to remove
container.RegisterInstance<IUserInformation>(
new UserInformation
{
UserId = 1,
...
});
return container;
}
You can use InjectionFactory:
container.RegisterType<IUserInformation, UserInformation>(
// User information is destroyed when the request ends.
// You could use an HttpSessionLifetimeManager as well, if it fits your needs
new HttpRequestLifetimeManager(),
new InjectionFactory(container => {
UserInformation userInfo = // TODO: build your userInformation from custom authentication
return userInfo;
}));
In an MVC3 project that I'm working on we're trying to move a lot of our logic that is currently in the controllers into a service layer and expose it as a REST Service in WCF.
So in our Global.asax we create a Service Route like so:
RouteTable.Routes.Add(new ServiceRoute
("Exampleservice", new WebServiceHostFactory(), typeof(ExampleService)));
and our controllers access the service something like this:
public class ExampleController : Controller {
private IExampleService service;
public ExampleController() {
this.service = new ExampleService();
}
public ActionResult Index() {
var results = service.GetAll();
return View(results);
}
}
The main point here being that we use the service class directly (without making requests over the network with an HttpClient).
Our website uses Windows Authentication (it's an intranet site) and we would like to keep it that way. My Question is, is there a way that I can get the User's Identity in the service class that will work both for how we have the Controllers using the service, and the way that WCF uses the service?
For example:
[ServiceContract]
public interface IExampleService
{
[WebGet(UriTemplate = "/")]
List<Results> GetAll();
}
public class ExampleService : IExampleService
{
List<Results> GetAll() {
// Get User Name Here
// In ASP.Net I would use User.Identity.Name
// If I was just worrying about the the REST service I would use
// ServiceSecurityContext.Current.WindowsIdentity.Name
}
}
The instruction suggested by #Ryand.Johnson is correct. The point here is that the controller do not send any credentials to the web service because it run under the asp.net user indentity not the identity of the currently loggedd user. The only way to pass the identity to the proxy is by embedding the call to the web service within an impersonation context this way:
using (WindowsImpersonationContext impersonatedUser = (User.Identity as System.Security.Principal.WindowsIdentity).Impersonate()){
//your proxy call here }
If still this way you get null you have to set manually the default credentials to your proxy
Yes, in the service security context...
OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name