I have users with different Roles. I want to deliver restricted view according to roles of the users. I have something in my Code which checks roles:
bool isAdmin = UserManager.IsInRole(currentUser.Id,"admin");
bool isEmployee = UserManager.IsInRole(currentUser.Id,"employee");
For above code to work, I need to instantiate currentUser. In other words I need to catch the information of the current user that is logged in. I tried something like var user = User.Identity.GetUserId(); and many other but can't find the working code. I would appreciate any help. Thank you!
Update:
here's my complete method code. y'all might wanna see until i check the above example.
public ActionResult Index()
{
var user = User.Identity.GetUserId();
bool isAdmin = UserManager.IsInRole(user, "admin");
bool isEmployee = UserManager.IsInRole(user, "employee");
if (isAdmin)
{
return View(db.Customers.ToList());
}
else
{
var restricted = db.Customers.Where(c => c.FirstName == "John");
return View(restricted);
}
}
[Authorize] attribute should be implemented in the desired restricted actionController method. The example is below.
[Authorize(Roles="Admin")]
public ActionResult Index()
{
return View();
}
This controller method is limited to the User with Role Admin. Furthermore, same action controller method could be included twice with different authorize tag.
I somehow figured out the solution. Here's the working code.
if(User.IsInRole("Admin"))
{
return View(db.Customers.ToList());
}
else
{
return View(db.MyUserInfo.ToList());
}
Related
I know about attribute-based authorization.
I know that we can add [Authorize (Role = 'RoleName')] to limit access for an action or a controller to that person only.
However, in this particular case, I need to programmatically check that user is in role, INSIDE action.
[HttpGet]
public IActionResult DomSomething()
{
// How can I know that user has a role here?
return Ok();
}
How can I do that?
Update
I'm not using ASP.NET Core Identity.
I'm using KeyCloak (OAuth provider).
You can check it with userManager.IsInRoleAsync(user, role.Name). Inject userManager on your controller. And use like below:
[HttpGet]
public IActionResult DomSomething()
{
if(userManager.IsInRoleAsync(user, role.Name)){
// Do your stuff
}
return Ok();
}
with the assumptions that your API is using authorize attribute, you can following inside the action.
HttpContext.User.IsInRole("role you want");
if this doesn't work then next you can try check in the claims list for role.
HttpContext.User.Claims
UserId and RoleName are stored in the Role table
In the following method, we check if the name of the given role in Table Role is for the desired person, it will send true :
private bool UserCheckInRole(int userId , string roleName)
{
if(RoleTable.Where(role => role.roleName == roleName , role.UserId ==
userId).Any()) return true;
return false;
}
[HttpGet]
public IActionResult DomSomething(string roleName = "")
{
if(UserCheckInRole(userId , roleName){
//true
}
// How can I know that user has a role here?
return Ok();
}
Thank You very much for answer my question, because i am very new in asp.net, I am really appreciated
I have a Session to identity the user in the controller
Session["UserId"] = UserNo;
Session["UserType"] = UserType;
What can i do to avoid the User such as the Session["UserType"] is "3" and then avoid him/her to login to some web pages?
I need to control that in the controller, view of filter config?
Create a BaseController and all the other controllers that you want to check User, should inherit it. Before a ActionResult requested, first the BaseController's constructor will work, so at constructor of BaseController you can do this,
public BaseController()
{
try
{
int UserID = 0;
if (System.Web.HttpContext.Current.Session["UserId"] != null)
UserID = Convert.ToInt32(System.Web.HttpContext.Current.Session["UserId"]);
if (!(UserID > 0))
{
System.Web.HttpContext.Current.Response.Redirect("controller/view");
}
}
catch (Exception ex)
{
throw ex;
}
}
Hope helps,
While all other answers are right and working, i would like to lead you to the more 'asp-ish' style of solving this problem, which also reduces your code because you do not have to write a if else in every of your controller.
In ASP.NET there is a Identity Concept which does exactly what you want to archive by hand.
Have a look here: https://learn.microsoft.com/de-de/aspnet/core/security/authentication/identity
Once you implemented the Identity, you can just mark your controller methods with [Authorize].
https://learn.microsoft.com/de-de/aspnet/core/security/authorization/introduction
Simply you have redirect the user if
public ActionResult Method()
{
if( Session["UserType"] = 3)
{
return View([forbidden page URL here]);
}
else
{
return View("controller/view");
}
}
Hope it Helps.
You just need to check your sessions value everytime a user tries to access a certain page.
EDIT:
Try this
public ActionResult Details(int id)
{
var details = context.checkIfUserExist(userID);
if (details == null)
{
return RedirectToAction("takeHimSomewhere");
}
return View(details);
}
This is a two-parter
Question 1 (the real question)
I have a DashboardController that is not tied to a model. A User must be logged in before they can access the dashboard. How can I run a check to see if a user is authenticated before every action is executed, and redirect them to the login view if not? I think OnActionExecuted is what I want, but I am not sure what the exact implementation should be. Am I on the right track here?
public class DashboardController : Controller
{
private ApplicationContext db = new ApplicationContext();
//
// GET: /Admin/
public ActionResult Index()
{
var categories = db.Categories.ToList();
return View(categories);
}
public ActionResult Product(int id)
{
var product = db.Products.Find(id);
return View(product);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if(Session["current_user"] == null)
{
// This (obviously) doesn't work - what should go here?
return RedirectToAction("Create", "Session");
}
base.OnActionExecuted(filterContext);
}
}
Question 2
If the user IS logged in, what is the right way to make the user accessible in all of these views? I have been told ViewBag is generally a bad idea - what should I use?
I can authorize controllers and actions by follow this link:
It's in brazilian portuguese originally, but the link below is translated to english.
https://translate.google.com.br/translate?sl=pt&tl=en&js=y&prev=_t&hl=pt-BR&ie=UTF-8&u=http%3A%2F%2Fdevbrasil.net%2Fprofiles%2Fblogs%2Fautentica-o-e-permiss-es-de-usu-rios-em-asp-net-mvc-4&edit-text=&act=url
You can get the logged user in views by
#HttpContext.Current.User.Identity.Name
PS: Sorry my bad english
Use [Authorize] atrribute.
For example:
[AcceptVerbs(HttpVerbs.Get)]
[Authorize]
public ActionResult add()
{
}
Then in the web.config
<authentication mode="Forms">
<forms name="my_cookie_name" loginUrl="~/login" defaultUrl="~/" timeout="2880"/>
</authentication>
If the user is not authenticated, it will get redirected automatically to the login page.
If you want something simple to control the identity of your users check the highest rated answer here: ASP.NET MVC - Set custom IIdentity or IPrincipal. It's a brilliant example. I use somthing similar in all my projects.
In my login action:
var user = _userService.system_login(systemlogin_model_post.email, systemlogin_model_post.password); // my user model
//... doing all sorts of validations
// once everyone is happy I create a cookie
Response.Cookies.Add(UserCookie.GetCookie(user));
Than using the code from the link above I create cookie:
public static class UserCookie
{
public static HttpCookie GetCookie(User user)
{
CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel { user_id = user.UserId, username = user.Username, roles = user.Roles ,session_token = GUIDGenerator.ToAlphaNumerical() };
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(serializeModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
user.UserId.ToString(),
DateTime.Now,
DateTime.Now.AddMinutes(30),
false,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
return new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
}
}
When [Authorize] is fired this code takes care of it:
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
newUser.user_id = serializeModel.user_id;
newUser.username = serializeModel.username;
newUser.roles = serializeModel.roles;
newUser.form_token = serializeModel.form_token;
HttpContext.Current.User = newUser;
}
}
1) Authorize attribute of ASP.NET MVC is totally focused on your first problem. You may even go for customization but not suggested for most of scenarios.
2) To assign the currently logged in user and visible in all the views, you may bind the user name using ViewBag/ViewModel property to Layout(_Layout.cshtml) so that it appears on top of every page where the layout is used.
Note: If you want to perform any pre action-invoke logic, then OnActionExecuting filter is the correct place, before entering that action method.
Precisely, you have to create a class and that class inherits the Controller class.
public class MyAuthentication : Controller
{
public MyAuthentication()
{
isAuthenticated();
}
private void isAuthenticated()
{
// do your authentication
//if user authenticated keep user details within a cookie, so that
// when the next request, program logic will search for cookie,
//if it is found then assume user was authenticated else redirect to login page.
}
}
And then inherits this MyAuthentication class in your project for all controllers
public class DashboardController : MyAuthentication
{
public ActionResult Index()
{
var categories = db.Categories.ToList();
return View(categories);
}
// rest of the codes
}
So that the authentication remains in single place. You can inherit this where ever you want.
If you need current user anywhere in the controller/action then better way is to set the user data when you perform authorization.
In your authorization filter , you can use
System.Web.HttpContext.Current.Items["userdata"]=userDataObject;
For authentication , this article can help.
http://www.dotnet-tricks.com/Tutorial/mvc/G54G220114-Custom-Authentication-and-Authorization-in-ASP.NET-MVC.html
First Put the Form Authentication Cookie when the user is logged in. like
[HttpPost]
public ActionResult Login(Acccount obj){
// check whether the user login is valid or not
if(UseIsValid){
FormsAuthentication.SetAuthCookie(obj.username, obj.RememberMe);
return redirectToAction("Index","DashBoard");
}
return View(obj);
}
*
and Use [Authorize] attribute. like*
[Authorize]
public class DashboardController : Controller
{
private ApplicationContext db = new ApplicationContext();
public ActionResult Index()
{
var categories = db.Categories.ToList();
return View(categories);
}
}
I have a problem with adding AntiForgeryToken. This is my code:
<%:Html.ActionLink("Text", "Action", new { ID = "Hello")})%>
and
RedirectToAction("Action", "Controller", new { ID = "HelloFromMe"});
Controller:
[ValidateAntiForgeryToken]
public ActionResult Action(String ID){
return View();
}
Does anybody have idea how to do it ?
It is impossible to use an AntiForgeryToken into a GET method.
GET methods should only be used for read-only operation on your server. If you want to do something else than a read-only operation, then, you should use a POST method.
Here the reason why this token is useful, how and when to use it. http://haacked.com/archive/2009/04/02/anatomy-of-csrf-attack.aspx
The idea of antiforgery token is to prevent attacker to generate POST / GET request on behalf of the user. Thats why we add something special to every POST / GET request, that is unknown to attacker.
The simplest implementation of custom antiforgery would look like this.
And it will be exactly safe as ValidateAntiForgeryToken.
public class ProfileController : AuthorizedAccessController
{
// GET
public ActionResult Details(int userId)
{
User user = this.Entities.User.First(u => u.Id == userId);
this.Session["ProfilePageAntiforgery"] = Guid.NewGuid(); // use RandomNumberGenerator to generate strong token
this.ViewBag.ProfilePageAntiforgery = this.Session["ProfilePageAntiforgery"];
return View(user);
}
public ActionResult DeleteMyProfile(int userId, string profilePageAntiforgery)
{
if ((string)this.Session["ProfilePageAntiforgery"] != profilePageAntiforgery)
{
return this.RedirectToAction("Details", new { userId });
}
User user = this.Entities.User.First(u => u.Id == userId);
this.Entities.User.Remove(user);
this.Entities.SaveChanges();
return this.RedirectToAction("ProfileDeleted");
}
}
View:
<div>
#Html.ActionLink("Delete my profile", "DeleteMyProfile", new {userId = this.Model.Id, profilePageAntiforgery = this.ViewBag.ProfilePageAntiforgery })
</div>
Making custom attributes out of this is a matter of technique.
Lets say one attribute to store token in session and viewbag and the other to validate.
If using Session is not OK (Web farm etc.) you can simply replace it by DB or other store.
I have a controller method called Edit in which the user can edit data they had created like so ...
public ActionResult Edit(int id)
{
Submission submission = unit.SubmissionRepository.GetByID(id);
User user = unit.UserRepository.GetByUsername(User.Identity.Name);
//Make sure the submission belongs to the user
if (submission.UserID != user.UserID)
{
throw new SecurityException("Unauthorized access!");
}
//Carry out method
}
This method works fine however it is a little messy to put in every controller Edit method. Each table always has a UserID so I was wondering if there was an easier way to automate this via an [Authorize] Attribute or some other mechanism to make the code cleaner.
Yes, you could achieve that through a custom Authorize attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
var rd = httpContext.Request.RequestContext.RouteData;
var id = rd.Values["id"];
var userName = httpContext.User.Identity.Name;
Submission submission = unit.SubmissionRepository.GetByID(id);
User user = unit.UserRepository.GetByUsername(userName);
return submission.UserID == user.UserID;
}
}
and then:
[MyAuthorize]
public ActionResult Edit(int id)
{
// Carry out method
}
and let's suppose that you need to feed this submission instance that we fetched into the custom attribute as action parameter to avoid hitting the database once again you could do the following:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
var rd = httpContext.Request.RequestContext.RouteData;
var id = rd.Values["id"];
var userName = httpContext.User.Identity.Name;
Submission submission = unit.SubmissionRepository.GetByID(id);
User user = unit.UserRepository.GetByUsername(userName);
rd.Values["model"] = submission;
return submission.UserID == user.UserID;
}
}
and then:
[MyAuthorize]
public ActionResult Edit(Submission model)
{
// Carry out method
}
I would suggest you pull the logic out of the action/controller and build a domain class to handle that logic.
Action methods should really only deal with getting data from and sending data to the view. You could create something generic enough to handle your needs but will also follow the single responsibility principal.
public class AuthorizedToEdit
{
protected override bool AuthorizeCore(string user, int itemId)
{
var userName = httpContext.User.Identity.Name;
var authUsers = SubmissionRepository.GetAuthoriedUsers(itemId);
return authUsers.Contains(user);
}
}
This would also allow you to have the flexibility later on to allow something like admin users
#if (Request.IsAuthenticated && User.IsInRole("Student"))
{
#Html.ActionLink("Edit", "Edit", new { id = item.StdID })
}
in my case, the loggedIn user is a student. so i say if the login request is authenticated, and if his role is student, then let the link for edit be accessible to him.
this below allows you to let the ordinary user OR the Admin perform edit also.
#if(Request.IsAuthenticated && User.IsInRole("Student") ||
User.IsInRole("Administrator"))
{
#Html.ActionLink("Edit", "Edit", new { id = item.StdID })
}
I recommend reading up on the AuthorizeAttribute (see here). Also, have you seen this post? It goes over how to override the authentication attribute innards and how to use IPrincipal and IIdentity.