Overriding OnAuthorization in ASP.NET MVC in base controller - asp.net

In my ASP.NET MVC application, I'm trying to figure out whether the user has access to a particular controller, restricted by the authorize data annotation as follows
[Authorize(Roles = "user")]
I'm attempting to override OnAuthorization in order to check:-
If the request is authenticated (which works great)
If the user is authorised to access the requested view (which doesn't work)
My user roles are stored in a SessionManager object I've created - SessionManager.ActiveUser.Roles
Here's what I have in the form of pseudo-code but if anybody could help me get this right, I'd really appreciate it.
public class HomeBaseController : Controller
{
protected override void OnAuthorization(AuthorizationContext context)
{
if (context.HttpContext.User.Identity.IsAuthenticated)
{
// these values combined are our roleName
bool isAuthorised = context.HttpContext.User.IsInRole(context.RequestContext.HttpContext.User.Identity.);
if (!context.HttpContext.User.IsInRole(---the roles associated with the requested controller action (e.g. user)---))
{
var url = new UrlHelper(context.RequestContext);
var logonUrl = url.Action("LogOn", "SSO", new { reason = "youAreAuthorisedButNotAllowedToViewThisPage" });
context.Result = new RedirectResult(logonUrl);
return;
}
}
}

As far as overriding OnAuthorization according to ProASP.NET MVC3 Book they do not recommend overriding it since the default implementation of this method securely handles content cached using OutputCache Filter.
If you are looking for Custom Authentication (using Forms Auth) and Authorization (Using Role provider logic then below is how I secured my application.
EDIT: The following logic uses in-built forms authentication and roles manager. Once user is authenticated and authorized the User Identity can be used to check both the authentication (User.Identity.IsAuthenticated) and the roles User.IsInRole("admin")
In Web.Config:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="15" slidingExpiration="true" enableCrossAppRedirects="false" protection="All" />
</authentication>
<roleManager enabled="true" defaultProvider="MyRolesProvider" cacheRolesInCookie="true" cookieProtection="All">
<providers>
<clear />
<add name="MyRolesProvider" type="MyApp.Library.CustomRolesProvider" />
</providers>
</roleManager>
For Role Authorization Extend RoleProvider and override methods as required.
public class CustomRolesProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// You need to return string of Roles Here which should match your role names which you plan to use.
//Some Logic to fetch roles after checking if User is Authenticated...
return new string[] { "admin" , "editor" };
}
//Rest all of them I have kept not implemented as I did not need them...
}
In Your controller Now you can use this:
[Authorize(Roles="Admin")]
public class AdminController : Controller
{
....
}
For Authentication I have implemented my custom Authentication Check but I still use Forms Authentication:
//This one calls by Custom Authentication to validate username/password
public ActionResult LogOn(LogOnViewModel model, string returnUrl)
{
if(Authenticate("test","test"))
{
.......
}
}
public bool Authenticate(string username, string password)
{
//Authentication Logic and Set the cookie if correct else false.
//..... your logic....
//.....
FormsAuthentication.SetAuthCookie(username, false);
}

Related

ASP.NET web application project ProfileBase.Create(userName) method always returns profile of current user

I am using this article to implement ASP.NET SQLMembership Profile Provider in my ASP.NET 4.0 Web Application project (NOT website project). I am using Forms authentication. On my admin page I need to display the profile info of all the users. But for some reason, the following code is always returning the profile of the current user instead of each user whose username is provided:
ProfileInfoCollection oProfInfoColl = ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All);
string sUserName = "";
foreach (ProfileInfo oPi in oProfInfoColl)
{
sUserName = oPi.UserName;
UserProfile oPc = UserProfile.GetUserProfile(sUserName);
oDataTbl.Rows.Add(new string[] { oPc.Company, oPc.FirstName, oPc.LastName, sUserName, Roles.GetRolesForUser(sUserName)[0] });
}
In the above code oPi.UserName correctly returns the username of each user but GetUserProfile(sUserName) always returns the profile of the currently logged in user.
When I display the Profile properties (FirstName, LastName etc.) on the page I see that these properties are for the currently logged in user in each row even though the UserName and roles of each users are displayed correctly in each row for the specified user. Please note that the username and the roles are not coming from the profile.
My UserProfile custom class is:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Profile;
using System.Web.Security;
namespace WebAppName
{
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
return Create(username) as UserProfile;
}
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
[SettingsAllowAnonymous(true)]
public string Company
{
get
{
return (string)HttpContext.Current.Profile.GetPropertyValue("Company");
}
set
{
HttpContext.Current.Profile.SetPropertyValue("Company", value);
}
}
[SettingsAllowAnonymous(true)]
public string FirstName
{
get
{
return (string)HttpContext.Current.Profile.GetPropertyValue("FirstName");
}
set
{
HttpContext.Current.Profile.SetPropertyValue("FirstName", value);
}
}
[SettingsAllowAnonymous(true)]
public string LastName
{
get
{
return (string)HttpContext.Current.Profile.GetPropertyValue("LastName");
}
set
{
HttpContext.Current.Profile.SetPropertyValue("LastName", value);
}
}
}
}
My web.config file has following Profile section:
<profile inherits="WebAppName.UserProfile" enabled="true" defaultProvider="ProjectNameSqlProvider" automaticSaveEnabled="true">
<providers>
<clear />
<add name="ProjectNameSqlProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="MyConnectionString" applicationName="ProjectName" />
</providers>
</profile>
I found my own mistake. In my UserProfile class I am getting and setting the properties of current user; whereas, Jon Galloway in his article that I referred to in my original post is clearly getting/setting the properties of specified user - that is what I should have done. Thanks to those who may have tried to help.

