MVC RoleProvider and Authorize attribute - c#

I have implemented my own role provider, and I'm not using the default one. It works to the point that it can tell when someone should or should not be able to view a page.
However, can it do the following:
If a user is not logged in, redirect to my login page
If a user IS logged in but does not have the correct role, redirect to a different page
I haven't figured out how to do this with the Authorize attribute, all I have is:
[Authorize(Roles="Admin")]
Basically I need to redirect to a different page based on what part of the authorization fails.
I've looked to see if it were something in web.config but nothing obvious jumps out.

VoodooChild answered #1.
For #2 -
What you can do is check if the user is logged on the login page and display a different message or an entirely different page (or even do a redirect to a different action).
Alternatively you can create your own authorization attribute. This will require that you use this attribute everywhere instead of the default AuthorizeAttribute
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "ActionName" },
{ "controller", "ControllerName" }
});
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
Update:
Just thought of another method. When a redirect is done to login page from a different page, a querystring ReturnUrl is also passed. So you can also check if it contains something AND the user is authenticated, chances are the user didn't have permission to view that page.

Off the top of my head, if you are using FormsAuthentication then to answer your first question - yes If the user is not Authenticated or logged in then it can be redirected to the log on page:
Make sure you have this in web.config file (not sure if you need anything beside this, will look into it..)
<authentication mode="Forms">
<forms loginUrl="~/AccountController/LogOn" timeout="2880" />
</authentication>
To answer your second question: "If a user IS logged in but does not have the correct role, redirect to a different page"
The way we did this was, we used the System.Web.Security.Roles.GetRolesForUser(username); method to get the Roles and based on this we redirected the user to the correct view, after login.
Hope this helps!

Related

ASP.NET FormsAuthenticationModule DefaultUrl overriding explicit

