ASP.NET MVC Routing - Pass inbound route value to outbound URLs automatically? - asp.net

I have an ASP.NET MVC application with an Admin area that deals with administering Companies and their child entities, such as Users and Products. The default route associated with a child entity is defined as follows:
"Admin/Company/{companyID}/{controller}/{id}/{action}"
I would like to ensure that, everywhere in the Admin area, whenever the incoming route includes companyID, that this value is automatically included in every generated URL. For example, if my User Edit page has a link defined with Html.ActionLink("back to list", "Index"), the routing system will automatically grab the companyID from the incoming route data and include it in the outgoing route, without having to explicitly specify it in the call to ActionLink.
I think there's more than one way to achieve this, but is there a preferred/best way? Does it scream for a custom route handler? Something else?
My goal is to not lose the current company context when navigating around in the sub-sections, and I don't want to use Session - that could burn me if the user opens up multiple companies in different browser windows/tabs.
Thanks in advance!

Todd,
I am using an ActionFilterAttribute in my MVC 2 application to make this happen. There may be better ways to do this:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
sealed class MyContextProviderAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// See if the context is provided so that you can cache it.
string myContextParam = filterContext.HttpContext.Request["myContextParam"] ;
if (!string.IsNullOrEmpty(myContextParam))
filterContext.Controller.TempData["myContextParam"] = myContextParam;
else
// Manipulate the action parameters and use the cached value.
if (filterContext.ActionParameters.Keys.Contains("myContextParam"))
filterContext.ActionParameters["myContextParam"] = filterContext.Controller.TempData["myContextParam"];
else
filterContext.ActionParameters.Add("myContextParam", filterContext.Controller.TempData["myContextParam"]);
base.OnActionExecuting(filterContext);
}
}

Related

How can i do custom authorization in my ASP.NET MVC application

I am making a small SAAS application. I am using the same database for all users and only differentiating between the data using username and ids. This means that the user can type in a new url in the browser and see other users data. This, of course is not a desirable approach. I would like to do a check to see if the current user can actually access the resources: eg.
http://myapplication.com/images/15
And if the user changes the url to
http://myapplication.com/images/16
I should do a check in my database to see if the current user actually has access to see the images with user id 16. And if not redirect to a "not authorized" page.
How do I implement this?
The first step is to make sure that you never have any ID's for the user itself in the url. For instance, never have http://example.com/?user=10. You should always get the users id from their authentication rather than from the URL (or posted values either).
The second step, is to use that ID in your queries. So, for instance, let's say they seek http://example.com/images/100, then in your database you should have a mechanism that links the asset's ownership to the user, either a userid or a mapping table of id's to asset's, etc.. This way, if the user isn't allowed access, it will just return an empty result set. It's impossible for the data to be returned, and the empty result set should tell your page that the item doesn't exist (not necessarily an authorization failure, just that the object doesn't exist).
Third, any pages which are inherently about the user, such as a user profile, account page, or dashboard should never have any ID's at all in the URL, it should just automatically go to the authenticated users page.
Finally, if you need to prevent the user from accessing an entire page or set of pages, then you should do this in the OnAuthorization event or similar (custom attribute, base class, etc..) or using the built-in attribute authorization and use role based authorization. Never do authorization in the PageLoad or similar event (such as the controller action), because by the time you get to that step a lot of work has already happened in the pipeline. It's best to block access long before the page even starts to setup. Authorization events happen at the very beginning of the pipeline.
Make an Action that check userId and returns error page or file
public FileResult Image(string imageName)
{
string UserId = MethodWhereYouGetCurrentUserID();
if(imageName == null) return View("~/Views/Shared/Error.cshtml", (object)"Null image");
string imageShortName = imageName.Split(".")[0];
if(!UserId == imageShortName) return View(~/Views/Shared/Error.cshtml, (object)"You can't access to this");
string path = Server.MapPath("~/Contant/images/"+imageName);
return File(path, "image/jpg");
}
RouteConfig file
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute
(
name: "ImageRoute",
url: "/images/imageName",
default: new {controller = "Home", action = "GetImage"}
);
}

ASP.NET Web API - method that is called for all requests prior to the routed method?

I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.

Unique way to identify page instance within HttpContext

