Web user and Admin restriction to certain controllers and views - asp.net

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,...")]

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.

Provider must implement the class 'System.Web.Profile.ProfileProvider'

Im implementing the ASP.NET Profile Provider alongside the Membership Provider using Jon Galloway's (slightly dated) example here:
and Im getting the error Provider must implement the class 'System.Web.Profile.ProfileProvider'
As per the example, I've created a custom UserProfile inheriting from ProfileBase
public class UserProfile : ProfileBase
{
public static UserProfile GetUserProfile(string username)
{
if (username != null)
{
var profile = Create(username) as UserProfile;
return profile;
}
}
public static UserProfile GetUserProfile()
{
var membershipUser = MembershipProvider.GetUser();
if (membershipUser != null)
return Create(membershipUser.UserName) as UserProfile;
}
[SettingsAllowAnonymousAttribute(true)]
public virtual string Name
{
get { return base["Name"] as string; }
set { base["Name"] = value; }
}
}
I'm calling above method from a Class library like this:
var profile = UserProfile.GetUserProfile(name);
In turn, Im calling that method from a Unit test project, which has an app.config:
<profile enabled="true" defaultProvider="MyProfileProvider" inherits="NAMESPACE.UserProfile, NAMESPACE">
<providers>
<clear/>
<add
name="MyProfileProvider"
connectionStringName="MembershipConnection"
applicationName="/"
type="NAMESPACE.UserProfile, NAMESPACE"
/>
</providers>
<properties>
<add name="Name" type="String"/>
</properties>
</profile>
Inheriting from ProfileBase (like Jon's example) should be enough right? I think i've used all variations on the 'type' and 'inherit' attributes, am I missing something here?
I've managed to at least save Profile values using a variation on this example
dynamic profile = ProfileBase.Create(UserName);
profile.Name = name;
profile.Save();
Not sure how to retrieve that same property yet tho, since ProfileBase has no such method...

Overriding OnAuthorization in ASP.NET MVC in base controller

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);
}

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