Session handling with RavenDB and ASP.NET MVC - asp.net-mvc

I have a service class UserService that gets an instance of IDocumentStore injected using AutoFac. This is working fine but now I'm looking at code like this:
public void Create(User user)
{
using (var session = Store.OpenSession())
{
session.Store(user);
session.SaveChanges();
}
}
Every action that writes to the db uses this same structure:
using (var session = Store.OpenSession())
{
dosomething...
session.SaveChanges();
}
What is the best way to eliminate this repetitive code?

The easiest way is implementing OnActionExecuting and OnActionExecuted on a base controller and use it.
let's imagine you create your RavenController like this:
public class RavenController : Controller
{
public IDocumentSession Session { get; set; }
protected IDocumentStore _documentStore;
public RavenController(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
Session = _documentStore.OpenSession();
base.OnActionExecuting(filterContext);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
using (Session)
{
if (Session != null && filterContext.Exception == null)
{
Session.SaveChanges();
}
}
base.OnActionExecuted(filterContext);
}
}
then all you need to do in your own controllers is inherit from RavenController like this:
public class HomeController : RavenController
{
public HomeController(IDocumentStore store)
: base(store)
{
}
public ActionResult CreateUser(UserModel model)
{
if (ModelState.IsValid)
{
User user = Session.Load<User>(model.email);
if (user == null) {
// no user found, let's create it
Session.Store(model);
}
else {
ModelState.AddModelError("", "That email already exists.");
}
}
return View(model);
}
}
Interesting enough, I have found a blog post showing exactly this technique ...
it does explain way more that what I did. I hope it helps you better
Building an ASP.NET MVC app using RavenDB as a Backing Store

Related

Trigger authorization validation manually

I've a custom AuthorizeAttribute in my website. It has some logic about the Result created for unathorized requests.
In some cases, I want to trigger its validation manually*. I don't know if its possible. As I haven't found how to do that, I thought that I could extract the logic to get the Result to a diferrent method, and call it when I want. But then I don't know how to execute the ActionResult (outside de controllers).
How can I do to manually execute authorize validation? If not possible, how can I do to execute an ActionResult outside a controller?
*I need to trigger it manually because some request may pass the validation (because the session is created) and then, when accessing my services, found that the session was closed by someone else. I wouldn't like to add a call to the services in OnAuthorization to reduce services calls.
I'm not sure if its the best, but I've found a way to get it working (still listening for better answers).
When I call the services and notice that the work session has expired, all I do is removing the active user in the web session.
My custom authorize attribute also implements IResultFilter and IExceptionFilter.
In both OnResultExecuted and OnException I validate the active user once more. If the session was removed, then apply the same ActionResult that I would apply in OnAuthorization.
Here is the final class:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IResultFilter, IExceptionFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnException(ExceptionContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
{
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
}
public static ActionResult Validate(HttpContextBase httpContext)
{
if (UserActiveInSession)
return null;
// Different rules to build an ActionResult for this specific case.
}
}
I did not get Diego answer's, But Just simply answering the title, I got it to work like that, You can use it as attribute on controllers actions and also trigger it manually at any place in C# or in Razor views.
namespace SomeNameSpace
{
public class CustomAuthorizeAttributeMVC : AuthorizeAttribute
{
private readonly string[] rolesParams;
public CustomAuthorizeAttributeMVC(params string[] roles)
{
this.rolesParams = roles;
}
public bool IsAuthorized { get {
//Do your authorization logic here and return true if the current user has permission/role for the passed "rolesParams"
string[] allowedRoles = new string[] {"role 1", "role 2", "role 3"};
return allowedRoles.Intersect(rolesParams).Any(); //for the example
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return this.IsAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//...
}
}
public class AuthorizeHelper
{
public static bool HasPermission(params string[] roles)
{
return new CustomAuthorizeAttributeMVC(roles).IsAuthorized;
}
}
}
Usage example:
[CustomAuthorizeAttributeMVC("role 2")]
public ActionResult SomeAction()
{
return Content("Authorized !");
}
public ActionResult SomeOtherAction()
{
if(AuthorizeHelper.HasPermission("role 2"))
{
return Content("Authorized !");
}
return Content("401 Not Authorized !");
}
And as said, it can be used in Razor views by calling it normally
#if(AuthorizeHelper.HasPermission("role 2")) {
//...
}
Thanks

