I’m using authentication with Active Directory within an ASP .NET MVC3 web application.
I want to control what components are rendered in the view. The option I’m considering is passing in the groups the logged in user is part of from the controller, and then using a helper object to check whether the user is part of the required group. Is this the best method to achieve this? If it is, then how would I achieve the controller part?
Thanks.
Well this could be achieved if you use a correct model linked to the page (view-model, not a class from your domain), if you follow this approach, the view model should contain all the information required by the view and nothing more, since your view requires to know which controls should be visible based on the current user roles, I would create a property for each control in the view model like this:
public class MyViewModel
{
public bool ShowMycontrol1 { get; set; }
}
in your controller populate this "flags" using the current user roles, and finally in the view check for these properties in order to show or hide the required controls
This is just one way to accomplish this, I'm sure there many more
You can write an extension method. Let's say you want to check whether user is admin or not. So, you do that using extension method like this;
public static class AuthHelper
{
public static bool IsUserAdmin(this HtmlHelper helper)
{
return helper.ViewContext.HttpContext.User.IsInRole("Administrator");
}
}
i hope this helps.
Related
I guess I have a pretty standard problem as I want to pass certain data from an asp.net MVC 5 controller to a shared view, in my case navigation.
I have a template that shows username and userpicture in the navigation shared view, so I need to pass the respective data to it.
My layout structure:
Shared\Layout
Shared\Header
Shared\Navigation
%Body%
Shared\Footer
Where the Layout ist the master view and of course I also have my other views like Home\Index.
As you can imagine, I have to show the username and his userpicture in every view except the login/registration or any error views.
These views do not use the layout at all, so everytime a view is rendered using the layout structure, the user is already logged in.
So I was researching about ways to pass data from my controller to the navigation view, although my controller returns the Index view and would appreciate some clarification on their disadvantages and valid choices in my use case:
Use case:
My project has a pretty dumb MVC application that the user can access. Once he logs into the MVC app authenticates the user against the same webapi where it get's it's data from and stores the access token as well as other user details for further requests. I'm not yet sure where to store that data. As far as I understand it, the options would be Cookies, Session and local storage.
As I am pretty new to asp.net, MVC and C# in general, I didn't yet figure out how to make the [Authorize] Attribute work inside the MVC app so it can mark the user as authenticated :/
I guess the key problem is that the MVC app does not have access to the database and therefore cannot check the login and populate the User Identity.
How to transfer data from controller to view:
ViewBag: The easiest way of passing data to the view. It is not strongly typed and can be accessed in all views. I was told it is kind of a bad practise to use it and was advised to use viewModels.
ViewData: seems to be kind of the same thing as viewdata.
ViewModel: A strongly typed model that is passed to the view and needs to be declared in any view that uses it. So if I want to use it in my navigation view, I'd need to declare it there. The big disadvantage of this approach is that every viewmodel needs to have kind of a baseViewModel so they have a common structure which appearently can cause problems later down the road and also prevents me from inheriting other models to populate my viewModelStructure.
Cookies: Obviously I can store data in cookies during login and then access them in the view, but the cookies HAVE to be there so I would not be able to save this information in the session or local storage
Session: I can also store data in the session, but the session expires when the user closes the browser tab.
LocalStorage: This is pretty new to me so I can't judge it.
User Identity: I just discovered that I can also access the user's identity from Context.User.Identity.
Global Filter like the [Authorize] attribute or a custom one: If I understand it correctly, with a global filter I can populate needed data automatically in every controller action and exclude the ones that dont need it like Login/register etc. I'm not yet sure how to apply this way because of my project structure (see above).
RenderAction: I could also call another controller method via the RenderAction helper method to always render that section of the page.
How do you guys solve this problem?
If you need more clarification, please do ask.
Thanks :)
Use a base ViewModel. You can still use inheritance to build up functionality in your view models, they'll all just share a common base view model, as you said.
I'm not sure what problems you envisage with this approach, but I would suggest they're outweighed by the benefit of strongly typed, maintainable view models, that can be used by all your views, including partial views and _Layout.
A suggestion to get you started based on your layout structure:
public abstract class ViewModelBase {
public HeaderViewModel Header {get;}
public NavigationViewModel Navigation {get;}
public FooterViewModel Footer {get;}
public ViewModelBase(HeaderViewModel header, NavigationViewModel navigation, FooterViewModel footer) {
Header = header;
Navigation = navigation;
Footer = footer;
}
}
public class HeaderViewModel {
// properties
public HeaderViewModel(...) {
}
}
public class NavigationViewModel {
// properties
public NavigationViewModel(...) {
}
}
public class FooterViewModel {
// properties
public FooterViewModel(...) {
}
}
I have a fairly simple (to explain) requirement for my ASP.NET MVC web application:
On any page, be able to display a message based on an event that happened on a preceding page. Messages may include dynamic content, such as information entered by the user in a form submission.
Similar to this...
The approach must work in POST-REDIRECT-GET scenarios.
If the user has two browser tabs (which share session state) open on the application, then any message should only be displayed in the tab in which the related event happened.
For example:
After submitting a feedback form, a message thanking the user (by name)
When logging in, a message telling the user the date/time when they last logged in to the application
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This seems like overkill.
An alternative is to store the message in TempData:
// In my controller
TempData["Message"] = "Some kind of message";
// In my shared master view
#if (TempData["Message"] != null)
{
<div class="message">#TempData["Message"]</div>
}
Using TempData is covered in a bit more detail in this blog posting. Interestingly, the author also suggests using a custom HTTP header as an alternative. An interesting idea, but does not work with POST-REDIRECT-GET.
Is using TempData the preferred option, or is there a "better" alternative? (e.g. something more strongly typed)
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
It is best practice to use ViewModels to communicate between View and Controllers. You can have a base View Model and all other View Models derived from that as below:
public class BaseVM
{
public string Message{ get; set;}
}
public class CreateViewModel: BaseVM
{
public string CustoomerName{ get; set;}
}
You can populate the Message property while returning the model to the controller as below:
public ActionResult Step2()
{
//Some Logic
step2Model.Message = "Yes, Success..!!";
return View(step2Model);
}
After that, on each view page, you can check if that property has something in it.You can do so as below:
#if(!string.IsNullOrEmpty(Model.Message))
{
//Show message
}
EDIT:
OP is well aware of this approach, but still keeping this answer as it has a snippet to show how to do this in code. Secondly, when it comes to use ViewModels, I agree with following comment by CodeCaster in his answer.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
You could make a convention in your project that a message to display would always go into ViewBag.InfoMessage dynamic variable. So, in your layout file you would display it if it's passed into the view from a controller.
More strict way would be to create a base model class with InfoMessage property and derive all other models / viewmodels from that base.
To persist the message through POST-REDIRECT-GET scenario, use a session variable which you'd clear once the value is sent to the view - to avoid displaying on more than one page.
Here some code:
public class BaseViewModel
{
public string InfoMessage { get; set; }
}
public class SpecificViewModel : BaseViewModel
{
// other model properties
}
In your controller then:
SpecificViewModel vm = new SpecificViewModel();
vm.InfoMessage = Session["InfoMessage"] as string;
Session["InfoMessage"] = null;
// other code
return View(vm);
Of course your view will have strongly typed model: SpecificViewModel.
Has anyone here structured your views, controller and actions in such a way that the page titles are set from the database. How do you identify one view from another ? In which phase of the lifecycle do you retrieve the page title and set it for the view/action method
a lot of posts are scattered for asp.net, php but then a efficient way for mvc I am yet to find
You could use action filters:
public class ControllerAndActionNameAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.ControllerName = filterContext.RequestContext.RouteData.Values["controller"].ToString();
filterContext.Controller.ViewBag.ActionName = filterContext.RequestContext.RouteData.Values["action"].ToString();
base.OnActionExecuting(filterContext);
}
}
But instead of putting the action- or controllername into the viewbag, you could load stuff from your database based on action and/or controller.
What if you have service or helper method that retrieves all the titles from the database and stores them to some sort of cache or static variable think Dictionary. Depending on how you plan to update with the site running determines when or how.
Then expose a static helper method that the views can call to retrieve their title.
You could probably even incorporate a T4 template to set a property for each view to call the method with the correct retrieval key
Currently i am working on asp.net MVC-2 applications. i met with a problem that how to get signup form data into controller class. There is a solution that we can use FormCollection object but how to get individual field values.
Can anyone please help?
The simplest way to handle posted values is with the FormCollection object you mentioned. You can access it like an array:
public ActionResult YourAction(FormCollection form)
{
// assuming a form element posted with the name, "user"
var user = FormCollection["user"];
return View();
}
The best way to handle posted values is by using a strongly typed view model. The view model would contain properties of your form. The MVC framework will automagically bind your form elements to this object, if possible.
So, your view model class might look like:
public class UserFormViewModel
{
public string Username { get; set; }
public int Age { get; set; }
}
If your HTML form contained two inputs with their name attributes set to Username and Age, then your controller action could be modified to use the strongly types view model just described:
public ActionResult UserForm(UserFormViewModel vm)
{
string username = vm.Username;
int age = vm.Age;
// persist to database, etc
return View();
}
Go and download the following project. There is a free pdf document. Work through the entire sample. It makes you understand all the basics you need to know about asp.net mvc framework
Mvc Music Store
And also if you create a default mvc project in visual studio, it should show you how to handle the signup form in the Register action.
So, I've successfully implemented my own MembershipProvider and that's working exactly as I intended it to.
I've decorated my controller actions with the [Authorize] attribute, and this is also work exactly as I want.
My question is, where should I put code that determines "how much" access a use has?
[Authorize] simply means that the current user is a valid user of the system. The current user may have UserAccess = 1, or 2, or 4, or 8 or whatever. Would I perform this check in the Controller method? or should I pass the user into my Repository class, and have the respository class return only those records which the current user has access to?
In other words, what's the best way to seperate this concern? since its related to authentication. I'm thinking that I should pass the user into the Repository.GetData() method, and have it perform the necessary lookups.
The second part of this queston, is how do I limit access on a specific View, based on the user? For example if the current user has UserAccess = 2, I want to omit some fields, but if its UserAccess = 4 I want to show all fields.
Update
After a little bit more research, it looks like I could potentially kill two birds with one stone, if I implement my own RoleProvider -- I see how I can do this to restrict data access on the controller [Authorize(Roles = "Admin)], and it looks like the best option for me. How can I use this to render my View differently based on Role? Would I make seperate Views and return the correct view from the Controller? Or make one View with in-line C#?
First part of your question: keep the controller thin and put the access level code in your repository class/model. Second part of your question: you can create different views for each access level. Or you can put logic in the view itself to check the access level of the user, but that's fairly hackish and not secure. I would just have the view not render any fields that come back null/empty from the model.
You can make your own AuthorizeAttribute that takes the UserRole needed to execute this action in parameter.
[CustomAuthorize(UserRole.Administrator)]
public ActionResult YourAction()
{
}