im confused about adding roles to existing project which i set the Authentication to "No Authentication".
i have database in mssql with field only "username" and "password". And i use it for authentication. My question is how i adding roles like "administrator" or "userA" or "guest" for Authorization. Im so new to Mvc. Thanks!
this is my controller code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace _3131.Controllers {
public class HomeController : Controller {
[Authorize]
public ActionResult Index(string button) {
ViewData["username"] = User.Identity.Name;
var viewdata = Convert.ToString(ViewData["username"]);
if(viewdata == "admin") {
return View();
}else if(viewdata == "userA") {
return View();
} else if(viewdata == "userB") {
return View();
} else {
return View();
}
}
[Authorize]
public ActionResult About() {
ViewData["username"] = User.Identity.Name;
var viewdata = Convert.ToString(ViewData["username"]);
if(viewdata == "admin") {
return View();
}else {
return View("Error");
}
}
[Authorize]
public ActionResult UserA() {
ViewData["username"] = User.Identity.Name;
var viewdata = Convert.ToString(ViewData["username"]);
if(viewdata == "userA" || viewdata == "admin") {
return View();
}else {
return View("Error");
}
}
[Authorize]
public ActionResult UserB() {
ViewData["username"] = User.Identity.Name;
var viewdata = Convert.ToString(ViewData["username"]);
if (viewdata == "userB" || viewdata == "admin") {
return View();
} else {
return View("Error");
}
}
[Authorize]
public ActionResult UserC() {
ViewData["username"] = User.Identity.Name;
var viewdata = Convert.ToString(ViewData["username"]);
if (viewdata == "userC" || viewdata == "admin") {
return View();
} else {
return View("Error");
}
}
}
}
There are multiple ways to achieve this.
1.You can create a Generic Principal, that accepts two parameters identity and roles, The Authorize attribute looks at the principal
attached to HttpContext to authorize the request.
https://www.sharpencode.com/article/MVC/filters-in-asp-net-mvc/authorize
2. You can create a Custom Role provider, which is the easiest.
Role Provider in MVC
3. You can override Authorize Attribute
Related
First of all i am new to MVC user authentication system. Code bellow is working fine for authenticate normal users but i wanted to log all user as per under MVC role based system. So admin user can only see admin controller and normal user cant see admin controller. I already made it on my admin controller i have added "[Authorize(Roles = "Admin")]" and i am also redirecting correctly to specific controller during login filter inside login controller. Now my issue is: How can i tell MVC "[Authorize(Roles = "Admin")]" is only accessed who has admin role? I mean how can i assign a user as admin from my login controller bellow? Ask any question if may have
Administrator Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Blexz.Controllers
{
[Authorize(Roles = "Admin")]
public class AdministratorController : Controller
{
// GET: Administrator
public ActionResult Index()
{
return View();
}
}
}
Login Controller:
//Login post
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(UserLogin login, string ReturnUrl="")
{
string Message = "";
using (BlexzWebDbEntities db = new BlexzWebDbEntities())
{
var v = db.Users.Where(x => x.Email == login.Email && x.IsEmailVerified == true).FirstOrDefault();
int RoleId = db.Users.Where(x => x.Email == login.Email).Select(x => x.RoleId).FirstOrDefault();
string RoleTypeName = db.Roles.Where(x => x.RoleId == RoleId).Select(x => x.RoleType).FirstOrDefault();
if (v != null)
{
if (string.Compare(Crypto.Hash(login.Password), v.PasswordHash) == 0)
{
int timeOut = login.RememberMe ? 43800 : 100; // 43800 == 1 month
var ticket = new FormsAuthenticationTicket(login.Email, login.RememberMe, timeOut);
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new System.Web.HttpCookie(FormsAuthentication.FormsCookieName, encrypted);
cookie.Expires = DateTime.Now.AddMinutes(timeOut);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else if (RoleTypeName == "Admin")
{
return RedirectToAction("Index", "Administrator");
}
else
{
return RedirectToAction("User", "Home");
}
}
else
{
Message = "Invalid Credential Provided";
}
}
else
{
Message = "Invalid Credential Provided";
}
}
ViewBag.Message = Message;
return View();
}
Remove FirstOrDefault from RoleTypeName selection and change it as
string[] RoleTypeName = db.Roles.Where(x => x.RoleId == RoleId).Select(x => x.RoleType);
and change the checking as
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else if (RoleTypeName.Contains("Admin"))
{
return RedirectToAction("Index", "Administrator");
}
else
{
return RedirectToAction("User", "Home");
}
Change your ticket as shown below
var ticket = new FormsAuthenticationTicket(
version: 1,
name: UserName,
issueDate: DateTime.Now,
expiration: DateTime.Now.AddSeconds(httpContext.Session.Timeout),
isPersistent: false,
userData: String.Join(",", RoleTypeName));
and After that in global.asax you would do something like this:
public override void Init()
{
base.AuthenticateRequest += OnAuthenticateRequest;
}
private void OnAuthenticateRequest(object sender, EventArgs eventArgs)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var decodedTicket = FormsAuthentication.Decrypt(cookie.Value);
var roles = decodedTicket.UserData.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries);
var principal = new GenericPrincipal(HttpContext.Current.User.Identity, roles);
HttpContext.Current.User = principal;
}
}
I'm attempting to create a session in my UserAccountsController
using System.Linq;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using POPPELWebsite.Models;
namespace POPPELWebsite.Controllers
{
public class UserAccountController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Register()
{
return View();
}
[HttpPost]
public ActionResult Register(UserAccount account)
{
if (ModelState.IsValid)
{
using (OurDbContext db = new OurDbContext())
{
db.userAccount.Add(account);
db.SaveChanges();
}
ModelState.Clear();
ViewBag.Message = account.FirstName + " " + account.LastName + " successfully registered.";
}
return View();
}
//Login
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserAccount user)
{
using (OurDbContext db = new OurDbContext())
{
var usr = db.userAccount.Single(u => u.Email == user.Email && u.Password == user.Password);
if (usr != null)
{
Session["UserID"] = usr.UserID.ToString;
}
}
}
}
}
I get an error saying
the name Session does not exist in the current context.
I need to do this part to complete a registration and login tutorial for mvc
The Session property does not exist in the Controller class in MVC 6, instead use HttpContext.Session to access the session property.
Ex:
// get values
string strValue = HttpContext.Session.GetString("StringKey");
int intValue = HttpContext.Session.GetInt32("IntKey");
byte[] byteArrayValue = HttpContext.Session.Get("ByteArrayKey");
// set values
HttpContext.Session.Set("ByteArrayKey", byteArrayValue);
HttpContext.Session.SetInt32("IntKey", intValue);
HttpContext.Session.SetString("StringKey", strValue);
Try this.
public ActionResult Login(User users)
{
if (ModelState.IsValid)
{
using (DataContext db = new DataContext())
{
var obj = db.Users.Where(u => u.Username.Equals(users.Username) && u.Password.Equals(users.Password)).FirstOrDefault();
if(obj !=null)
{
System.Web.HttpContext context = System.Web.HttpContext.Current;
context.Session["UserId"] = obj.UserId.ToString();
context.Session["Username"] = obj.Username.ToString();
return RedirectToAction("Dashboard");
}
}
}
return View(users);
}
I have a MVC project with 2 areas: Admin and Client. I also have a login page in the main controller. What I want to do is to Authenticate a user based on its roles. If the user is for client they can't login to admin and the other way around.
For example if you try Localhost/admin, the code checks if the user is authorised. If not it redirects you to Localhost/admin/AccountLogin. The same for Localhost/client to Localhost/client/account/login.
I want to use a customAuthorize rather than [Authorize(Roles="Admin")].
everything works fine if I don't use roles, but the problem is if you login as client you can simply change the url and go to admin. So I tried to use roles.
In admin area:
An account Controller:
public class AccountController : MainProject.Controllers.AccountController
{ }
A home controller:
[CustomAuthorize("Admin")]
public class HomeController : Controller
{
public ActionResult HomePage()
{
return View();
}
}
The custom Authorise:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string _loginPage { get; set; }
private string _customRole { get; set; }
public CustomAuthorizeAttribute(string userProfilesRequired)
{
_customRole = userProfilesRequired;
_loginPage = "/" + _customRole + "/Account/Login";
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var formsIdentity = filterContext.HttpContext.User.Identity as System.Web.Security.FormsIdentity;
// I want to check if the role of current user is the same as the controller If not redirect to the /account/login page.
var validRole = this.Roles == _customRole;//filterContext.HttpContext.User.IsInRole(_customRole);
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (!validRole)
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
}
else
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
base.OnAuthorization(filterContext);
}
}
The Account Controller in Main Controller:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
//I need to set the roles here but not sure how
return RedirectToAction("homePage", "Home", new { area = GetArea() });
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
}
and it the web config:
<forms loginUrl="~/Account/Login" timeout="200" />
</authentication>
<authorization>
<allow roles="Admin,Client" />
</authorization>
I searched a lot in the web but couldn't find a proper answer. I appreciate if you Could help me out to correctly implement this authorisation in MVC.
I just want to know how can I set a role to a user when login. At the moment if I set a user in login, it can't remember when it gets to CustomAuthorize class.
Any help?
Cheers,
There are a lot of ways to this but I will tell you what I used in this case.
You don't actually need to create a custom Authorization Attribute but instead make use of PostAuthenticateRequest event Handler in Global.asax given that you have a "table" roles in your database.
Add the code below in Global.asax
public override void Init()
{
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated && User.Identity.AuthenticationType == "Forms")
{
string[] roles = GetRoleOfUser(Context.User.Identity.Name);
var newUser = new GenericPrincipal(Context.User.Identity, roles);
Context.User = Thread.CurrentPrincipal = newUser;
}
}
public string[] GetRoleOfUser(string username)
{
string[] usersInRole;
// Get the Role of User from the Database
// Should be of String Array
// Example Query to Database: 'Select UserRole FROM User WHERE Username = "arash"'
// It doesnt matter if user has One or more Role.
return usersInRole;
}
Then your account controller should be this.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
return RedirectToAction("HomePage", "Home");
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
Now for example there is an Action in your HomeController that can only be access by Admin. You can just decorate the action with Authorize attribute like this below.
HomeController.cs
[Authorize(Roles = "Admin")]
public ActionResult AdminHomepage()
{
//For Admin Only
return View();
}
[Authorize(Roles = "Client")]
public ActionResult ClientHomepage()
{
//Client only Homepage, User with Role "Admin" cant go here.
return View();
}
[AllowAnonymous]
public ActionResult HomePageForAll()
{
//For Everyone
return View();
}
[Authorize(Roles = "Client,Admin")]
public ActionResult HomePageForClientAndAdmin()
{
return View();
}
public ActionResult HomePage()
{
return View();
}
The user will be redirected to Login URL if they are not authorized given that it is specified in Web.config (Which you already have set).
I have an action method and that can be accessed by Admin only
// Action Methods
[AuthorizationService] // My custom filter ,you can apply at controller level
public ActionResult ProjectList(Employee emp)
{
// do some work
}
//Employee class
public class Employee
{
string Name{get;set;}
string Role{get;set;}
}
// My custom filter
class AuthorizationService : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Employee = filterContext.ActionParameters["emp"] as Employee;
if (Employee.Role!="Admin")
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { action = "Login", Controller ="Home"}));
}
}
}
In the 1st example:-
i am assigning User.Identity.Name value to the variable id. i am able to get the value after that i am Redirecting to some other view here i am using Redirect(ReturnUrl) now i am able to get the User.Identity.Name value in the other controller(Redirected view) also
But in the 2nd example :-
i am assigning User.Identity.Name value to the variable id i am able to get the value after that i am Redirecting to some other view here i am using return Redirect(ReturnUrl);when i am using return Redirect(ReturnUrl);am not able to get the User.Identity.Name value in the Redirected url
Example 1:-
public ActionResult SignIn(string ReturnUrl)
{
if (ReturnUrl == "/" || string.IsNullOrEmpty(ReturnUrl))
{
ReturnUrl = "/Dashboard";
}
var id=HttpContext.Current.User.Identity.Name;
Response.Redirect(ReturnUrl);
return View();
}
Example 2:-
public ActionResult SignIn(string ReturnUrl)
{
if (ReturnUrl == "/" || string.IsNullOrEmpty(ReturnUrl))
{
ReturnUrl = "/Dashboard";
}
var id=HttpContext.Current.User.Identity.Name;
return Redirect(ReturnUrl);
}
My Controller:- In this function if i am return Redirect(ReturnUrl); i am not able to get the User.Identity.Name value in CompanyRequired filter if i am using Response.Redirect(ReturnUrl); return View(); then able to get the User.Identity.Name value in CompanyRequired filter but i have to use return Redirect(ReturnUrl);
[HttpPost]
public async Task<ActionResult> SignInCallback()
{
var token = Request.Form["id_token"];
var state = Request.Form["state"];
var claims = await ValidateIdentityTokenAsync(token, state);
string ReturnUrl = state.Substring(state.IndexOf('?') + 1);
var id = new ClaimsIdentity(claims, "Cookies");
Request.GetOwinContext().Authentication.SignIn(id);
if (ReturnUrl == "/" || string.IsNullOrEmpty(ReturnUrl))
{
ReturnUrl = "/Dashboard";
}
var Id = User.Identity.Name;
return Redirect(ReturnUrl);
}
View:- Here i control will go to the CompanyRequired filter there i need a User.Identity.Name value in that i am getting value null
[Authorize,CompanyRequired]
public class DashBoardController : BaseController
{
public ActionResult Index()
{
return View();
}
}
CompanyRequired Filter:-
public class CompanyRequiredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var coCookie = filterContext.HttpContext.Request.Cookies["CoId"];
if (coCookie == null)
{
var Id= HttpContext.Current.User.Identity.Name.Int(); **//here i need to get the value but i am getting null value**
IdNmList cos = new EmployeeDAL().GetCompany(Id);
if (cos.Count == 0)
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary
{
{ "controller" , "Company"},
{ "action" , "Add"}
});
}
else if (cos.Count == 1)
{
filterContext.HttpContext.Response.Cookies.Add(new HttpCookie("CoId", cos[0].Id.ToString()));
}
else
{
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary
{
{ "controller", "Company" },
{ "action", "Select" },
{ "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
});
}
}
base.OnActionExecuting(filterContext);
}
}
Here is my scenario: (These all have to accomplished in the same view as an accepted requirement)
User enters a few search criterias to search users.
Page lists the search results with an update link besides.
User clicks on one of the update links and a form appears to enable editing the data.
User does changes and saves the data that binded to form.
I used a view model for this view. Here it is.
[Serializable]
public class UserAddModel
{
public UserSearchCriteria UserSearchCriteria { get; set; }
public UserEntity User { get; set; }
public List<UserPrincipalDto> SearchResults { get; set; }
}
And here is my controller:
using System;
namespace x.Web.BackOffice.Controllers
{
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
[Authorize(Roles = "Admin")]
public class UserController : Controller
{
private readonly IAuthentication _authentication;
private readonly List<RoleEntity> roles = new Y.Framework.Data.Repository<RoleEntity>().Get(null).ToList();
private Repository<UserEntity> repository = new Repository<UserEntity>();
[ImportingConstructor]
public UserController(IAuthentication authentication)
{
this._authentication = authentication;
}
public ActionResult Index()
{
return View(new UserAddModel());
}
[HttpPost]
public ActionResult GetSearchResults(UserAddModel model)
{
if (ModelState.IsValid)
{
try
{
List<UserPrincipalDto> results =
_authentication.SearchUsers(
ConfigurationManager.AppSettings["DomainName"],
model.UserSearchCriteria.FirstName,
model.UserSearchCriteria.LastName,
model.UserSearchCriteria.Username);
model.SearchResults = results;
Session["UserAddModel"] = model;
return View("Index", model);
}
catch (Exception ex)
{
Logger.Log(ex, User.Identity.Name);
}
}
else
{
ModelState.AddModelError("", "Error!");
}
Session["UserAddModel"] = model;
return View("Index", model);
}
public ActionResult Save(string username)
{
UserAddModel model = Session["UserAddModel"] as UserAddModel;
UserEntity exists = repository.Get(u => u.Username == username).FirstOrDefault();
if (exists == null)
{
UserPrincipal userPrincipal =
_authentication.GetUserDetails(
ConfigurationManager.AppSettings["DomainName"],
username);
model.User = new UserEntity();
model.User.Id = userPrincipal.Guid.Value;
model.User.FirstName = userPrincipal.DisplayName.FullNameToFirstName();
model.User.LastName = userPrincipal.DisplayName.FullNameToLastName();
model.User.Email = userPrincipal.EmailAddress;
model.User.Username = userPrincipal.SamAccountName;
}
else
{
model.User = new UserEntity();
model.User.Id = exists.Id;
model.User.FirstName = exists.FirstName;
model.User.LastName = exists.LastName;
model.User.Email = exists.Email;
model.User.Username = exists.Username;
model.User.RoleId = exists.RoleId;
}
ViewBag.Roles = roles;
return View("Index", model);
}
[HttpPost]
public ActionResult Save(UserAddModel model)
{
UserEntity exists = repository.Get(u => u.Id == model.User.Id).FirstOrDefault();
if (exists == null)
{
Result result = repository.Save(model.User);
HandleResult(result, model);
}
else
{
Result result = repository.Save(model.User, PageMode.Edit);
HandleResult(result, model);
}
ViewBag.Roles = roles;
return View("Index", model);
}
}
}
As you see there are two different forms in my view and I'm storing the whole view model in Session in my controller. But I think this is not fine enough. What if session expires or what if I have to deploy my application using a load balancer?
What is the best way to develop this kind of page? I'm open to any kind of suggestions.
Thanks in advance,