Web user and Admin restriction to certain controllers and views

I am developing a cloud application on ASP.Net MVC. I have problems in implementation of login system.
What I did
I used FormsAuthentication.SetAuthCookie(...) for login of web users.
and Authorize in controller like that
[Authorize]
[HttpGet]
public ActionResult AdminPage(){...}
What the Problem is
When i sign in with web users credentials and tries to access admin controllers it have nothing to stop that user and he can access all(including admin pages).
What type of Login implimentation should i use to tackle this. Remember keep the security in mind.
I also saw ASP.Net MemberShip Class but it generates Database on its own. Can it be mold and works good with user define Database.
You can specify roles on the Authorize attribute.
Example
[Authorize(Roles="Admin")]
This means that only users that have a role of admin will have access to that method.
Update
If you are using custom roles, you will need to create a Custom Role Provider. This will allow you to override the RoleProvider methods and add your own logic.
Example
public class CustomRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
// Custom logic
}
public override string[] GetRolesForUser(string username)
{
// Custom logic
}
public override string[] GetAllRoles()
{
// Custom logic
}
}
A detailed tutorial can be found here Custom Role Providers
I do not want to steal Colin Bacon's answer. Instead, I'll add some additional information; I hope second opinion won't hurt.
You need [Authorize(Roles = "Admin")] to restrict access to Admin role only like this -
[Authorize(Roles = "Admin")]
public ActionResult AdminPage(){...}
Since your MVC application already uses FormsAuthentication, you do not need to use MembershipProvider.
However, you need to implement Custom Role Provider and override GetRolesForUser method (the rest of the methods are optional).
Basically, AuthorizeAttribute will call GetRolesForUser method when a user access an action with [Authorize(Roles = "Admin")].
public class CustomRoleProvider : RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// Query admin table.
if(user is in admin table)
return new[] {"Admin"};
return new[] {};
}
....
}
first in web.config add this (system.web)
<roleManager enabled="true" defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleRoleProvider,WebMatrix.WebData" />
</providers>
</roleManager>
<membership defaultProvider="simple">
<providers>
<clear />
<add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider,WebMatrix.WebData" />
</providers>
</membership>
then add this line to Global.asax in Application_Start
WebMatrix.WebData.WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile", "UserId", "UserName", autoCreateTables: true);
then you can add users to roles like this
var roles = (SimpleRoleProvider)Roles.Provider;
roles.AddUsersToRoles(new[] { "username1", "username2", ... },
new[] { "role1", "role2", .... });
then on the controllers or methods you can add AuthorizeAttribute like below
[Authorize(Roles="role1,role2,...")]

Application_AuthenticateRequest infinite loop

I recently added Forms-based Authentication to an MVC 3 project. There seems to be a problem between my Application_AuthenticateRequest function(Global.asax.cs file) and my settings for my Web.Config file because my Application_AuthenticateRequest function seems to get called infinitely. How can I change my configurations for this to work properly and how can I allow a user access to both the login page and the default page, while still denying access to the other pages?
//Global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie)
{
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch (Exception ex)
{
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket)
{
// Cookie failed to decrypt.
return;
}
string[] roles = authTicket.UserData.Split('|');
// Create an Identity object
FormsIdentity id = new FormsIdentity(authTicket);
// This principal will flow throughout the request.
UserPrincipal principal = new UserPrincipal(id, roles);
// Attach the new principal object to the current HttpContext object
Context.User = principal;
Thread.CurrentPrincipal = principal;
}
//Web.Config
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn"
protection="All"
cookieless="UseCookies"
slidingExpiration="false"
timeout="30" />
</authentication>
<authorization>
<deny users="?" />
<allow users="*"/>
</authorization>
Per the comments on Rob's answer...
"So technically, I need all pages blocked except for default, login and registration pages."
You can add the AuthorizeAttribute to the GlobalFilterCollection which is a collection of filters that get applied to all actions on controllers. Then, on your controllers or actions you can add [AllowAnonymous] to the specific ones you want anyone to access. See below for an example.
Create a file called FIlterConfig.cs in the App_Start folder
If this exists already, just add the line: filters.Add(new AuthorizeAttribute());
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
}
This will require every Controller and Action to use Authorization by default.
You can make a Controller or Action not authorized by adding the following to your Action or Controller like so.
[AllowAnonymous]
public class MyController
{
public ActionResult MyAction()
{
return View();
}
}
All actions in that controller will be available.
OR
public class MyController
{
[AllowAnonymous]
public ActionResult MyAction()
{
return View();
}
}
Only that action on the controller will be available.
This will be called for every request, not just when the user logs in for the first time.
You can use the [Authorize] attribute to limit access to certain controllers or even methods.
I'd suggest looking through some tutorials or the documents to understand how authentication works in MVC:
http://msdn.microsoft.com/en-us/library/ff398049(v=vs.98).aspx

