asp.net MVC confused by custom attributes creation - asp.net-mvc

hi i am trying to create a custom attribute for my MVC application so that i can call [CheckLogin] this is to check my cookie as i am not using forms authentification.
i have created a class CheckLogin and this is in my App_Code folder and the code is as follows:
using System.Web.Mvc;
using System.Attributes;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web;
using System;
namespace corian_MVC.Controllers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public CheckLoginAttribute() {}
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/Admin/Login",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
}
what it does is not important here, the problem is i cant get my code to recognise this attribute if it is one in the first place, also how do i redirect to action if the login is failed ????
many thanks
my admin class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace corian_MVC.Controllers
{
[HandleError]
public class AdminController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
//check login is not banned
if ((int)Session["LoginCount"] >= 3) RedirectToAction("TooMany");
return View();
}
public ActionResult Fraud()
{
Session["LoginCount"] = 3;
return View();
}
public ActionResult TooMany()
{
return View();
}
[CheckLogin]
public ActionResult Welcome()
{
return View();
}
private void Createcookie()
{
}
}
}

This scenario is best handled by implementing an IAuthorizationFilter.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/loginUrl?ReturnUrl={0}",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
Then you can apply this attribute either at the controller level or at some particular actions.
By the way do you have any particular reason for not using the built-in FormsAuthentication?

Include .cs file with your attribute to the solution. Just placing it "near default.aspx" is not enough.

Related

Authorize a parameter for an action in MVC5

I commonly need to authorize a particular parameter to be evaluated in a service call within an action in MVC5. For instance, let's say that my action is public ActionResult Edit(string partnerName).
Today, I handle this by always evaluating if (!User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName)) throw new UnauthorizedException();
However, I would like to be able to do something like this:
[Authorize(Roles = THIS_MODULE_ID)]
public ActionResult Edit([AuthorizePartnerModule(THIS_MODULE_ID)] string partnerName)
{
...
}
To be clear, 1) I don't think the AuthorizeAttribute would be necessary if this were implemented as I envision, and 2) the thing that doesn't exist is the AuthorizePartnerModuleAttribute.
Is there a ready-made attribute or tutorial that explains how this may be accomplished? And if not, is this not advisable to do?
You could extend authorization with a custom authorization filter by creating a subclass of AuthorizeAttribute
using System.Web;
using System.Web.Mvc;
namespace Filters
{
public class AuthorizePartnerModule : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// getting the parameter from the request
string partnerName = httpContext.Request.Params["groupId"].ToString();
// custom validation
return User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName);
}
}
}
And then, you could validate your action method with:
[AuthorizePartnerModule(Roles = THIS_MODULE_ID)]
public ActionResult Edit(string partnerName)
{
...
}
Another option would be to create a custom ActionFilter (an implementation of IActionFilter). An ActionFilter implements two methods:
OnActionExecuting is executed right before the action method
OnActionExecuted is executed right after the action method execution.
So, you could make the necessary validation with something like:
using System.Web.Mvc;
namespace Filters {
public class AuthorizePartnerModule : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
// getting the parameter from the request
string partnerName = filterContext.ActionParameters["partnerName"].ToString();
// custom validation
if (!User.CanAccessPartnerModule(THIS_MODULE_ID, partnerName))
{
filterContext.Result = new HttpNotFoundResult();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// do nothing
}
}
}
In this case, however ,you would have to validate like that:
[Authorize(Roles = THIS_MODULE_ID)]
[AuthorizePartnerModule]
public ActionResult Edit(string partnerName)
{
...
}

ASP.NET MVC WebApi: No parameterless constructor defined for this object

