I am using ASP.NET 4.0 Framework.I have a directory which contains 10 PDF files i.e pdf1,pdf2....pdf10. On button click i am using Response.Redirect & passing Pdf file path in order to open it in the browser. but, this enables user to view the path(url) of the PDF folder using this url he can open any other pdf directly. How can i stop him accessing PDF directly from the url
Use Request.ServerVariables["HTTP_REFERER"] this will tell you where the request had come from. If its not on your site then take appropriate action.
e.g.
if(Request.ServerVariables["HTTP_REFERER"].ToLower().IndexOf("mysite.com") == -1){
// Not from my site
Response.Redirect("NotAllowed.aspx");
}
This link may help you to stop him accessing PDF directly from the url.
Use this code in Global.asax.cs and Call [NoDirectAccess] to all controllers
//Prevent direct URL access: Call [NoDirectAccess] to all controllers to block
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NoDirectAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.UrlReferrer == null ||
filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Home", action = "Login", area = "" }));
}
}
}
You will need to add a secure layer. If you are using MVC it will probably be simpler to implement since you will do the authorisation in the controller action. However, for classic ASP you will probably need to implement a custom handler.
There is no easy solution to this. You could devise some sort of rolling code based on the server date/time that must be part of the query string and check for the correctness of this in the page load, if you make it sufficiently complicated / long, then people will not be able to enter this manually.
Related
I need to port a website to asp.net and decided to use Umbraco as the underlying CMS.
The issue I'm having is I need to retain the URL structure of the current site.
The current URL template looks like the following
domain.com/{brand}/{product}
This is hard to make a route for since it mixes in with all the other content on the site. Like domain.com/foo/bar which is not a brand or product.
I've coded up a IContentFinder, and injected it into the Umbraco pipeline, that check the URL structure and determins if domain.com/{brand} matches any of the known brands on the site, in which case i find the content by its internal route domain.com/products/ and pass along {brand}/{model} as HttpContext Items and return it using the IContentFinder.
This works, but it also means no MVC controller is called. So now I'm left with fetching from the database in the cshtml file which is not so pretty and kind of breaks MVC conventions.
What i really wan't is to take the url domain.com/{brand}/{product} and rewrite it to domain.com/products/{brand}/{product} and then being able to hit a ProductsController serving up the content based on the parameters brand and product.
There are a couple of ways to do this.
It depends a bit on your content setup. If your products exist as pages in Umbraco, then I think you are on the right path.
In your content finder, remember to set the page you've found on the request like this request.PublishedContent = content;
Then you can take advantage of Route Hijacking to add a ProductController that will get called for that request: https://our.umbraco.org/Documentation/Reference/Routing/custom-controllers
Example implementation:
protected bool TryFindContent(PublishedContentRequest docReq, string docType)
{
var segments = docReq.Uri.GetAbsolutePathDecoded().Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
string[] exceptLast = segments.Take(segments.Length - 1).ToArray();
string toMatch = string.Format("/{0}", string.Join("/", exceptLast));
var found = docReq.RoutingContext.UmbracoContext.ContentCache.GetByRoute(toMatch);
if (found != null && found.DocumentTypeAlias == docType)
{
docReq.PublishedContent = found;
return true;
}
return false;
}
public class ProductContentFinder : DoctypeContentFinderBase
{
public override bool TryFindContent(PublishedContentRequest contentRequest)
{
// The "productPage" here is the alias of your documenttype
return TryFindContent(contentRequest, "productPage");
}
}
public class ProductPageController : RenderMvcController {}
In the example the document type has an alias of "productPage". That means that the controller needs to be named exactly "ProductPageController" and inherit the RenderMvcController.
Notice that it does not matter what the actual pages name is.
I am migrating an ASP.NET application to ASP.NET Core and they have some calls to HttpServerUtility.Transfer(string path). However, HttpServerUtility does not exist in ASP.NET Core.
Is there an alternative that I can use? Or is Response.Redirect the only option I have?
I want to maintain the same behaviour as the old application as much as possible since there is a difference in between Server.Transfer and Response.Redirect.
I see some options for you, depending on your case:
Returning another View: So just the HTML. See answer of Muqeet Khan
Returning another method of the same controller: This allows also the execution of the business logic of the other action. Just write something like return MyOtherAction("foo", "bar").
Returning an action of another controller: See the answer of Ron C. I am a bit in troubles with this solution since it omits the whole middleware which contains like 90% of the logic of ASP.NET Core (like security, cookies, compression, ...).
Routing style middleware: Adding a middleware similar to what routing does. In this case your decision logic needs to be evaluated there.
Late re-running of the middleware stack: You essentially need to re-run a big part of the stack. I believe it is possible, but have not seen a solution yet. I have seen a presentation of Damian Edwards (PM for ASP.NET Core) where he hosted ASP.NET Core without Kestrel/TCPIP usage just for rendering HTML locally in a browser. That you could do. But that is a lot of overload.
A word of advice: Transfer is dead ;). Differences like that is the reason for ASP.NET Core existence and performance improvements. That is bad for migration but good for the overall platform.
You are correct. Server.Transfer and Server.Redirect are quite different. Server.Transfer executes a new page and returns it's results to the browser but does not inform the browser that it returned a different page. So in such a case the browser url will show the original url requested but the contents will come from some other page. This is quite different than doing a Server.Redirect which will instruct the browser to request the new page. In such a case the url displayed in the browser will change to show the new url.
To do the equivalent of a Server.Transfer in Asp.Net Core, you need to update the Request.Path and Request.QueryString properties to point to the url you want to transfer to and you need to instantiate the controller that handles that url and call it's action method. I have provided full code below to illustrate this.
page1.html
<html>
<body>
<h1>Page 1</h1>
</body>
</html>
page2.html
<html>
<body>
<h1>Page 2</h1>
</body>
</html>
ExampleTransferController.cs
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace App.Web.Controllers {
public class ExampleTransferController: Controller {
public ExampleTransferController() {
}
[Route("/example-transfer/page1")]
public IActionResult Page1() {
bool condition = true;
if(condition) {
//Store the original url in the HttpContext items
//so that it's available to the app.
string originalUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}{HttpContext.Request.QueryString}";
HttpContext.Items.Add("OriginalUrl", originalUrl);
//Modify the request to indicate the url we want to transfer to
string newPath = "/example-transfer/page2";
string newQueryString = "";
HttpContext.Request.Path = newPath;
HttpContext.Request.QueryString = new QueryString(newQueryString);
//Now call the action method for that new url
//Note that instantiating the controller for the new action method
//isn't necessary if the action method is on the same controller as
//the action method for the original request but
//I do it here just for illustration since often the action method you
//may want to call will be on a different controller.
var controller = new ExampleTransferController();
controller.ControllerContext = new ControllerContext(this.ControllerContext);
return controller.Page2();
}
return View();
}
[Route("/example-transfer/page2")]
public IActionResult Page2() {
string originalUrl = HttpContext.Items["OriginalUrl"] as string;
bool requestWasTransfered = (originalUrl != null);
return View();
}
}
}
Placing the original url in HttpContext.Items["OriginalUrl"] isn't strictly necessary but doing so makes it easy for the end page to know if it's responding to a transfer and if so what the original url was.
I can see this is a fairly old thread. I don't know when URL Rewriting was added to .Net Core but the answer is to rewrite the URL in the middleware, it's not a redirect, does not return to the server, does not change the url in the browser address bar, but does change the route.
resources:
https://weblog.west-wind.com/posts/2020/Mar/13/Back-to-Basics-Rewriting-a-URL-in-ASPNET-Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0
I believe you are looking for a "named view" return in MVC. Like so,
[HttpPost]
public ActionResult Index(string Name)
{
ViewBag.Message = "Some message";
//Like Server.Transfer() in Asp.Net WebForm
return View("MyIndex");
}
The above will return that particular view. If you have a condition that governs the view details you can do that too.
I know that this is a very old question, but if someone uses Razor Pages and is looking to a Server.Transfer alternative (or a way to return a different view depending on a business rule), you can use partial views.
In this example, my viewmodel has a property called "UseAlternateView":
public class TestModel : PageModel
{
public bool UseAlternateView { get; set; }
public void OnGet()
{
// Here goes code that can set UseAlternateView=true in certain conditions
}
}
In my Razor View, I renderize a diferent partial view depending of the value of the UseAlternateView property:
#model MyProject.Pages.TestModel
#if (Model.UseAlternateView)
{
await Html.RenderPartialAsync("_View1", Model);
}
else
{
await Html.RenderPartialAsync("_View2", Model);
}
The partial views (files "_View1.cshtml" and "_View2.cshtml"), contain code like this:
#model MyProject.Pages.TestModel
<div>
Here goes page content, including forms with binding to Model properties
when necessary
</div>
Obs.: when using partial views like this, you cannot use #Region, so you may need to look for an anternative for inserting scripts and styles in the correct place on the master page.
I have a website that has for examples pages A, B, C, D,.... I want that the user can only access page A once he has registered. To access the remaining pages the user needs to complete some joining formalities which include making a payment among others.
Now the simple way would be where I add a check in all my pages currently completed to make sure that the pages B, C, D... requested are displayed only if the user has completed all joining formalities. This will need me to re work all those existing pages all over again. Also, all future pages developed will need to remember to add that check.
To come around this I used a custom authorize attribute class that inherits from AuthorizeAttribute and made a check in the overridden OnAuthorization method there as in the snippet below
else if (!SessionCache.FormalitiesCompleted)
{
//force completion of formalities
if (!( string.Equals(filterContext.ActionDescriptor.ActionName, "Index", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Options", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Index_B", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Index_C", StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, "Formalities", StringComparison.InvariantCultureIgnoreCase)))
{
string _action = filterContext.ActionDescriptor.ActionName;
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Formalities", action = _action, area = "FormalitiesArea" }));
}
}
As you can see there are too many OR conditions in the IF. I suspect this will increase as the complexity of the formality increases.
Is there a better way to enforce the user doesn't visit any other page on the site unless the formalities are completed?
Thanks for your time.
You probably should look at the [Authorize attribute] http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx If you decorate an action with that attribute only logged in users can access it and others will be redirected to the login page.
For Paid/Registered users, I would inherit from this and make a PaidUserAuthorize attribute:
public class PaidUserAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if(filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectResult("/PaymentPage");
}
}
}
Therefore decorating an action with
[PaidUserAuthorize(Roles = "PaidUser", "Super User")]
will redirect unpaid users to the payment page.
After a user pays - add them to the PaidUser role. So when they go to the action again they will be allowed access.
You can create an Actin Filter and apply it to the Actions or Classes that you want to restrict access too. In you actions filter, you'll want to override the OnActionExecuting method and implement your checking logic in there. You can allow or deny access to those views based on whatever you want to do
Check this link out for a sample
What about using the [Authorize] annotation in your Controllers? By creating your own AuthorizeAttributes you should be able to customise the authorisation per action based on whatever MembershipProvider you choose to use or create.
I have settings in AppSettings (web.config) and I need to pass them to an external javascript file.
In ASP.NET I would think of an ASHX handler to write the javascript file to the response replacing placeholders with the settings values.
Is there a better way to do it in ASP.NET MVC? Thank you.
You could send them via a JsonResult?
In your JS, you'd have a request which sends a GET/POST request to a particular action (let's call it GetAppSetting(), and the corresponding value is returned in the response.
For security reasons, I would restrict what can be requested though...
public JsonResult GetAppSetting(string id)
{
//You could check what's been requested here if you want to make sure you're only returning information that you may not wish to send.
string appSetting = AppSettings[id];
if(string.IsNullOrEmpty(appSetting) == false)
{
return Json(appSetting, JsonRequestBehavior.AllowGet);
}
//Handle non-existent settings here...
throw new Exception("This setting does not exist");
}
Alternatively, it has been suggested by Chris Marisic in the comments that you may want to absolutely limit this to just a specific set of key/values for developer reasons. Therefore, here is a quick example of that...
public JsonResult GetAppSettings()
{
var appSettings = new Dictionary<string, string>();
appSettings.Add("myAppSetting1", AppSettings["myAppSetting1"]);
appSettings.Add("myAppSetting2", AppSettings["myAppSetting2"]);
appSettings.Add("myAppSetting3", AppSettings["myAppSetting3"]);
return Json(appSettings, JsonRequestBehavior.AllowGet);
}
Note the JsonRequestBehavior.AllowGet in the JsonResults (MVC 2 only). This is because, by default, ASP.NET MVC 2 will not allow GET requests on actions which return a JsonResult. You can circumvent this by adding the JsonRequestBehaviour, but I should probably mention that you should consider doing a post request in order to retrieve this information, and remove this behaviour in your action.
I there a simple way when using ASP.NET 4.0 routing with Web Forms to produce a route that will act as some kind of wildcard?
It seems to me that within WebForms, you have to specify a route for every page - I am looking for some kind of generic route that can be used where nothing specific is required, perhaps mapping directly from path to path so...
http://somedomain.com/folder1/folder2/page would possibly map to folder1/folder2/page.aspx
Any suggestions?
Thanks
You can match all remaining routes like this:
routes.MapPageRoute("defaultRoute", "{*value}", "~/Missing.aspx");
In this case, we know all routes, and want to send anything else to a "missing"/404 page. Just be sure to put this as the last route, since it is a wildcard and will catch everything.
Alternatively you could register a route the same way, but internally does mapping to a page, like this:
routes.Add(new Route("{*value}", new DefaultRouteHandler()));
That handler class would do your wildcard mapping, something like this:
public class DefaultRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
//Url mapping however you want here:
var pageUrl = requestContext.RouteData.Route.Url + ".aspx";
var page = BuildManager.CreateInstanceFromVirtualPath(pageUrl, typeof(Page))
as IHttpHandler;
if (page != null)
{
//Set the <form>'s postback url to the route
var webForm = page as Page;
if (webForm != null)
webForm.Load += delegate { webForm.Form.Action =
requestContext.HttpContext.Request.RawUrl; };
}
return page;
}
}
This is broken a bit in odd places to prevent horizontal scrolling, but you get the overall point. Again, make sure this is the last route, otherwise it'll handle all your routes.
Additionally - Keep in mind that you need to add an exception for the .axd files in your Global.asax file if there are validation controls in your web app:
http://basgun.wordpress.com/2010/10/25/getting-syntax-error-in-asp-net-routing-due-to-webresource-axd/
Otherwise, you will keep getting a syntax error because the routing picks up the .axd files and not properly loads the JavaScript files needed for the validation controls.