Individual page authorization based on roles in ASP.NET MVC 5 - asp.net

I am pretty new to ASP.NET MVC, and I'm trying to build a web-site that uses MVC 5's built-in authorization methods.
Here's what I've done so far:
Created a number of users in the AspNetUsers table.
Created a number of roles in the AspNetRoles table.
Assigned roles to users via the AspNetUserRoles table by connecting RoleID and UserID.
Now, to set up a single page to only show certain content to users with the admin-role, and hide it otherwise, I've done this:
#if(User.IsInRole("Admin"))
{
<p>You are logged in as an admin.</p>
} else
{
<p>You are not logged in as an admin.</p>
};
Is this OK to do, or is this bad? I've played around with it for quite some time, and it works as expected (as far as I can tell).
I know I can create CustomAuthorizationAttributes and assign these to the ActionMethods in the Controller, but I'm not 100 % comfortable with the syntax on this.

If you are happy with the syntax, this is fine.
But you cannot forget to protect the view itself with the Authorize attribute. You can use the default as following
[Authorize(Roles = "Admin")]
public ActionResult Register()
{
...
return View();
}

Related

can't insert any data to database using controller [EF Core]

I am working on my Uni project that uses database with two tables: Role and User.
Users.RoleId is a foreign key referring to Role.Id
I generated Controllers for each table/model classes using Scaffolding template "MVC Model with views using Entity Framework", so RolesControler.cs and UsersControler.cs are almost the same.
I can create, edit or delete roles using different views which can be seen here.
As you can see, new role is inserted into the database.
While trying to do the exact thing using views specified for UsersController.cs I can't add or modify a user. I fill all the inputs but submitting won't give any results, page is not refreshing (not redirecting to index.cshtml), no data is added to the database, just nothing happens, I can click "Create" button forever.
I tried adding users manually using SQL query in SQL Server Object Explorer and they appear on the list. What's interesting I can delete them using Delete method. The button reacts, redirect me to main page and deletes record from database.
Also while trying to create new User I can only select Roles that already exists - which is intended - but that suggests that the referrence between User.RoleId and Role.Id is done properly.
Here is code for Create method in UsersControler.cs:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,Surname,DateOfBirth,Login,RoleId,IsDeleted")] User user)
{
if (ModelState.IsValid)
{
_context.Add(user);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["RoleId"] = new SelectList(_context.Roles, "Id", "Id", user.RoleId);
return View(user);
}
I don't really know why everything is fine for Role table/model, but when it comes to User I can only delete records that already exist in database.
EDIT:
I tried inserting new user using invalid date (year 32121) and validation error occurs, which for me indicates that the submit button reacts and validates input data, but if it's valid, it doesn't insert the record to database and doesn't redirect into index page.
May I ask what your model classes for Role and User look like? I suspect that your data types are not matching. I have a nearly similar problem as you, only that I cannot even enter the foreign key

Strategies to prevent the user from accessing GET request of other ids

I am currently brainstorming and researching right now about implement sessions for my ASP MVC application.
I already did the Authentication and Authorization part but now i have to fix this issue:
[HttpGet]
[Authorize(Roles="user")]
public ActionResult Details(int id)
{
EmployeeContext emp = new EmployeeContext();
student st = emp.students.Single(x => x.id == id); //get the student
return View(st);
}
After the user successfully logged in, he will be directed to the details page by using a GET method.
My problem is that, the user can change the id and he will be able to view the other detail's page and i don't want that to happen. I want to make it possible that the only page that he can request is anything that has his id.
For my case: strictly http://localhost:55319/Enrollment/Details/1 only and not http://localhost:55319/Enrollment/Details/2
Here is one of the solutions that i thought of but i don't feel that it's "scalable".
Proposed solution:
store the id in a session and use that to compare on the id that the user is currently trying to "get", but then again, i will have to type to them on every controller and i am trying to avoid that.
I read something about making a class that will inherit ActionFilterAttribute but the only usability that i know about it is "redirecting" if there is no current session.
Can anyone give me tips on how to implement it?

Render different views for different roles just by one action in asp.net mvc

Suppose a web application which has three part or so-called three access level:
One for every visitor (just for seeing the content and no need for authentication)
One for Users (Authorized for users)
One for the Administrator (authorized for admin)
now, administrator has access for every content and every operation in the system and Users could do some operations. I don't wanna to create separate areas for Users and Administrator because I don't want to repeat the same code in every area. for example both admin and user can create product, see the list of products, create catalog and... and also every visitor can also sees the list of product, blog posts, ...
So it's not a good idea to separate and make the code duplicated just for separating the tasks. I haven't created any area and I want to control the authentication and authorization by defining the user role when he/she is in the system(ideas!?) but the main issue comes when I want to have separate user interface (views) for users and admin. as I want to use just one Controller for products, Catalog, ... and set authentication and authorization for them, how can I render different view for every request by admin and user? I also don't want to make my code dirty by putting bunch of if/else to define which view to render (I'd rather to duplicate the code in areas!), any solution?
Probably the easiest solution is to write your own RazorViewEngine(Assuming you are using razor).
Then when you want to retrieve a view for a user, you can check the user role and assign the view you want. This is a basic and crude example:
public override ViewEngineResult FindPartialView(
ControllerContext controllerContext,
string partialViewName,
bool useCache)
{
if (controllerContext.User.IsInRole("Admin"))
{
var adminViewLocations = new string[] {"~/AdminViews/" }
return new ViewEngineResult(adminViewLocations);
}
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
Doing this means that all users use the same controllers and authentication, but the views change based on roles (or whatever you want).
You can read more about A Custom View Engine with Dynamic View Location.

Custom fields in ASP.Net Login/Register?

I have to edit the Login/Registration that ASP provides to include a custom dropdown ("BranchID") menu that saves to the database so each user has its own Branch. I am using ASP Membership system, and of course it saves to the ASPNETMDF database it creates. Googling has net me some results but I am quite confused. I know there are "User Profiles", and I I can save this Profile data, but what I am not quite sure is if its a temporary measure or if it does record to the database.
I could make my own custom membership system, use the built it and adapt it or use the user profiles. What is the best course of action? I'd vastly prefer to adapt/edit the built in Membership system and add the data I require to it but I still don't haven't a clear answer to what I should do or what's best.
You have two choices:
Create a CustomMembershipProvider , and if you need to a CustomRoleProvider, you can do this by implementing .NET's MembershipProvider. Sample: http://www.codeproject.com/Articles/165159/Custom-Membership-Providers
Create a separate table that stores additional user information, i.e., "BranchID", and add a one-to-one relationship between your table and .NET's Membership
It's really up to you which one you choose.
MembershipProvider is pretty easy to extend. Assuming the branch is something they have to select to authenticate? You should be able to extend authenticate to do something like:
public class MyCustomMembershipProvider : MembershipProvider
{
/*
....
*/
public bool ValidateUser(string username, string password, string branch)
{
return (::ValidateUser(username, password) && MyCustomRoutine(username, branch));
}
}

Use different authorize attributes for controllers to access the same logic from different environments

I have an ASP.NET MVC website, where most of my controllers are decorated with Authorize attributes, to enforce forms authentication.
I'm about to make this website available on Facebook via a Facebook app, but for my FB users I want to use a different authentication/authorization, I want to use CanvasAuthorize attribute on my controllers.
The problem is that I can't use both on my controllers/actions, because then both of them would be enforced to access the relevant action, but I want only Authorize for the normal website and I want only the CanvasAuthorize when the website is accessed from FB (via FB app).
I started to
refactor hugely my existing controllers to 'controllerhelpers'
make existing controllers (with authorize attribute) use the controllerhelpers relevant method
create new controllers (decorated with CanvasAuthorize) for the FB-app, which use the relevant controllerhelper methods also
But this is huge work, and I'm not sure whether this is the way to go, or there is a much easier an elegant way to work.
Of course I want to use the same views, and in my cshtmls I'm using specific controllers's Url.Action methods, so with my current approach when I'm inserting action-paths in my cshtmls (for eg. jQuery ajax Url properties) I have to make an if-statement to use for example the 'PersonalController' when the normal website is used and use the 'FBPersonalController' when the website is used as a FB app.
In this case PersonalController is decorated with [Authorize] and FBPersonalController is decorated with [CanvasAuthorize].
So, any feedback is appreciated ;)
Thanks!
Xoyoja's answer lead me to this solution. I don't mark it as 'accepted answer' because I'm evaluating it, but maybe it is worth discussing:
No, not all of them should be decorated. But with your proposal, I came to the following:
IEnumerable<Func<ControllerContext, ActionDescriptor, object>> conditions =
new Func<ControllerContext, ActionDescriptor, object>[] {
(ctrlCtx, actDesc) =>
{
if(FacebookWebContext.Current.SignedRequest != null)
{
return new CanvasAuthorize();
}
else
{
if(ctrlCtx.Controller.GetType() == typeof(AccountController)
&& actDesc.ActionName == "LogOn")
{
return null;
}
return new AuthorizeAttribute();
}
},
};
When my website is accessed from FB, it seems that the SignedRequest is not null, so the CanvasAuthorize can be used.
If my website is accessed from it's normal published url, then I use the AuthorizeAttribute.
The AccountController and "LogOn" action-specific logic is required to allow logging in to the website from the public url. From Facebook the Context contains the Facebook UserID which would implicitely do the authentication.
I'm still thinking about implications, worst-case-scenarios, backdoors whether this could harm me or not.
Can you use Conditional Filter to support both [Authorize] and [CanvasAuthorize]? As I tested in a simple ASP.NET MVC3 application, it works. Do you think it helps?
On the other hand, a nice solution would be, if you could change your design, put the authentication stuff in one place, that is, FormsAuthenticationService, FacebookAuthenciationService, and OpenIDAuthencitationService implement an interface called "IAuthenticationService". Call the standard FomsAuthentication.SetAuthCookie method upon completing the Facebook OAuth flow. The Authorize attribute should then work properly. Refer to this question and check code snippets from here (Create.aspx and SessionController.cs). Please evaluate.

Resources