You can get the name of a page within HttpContext via Request.Path.
Is there a way to distinguish between different requests from the same page?
That is when two different instances of yourpage.aspx make a request, how can you distinguish between the two using HttpContext?
you probably want to do this in a base Page class, but here's what i would do
public partial class Default : System.Web.UI.Page
{
private Guid _instanceID;
public Guid InstanceID
{
get { return _instanceID; }
}
/// <summary>
/// Constructor
/// </summary>
public Default()
{
this._instanceID = Guid.NewGuid();
}
}
then using the HttpContext somewhere else in your code...
if (HttpContext.Current.CurrentHandler is Default)
{
((Default)HttpContext.Current.CurrentHandler).InstanceID;
}
Nothing built into ASP.NET will allow you to differentiate different "page instances" or requests from them.
However, you can easily add a Guid to your view state to uniquely identify each page. This mechanism works fine when you are in the Page class itself. If you need to identify requests before you reach the page handler, you need to use a different mechanism (since view state is not yet restored).
The Page.LoadComplete event is a reasonable place to check if a Guid is associated with the page, and if not, create one.
If you're using authentication, would it work for you to distinguish which user submitted the page?
You could use System.Web.Httpcontext.Current.User.Identity.Name.
just throwing this out there: NInject (and other DI containers) use a scoping mechanism based on the HttpContext.Current object itself, so depending on what you're trying to do, you could attempt to retrieve a state object from the DI container and go from there.

Custom behavior in a web application

I am working on an ASP.NET WebForms project, and we need the ability to configure behavior throughout the application based on the current user's "group". This applies to almost all aspects of the application, including site navigation, showing/hiding certain user controls on pages, and executing custom business logic in some cases. However, the vast majority of the application behavior is shared among groups, so we've ruled out the idea of creating entirely separate apps.
Essentially, what I'm looking for is an architectural approach to implementing custom behavior in an ASP.NET WebForms application. Is there a better approach than sprinkling if/else statements throughout the code base in the view layer, the business layer, and the persistence layer?
Edit: Some examples:
If a user in in Group A, their
navigation will consist of all
navigation from Group B plus a few
additional links.
If a user is in Group A, a page will
show user controls c1, c2, and c3.
If the user is in Group B, they will
only see c1 and c3 on the same page.
If a user saves some data on a form
and they are in Group A, send a
notification email. If the user is
in Group B, send a text message
instead.
We can solve all of these specific problems, but are looking for a way to encapsulate this behavior as much as possible so it's not scattered across the code base.
Edit: There are some interesting answers related to dynamically loading user controls. Should the logic to determine which controls to load or which behavior to use based on the user's group be encapsulated in one (non-cohesive) class, e.g.:
GroupManager.GetNavigationControl(int groupId) // loads site nav control based on group
GroupManager.PerformNotification(int groupId) // sends text or email based on group
Or should this logic exist as close as possible to the location in code where it is used, and therefore be spread across the different layers of the code base?
Well there's not a ton of details to go on here, but I would suspect you might benefit from polymorphism (i.e. various interface implementations) to deal with the parts of the application that differ between user groups. An Inversion of Control container like Spring.NET can help you wire up/configure these various implementations together based on the current user role. You might also benefit from Spring's Aspect Oriented Programming API in which you can decorate methods in your business layer/data access layer so that authorization logic can be executed.
By "Groups" do you mean "Roles"? If you're talking about roles, you can set your behavior by doing something like this
If User.IsInRole("SomeRandomRole") Then
'Do some random behavioral crap
ElseIF User.IsInRole("TheCoolRole") Then
'Do some cool behavioral crap
Else
'Do generic crap
End If
Another option might be to use UserControls based on roles. So when you have a page load, it will load a usercontrol based on the role that requested it.
you could have an PlaceHolder sitting empty and call the LoadControl method from the codebehind.
Then all your user controls would match your roles
Role = Admin | UserControl = Admin.ascx
Role = User | UserControl = User.ascx
Without going into too much detail and going on about IoC and all the like, I think I'd keep it pretty simple and have a plain old factory class that you would use to return the appropriate instantiated UI elements [user controls] based on the current user making the request. In doing this, you will have all of your 'if' statements in one single location. To displense with the 'if' statements you could simply create a mapping config file or DB table that contains references to the user controls to use when a user belongs to a particular group.
Note: Both of these options will result in the creation of dynamic controls on the page which is not without its own complications but I have successfully been using dynamic controls in my apps without issue for a while now - it was just a matter of getting down and dirty with the page life-cycle more than I initially felt comfortable with.
You could also inherit from the Principal object to handle this however you would like.
Here's how I have done it in an application that has custom rules like this:
Created my own IPrincipal object that descended from my "Person" object to be able to inherit the ability to look up groups and roles and such:
public class Principal : MyNamespace.Person, IPrincipal {
}
Make the current context use my IPrincipal object:
protected void Application_AuthenticateRequest(Object Sender, EventArgs E) {
if (HttpContext.Current.User != null &&
HttpContext.Current.User.Identity.IsAuthenticated &&
HttpContext.Current.User.Identity is FormsIdentity) {
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
HttpContext.Current.User = new MyNamespace.Principal(id);
}
}
I then made static methods so that I didn't have to cast every time I wanted to get the current user like this:
public class CurrentUser {
/// <summary>
/// Is the current user authenticated
/// </summary>
static public bool IsAuthed {
get { return System.Web.HttpContext.Current.User.Identity.IsAuthenticated; }
}
/// <summary>
/// Returns the Principal object in case it is needed. Also used for other static properties of this class.
/// </summary>
static public MyNamespace.Principal User {
get {
return (MyNamespace.Principal)System.Web.HttpContext.Current.User;
}
}
}
Then you can call things like CurrentUser.User.IsInGroup().

