I am new to ASP.NET MVC and learning how to custom Roles using FormAuthentication from this tutorial link
This code below is stored the Roles. It works fine when I perform this [Authorize(Roles="admin")] in the controller
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Is there a way to get the actual Role Name based on the current User.Identity? like below pseudo-code.
[Authorize]
public ActionResult Index()
{
bool isAdmin = System.Web.HttpContext.Current.User.IsInRole("admin"); // This also works correctly.
Response.Write("role: " + isAdmin);
string roleName = // The Code of How to get the actual Role Name
Response.Write("roleName: " + roleName); //e.g Admin, User...
return View();
}
From Comment: Do you know any good article about OWIN cookie
authentication for custom table for username and roles?
It has few pieces, so I created a sample project in GitHub AspNetMvcActiveDirectoryOwin. The original souce is to authenticate with AD, but you just need to modify ActiveDirectoryService class where you query custom tables.
The following three are the main classes -
AccountController
ActiveDirectoryService
OwinAuthenticationService replaces FormsAuthentication.
Related
I have inherited an application with database. The database has following tables related to authentication and authorization.
User Table
UserName
Password
UserTypeId
UserType Table
UserTypeId
UserTypeDesc
The User Type table stores the roles for the user e.g. Admin, Editor, etc.
If I want to implement authorization like below
[Authorize(Roles="Admin, Editor")]
public IHttpActionResult GetOrders()
{
//Code here
}
Where and what should I code so that the roles are available to the authorize attribute ?
Edit
I already have a database. So I cannot use the AspNetUserRoles or AspNetRoles tables. I need to set the roles using my custom tables.
Edit2
As asked by #Nkosi, here is code snippet of how authentication is implemented. The actual implementation calls the business layer service and performs encryption and other stuff but I have simplified the snippet
public HttpResponseMessage Authenticate(User user)
{
var isValid = myRepository.Exists(a => a.UserName == user.UserName && a.Password == user.Password);
if(isValid)
{
FormsAuthentication.SetAuthCookie(user.UserName,false);
}
}
This method is called from the login page where user enters the UserName and Password
Using these Answers for reference
Having Trouble with Forms Authentication Roles
FormsAuthentication Roles without Membership
After having set the auth cookie on login like you did originally,
you do the following in your Global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
FormsIdentity formsIdentity = new FormsIdentity(ticket);
ClaimsIdentity claimsIdentity = new ClaimsIdentity(formsIdentity);
//get the user from your custom tables/repository
var user = myUserRepository.GetUserByEmail(ticket.Name);
if(user!=null){
var userTypeId = user.UserTypeId;
var role = myUserTypeRepository.GetUserTypeById(userTypeId);
if(role != null) {
//Assuming the roles for the user e.g. Admin, Editor, etc.
// is in the UserTypeDesc property
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role.UserTypeDesc));
}
}
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
System.Threading.Thread.CurrentPrincipal = claimsPrincipal ;
if (System.Web.HttpContext.Current != null) {
System.Web.HttpContext.Current.User = claimsPrincipal ;
}
}
}
The nice thing about how they implemented it is that it handles Claims based roles using the ClaimsIdentity and ClaimsPrincipal objects, without putting the roles in the user's cookie. It also handles authentication in the Global.asax.cs file without having to resort to putting in custom authorize attributes.
Your question was very easy. You just need to sync these 2 tables with AspNetUserRoles and AspNetRoles tables respectively. Actually, Authorize attribute by default checks these two tables. So your roles need to reflect in them. These tables are made by default by EF if you select MVC template project.
i am doing this in order to authorize user.
[Authorize(Users = #"user1, user2, user3")]
public class MyController : Controller
{
// my stuff
}
i want to do authorization from the list of user which are in database table..
This is how I got it done:
Create a new class (which inherits from AuthorizeAttribute class).
public class CustomAuthorizeAttribute : AuthorizeAttribute
Override the AuthorizeCore method (in CustomAuthorizeAttribute class) and include your custom logic in it.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isUserAuthorized = false;
// custom logic goes here
// You can get the details of the user making the call using httpContext
// (httpContext.User.Identity.Name)
// Then get the information you have stored on your db, and compare it
// with these details.
// Set isUserAuthorized to true if the values match
return isUserAuthorized;
}
Decorate your controller action method with the attribute that you just created.
[CustomAuthorize]
public ActionResult DoSomething(string something, string someOtherThing)
This link form Gotalove is helpful.
try the following:
"using the link shared by #VikasRana http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
I got rid of my enum Role and my method
public CustomAuthorizeAttribute(params object[] roles)
{ ...}
I then changed Role in my model to be a string e.g. User.Role="Admin" instead of int. In my onAuthorization method I changed it to:
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Controller.TempData["ErrorDetails"] = "You must be logged in to access this page";
filterContext.Result = new RedirectResult("~/User/Login");
return;
}
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Controller.TempData["ErrorDetails"] = "You don't have access rights to this page";
filterContext.Result = new RedirectResult("~/User/Login");
return;
}
}
and in my global.asax added this.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true && Request.IsAuthenticated== true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (GManagerDBEntities db = new GManagerDBEntities())
{
User user = db.Users.SingleOrDefault(u => u.Username == username);
roles = user.Role;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//something went wrong
}
}
}
}
"
Source: Custom user authorization based with roles in asp.net mvc
PS.: In this link, in the same post, there is a second way to fix your problem.
In the bottom of the post.
If this can't to help you, you should try it to.
Is HttpContext.Current.User in global asax not the same as HttpContext.User in an action method? I assigned the user some roles, but they seem to get lost.
The code below shows what is happening. Both Asserts get hit when a user is logged on, first in global asax, then the action method. However they give different results.
First this:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// ... omitted some code to check user is authenticated
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
string[] roles = new string[] { "admin", "user" };
HttpContext.Current.User =
new System.Security.Principal.GenericPrincipal(identity, roles);
Assert(HttpContext.User.IsInRole("admin"));
}
Then this in my action method:
public ActionResult Index()
{
bool isAdmin = HttpContext.User.IsInRole("admin");
Assert(isAdmin); // this fails, isAdmin is false
// ...
}
I used the following resources
This SO answer
http://csharpdotnetfreak.blogspot.com/2009/02/formsauthentication-ticket-roles-aspnet.html
Your question tags say "aspnet-mvc (3 and 4)", so do you have the option of using the following to make your life easier? If you are using Simple Membership from the MVC 4 Internet Application template in VS2012 this will just work out of the box for you):
WebSecurity.CreateUserAndAccount(name, password) - to create a user
Roles.AddUserToRole (and AddUserToRoles) - add a user to a role
Roles.IsUserInRole - tests if a user is in a role
[Authorize(Roles = "admin")] - [Authorize] can enforce roles on an entire controller, or on an action
CreateUserAndAccount has the advantage that it's easy to set properties for the UserProfile as well, for example:
WebSecurity.CreateUserAndAccount(newUser.UserName, newUser.Password,
new { FullName = newUser.FullName, Email = newUser.Email, Timezone = newUser.TZ });
Roles.AddUserToRoles(newUser.UserName, new[] {"admin", "user"});
Edit, I realise the above doesn't answer your original question about .User property equivalence.
HttpContext in a Controller is a property: Controller.HttpContext. HttpContext in global.asax.cs is the static class, so that's why you use HttpContext.Current. They refer to the same thing.
If you run the following code, you can see they are apparently the "same principal". So the question is what happened to the roles you assigned?
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
...
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
string[] roles = new string[] { "admin", "user" };
identity.Label = "test label";
System.Security.Principal.GenericPrincipal ppl = new System.Security.Principal.GenericPrincipal(identity, roles);
HttpContext.Current.User = ppl;
... }
public ActionResult Index() {
bool isAdmin = HttpContext.User.IsInRole("admin");
bool isAdmin2 = System.Web.HttpContext.Current.User.IsInRole("admin");
System.Web.Security.FormsIdentity identity = (System.Web.Security.FormsIdentity)HttpContext.User.Identity;
// The label is carried through from Application_AuthenticateRequest to Index.
string label = identity.Label;
}
The problem is, you assigned a GenericPrincipal to .User. Depending on the RoleProvider, this can be overwritten (e.g. by the RoleManagerModule) during PostAuthenticateRequest and (for example) turned into a RolePrincipal. This can then defer back to the database (again depending on provider) to get the roles, so over-writing your roles. If you do the work in Application_OnPostAuthenticateRequest you might be ok.
I am new MVC 3 user and I am trying to make admin through SQL database.
First of all, I have Customer entity and admin can be defined through admin field which is boolean type in Customer entity.
I want to make to access admin only in Product page, not normal customer.
And I want to make [Authorize(Roles="admin")] instead of [Authorize].
However, I don't know how can I make admin role in my code really.
Then in my HomeController, I written this code.
public class HomeController : Controller
{
[HttpPost]
public ActionResult Index(Customer model)
{
if (ModelState.IsValid)
{
//define user whether admin or customer
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["rentalDB"].ToString());
String find_admin_query = "SELECT admin FROM Customer WHERE userName = '" + model.userName + "' AND admin ='true'";
SqlCommand cmd = new SqlCommand(find_admin_query, conn);
conn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
//it defines admin which is true or false
model.admin = sdr.HasRows;
conn.Close();
//if admin is logged in
if (model.admin == true) {
Roles.IsUserInRole(model.userName, "admin"); //Is it right?
if (DAL.UserIsVaild(model.userName, model.password))
{
FormsAuthentication.SetAuthCookie(model.userName, true);
return RedirectToAction("Index", "Product");
}
}
//if customer is logged in
if (model.admin == false) {
if (DAL.UserIsVaild(model.userName, model.password))
{
FormsAuthentication.SetAuthCookie(model.userName, true);
return RedirectToAction("Index", "Home");
}
}
ModelState.AddModelError("", "The user name or password is incorrect.");
}
// If we got this far, something failed, redisplay form
return View(model);
}
And DAL class is
public class DAL
{
static SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["rentalDB"].ToString());
public static bool UserIsVaild(string userName, string password)
{
bool authenticated = false;
string customer_query = string.Format("SELECT * FROM [Customer] WHERE userName = '{0}' AND password = '{1}'", userName, password);
SqlCommand cmd = new SqlCommand(customer_query, conn);
conn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
authenticated = sdr.HasRows;
conn.Close();
return (authenticated);
}
}
Finally, I want to make custom [Authorize(Roles="admin")]
[Authorize(Roles="admin")]
public class ProductController : Controller
{
public ViewResult Index()
{
var product = db.Product.Include(a => a.Category);
return View(product.ToList());
}
}
These are my source code now. Do I need to make 'AuthorizeAttribute' class?
If I have to do, how can I make it? Could you explain to me? I cannot understand how to set particular role in my case.
Please help me how can I do. Thanks.
I know this question is a bit old but here's how I did something similar. I created a custom authorization attribute that I used to check if a user had the correct security access:
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
// Get the roles from the Controller action decorated with the attribute e.g.
// [AccessDeniedAuthorize(Roles = MyRoleEnum.UserRole + "," + MyRoleEnum.ReadOnlyRole)]
var requiredRoles = Roles.Split(Convert.ToChar(","));
// Get the highest role a user has, from role provider, db lookup, etc.
// (This depends on your requirements - you could also get all roles for a user and check if they have the correct access)
var highestUserRole = GetHighestUserSecurityRole();
// If running locally bypass the check
if (filterContext.HttpContext.Request.IsLocal) return;
if (!requiredRoles.Any(highestUserRole.Contains))
{
// Redirect to access denied view
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
}
}
Now decorate the Controller with the custom attribute (you can also decorate individual Controller actions):
[AccessDeniedAuthorize(Roles="user")]
public class ProductController : Controller
{
[AccessDeniedAuthorize(Roles="admin")]
public ViewResult Index()
{
var product = db.Product.Include(a => a.Category);
return View(product.ToList());
}
}
Your Role.IsInRole usage isn't correct. Thats what the
[Authorize(Roles="Admin")] is used for, no need to call it.
In your code you are not setting the roles anywhere. If you want to do custom role management you can use your own role provider or store them in the auth token as shown here:
http://www.codeproject.com/Articles/36836/Forms-Authentication-and-Role-based-Authorization
note the section:
// Get the stored user-data, in this case, user roles
if (!string.IsNullOrEmpty(ticket.UserData))
{
string userData = ticket.UserData;
string[] roles = userData.Split(',');
//Roles were put in the UserData property in the authentication ticket
//while creating it
HttpContext.Current.User =
new System.Security.Principal.GenericPrincipal(id, roles);
}
}
However an easier approach here is to use the built in membership in asp.net.
Create a new mvc project using the 'internet application' template and this will all be setup for you. In visual studio click on the "asp.net configuration" icon above solution explorer. You can manage roles here and assignment to roles.
I have overridden the membership methods to create a custom membership.
In the account model I've overridden the method CreateUser:
public override MembershipUser CreateUser(string username, string password,
string email, string passwordQuestion, string passwordAnswer,
bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(
username, password, true);
OnValidatingPassword(args);
if (args.Cancel)
{
status = MembershipCreateStatus.InvalidPassword;
return null;
}
if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
{
status = MembershipCreateStatus.DuplicateEmail;
return null;
}
MembershipUser u = GetUser(username, false);
if (u == null)
{
UserRepository _user = new UserRepository();
// Here I call my new method which has fields I've created in the
// User table; I'm using entity framework.
_user.CreateUser(username, password, email);
status = MembershipCreateStatus.Success;
return GetUser(username, false);
}
else
{
status = MembershipCreateStatus.DuplicateUserName;
}
return null;
}
public MembershipUser CreateUser(string username, string password,
string email)
{
using (CustomMembershipDB db = new CustomMembershipDB())
{
User user = new User();
user.UserName = username;
user.Email = email;
user.PasswordSalt = CreateSalt();
user.Password = CreatePasswordHash(password, user.PasswordSalt);
user.CreatedDate = DateTime.Now;
user.IsActivated = false;
user.IsLockedOut = false;
user.LastLockedOutDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
//Generate an email key
// user.NewEmailKey = GenerateKey();
db.AddToUsers(user);
db.SaveChanges();
//send mail
// SendMail(user);
return GetUser(username);
}
}
Now here I need to add more two fields like first name and last name but how can I pass it to the above method?
As the override method CreateUser will give me an error if I add parameters like firstname and last name into it :(
You need to implement Custom Membership User. Here is a sample implementation:
http://msdn.microsoft.com/en-us/library/ms366730.aspx
Also take a look at this thread:
Implement Custom MembershipUser and Custom MembershipProvider
Implementing Custom MembershipUser
You can leave the AspNetUsers table intact, and create a new table to store the extra information (linked to the original one). This way you'll not break any existing code in the membership provider.
The original AspNetUsers table has:
[Id],[Email],[EmailConfirmed],[PasswordHash],[SecurityStamp],[PhoneNumber],[PhoneNumberConfirmed],[TwoFactorEnabled],[LockoutEndDateUtc],[LockoutEnabled],[AccessFailedCount],[UserName]
The new table to store extra data can have for example:
[Id],[UserId][DateOfBirth],[Biography], etc.
Where [UserId] is the foreign key to AspNetUsers table.
One advantage of this approach, is that you can create multiple types of users, each storing its related info in a different table, while common data is still in the original table.
How to:
First update the RegisterViewModel to contain the extra data you want.
Update the Register method in the Account Controller, here's the original method updated with the code to insert new profile data:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// Start of new code ----------------------------------------
// Get Id of newly inserted user
int userId = user.Id; // Get Id of newly inserted user
// Create a profile referencing the userId
AddUserProfile(userId, model);
// End of new code ----------------------------------------
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
return View(model);
}
Implement the AddUserProfile(int userId, RegisterViewModel model) method as you wish. You'll collect the extra data from the model object along with the userId and save the new profile object in the DB.
Make a class that inherits from MembershipProvider and implement methods that are identical by just calling the SqlMembershipProvider but change others that you want a different Functionality.
Take a look at this article SQLite 3.0 Membership and Role Provider for ASP.NET 2.0
UPDATE:
The Membership system in ASP.NET was designed to create a standardized
API for working with user accounts, a task faced by many web
applications (refer back to Part 1 of this article series for a more
in-depth look at Membership). While the Membership system encompasses
core user-related properties - username, password, email address, and
so on - oftentimes additional information needs to be captured for
each user. Unfortunately, this additional information can differ
wildly from application to application.
Rather than add additional user attributes to the Membership system,
Microsoft instead created the Profile system to handle additional user
properties. The Profile system allows the additional, user-specific
properties to be defined in the Web.config file and is responsible for
persisting these values to some data store.
Reference: Examining ASP.NET's Membership, Roles, and Profile - Part 6
This is how I have accomplished somthing like this. I added event onCreatedUser to CreateUserWizard and when you press CreateUser button it loads method
protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
MembershipUser mu = Membership.GetUser(CreateUserWizard1.UserName);
int idOfInsertedUser = (int)mu.ProviderUserKey;
TextBox tb1 = (TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("FirstName";
string firstName= tb1.Text;
TextBox tb2 = (TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("LastName";
string lastName= tb2.Text;
// now you have values of two more fields, and it is time to call your Database methods for inserting them in tables of choice...
}