Same code for each mvc actionresult in home controller - c#

So I have some generic actionresults that link to various views for the time being. The layout page contains a call to adfs to populate a logged in user name that has to be for each page. Looks like this:
<div class="float-right">
<section id="login">
Hello, <span class="username">#ViewBag.GivenName #ViewBag.LastName</span>!
</section>
</div>
In the home controller, what makes this logged in name work is this code here:
public ActionResult Index()
{
ClaimsIdentity claimsIdentity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
Claim claimGivenName = claimsIdentity.FindFirst("http://sts.msft.net/user/FirstName");
Claim claimLastName = claimsIdentity.FindFirst("http://sts.msft.net/user/LastName");
if (claimGivenName == null || claimLastName == null)
{
ViewBag.GivenName = "#FAIL";
}
else
{
ViewBag.GivenName = claimGivenName.Value;
ViewBag.LastName = claimLastName.Value;
}
return View();
}
But as mentioned earlier, I need this to display when a user goes to each link (actionresult). Therefore, I am having to post all the code above into each actionresult in order to achieve this.
Is there some way I can have this apply to each actionresult as a whole rather than having to duplicate code from one action to another? I did try just to register into an actionresult for my _Layout.cshtml and make the call to that partialview, but that didn't give me favorable results. I am sure it is something simple that I am missing.
Hoping some of you can help. Thanks much.

We use an abstract controller and override its OnActionExecuting method to execute code before the actual action method gets invoked. With this abstract controller, all you have to do is make any other controllers inherit from it to gain it's functionality. We also use this base controller as a place to define other helper methods that other controllers which extend it can use, such as GetUsernameForAuthenticatedUser().
public abstract class AbstractAuthenticationController : Controller
{
private readonly IAuthenticationService _authService;
protected AbstractAuthenticationController()
{
_authService = AuthenticationServiceFactory.Create();
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
EnsureUserIsAuthenticated();
}
internal void EnsureUserIsAuthenticated()
{
if (!_authService.IsUserAuthenticated())
{
_authService.Login();
}
}
protected string GetUsernameForAuthenticatedUser()
{
var identityName = System.Web.HttpContext.Current.User.Identity.Name;
var username = _authService.GetUsername(identityName);
if (username == null) throw new UsernameNotFoundException("No Username for " + identityName);
return username;
}
}
This functionality could also be implemented in an Attribute class which allows you to decorate your controllers as opposed to using inheritance but the end result is the same. Here is an example of a custom controller attribute implementation.

You could create a base controller and make all the controllers inherit from it. Move the code that sets the given and last names into a separate, protected method and call it whenever you need. I think you could call the function in the Initialize method of the base controller. This way you won't need to call it directly into the actions.
You could also make a hierarchy of models and have GivenName and LastName as properties on the base model, instead of working with the ViewBag.

Another alternative to using OnActionExecuting as this is just for a set part of the template would be to give it its own action method that returns a partial and call #Html.Action()

Related

MVC C# preventing normal users from accessing admin control URL - No roles

Right now, my user table has a bool called Admin. As the code shows, if user.admin = true, the user is able to see the admin area button and access it.
#if (Common.UsuarioLogueado.Admin) {
<li>Admin control panel</li>
}
This is working as intended. However, non admin users can still go to the control panel by accessing it´s url http://localhost/appName/admin/ClientesAdmin/list
How do I prevent such thing? I was thinking about showing an error msg
Going along with the other answers about using Roles, and the AuthorizeAttribute.. which in my opinion is the better way to achieve what you're trying to do, there is another way.
You could just simply redirect the user to another page.. preferable an error page saying you don't have access to the requested page, or just a 401 page which the AuthorizeAttribute would do if you weren't authorized.
Alternate Solution
public class ClientesAdmin : Controller {
// [Authorize(Roles="Admin")] could do it this way
public ActionResult List() {
// or..
if(!Common.UsuarioLogueado.Admin)
{
return new HttpStatusCodeResult(401);
// or
// return View("Error") // usually there is an 'Error' view the Shared folder
}
return View();
}
}
This is not the best solution but I don't know how far along your project is, but simply an alternate solution.
This is how I do it. However your membership system needs to be using ASP.Net Roles for this to work properly.
In your controller you just add the data annotation Authorize. for the function to be accessed by the client, they must be logged in and have the roll specified in the function.
This solution may not be direct cut and paste, but you can see the basic usage then perhaps do a little more research on the Authorize functionality.
public class MyController : Controller {
[Authorize(Roles="Admin")]
public ActionResult AdminIndex() {
return View();
}
[Authorize(Roles = "basic")]
public ActionResult BasicUsersIndex() {
return View();
}
}
Ideally you should be using role based access control. By limiting access by the role, rather than a boolean value in a table you could decorate your CientesAdmin controller with an Authorize Attribute like below.
[Authorize(Roles = "Admin")]
public class CientesAdminController : Controller
{
}
You could also use razor helpers to check if a user IsInRole("Admin").
There is a lot of help on the net to guide you down this path, but if your app is already developed you probably want to stage your changes. Then the recommendation would be to create your own AuthoriseAttribue. Something like.
public class RestrictAccessToAdmins : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Do the default Authorise Logic (Check if user is loggedin)
base.AuthorizeCore(httpContext);
if (httpContext.User.IsInRole("Admin")) return true;
var id = httpContext.User.Identity.GetUserId();
using (ApplicationDbContext context = new ApplicationDbContext())
{
//Implement you own DB logic here returning a true or false.
return context.Common.First(u => u.userid == id).UsuarioLogueado.Admin;
}
}
}
To use the attribute you'd do the following.
[RestrictAccessToAdmins]
public class CientesAdminController : Controller
{
}
Then over time, with better understanding of the default authorise attribute and a bit of refactoring you could easily change the attribute to below :)
[RestrictAccessToAdmins(Roles = "Admin")]
public class CientesAdminController : Controller
{
}