Get Session from HttpActionContext

I'm trying to create a permission attribute to configure in each action of my controllers so this custom attribute should take the sessionId from the user.
My code is like that:
public class PermissionChecker: ActionFilterAttribute
{
private int _permissionId { get; set; }
private IUserSelectorService _userService { get; set; }
public PermissionChecker(int permissionId)
{
_permissionId = permissionId;
_userService = new UserSelectorService();
}
public PermissionChecker(int permissionId, IUserSelectorService userService)
{
_permissionId = permissionId;
_userService = userService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (_userService.HasPermission(_permissionId, /* here I must pass the session["Id"]*/)){
base.OnActionExecuting(actionContext);
return;
}
throw new HttpException(401, "Unauthorized");
}
}
Use this
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
var id = filterContext.HttpContext.Session["Id"];
}
}
EDIT
Given the fact that you're using MVC 4 and you don't have
public override void OnActionExecuting(ActionExecutingContext filterContext)
Try using
System.Web.HttpContext.Current.Session
if you are trying to access using ActionFilterAttribute then OnActionExecting event it wont give the accessibility of HttpContext with System.Web.Http.
Instead of that If you are trying to access using System.Web.Mvc it will provide you the current session with onActionExecting event with help of ActionExecutingContext class.

How to disable a global filter in ASP.Net MVC selectively

