I'm need to create a aspnet mvc app that has following verification http://domain.com/accounta/controller/view/id, this account has to be checked once in the database is validated and if it should continue in the url, otherwise the customer will be redirected to a page of nonexistent account, the problem I found is that in every controller method I'll have to be validated? There is a more peaceful for it?
ex:
public ActionResult Index()
{
if ((host != null) && (host.IndexOf(".") < 0))
{
sessao = SessionController.GetInstance();
if (sessao.Conta.dsHost != null)
{
return View(sessao.Conta);
}
else
{
using (var contexto = new ThalentoEntities())
{
sessao.Conta = contexto.TH_Conta.Single(q => q.dsHost == host && q.flAtivo == true);
if (sessao.Conta.dsHost != null)
return View(sessao.Conta);
else
return Redirect("/erro/no_account");
}
}
}
else
{
return Redirect("/erro/no_account");
}
return View();
}
abovethe code of each method in controllers..
and bellow code of global.asax
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { host= UrlParameter.Optional, controller = "principal", action = "index", id = UrlParameter.Optional }
);
You can use AuthorizeAttribute. Example:
public class CustomAuthorizeAttrinute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
// override standard error result
if (filterContext.Result is HttpUnauthorizedResult)
{
string url = "~/account/logon";
if (filterContext.HttpContext.Request != null)
url += "?rb=" + filterContext.HttpContext.Request.RawUrl;
if (LoginLib.IsLogged())
LoginLib.Logout();
filterContext.Result = new RedirectResult(url);
}
}
}
public class AdminAuthorizeAttribute : CustomAuthorizeAttrinute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return LoginLib.IsLogged<Admin>();
}
}
And then in controller
[AdminAuthorize]
public ActionResult Index()
{
var model = new FooModel();
model.Secret = "This is for admins only!";
return View(model);
}
I'd start with the routing - you should teach the routing engine to recognize the account in the url, here's how:
routes.MapRoute(
"AccountUrl",
"{account_name}/{controller}/{action}/{id}",
new { host= UrlParameter.Optional, account_name = "", controller = "principal", action = "index", id = UrlParameter.Optional }
);
You should add this code before the the "Default" route in your Global.asax.
Then you'll need to figure out a way to execute the account validation logic before each action. You can achieve this with Filters. Here's a reference code for your case:
public class ValidateAccountAttribute: FilterAttribute, IActionFilter {
public void OnActionExecuting(ActionExecutingContext filterContext) {
if (filterContext.RouteData.Values.ContainsKey("account_name") ||
!IsAccountExists((string)filterContext.RouteData.Values["account_name"]))
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new {controller = "account", action = "login"}));
}
private bool IsAccountExists(string accountName) {
// TODO: Implement
throw new NotImplementedException();
}
public void OnActionExecuted(ActionExecutedContext filterContext) {
}
}
It just validates the account_name routing value and redirects to login page if it's null. You can change the redirect url to whatever you need.
This filter can be applied globally (which is probably not what you need), to specific action or whole controller.
Hope that helps.
Related
I am trying to redirect to controller action method from appliction start method in Gloabal.asax.cs. How do i redirect when application start?
I tried :
public class SomeStartupClass
{
public ActionResult Init()
{
return new RedirectToRouteResult(
new RouteValueDictionary(
new
{
area = "",
controller = "InvoiceAddlines",
action = "King"
}
)
);
}
}
And in Global.asx.cs :
protected void Application_Start()
{
SomeStartupClass sched = new SomeStartupClass();
sched.Init();
}
So far this is not working, a i will appreciate assistance.
i had previously been handling roles/access rights with my own classes and if statements, in which the original URL of user was saved incase he is not even logged in, Once user logs in he is redirected to his original page. Now below is my old code. I am having difficulty to use same logic in my custom authorize attribute. please guide. Thank you
(Old method) Wrapper in every action method
[HttpGet]
public ActionResult Index(string DealType)
{
User user = Session["CurrentUser"] as User;
if (user != null)
{
if (user.IsInRole(RoleType.MASTER) || user.IsInRole(RoleType.VIEW))
{
// Money Shot
List<Deal> deals = dataBase.Deals.Where(d => d.DealType.Equals(DealType)).ToList();
return View(deals);
}
else
{
return PartialView("_unauthorize");
}
}
else
{
// I need to handle this part in custom attribute
return RedirectToAction("Login", "User", new { RedirectURL= string.Format("/{0}/{1}", "Deal", "Index") });
}
}
and in my login action method i used this
public ActionResult Login(User model){
//Code of matching username and password...
//Validations/ exceptions handling of incorrect passwords
if (!string.IsNullOrEmpty(RedirectURL))
{
return Redirect(RedirectURL);
}
else
{
return RedirectToAction("Index", "Home");
}
}
Now since i learned about custom attributes i applied them like below
public class AuthorizeUserAttribute : AuthorizeAttribute
{
public AuthorizeUserAttribute(params RoleType[] roleTypes)
{
AccessLevels = roleTypes;
}
// Custom property
public RoleType[] AccessLevels { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
User user = HttpContext.Current.Session["CurrentUser"] as User;
if (user != null)
{
if (user.IsInRole(AccessLevels))
{
return true;
}
else
{
return false;
}
}
else
{
//redirect URL should be save here but this is boolean method!
return false;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "User",
action = "Unauthorised"
})
);
}
}
i used them like this
[AuthorizeUser(RoleType.DELETE, RoleType.ADMIN)]
Now issue is that if user has completely not even logged in the URL which he was accessing should be saved and once he logs in he should be redirected to where he came from. Hope i explained it well.
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
RedirectToRouteResult routeData = null;
var returnUrl = string.Empty;
if(filterContext.HttpContext.Request.Url != null)
returnUrl = filterContext.HttpContext.Request.Url.LocalPath;
if (CurrentUser == null)
routeData = new RedirectToRouteResult(
new RouteValueDictionary(new {controller = "Account", action = "Login", returnUrl = returnUrl}));
else
routeData = new RedirectToRouteResult(
new RouteValueDictionary(new {controller = "Error", action = "AccessDenied"}));
filterContext.Result = routeData;
}
In the code above (inside of your custom AuthorizeAttribute) you can capture the return URL using the available request information.
This will make your returnUrl available within the Request.QueryString[] dictionary.
On your Login view you'll need to put something like this to make it actionable.
#{
ViewBag.ReturnUrl = Request.QueryString["returnUrl"];
}
and then in your login form:
#using (Html.BeginForm("Login", "Account", new {returnUrl = ViewBag.ReturnUrl}, FormMethod.Post, new{#class="form-horizontal form-material", #onsubmit="return loading_event();", #id="loginForm"}))
I have an MVC app where I have an Admin area. When user is not logged in I'm redirecting the request to login page with custom AuthorizeAttribute.
public class UserAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = AdminGlobals.CurrentUser;
if (user == null || !user.IsActive)
{
return false;
}
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var user = AdminGlobals.CurrentUser;
var context = HttpContext.Current;
//Do not redirect if the request is already redirecting
if (context.Response.IsRequestBeingRedirected) return;
if (user == null || !user.IsActive)
{
context.Response.Redirect("/login", true);
}
else
{
context.Response.Redirect("/unauthorized", true);
}
}
}
Controller and action:
[UserAuthorize]
public ActionResult Index()
{
return View();
}
And in the view I have:
Hello #AdminGlobals.CurrentUser.Title
When the application is in debug mode I'm having NullReferenceExceptions for inside the view and also the layout page since it tries to use same variable #AdminGlobals.CurrentUser which is null. I know I can easily prevent this but I cannot understand why the view is rendered when the request is not authorized. Is it possible to use RedirectToAction inside AuthorizeAttribute to prevent rendering of the view?
I'm not sure that using Response in a filter is the way to go. Typically, you leverage the filterContext to perform your redirect. For example:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var user = AdminGlobals.CurrentUser;
var context = filterContext.HttpContext;
//Do not redirect if the request is already redirecting
if (context.Response.IsRequestBeingRedirected) return;
var routeData = new RouteValueDictionary(new {
controller = "Home",
action = "Unauthorized"
});
if (user == null || !user.IsActive)
{
routeData = new RouteValueDictionary(new { controller = "Home", action = "Login" });
}
filterContext.Result = new RedirectToRouteResult("Default", routeData);
}
I'm assuming from your code that both actions are on the HomeController; otherwise, update the controller name to the correct value.
"Default," in the constructor for the RedirectToRouteResult object, is the name of the route in your RouteConfig that you want to be applied.
Also, use the HttpContext from the filerContext.
I have a custom auth set up so that a user is stored as a session variable. Once they go through the Account/LogIn process I store the details returned from a 3rd party API as a user in the session like this:
Session["User"] = new UserViewModel(result);
I want to check the user is present before every controller action so I have made a BaseController with the following check in it:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Session["User"] != null)
base.OnActionExecuting(filterContext);
else
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { action = "LogIn", controller = "Account" }));
Each of the controllers then inherits from the BaseController so that it redirects to the Log In page if there is no user. I don't inherit from the BaseController for the AccountController so that it doesn't get in an infinite loop of checking and redirecting, but I also want to have specific pages not check for log in. Is there any way to do this, i.e. write an exception rule in the same way that you might have [AllowAnonymous]?
You could use a filter on those methods as:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class ActionCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower().Trim();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower().Trim();
// this is just a sample.. you can implement any logic you want
if (!actionName.StartsWith("your method name") && !controllerName.StartsWith("your controller name"))
{
var session1 = HttpContext.Current.User.Identity.Name;
HttpContext ctx = HttpContext.Current;
//Redirects user to login screen if session has timed out
if (session1 == null)
{
base.OnActionExecuting(filterContext);
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Account",
action = "LogOff"
}));
}
}
}
}
then on the controllers put the attribute as:
[ActionCheck]
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
}
or on specific action methods as:
[ActionCheck]
public Actionresult SomeMethod()
{
return View();
}
I want to make the following:
when the url doesn't have an instID, i want to redirect to the "Instelling" action
in this controller, every method needs the instID.
[RequiredParameter(parameterName="instID", controllerToSend="Instelling")]
public ActionResult Index(int? instID) {
//if (!instID.HasValue) {
// return RedirectToAction("Index", "Instelling");
//}
var facts = _db.Instellingens.First(q => q.Inst_ID == instID).FacturatieGegevens;
return View(facts);
}
so this is in the controller.
the actionfilter:
namespace MVC2_NASTEST.Controllers {
public class RequiredParameterAttribute : ActionFilterAttribute {
public string parameterName { get; set; }
public string actionToSend { get; set; }
public string controllerToSend { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (parameterName != string.Empty) {
if (filterContext.ActionParameters.ContainsKey(parameterName) && filterContext.ActionParameters[parameterName] != null) {
string s = "test";
//all is good
} else {
//de parameter ontbreekt. kijk of de controller en de action geset zijn.
if (actionToSend == string.Empty)
actionToSend = "Index";
if (controllerToSend == string.Empty) {
controllerToSend = filterContext.Controller.ToString();
controllerToSend = controllerToSend.Substring(controllerToSend.LastIndexOf(".") + 1);
controllerToSend = controllerToSend.Substring(0, controllerToSend.LastIndexOf("Controller"));
}
UrlHelper helper = new UrlHelper(filterContext.RequestContext);
string url = helper.Action(actionToSend, controllerToSend);
HttpContext.Current.Response.Redirect(url);
//filterContext.HttpContext.Response.Redirect(url, true);
}
}
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {
base.OnActionExecuted(filterContext);
}
}
}
the thing is: it does work, however, the action itself first gets executed, THEN the redirect happens. this is not what I wanted.
Perhaps i shouldnt use actionfilters but just add a route?
in this case, how would i redirect the route to another controller if the instID is missing?
Rather than creating an action filter (which runs just before the return of the action method), you could consider changing to an Authorization Filter which would allow you to redirect to an alternative controller & action
Something like this (pseudo code):
public class RequiredParameterAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// read instID from QueryString
// if instId is null, return false, otherwise true
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.result = new RedirectToRouteResult( new { controller = "MyController" , action = "MyAction" } )
}
}
This was the first result on a question I asked on Google, so I'd like to propose a different answer. Instead of doing the redirect from the action assign a redirect to the filterContext.Result like this:
filterContext.Result = new RedirectResult(url);
If the result property of the filterContext is not null then the underlying action will not be executed. Because you're performing a redirect outside the context of the call you will still execute the action method.