how to work with asp.net mvc project that's have only one user

I am working on a project to be used in a company. The system will have only 1 administrator account.
The administrator will add system users and each user can create his own contacts.
I created a WCF service to connect with the database, an asp.net mvc3 project for admin, and another WPF app for system users.
My questions is:
I have only one user (admin) to log in with this asp.net mvc project:
how do I work with this situation?
I think membership provider and database are not required because I am only working with one user, right??
Try this:
web.config:
<authentication mode="Forms">
<forms loginUrl="~/Admin/LogOn" timeout="2880" >
<credentials passwordFormat="SHA1">
<user name="admin" password="5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"/>
</credentials>
</forms>
</authentication>
Password format is set to SHA1, so Your password won't be visible in clear text. It still can be cracked though. Generate Your own hash using online SHA1 generator for example.
loginUrl is a route to Your login page (duh :P), so change it if it's different.
CredentialsViewModel:
public class CredentialsViewModel
{
[Required]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
View model for Your login view.
AdminController:
public ViewResult LogOn()
{
return View();
}
[HttpPost]
public ActionResult LogOn(CredentialsViewModel model, string returnUrl)
{
if(ModelState.IsValid)
{
if(FormsAuthentication.Authenticate(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return Redirect(returnUrl ?? Url.Action("Index", "Admin"));
}
else
{
ModelState.AddModelError("", "Incorrect username or password");
}
}
return View();
}
[Authorize]
public ViewResult Index()
{
return View();
}
So LogOn action will authenticate credentials passed from the view; compare it with web.config data.
Important part here is [Authorize] attribute which will prevent access from unauthorized users.
You'll definitely need a User table to handle non-Admin accounts. It would be trivial to add the Admin user to this table as well, and add a column labeled "IsAdmin" where only that one is set to true. Membership and Role providers can do this nicely, and may be faster to build what you need, though they tend to barf all over your database.
.. as a side-note, though you're quite confident there will only ever be one Admin account, I suspect that requirement will change eventually, and probably without notice.

How do you get asp.net anonymous authentication token for profile support?

So I have an asp.net Web Application (Not Web Site) that I am trying to support profiles for anonymous users. I have a form and I want anonymous users to be able to enter their name and email only once, and have that information automatically accessible on the next load for them.
In my Web.config I have anonymous ID setup like so:
<anonymousIdentification enabled="true" cookieless="AutoDetect" />
I have my profile section setup like this:
<profile defaultProvider="SqlProvider" enabled="true" inherits="QA_Web_Tools.UserProfile">
<providers>
<clear />
<add connectionStringName="QAToolsConnectionString" name="SqlProvider"
type="System.Web.Profile.SqlProfileProvider" />
</providers>
</profile>
Finally, due to my app being a Web App and not a Web Site, I am using the profiles via this custom object:
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
return Create(username) as UserProfile;
}
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
[SettingsAllowAnonymous(true)]
public string FullName
{
get { return base["FullName"] as string; }
set { base["FullName"] = value; }
}
[SettingsAllowAnonymous(true)]
public string BuildEmail
{
get { return base["BuildEmail"] as string; }
set { base["BuildEvmail"] = value; }
}
}
This code is based off of this reference.
The issue is that that code does not support anonymous users, or if it does I don't know how. I can't use the GetUserProfile() method with no parameters because if the user is anonymous, Membership.GetUser() is null. I could pass in the anonymous ID token into the first GetUserProfile(string username) method but I cant' find any way to get the anonymous ID token for the current user.
Does anyone know how to get this information? Google doesn't seem to be returning useful results.
Thanks,
Success!
I changed:
public static UserProfile GetUserProfile()
{
return Create(Membership.GetUser().UserName) as UserProfile;
}
to
public static UserProfile GetUserProfile()
{
return HttpContext.Current.Profile as UserProfile;
}
and now it works!

Resources