I have set up a global filter for all my controller actions in which I open and close NHibernate sessions. 95% of these action need some database access, but 5% don't. Is there any easy way to disable this global filter for those 5%. I could go the other way round and decorate only the actions that need the database, but that would be far more work.
You could write a marker attribute:
public class SkipMyGlobalActionFilterAttribute : Attribute
{
}
and then in your global action filter test for the presence of this marker on the action:
public class MyGlobalActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipMyGlobalActionFilterAttribute), false).Any())
{
return;
}
// here do whatever you were intending to do
}
}
and then if you want to exclude some action from the global filter simply decorate it with the marker attribute:
[SkipMyGlobalActionFilter]
public ActionResult Index()
{
return View();
}
Though, the accepted answer by Darin Dimitrov is fine and working well but, for me, the simplest and most efficient answer found here.
You just need to add a boolean property to your attribute and check against it, just before your logic begins:
public class DataAccessAttribute: ActionFilterAttribute
{
public bool Disable { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Disable) return;
// Your original logic for your 95% actions goes here.
}
}
Then at your 5% actions just use it like this:
[DataAccessAttribute(Disable=true)]
public ActionResult Index()
{
return View();
}
In AspNetCore, the accepted answer by #darin-dimitrov can be adapted to work as follows:
First, implement IFilterMetadata on the marker attribute:
public class SkipMyGlobalActionFilterAttribute : Attribute, IFilterMetadata
{
}
Then search the Filters property for this attribute on the ActionExecutingContext:
public class MyGlobalActionFilter : IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.Filters.OfType<SkipMyGlobalActionFilterAttribute>().Any())
{
return;
}
// etc
}
}
At least nowadays, this is quite easy: to exclude all action filters from an action, just add the OverrideActionFiltersAttribute.
There are similar attributes for other filters: OverrideAuthenticationAttribute, OverrideAuthorizationAttribute and OverrideExceptionAttribute.
See also https://www.strathweb.com/2013/06/overriding-filters-in-asp-net-web-api-vnext/
Create a custom Filter Provider. Write a class which will implement IFilterProvider. This IFilterProvider interface has a method GetFilters which returns Filters which needs to be executed.
public class MyFilterProvider : IFilterProvider
{
private readonly List<Func<ControllerContext, object>> filterconditions = new List<Func<ControllerContext, object>>();
public void Add(Func<ControllerContext, object> mycondition)
{
filterconditions.Add(mycondition);
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return from filtercondition in filterconditions
select filtercondition(controllerContext) into ctrlContext
where ctrlContext!= null
select new Filter(ctrlContext, FilterScope.Global);
}
}
=============================================================================
In Global.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
MyFilterProvider provider = new MyFilterProvider();
provider.Add(d => d.RouteData.Values["action"].ToString() != "SkipFilterAction1 " ? new NHibernateActionFilter() : null);
FilterProviders.Providers.Add(provider);
}
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
Well, I think I got it working for ASP.NET Core.
Here's the code:
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Prepare the audit
_parameters = context.ActionArguments;
await next();
if (IsExcluded(context))
{
return;
}
var routeData = context.RouteData;
var controllerName = (string)routeData.Values["controller"];
var actionName = (string)routeData.Values["action"];
// Log action data
var auditEntry = new AuditEntry
{
ActionName = actionName,
EntityType = controllerName,
EntityID = GetEntityId(),
PerformedAt = DateTime.Now,
PersonID = context.HttpContext.Session.GetCurrentUser()?.PersonId.ToString()
};
_auditHandler.DbContext.Audits.Add(auditEntry);
await _auditHandler.DbContext.SaveChangesAsync();
}
private bool IsExcluded(ActionContext context)
{
var controllerActionDescriptor = (Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor;
return controllerActionDescriptor.ControllerTypeInfo.IsDefined(typeof(ExcludeFromAuditing), false) ||
controllerActionDescriptor.MethodInfo.IsDefined(typeof(ExcludeFromAuditing), false);
}
The relevant code is in the 'IsExcluded' method.
You can change your filter code like this:
public class NHibernateActionFilter : ActionFilterAttribute
{
public IEnumerable<string> ActionsToSkip { get; set; }
public NHibernateActionFilter(params string[] actionsToSkip)
{
ActionsToSkip = actionsToSkip;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (null != ActionsToSkip && ActionsToSkip.Any(a =>
String.Compare(a, filterContext.ActionDescriptor.ActionName, true) == 0))
{
return;
}
//here you code
}
}
And use it:
[NHibernateActionFilter(new[] { "SkipFilterAction1 ", "Action2"})]

Autofac, (Fluent) nHibernate, ISession "Session is closed!" intermittently

I'm having random "Session is closed!" errors with the following configuration of Autofac and Fluent nHibernate:
Global.asax.cs:
builder.Register(x => new NHibernateConfigurator().GetSessionFactory()).SingleInstance();
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerHttpRequest();
NHibernateConfigurator.cs
public class NHibernateConfigurator
{
public Configuration Configure()
{
var configuration = new Configuration();
configuration.SessionFactory()
.Proxy.Through<ProxyFactoryFactory>()
.Integrate.Using<Oracle10gDialect>();
FluentConfiguration fluentConfiguration = Fluently.Configure(configuration);
fluentConfiguration.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>());
return fluentConfiguration.BuildConfiguration();
}
public ISessionFactory GetSessionFactory()
{
var configuration = Configure();
return configuration.BuildSessionFactory();
}
}
SomeController.cs:
private readonly IRepository repository;
public SomeController(IRepository repository)
{
this.repository = repository
}
[Transaction]
public ActionResult Index()
{
var result = repository.GetUsers();
return View(result);
}
TransactionAttribute.cs
public class TransactionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
DependencyResolver.Current.GetService<ISession>().BeginTransaction(IsolationLevel.ReadCommitted);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;
if (currentTransaction.IsActive)
{
if (filterContext.Exception != null && filterContext.ExceptionHandled)
{
currentTransaction.Rollback();
}
}
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
ITransaction currentTransaction = DependencyResolver.Current.GetService<ISession>().Transaction;
base.OnResultExecuted(filterContext);
try
{
if (currentTransaction.IsActive)
{
if (filterContext.Exception != null && !filterContext.ExceptionHandled)
{
currentTransaction.Rollback();
}
else
{
currentTransaction.Commit();
}
}
}
finally
{
currentTransaction.Dispose();
}
}
}
IRepository.cs:
public interface IRepository
{
IList<User> GetUsers();
}
Repository.cs:
public class Repository : IRepository
{
private readonly ISession session;
public Repository(ISession session)
{
this.session = session;
}
public IList<User> GetUsers()
{
return session.QueryOver<User>().List();
}
}
This current set-up works, but seems to fail intermittently (after a few page reloads or restarts of Cassini) with GetUsers throwing "Session is closed!" errors. I thought registering the ISessionFactory on an InstancePerHttpRequest would avoid these issues. No luck. Any ideas? New to nHibernate and Autofac, so if I failed to post enough relevant information, let me know.
It appears my configuration is working perfectly for any nHibernate calls outside of my custom membership providers and custom role providers - they are the issue as far as I can tell, which means this question isn't really addressing the issue.
How is your Repository registered? Is it InstancePerHttpRequest (which is should be), or Singleton (which it shouldn't).
I have had a similar problem using MVC 3 action filters and seems they are cached quite aggressively in MVC 3 and found that sessions were not always being opened as OnActionExecuted not always fire.
Move your session into the controller constructor like so:
public SomeController(
ISession session,
ILogger logger,
IRepository<Something> someRepository )
{
_session = session;
_logger = logger;
_someRepository = someRepository;
}
In your action where you want to wrap a transaction:
using (var transaction = _session.BeginTransaction())
{
// do something with your repository
_someRepository.Add(new Something());
transaction.commit();
}

How do I restrict access to certain pages in ASP.NET MVC?

I wish to lock out access to a user's EDIT page (eg. /user/pure.krome/edit) if
a) Identity.IsAuthenticated = false
or they are authenticated but
b) Idenitity.Name != user name of the user page they are trying to edit
c) Identity.UserType() != UserType.Administrator // This is like a Role, without using RoleProviders.
I'm assuming u can decorate a controller or a controller's action method with something(s), but i'm just not sure what?
Look at the AuthorizeAttribute.
ASP.Net MVC: Can the AuthorizeAttribute be overriden?
A custom attribute derived from AuthorizeAttribute is what I use to do this. Override the OnAuthorize method and implement your own logic.
public class OnlyUserAuthorizedAttribute : AuthorizeAttribute
{
public override void OnAuthorize( AuthorizationContext filterContext )
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizeResult();
}
...
}
}
I implemented the following ActionFilterAttribute and it works to handle both authentication and roles. I am storing roles in my own DB tables like this:
User
UserRole (contains UserID and RoleID foreign keys)
Role
public class CheckRoleAttribute : ActionFilterAttribute
{
public string[] AllowedRoles { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userName = filterContext.HttpContext.User.Identity.Name;
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (AllowedRoles.Count() > 0)
{
IUserRepository userRepository = new UserRepository();
User user = userRepository.GetUser(userName);
bool userAuthorized = false;
foreach (Role userRole in user.Roles)
{
userAuthorized = false;
foreach (string allowedRole in AllowedRoles)
{
if (userRole.Name == allowedRole)
{
userAuthorized = true;
break;
}
}
}
if (userAuthorized == false)
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + String.Format("?ReturnUrl={0}", filterContext.HttpContext.Request.Url.AbsolutePath), true);
}
}
I call this like this...
[CheckRole(AllowedRoles = new string[] { "admin" })]
public ActionResult Delete(int id)
{
//delete logic here
}

Resources