Most Correct way to redirect page with Model-View-Presenter Pattern

What is the best way to call a Response.Redirect in the Model-View-Presenter pattern while adhering to correct tier separation?
One way I handled this is for the presenter to raise an event (like Succeeded or something) that the view would subscribe to. When the presenter finished it's processing, it would raise the event, which would get handled by the View. In that handler, the view would redirect to the next page.
This way, the presenter doesn't need to know anything about pages or URLs or anything. It just knows when it has completed its task and lets the view know by raising an event. You can raise different events if the presenter succeeded or failed, in case you need to redirect to different places.
I do not know whether it is the most correct way, conceptually.
But what I did in my last MVP-applications, is create a wrapper around HttpContext.Current which I called HttpRedirector.
I also created a dummy redirector for testing purposes. Both keep track of the last redirected url, so that I can check in my unit tests that the redirect actually happened when I call a method on my controller/presenter. With an IOC-container I am able to switch the implementation of IRedirector based on the environment (production/test).
The way we do it works nicely once some ground work is laid. I'm sure there are several ways to skin a cat though. (Who skins cats anyway. Cats are cute and cuddly!)
First, this will only work on the ASP.Net compiled Web Projects, not Websites.
Each page should inherit from a custom abstract base class which looks something like this:
public abstract class PageBase : Page
{
private static string _baseUrl = "/";
public static string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected static string BuildUrl(string basePath)
{
if( !string.IsNullOrEmpty(basePath) && basePath.StartsWith("~/"))
{
basePath = basePath.replace("~/", BaseUrl);
}
return basePath;
}
protected static string LoadView(string path)
{
Response.Redirect(path);
}
}
Each page also implements a page-specific interface. Each page-specific interface also inherits from a base interface:
public interface IPageBase()
{
void LoadView(string path);
}
Then it's a matter of each page defining it's own version of BaseUrl. You might want to account for querystrings/path encryption/etc.
Finally, any of your presenters (which should be referencing the page-specific interfaces) can grab the static BuildUrl() on a desired page to view and then call LoadView() with the returned path.
It depends how generic your presenters are. If your presenters are totally UI agnostic (can be reused between WinForms and WebForms) then you'll have to abstract the redirection operation. In WebForms, the redirection operation would be implemented in the view by a Response.Redirect. In WinForms, (I disclaim lots of experience with WinForms) my guess is that it would be implemented by SomeForm.Show.
One simple, off-the-top-of-my-head option would be to include in the view's interface a ShowViewX() method. You can have one for each form the view could logically redirect to. Alternatively, the view can implement an interface method like Show(ConnectedViews) where ConnectedViews is an enum that includes a value for each of the views that can be "redirected" to from a particular view. This enum would live at the presenter level.
The above approaches are specific to view-presenter pairs. You could instead implement it as a system-wide thing. The logic would be similar to above, implemented in the base view and presenter. There would be a ShowView__() for each form, or a Show(Views) method where Views is an enum of all forms.
It's a toss-up between encapsulation and DRY-ness.

Resources