How to share controller actions/view with other controllers

I would like to call the MainController.GetData() action from a few different URLs without a bunch of copied/pasted code. Is there a preferred way people tackle this? Seems like I might be able to do it with routing as well. I would just like to reuse the view and action since all that code would be the same if I made a version for the GetMyData() action.
**Example urls**
*/main/getdata
/other/getmydata
/diffferent/getothersdata?userid=3
public ActionResult MainController::GetData()
{
var data = GetData();
return View(collection);
}
public ActionResult OtherController::GetMyData()
{
var userId = GetCurrentUserId();
var data = GetData(userId);
return View("../main/getdata", collection);
}
Although controllers look like simple classes, their behavior inside the MVC framework is more specialized. You can't, or perhaps it's better to say you shouldn't, just call an action from one in an action for another, as if it's just any old method. Mostly, this is because controllers have context, and you have to instantiate them and set them up just right so that everything works. That's non-trivial to do inside of an action method and it's going to make your code ugly as hell.
You have two choices really. If you just want the result of the action, the best method is to utilize HttpClient and actually submit an HTTP request for it, just like any other request that would activate it.
However, based on your problem description, option two is probably more appropriate. You can create a base class and then inherit from that. For example, if you know two controllers are both going to need GetData, you can do something like:
public abstract class BaseController : Controller
{
protected IEnumerable<Data> QueryData(int? userId = null)
{
...
}
}
public class MainController : BaseController
{
public ActionResult GetData()
{
var data = QueryData();
return View(data);
}
}
public class OtherController : BaseController
{
public ActionResult GetMyData()
{
var userId = GetCurrentUserId();
var data = QueryData(userId);
return View(data);
}
}
In other words, you factor out the common functionality into something both the actions on both derived controllers can use.
This is all you needed for this scenario, but you can also implement entire actions on the base controller. For example, if you added a Foo action to BaseController, both MainController and OtherController would then responds to requests for Foo, without having to actually explicitly define that. You can then also override these actions if you need to, as well.

MVC5 base controller that does setup of things that should always be done