I have an ASP.NET MVC 4 Application that I want to implement Unit of Work Pattern.
In my Web Project I have:
IocConfig.cs
using System.Web.Http;
using NinjectMVC.Data;
using NinjectMVC.Data.Contracts;
using Ninject;
namespace NinjectMVC
{
public class IocConfig
{
public static void RegisterIoc(HttpConfiguration config)
{
var kernel = new StandardKernel(); // Ninject IoC
// These registrations are "per instance request".
// See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
.InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<INinjectMVCUow>().To<NinjectMVCUow>();
// Tell WebApi how to use our Ninject IoC
config.DependencyResolver = new NinjectDependencyResolver(kernel);
}
}
}
Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace NinjectMVC
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Tell WebApi to use our custom Ioc (Ninject)
IocConfig.RegisterIoc(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
PersonsController.cs
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NinjectMVC.Data.Contracts;
using NinjectMVC.Model;
namespace NinjectMVC.Controllers
{
public class PersonsController : ApiControllerBase
{
public PersonsController(INinjectMVCUow uow)
{
Uow = uow;
}
#region OData Future: IQueryable<T>
//[Queryable]
// public IQueryable<Person> Get()
#endregion
// GET /api/persons
public IEnumerable<Person> Get()
{
return Uow.Persons.GetAll()
.OrderBy(p => p.FirstName);
}
// GET /api/persons/5
public Person Get(int id)
{
var person = Uow.Persons.GetById(id);
if (person != null) return person;
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
// OData: GET /api/persons/?firstname=\'Hans\''
// With OData query syntax we would not need such methods
// /api/persons/getbyfirstname?value=Joe1
[ActionName("getbyfirstname")]
public Person GetByFirstName(string value)
{
var person = Uow.Persons.GetAll()
.FirstOrDefault(p => p.FirstName.StartsWith(value));
if (person != null) return person;
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
// Update an existing person
// PUT /api/persons/
public HttpResponseMessage Put(Person person)
{
Uow.Persons.Update(person);
Uow.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
When I try to surf: http://www.domain.com/Persons/Get Im getting:
No parameterless constructor defined for this object.
Is there any stuff I have missed? I would appreciate any help.
Here is the zip file of the project for better references:
http://filebin.ca/E6aoOkaUpbQ/NinjectMVC.zip
Wep.API uses a different IDependencyResolver than the MVC framework.
When you use theHttpConfiguration.DependencyResolver it only works for ApiControllers. But your ApiControllerBase derives from Controller...
So your ApiControllerBase should inherit from ApiController.
Change it in your ApiBaseController.cs:
public abstract class ApiControllerBase : ApiController
{
}
If you want to inject dependencies to regular Controller derived classes you need to use ( in your IocConfig):
System.Web.Mvc.DependencyResolver.SetResolver(new NinjectMvcDependencyResolver(container));
Note that in this case you cannot use your NinjectDependencyResolver because it's for ApiControllers.
So you need a different NinjectMvcDependencyResolver which should implement System.Web.Mvc.IDependencyResolver.
public class NinjectMvcDependencyResolver: NinjectDependencyScope,
System.Web.Mvc.IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
}
I know this is an old thread, but this might be useful for someone. :)
In my case, it was missing the DependencyResolver on NinjectWebCommon.cs.
I followed this article and everything worked fine.
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;

Session across controller becomes null in MVC

My session becomes null when I redirect to another controller's Action, what should I do?
With regards to the comment you posted me, here is what I was thinking. In the Controller where you need the session use something similar to this:
//Controller A
public class TheController : Controller
{
public ActionResult Index(){
Session["yourSession"] = "Hello World";
return View();
}
}
//Controller B
public class MyController : Controller
{
string textString;
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
textString = ctx.HttpContext.Session["yourSession"].ToString();
}
public ActionResult Index(){
string currentText = textString;
return View();
}
}
I tested the suggestion from (http://stackoverflow.com/questions/889516/session-null-in-asp-net-mvc-controller-constructors), and the contents of the session were available.
You have to create a unique base controller with a session property, then all controllers within your project will inherit from that BaseController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MonitoringCSP.Controllers
{
//[AllowAnonymous]
//[Authorize(Roles = "admin")]
public class BaseController : Controller
{
private HttpSessionStateBase _session;
protected HttpSessionStateBase CrossControllerSession
{
get
{
if (_session == null) _session = Session;
return _session;
}
set {
_session = Session;
}
}
}
}
Usage sample
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using MonitoringCSP.Models;
namespace MonitoringCSP.Controllers
{
[AllowAnonymous]
public class AccountController : BaseController
{
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
/*session*/
CrossControllerSession["UserName"] = User.Identity.Name;
/*end session*/
return RedirectToAction("someAction");
}
}
}
I realized that I was clearing and destroying all sessions prior to setting the new session on login like this
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
Session.Abandon();
Session.Clear();
When I removed these lines, everything started working like #Clayton said, so I removed these lines and replaced it with Session.Remove('sessionvariablename'), I am still not sure what issue were the above lines causing, but my code started working.
Make sure your controller does not have this attribute set on it:
[SessionState(SessionStateBehavior.Disabled)]

asp.net mvc authorization problem

I am trying to add authorization to my controllers and it's not working...
I am not sure where to look in my program, but adding the
[Authorize]
filter in my controller is not working, let alone anything like
[Authorize(Roles = "Manager")]
I have been able to get this working in the default application that is provided when creating a new MVC project (i.e., I am able to make the "about" tab redirect to the login screen if I'm not logged in), so I assume I have mucked things up along the way as I've built my app. Does anyone know where I should be looking to fix this? I have users and they have roles; I'm using the ASP.net schema that is auto-created; I've examined my web.config file up and down and although I'm pretty new to this, nothing seems to be out of place. I have no clue why my authorization filters aren't working.?.
I wrote a custom attribute to solve this problem. You can attribute your controller methods as follows:
[RequiresRole(Role="Admin")]
public ActionResult Index()
{
int i = 5 + 5;
return View();
}
The code for the attribute is as follows....
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace Web.Controllers
{
public class RequiresRoleAttribute : ActionFilterAttribute
{
public string Role { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (string.IsNullOrEmpty(Role))
{
throw new InvalidOperationException("No role specified.");
}
string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;
string redirectUrl = string.Format("?returnUrl={0}", redirectOnSuccess);
string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
bool isAuthorised = filterContext.HttpContext.User.IsInRole(this.Role);
if (!isAuthorised)
{
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
}
}
}
}

Base controller class

I have a base controller class and I would like to pass a Message from the Base class to all controllers and for that message to be available to all views.
I've created a basic version below...
Section Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public ActionResult Section()
{
ViewData["Message"] = "THIS IS A TEST";
return View();
}
}
}
Home Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class HomeController : SectionController
{
public ActionResult Index()
{
return View();
}
}
}
View
<%= Html.Encode(ViewData["Message"]) %>
I know I can do this in the home controller but I'm just testing at the mo.
I'm not getting any errors with the above but I'm also not displaying the message on my view?
I'm using this tutorial http://www.asp.net/LEARN/mvc/tutorial-13-cs.aspx The Good Solution part, if that helps.
Think I've got it working now used the code below on my sectionController...
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
//return View();
}
}
}
Is this an ok solution?
You're setting your ViewData in the Section action method of your base controller, do you actually want to be setting it in the constructor of your base controller?
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
}
HomeController.Index isn't calling SectionController.Section.
Because none of the requests are mapped to action "Section" in SectionController. If you mapped a request like domain/Section/Section, you would see your message in your view (Assuming that you are using default routing and have a view named "Section").
What you need to do is, placing your message into the viewdata on a method that runs every time an action is run. You can do it in OnActionExecuting like:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewData["Message"] = "THIS IS A TEST";
base.OnActionExecuting(filterContext);
}
in the SectionController.

Resources