I have a mature ASP.NET web application using FormsAuthentication (FA) to manage logins. Under certain situations, I would like to redirect the "just logged in" user to a different URL to the one that FA uses. As per standard functionality, FA will redirect to our normal homepage (specified in web.config) unless a redirectUrl was used when it hits a page that requires an authenticated user.
In my system, after the user's username/password is validated I typically use
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Also calls SetAuthCookie()
which handles most situations. However, depending on certain conditions (primarily based on the newly logged in user's role) I want to redirect to a different destination. My thoughts for doing this are to call SetAuthCookie() myself and then use Response.Redirect(myUrl, false); and ApplicationInstance.CompleteRequest().
Despite doing this, the very next request comes in using for the URL defined in my tag of web.config.
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="120" cookieless="UseCookies" defaultUrl="~/?raspberry=true" />
</authentication>
Here is the actual code I am using (if a different url is required, it is specified by the overrideUrl parameter:
internal static void CreateTicket(string userName, string overrideUrl)
{
// Ref: http://support.microsoft.com/kb/301240
if (overrideUrl == null)
{
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Includes call to SetAuthCookie()
}
else
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie: true, strCookiePath:FormsAuthentication.FormsCookiePath);
HttpContext.Current.Response.Redirect(overrideUrl, false);
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
If I pass in a value of /special/path for overrideUrl I would like the next request to come in to be '/special/path'. Instead I am seeing /?raspberry=true
Is something else forcing defaultUrl?
Is there a way to "snoop" into the Response object while debugging to see if a Redirect is already in place? or set a breakpoint whenever it gets set so I can look at the call stack?
EDIT: At the end of my method, the Response object is showing the following properties:
RedirectLocation: "/special/path"
Status: "302 Found"
StatusCode: 302
StatusDescription: "Found"
IsRequestBeingRedirected: true
HeadersWritten: false
which all looks absolutely correct.
Thanks for any advise.
Okay, I can see what is causing it. It is the fact that I am using the Login Web Control and running my code as part of the Authenticate event. Looking at the reference source for the Login Web Control, the Authenticate event is raised by its AttemptLogin method (search for it in ref source). After raising the event and seeing that Authentication was successful, it then goes on to:
Call SetAuthCookie itself (I've already done this myself but presumably the only thing I should be doing in my code is determining if authentication was successful or not, and not messing with AuthCookie or redirects)
Performing a Redirect (overwriting my carefully crafted Redirect)
I'm going to have to figure out a solution as there these methods are private (can't override by inheriting the usercontrol) and there appears to be no option for overring or suppressing the user of it's GetRedirectUrl().

Rewriting Url in ASP.Net MVC

I am struggling over this issue since yesterday.I am working on a web application which includes email service.Email includes 'link' to a certain page of that application.
Now:I have 2 scenarios:
1) If the user is logged in(in the application) already and he hit
the url he got in his email,the link will open perfectly.
2) If the user is not logged in(in the application) then the url
will not open and will redirect itself to the login page with the
functionality added in the BaseController.
*
Now what I want is when the user logs in after hitting the url and on
successfully login the user gets redirect to the link provided in the
Email.
*
for eg: If the user gets an email to the detail page of an employee,So on successfully login the user gets redirect to the Detail page of the employee.
Something like appending the redirecturl to the login page.
I think rewriting url is what I should be doing.
But I dont know how can I use that in this case.Help me out.
The default project template that comes with ASP.NET MVC 5 behaves exactly as you describe.
If you want to redirect to a custom login URL, reconfigure the LoginPath property of the CookieAuthenticationOptions object
LoginPath = new PathString("/Account/Login")
In the default template this is done in the Startup.Auth.cs class.
NOTE: If you are using an old version of ASP.NET MVC, the default project template behaved in the same way. But previously this was implemented using Forms Authentication, so in order to redirect to a custom login URL you would then have to set the loginUrl attribute of the <forms> tag in the Web.config file
By default if a user tries to access the authorized page when he is not authorized the automatically gets redirected to the log in page or the page which is configured in web.config file for the element. And you can see the query string returnUrl having the url that was tried to access initially get appended to the log in url.
To access the return url, include a new parameter as returnUrl and maintain the return url in a hidden field by model data to access on post back for redirection.
If the user is authenticated then on post back then redirect the user to the specified page what he intended to go for.
I don't remember exactly but few month ago I implemented similar functionality and i had to save returnUrl explicitly (due to MVC bug or something) - Refer this link
AccountController.cs - Snapshot
[HttpGet]
[AllowAnonymous]
public ActionResult Login(string returnUrl, string userName)
{
// You login method logic....
// Add this line to save the returnUrl value
ViewBag.ReturnUrl = returnUrl;
}
Login.cshtml - Snapshot
#using (Html.BeginForm("Login", "Account", FormMethod.Post ,new {ReturnUrl = ViewBag.ReturnUrl}))
{
<input type="hidden" name="ReturnUrl" value="#Request.QueryString["ReturnUrl"]" />
// .....
}
See if this helps in your case.

Notify user of inadequate role ASP.NET MVC 4 simplemembership simplerole

I am using ASP.NET MVC4 SimpleMembership and SimpleRoleProvider to determine authorization before exposing certain methods.
For example:
[Authorize(Roles = "Admin,Corporate")]
public ActionResult Edit(int id = 0)
{
//some code
return View(model)
}
If the user is not in the "Admin" or "Corporate" role (or their session has expired), they are correctly sent to the /Account/Login page.
However, one tester brought up a good point that once on this Login page, there is no hint as to why the user was sent here. If they simply aren't authorized to access the page they are trying to access, they keep logging in again and again and thinking the site is broken.
Ordinarily, I would add a property to the model or pass an optional parameter in the url with a message such as,
You do not have adequate permissions to access that page.
Please log in as an administrator.
or something to that effect. However, because the filter happens before they enter the method, where / how would I add the message?

Restrict access to the site for specific role