I'm working with an MVC5 project. In this project I have a few things I always want to do in pretty much every response to the client.
For example, I always want to see if the user is logged in, and if so put the name of the user and their id into a the ViewBag variable for use in the .cshtml file.
I have a base controller which all other controllers inherit from. My first thought was to do these things in the constructor of that controller, but this does not work as the User variable does not exist yet.
Is there another way to do this, without calling a Setup() method in each Action?
Can I listen to some event that fires before an ActionResult is returned and insert my ViewBag data there?
Example of what does not work ;)
[InitializeSimpleMembership]
public class BaseController : Controller
{
protected USDatabase _database = new USDatabase();
public BaseController()
{
if (User.Identity.IsAuthenticated == true)
{
var usr = _database.UserProfiles.Where(x => x.UserName.ToLower() == User.Identity.Name.ToLower()).FirstOrDefault();
if (usr != null)
{
ViewBag.UserName = usr.UserName;
ViewBag.UserId = usr.Id;
}
}
}
}
My solution after reading the ideas in the answers below:
Created an Actionfilter I triggered on the base controller.
public class UserDataFilter : ActionFilterAttribute
{
//OnActionExecuting – This method is called before a controller action is executed.
//OnActionExecuted – This method is called after a controller action is executed.
//OnResultExecuting – This method is called before a controller action result is executed.
//OnResultExecuted – This method is called after a controller action result is executed.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var User = filterContext.HttpContext.User;
if (User.Identity.IsAuthenticated == true)
{
using (var db = new USDatabase()) {
var usr = db.UserProfiles.Where(x => x.UserName.ToLower() == User.Identity.Name.ToLower()).FirstOrDefault();
if (usr != null)
{
var ViewBag = filterContext.Controller.ViewBag;
ViewBag.UserName = usr.UserName;
ViewBag.UserId = usr.Id;
}
}
}
}
}
Base controller now looks like this:
[InitializeSimpleMembership]
[UserDataFilter]
public class BaseController : Controller
{
protected USDatabase _database = new USDatabase();
public BaseController()
{
}
}
And all my other controllers Implement the BaseController.
Yes .. what you need is an Action Filter, action filters are .net attributes inherit from ActionFilterAttribute you can do what you specified using them, here is a link to understanding them and a few basic samples on what you do with them:
http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/understanding-action-filters-cs
Take a look at http://msdn.microsoft.com/en-us/library/system.web.mvc.controller(v=vs.98).aspx: THere are various Events you could use, depending on the exact circumstances.
For example: OnActionExecuting, OnActionExecuted

Role Management in MVC3

