Similar questions have been asked before but not quite the same (unless I missed it)
I want to pass IUserInfo class instance through my Service, Domain , Domain Events, Domain Event Handlers...
Whats is the best way to do it.
Should I
I am stuck at Domain Event Handlers where I want to use the data for auditing as well as sending emails.
I want to be able to use the CurrentUser information from almost anywhere in my application.
With threading as threads are pooled I am skeptical if the reuse of threads will reset the data. If not please shopw me how to use threading to pass IUser instance.
Regards,
Mar
I've done this kind of thing before using IoC. The benefit of this is that it's very testable -- you can stub out your user info for testing -- and reasonably readable and easy to follow.
My approach might not be ideal, but I find it working quite well. Thing what I did - I decided not to use dependency injection pass current user everywhere directly because that was getting too cumbersome and switched to static context. Problem with contexts - they are a bit difficult to manage.
This one is defined in my domain:
public static class UserContext{
private static Func<User> _getCurrentUser;
private static bool _initialized;
public static User Current{
get{
if(!_initialized)
throw new Exception("Can i haz getCurrentUser delegate?");
var user=_getCurrentUser();
return user??User.Anonymous;
}
}
public static void Initialize(Func<User> getCurrentUser){
_getCurrentUser=getCurrentUser;
_initialized=true;
}
}
Note that delegate is static - for whole app only one at a time. And I'm not 100% sure about it's life cycle, possible memory leaks or whatnot.
Client application is responsible to initialize context. My web application does that on every request:
public class UserContextTask:BootstrapperTask{
private readonly IUserSession _userSession;
public UserContextTask(IUserSession userSession){
Guard.AgainstNull(userSession);
_userSession=userSession;
}
public override TaskContinuation Execute(){
UserContext.Initialize(()=>_userSession.GetCurrentUser());
return TaskContinuation.Continue;
}
}
Using mvcextensions library to stream-line bootstrapping tasks. You can just subscribe for according events in global.asax for that.
In client side (web app), I implement application service named IUserSession:
public User GetCurrentUser(){
if(HttpContext.Current.User==null) return null;
var identity=HttpContext.Current.User.Identity;
if(!identity.IsAuthenticated) return null;
var user=_repository.ByUserName(identity.Name);
if(user==null) throw new Exception("User not found. It should be. Looks bad.");
return user;
}
There is some more lame code necessary in order to use forms auth with roles w/o membership provider and role provider. But that's not the point of this question.
At domain level - I'm explicitly describing permissions that users might have like this one:
public class AcceptApplications:IUserRights{
public bool IsSatisfiedBy(User u){
return u.IsInAnyRole(Role.JTS,Role.Secretary);
}
public void CheckRightsFor(User u){
if(!IsSatisfiedBy(u)) throw new ApplicationException
("User is not authorized to accept applications.");
}
}
Cool thing is - those permissions can be made more sophisticated. E.g.:
public class FillQualityAssessment:IUserRights{
private readonly Application _application;
public FillQualityAssessment(Application application){
Guard.AgainstNull(application,
"User rights check failed. Application not specified.");
_application=application;
}
public bool IsSatisfiedBy(User u){
return u.IsInRole(Role.Assessor)&&_application.Assessors.Contains(u);
}
public void CheckRightsFor(User u){
if(!IsSatisfiedBy(u))
throw new ApplicationException
("User is not authorized to fill quality assessment.");
}
}
Permissions can be checked vica versa too - User has these fellas:
public virtual bool HasRightsTo<T>(T authorizationSpec) where T:IUserRights{
return authorizationSpec.IsSatisfiedBy(this);
}
public virtual void CheckRightsFor<T>(T authorizationSpec) where T:IUserRights{
authorizationSpec.CheckRightsFor(this);
}
Here's my aggregate root base class:
public class Root:Entity,IRoot{
public virtual void Authorize(IUserRights rights){
UserContext.Current.CheckRightsFor(rights);
}
}
And here's how I check permissions:
public class Application{
public virtual void Accept(){
Authorize(new AcceptApplications());
OpeningStatus=OpeningStatus.Accepted;
}
}
I hope that helps...