I need authorization attribute to ensure that authenticated user can access in secure area to manage his and only his data.
I was thinking to retrieve httpContext.User.Identity.Name and to match that to retrieved username from the database.
Is this basically good (secure) approach? or it can be done with better secure on the mind?
So, my controller is decorated with custom attribute
[UserAccountAuthorizeAttribute]
public ActionResult Edit(string username)
{
return View();
}
and I'm overriding AuthorizeAttribute but in the debug mode got null value on following line
var rd = httpContext.Request.RequestContext.RouteData;
var username = rd.Values["username"]; // null
what can be aproblem ?
You shouldn't be putting the username in the querystring.
If you're using the built in ASP.NET Membership provider your action would look something like this.
[Authorize]
public ActionResult Edit()
{
var userId = (Guid)Membership.GetUser().ProviderUserKey;
var someService = new MyService();
var someData = someService.GetSomeDataByUserId(userId);
//...
}
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();
}
I want to get the below roles(Admin,IT,..) from the database without hard coding on top of the action result. Please provide any help.
[Authorize(Roles = "Admin,IT")]
public ActionResult Index()
{
}
There aren't any super-easy ways to do this. You can apply the [Authorize] attribute to a controller instead of an action, but it is still "hard-coding" it.
You could create a custom Authorization attribute ([link])1, but you would have to store the Routing values in the database, as well as the Roles that were allowed to access the route. However this just shifts the burden of making manual changes into the database from the code.
I don't really think that this should really be considered "Hard Coding" as you have to declare your authorization somewhere, and you can still have different users with different permissions in different environments. Who else but the developer should know best which routes require which authorization? Would you want to break your access control because you changed the routing somewhere?
create an Action finter
public class ValidationPermission : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(System.Web.HttpContext.Current.Session["UserName"] == null)
System.Web.HttpContext.Current.Response.RedirectToRoute("Login");
else{
// code check CheckPermission
}
}
}
Action controller
[ValidationPermission(Action = ActionEnum.Read, Module = CModule)]
public ActionResult Index()
{
// something code
}
You can try with this way
public static Role {
public static string Admin ="Admin";
public static string IT ="IT";
}
[Authorize(Roles = Role.Admin,Role.IT)]
public ActionResult Index()
{
}
I'm trying to get the querystring that is in the browser,
an example /UserHome/UserProfile?username=dangercoder. I'm working in WebApi controller
from the following url i want to get "dangercoder" so I can use the dangercoder username in my AddPost method that is in my Web Api
what i've done so far in the method (the reciever id can be hardcoded but ofcourse i want it to be dangercoder username converted to an id.
[System.Web.Http.HttpPost]
public IHttpActionResult AddPost(Post post)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = User.Identity.Name;
post.SenderID = UserRepository.GetUserId(user);
// QueryString that returns username in the parameter below.
post.RecieverID = UserRepository.GetUserId();
PostRepository.AddNewPost(post);
return CreatedAtRoute("DefaultApi", new { id = post.ID }, post);
}
As far as I see, the view UserProfile contains HTML form with Post fields.
You may create hidden field SenderId in the form with user's ID.
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.