I have 3 roles: Registered Users, Approved Users, and Admins.
Access to the site is only available to Approved users and Admins.
To restrict anonymous access I've added a filter in FilterConfig as follows:
filters.Add(new System.Web.Mvc.AuthorizeAttribute());
Now, for registered users I want them to redirect to a landing page saying:
Please contact one of the administrators to approve you.
I'm not really sure what's the correct way to do that.
I can setup authorize attribute on each of the controllers, but I'm not sure if that's a good way.
Also, I'm not sure where I should specify default redirect action based on the role.
I know that I can specify default redirect action in RouteConfig.cs but not sure where to specify a role.
StanK is right that having [Authorize] attribute will redirect all users who are not logged-in to the login page. That's half of your dillema.
From there you need to alter your logon method to check if a newly logged-in user has the right role (e.g. ConfirmedUser). This is tricky because User.IsInRole("ConfirmedUser") will always be false in your logon method. This is because the User object is populated by the http object, which will not be re-populated until the next re-cycle. Luckily, you can use the Roles.IsUserInRole(userName, "ConfirmedUser") to check if the user has the right role.
So, within your logon method, after authenticating user, log the user out and re-direct them to an [AllowAnonymous] method which informs them that they are not yet confirmed.
if (Roles.IsUserInRole(userName, "ConfirmedUser")
{
FormsAuthentication.SignOut();
return RedirectToAction("WarningMsg", "Home");
}
You should be able to use the [Authorize] attributes for this.
Restricted pages will have their controller or action decorated with [Authorize(Roles="Approved User,Admin")], the 'landing page' for registered users would be [Authorize(Roles="Registered User,Approved User,Admin")] and the Logon action would have [AllowAnonymous].
If the user is not authorised, they would be re-directed to Account/Login. You would need to build some logic in this action that redirects "Registered Users" who are already logged in to your landing page. Others should just see the standard login page.
EDIT
The logic to redirect "Registered Users" from the login page to the landing page would look something like this
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
if (User.Identity.IsAuthenticated && Roles.IsUserInRole("Registered User"))
return RedirectToAction("LandingPage");
ViewBag.ReturnUrl = returnUrl;
return View();
}

What is a quick way I can add simple authentication to a few ASP.NET MVC routes, without implementing the whole Membership provider jazz?

I've created a demo website for my boss and one of the requirements is I need to add some simple authentication to his 3 admin views/routes.
What is the simplest, quickest way I can do this without implementing a whole membership provider? I honestly don't even care if the user/pass is hardcoded on the server side, I just need it so they can't access those 3 views without having authenticated in some way.
I would go this route.
Add this to your web.config (could omit the SHA1 and use a plain text password if you want):
<authentication mode="Forms">
<forms loginUrl="~/admin" timeout="2880">
<credentials passwordFormat="SHA1">
<user name="admin" password="4f3fc98f8d95160377022c5011d781b9188c7d46"/>
</credentials>
</forms>
</authentication>
Create a simple view for username and password and in the action method that receives the username and password go with this...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string username, string password)
{
if (FormsAuthentication.Authenticate(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
return RedirectToAction("Index", "Home");
}
else
{
ViewData["LastLoginFailed"] = true;
return View();
}
}
FormsAuthentication.Authenticate() automatically checks the username and password against the credentials node we created earlier. If it matches it creates your auth cookie with a "Remember Me" value of false and redirects you to the index view of your home controller. If it doesn't match it returns to the login page with ViewData["LastLoginFailed"] set to true so you can handle that in your view.
PS - Now that you have an easy way of authorizing don't forget to put the [Authorize] filter over the actions or controllers you want to protect.
easiest would be to select the menu [project] followed by [ASP.NET Configuration] in Visual Studio.
It'll set up a membership db for you. then add a couple of roles and users in the configuration manager that pops up.
that's it! Then simply decorate your actions/controllers with [Authorise] and check for some rights based on the user name. <= hard coded for the demo

Categories