I want to add a functionality to application such that only admin can create users and he can provide access to particular pages to user.
He can create roles and can provide users different roles.
I am using Visual Studio 2010 and building this application in MVC3.
Please give me suggestions to make over it.
Thanks in advance.
1.Decorate your user creation and permission setting actions with Authorize attribute
(Notify, that usage of Roles property of AuthorizeAttribute requires implementation of MembershipProvider (standart or custom) and registering it in web.config)
public class AccountController : Controller
{
[HttpGet, Authorize(Roles = "Admin")]
public ViewResult CreateUser()
{
return View();
}
[HttpPost, Authorize(Roles = "Admin")]
public ActionResult CreateUser()
{
//... call service method to create user
}
[HttpPost, Authorize(Roles = "Admin")]
public ActionResult AssignPageToUser(int userId, string controllerName, string ActionName)
{
//... insert record into table (UserPermissions) with attributes (userId, actionName, controllerName)
}
// other methods without decoration by authorize attribute
}
Next paragraphs are correct if you really want to have full control on action permissions separately for each user.
If you think, that your permissions can group in finite and small number on roles - you can decorate all actions/controllers by authorize attribute and specify roles, for which action/controller available: [Authorize("Customer, Manager, RegionalAdmin")] and give admin possibility to assign roles to users. But remember, that in is enough to be in only 1 of listed roles to get access, you can't require by this attribute, for example and Admin, and Manager roles.
If you want to require necessarily more than 1 role, use multiple attributes:
public class MyController:Controller
{
[Authorize(Roles = "Manager")]
[Authorize(Roles = "Admin")]
public ActionResult Action1()
{
//...
}
}
2.For your pages you can create your own filter attribute, inherited from authorize attribute, that will check, if action is available for user (i think you want to assign actions but not views to user).
public UserPermissionRequiredAttribute: AuthorizeAttribute
{
public OnAuthorization(AuthorizationContext filterContext)
{
var isAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
var userName = filterContext.HttpContext.User.Identity.Name;
var actionName = filterContext.ActionDescriptior.ActionName;
var controllerName = filterContext.ActionDescriptior.ControllerDescriptor.ControllerName;
if (isAuthenticated && myUserActionPermissionsService.UserCanAccessAction(userName, actionName, contollerName)
{
filterContext.Result = HttpUnauthorizedResult(); // aborts action executing
}
}
}
3.Decorate actions (controllers), that accessible for users granted by admin:
MySpecialController: Controller
{
[UserPermissionRequired]
Action1()
{
//...
}
[UserPermissionRequired]
Action2()
{
//...
}
Action3()
{
//...
}
}
I don't recommend to use base controller for that aim, because attribute usage is more flexible (you have control on action/controller level instead of only controller level), it is better way to implement separated responsibility. Base controller and filter attribute usage correlated as polymorphism and switch operator.
You're asking a very broad question, and it would take some time to review all your requirements. In any case, you could start by adding a user property to a controller from which all other controllers inherit. Then, you could interrogate that user instance to determine whether they have access to the current route. This solution should give you the foundation you need to add some administrative views for your business requirements.
public class MegaController
{
protected User CurrentUser { get; set; }
protected override void Initialize(RequestContext context)
{
if (requestContext.HttpContext.User.Identity.IsAuthenticated)
{
var userRepository = new UserRepository();
CurrentUser = userRepository.GetUser(
requestContext.HttpContext.User.Identity.Name);
}
}
}
The User and UserRepository types can be your own design. You could use LINQ To Entities to wrap a table named "User" and then within your controllers, you could have access to any fields in that table.
Then, subclass all controllers from MegaController
public class AdminController : MegaController
{
public ActionResult Action1()
{
return View();
}
}
public class SomeOtherController : MegaController
{
public ActionResult Action1()
{
return View();
}
}
Now, this doesn't completely solve your "admin" issue. To do so, you could include logic in MegaController.Initialize() to interrogate the request information. Once you have the requested route and user in context, your code could make a decision whether to allow the request, redirect it, etc.
protected override void Initialize(RequestContext context)
{
// ...
if(context.HttpContext != null)
{
if(context.HttpContext.Request.Path == "some/restricted/route"
&& CurrentUser.Role != "Admin")
{
// or similar error page
var url = Url.Action("UnAuthorized", "Error");
context.HttpContext.Response.Redirect(url);
}
}
}
One caveat with this method is that any new controllers added to your application would have to inherit from MegaController, an architecture that could be easily missed by future developers on the project.
Read about plain old forms authentication to add support for roles and user management.
Then use the [Authorize(Roles="RoleName1")] on controllers or actions to control access.
Check MvcMembership, also available on Nuget. You will have all the basics for User Management in an ASP.NET MVC 3 site.
You will need a user / role provider. Read this tutorial to learn how to setup a database that will hold your users and roles. Once it is setup, you will have all the stored procedures needed for first setup / manual testing created.

ASP.NET MVC, 'Ticket Required' Attribute

I am attempting to build a system that allows users to perform certain actions, but their account must have a specific 'Ticket' per time they do it. For instance, suppose they wish to create a Product, they would need a CreateProductTicket.
I could simply do this with some 'if' statements, sure, but I want to try a bit more of a robust solution. My structure looks something like this...
interface ITicket<T> where T : ITicketable
{
}
My basic goal is to build an Attribute, perhaps like the following..
public class TicketRequiredAttribute : Attribute
{
public TicketRequiredAttribute(ITicket<T> ticket)
{
if(ticket == null)
return;
}
}
And to be able to decorate Controller or Repository Actions with this. So like ...
ProductsControlller
[TicketRequired(CreateProductTicket)]
public ActionResult CreateProduct(Product product)
{
// ... **I am unsure how to tell if TicketRequired was true or not**
}
Problem 1
I'm not familiar enough with attributes to know how to tell if TicketRequired was 'met' or not. Can anyone enlighten me on this?
Problem 2
The problem I am running into is with database querying. I want to be able to check the user (IMembershipRepository has a GetUser method), but I'm not entirely certain how to do that through an attribute.
Using Castle.Windsor, I have my Dependency Injection set up to inject repositories into controllers. I suppose I could pass the IMembershipRepository through the TicketRequired constructor, but I have a feeling that will become very messy - and extremely unstable. Is there a more logical way to approach this?
You're almost there. You can find more details at http://www.asp.net/mvc/tutorials/understanding-action-filters-cs
I would only use the attribute on the action since the website is where I do all my authorization.
Here is a possible solution. I have not tested this, but it should work. You'll need to verify the way I'm redirecting, not sure if that's the proper way.
public class TicketRequiredActionFilter : ActionFilterAttribute
{
private Type _ticketType;
public TicketRequiredAttribute(Type ticketType)
{
_ticketRequired = ticketType;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
UserServices userServices = GetUserServicesViaDIContainer(); // you'll need to figure out how to implement this
string userId = filterContext.HttpContext.User.Identity.Name
bool hasTicket = userServices.HasTicket(_ticketType, (int)userId); // again, you'll need to figure out the exact implementation
if(!hasTicket)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Home" }, {"action", "NoPermission" } })
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
In your controller:
[TicketRequiredActionFilter(typeof(CreateProductTicket))]
public ActionResult MyMethod()
{
// do stuff as if the person is authorized and has the ticket
}
If the user doesn't have the ticket, a redirect is issues;, otherwise, continue as normal.
This sounds very much like user roles.
How are you handling the user membership? If your using the built-in asp.net membership you can use roles. So each user will have a certain number of roles in your case one of the will be "CreateProductTicket" then you can decorate your action or controller with the Authorize attribute. Something like:
[Authorize(Roles="CreateProductTicket")]
public ActionResult CreateProduct(Product product)
If a user doesn't have the role or is not authorized then they can access the action.

Categories

Resources