Serving static files in ASP.NET to authorized users only - asp.net-mvc

I am trying to secure a folder under my project that just has some static files, a combination of .htm and .js files. I have tried creating a custom HttpHandler like:
public class StaticFilesHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.IsAuthenticated)
{
// continue with the request
}
else
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
public bool IsReusable => false;
}
Then register it to be used with a route via Route.Config
routes.RouteExistingFiles = true;
routes.Add("helpRoute", new Route("folder/*.htm", new StaticFilesRouteHandler ()));
and a route handler to provide the
public class StaticFilesRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext context)
{
return new StaticFilesHttpHandler ();
}
}
and also via web.config under system.webServer
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="StaticFilesHttpHandler "/>
</handlers>
Files in the folder are provided by a 3rd party. I am to call a function inside a js file in the folder which then redirects the user to a proper .htm file inside it's sub structure. I do not want users to be able to type the url and access any of the files. What am I doing wrong?

can you change the type to TransferRequestHandler and make sure your path is correct.
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="TransferRequestHandler" />
</handlers>
in your global.asax file you can access the request in Application_BeginRequest to verify if the request is authenticated or not.

Related

umbraco MVC custom routes using a dot in url

I have a problem with using a dot in url umbraco MVC custom routes.
/logo/images/image.jpg?width=100 gives following errors:
[NullReferenceException: Object reference not set to an instance of an object.]
Umbraco.Web.Mvc.UmbracoVirtualNodeByIdRouteHandler.FindContent(RequestContext requestContext, UmbracoContext umbracoContext) +18
Umbraco.Web.Mvc.UmbracoVirtualNodeRouteHandler.GetHttpHandler(RequestContext requestContext) +48
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +11987058
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +141
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +91
/logo/images/image.jpg/?width=100
Works, but this isn’t a good solution for me.
I have tried adding this in webconfig
<location path="logo">
<!-- This only applies it to the relevant path and keeps the protection in place for elsewhere -->
<system.web>
<httpHandlers>
<add path="/images/*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" />
</httpHandlers>
</system.web>
<!-- Required for IIS 7.0+ -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</location>
taken from https://average-joe.info/allow-dots-in-url-iis/
but it won't work:(
My custom route looks like this:
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//custom route
RouteTable.Routes.MapUmbracoRoute(
"images",
"logo/{action}/{key}",
new
{
controller = "Image",
key = UrlParameter.Optional,
},
new ProductsRouteHandler(4884));
}
}
public class ProductsRouteHandler : UmbracoVirtualNodeByIdRouteHandler
{
public ProductsRouteHandler(int realNodeId) : base(realNodeId)
{
}
protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, IPublishedContent baseContent)
{
return base.FindContent(requestContext, umbracoContext, baseContent);
}
}
I'am using umbraco vs.7.4.3
The UmbracoModule ignores Urls with a file extension, so an UmbracoContext will never get created for a request containing a file extension.
You can create a context using UmbracoContext.EnsureContext, however if you did this in FindContent method of your handler, you'd encounter this exception. This is caused by a stale variable on line 18 of the UmbracoVirtualNodeRouteHandler holding a reference to a null UmbracoContext, and doesn't pick up the freshly created context.
The following is how worked around it so I could call EnsureContext before the VirtualNodeRouteHandler gets called.
var route = routes.MapRoute("RouteName", "some/url/file.ext", new
{
controller = "MyController",
action = "Index"
}
route.RouteHandler = new UrlWithExtensionHandler();
Notice its not the MapUmbracoRoute, but the standard MVC Map Route, and a standard MVC IRouteHandler which calls EnsureContext before returning an instance of a UmbracoVirtualNodeRouteHandler.
public class UrlWithExtensionHandler : IRouteHandler
{
#region Implementation of IRouteHandler
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// init umbraco context
var httpContext = new HttpContextWrapper(HttpContext.Current);
UmbracoContext.EnsureContext(
httpContext,
ApplicationContext.Current,
new WebSecurity(httpContext, ApplicationContext.Current),
UmbracoConfig.For.UmbracoSettings(),
UrlProviderResolver.Current.Providers,
false);
var handler = new UrlWithExtensionVirtualNodeRouteHandler();
return handler.GetHttpHandler(requestContext);
}
#endregion
}
public class UrlWithExtensionVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
protected override IPublishedContent FindContent(RequestContext requestContext,
UmbracoContext umbracoContext)
{
return someIPublishedContent;
}
}
Not an ideal solution, but a valid workaround until the stale variable issue gets merged into core - I've submitted a PR to fix it
A few others have had the same issue too http://issues.umbraco.org/issue/U4-9384

Using MiniProfiler with MVC 5

Edit
Got the answer here
So I wanted to check out MiniProfiler to troubleshoot some performance issues.
Before using it on production code I wanted to try it out with the a sample so went ahead with creating a MVC 5 application. This is plain vanilla app that gets created with the template.
Added this code in the Index() method of HomeController:
var profiler = MiniProfiler.Current;
using (profiler.Step("Set page title"))
{
ViewBag.Title = "Home Page";
}
using (profiler.Step("Doing complex stuff"))
{
using (profiler.Step("Step A"))
{ // something more interesting here
Thread.Sleep(100);
}
using (profiler.Step("Step B"))
{ // and here
Thread.Sleep(250);
}
}
return View();
Added this line below the jquery bundle in _Layout:
#Scripts.Render("~/bundles/jquery")
#StackExchange.Profiling.MiniProfiler.RenderIncludes()
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
Ran the app.
Nothing shows up. No profiling, nothing.
What am I missing?
Regards.
This is what I had to do to get MiniProfiler working in my ASP.NET MVC5 project:
Installed the MiniProfiler and MiniProfiler.MVC4 NuGet packages (the MVC4 package supports MVC5)
Add the following to Application_Start() in Global.asax:
protected void Application_Start()
{
...
// Setup profiler for Controllers via a Global ActionFilter
GlobalFilters.Filters.Add(new ProfilingActionFilter());
// initialize automatic view profiling
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}
}
Add the following to 'Application_BeginRequest()' and 'Application_EndRequest()', also in Global.asax:
protected void Application_BeginRequest()
{
if (Request.IsLocal)
{
MiniProfiler.Start();
}
}
protected void Application_EndRequest()
{
MiniProfiler.Stop();
}
Add the following to _Layout.cshtml (just before the </body> tag):
...
#StackExchange.Profiling.MiniProfiler.RenderIncludes()
</body>
</html>
Add the following to the <handlers> section of Web.config:
<system.webServer>
...
<handlers>
...
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*"
type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified"
preCondition="integratedMode" />
...
</handlers>
</system.webServer>
That was enough to profile each of the MVC Controller Actions and Views.
In my particular project I was using Entity Framework 6, so I also did the following:
a) Installed the MiniProfiler.EF6 package
b) Added the following to the end of Application_Start() in Global.asax:
...
MiniProfilerEF6.Initialize();
}
Also you have to add call:
MiniProfiler.Start();
In Global.asax.cs to Application_BeginRequest event.
And:
MiniProfiler.Stop();
In Global.asax.cs to Application_EndRequest event.

MVC 3 getting values from AppSettings in web.config

In normal ASP.NET Web forms sites I would use web.configs "appsettings" to add application setting data to a site. However, I am not able to retrieve setting values this way when using MVC 3.
First off, there are 2 web.config files. One in the root of the site, the second is listed in the Views area. I assume I want to put my appsettings information in the root web.config file, correct? (putting it in the other under views seems to produce an error stating "AppSettings" can only appear once per web application.)
When I try to retrieve it (C#: System.Configuration.ConfigurationManager.AppSettings["SettingName"]) I get a blank or empty/null return value. What am I doing wrong?
I should mention that I am actually retrieving this information in a Class file under the Models area for set specific values for a model using get; set;. Is it possible that I'm not allowed to do this in Models?
In a Controller.cs:
WindowsLiveConnect.ServiceConfiguration WLSC = new WindowsLiveConnect.ServiceConfiguration();
ViewBag.ClientID = SC.ClientID; // This returns empty
In web.config
...
<appSettings>
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="ClientID" value="0000000040062A3F" />
<add key="ClientSecret" value="SUPERSECRETPASSWORD" />
<add key="RedirectURL" value="http%3A%2F%2Fwww.quilnet.com" />
</appSettings>
...
In the Model.cs file:
public class ServiceConfiguration
{
private string clientid;
private string clientsecret;
private string redirecturl;
public string ClientID
{
get { return clientid; }
set
{
clientid = System.Configuration.ConfigurationManager.AppSettings["ClientID"];
}
}
public string ClientSecret
{
get { return clientsecret; }
set
{
clientsecret = System.Configuration.ConfigurationManager.AppSettings["ClientSecret"];
}
}
public string RedirectURL
{
get { return redirecturl; }
set
{
redirecturl = System.Configuration.ConfigurationManager.AppSettings["RedirectURL"];
}
}
}
Usually I'm using AppSettings static class to access those parameters. Something like this:
public static class AppSettings
{
public static string ClientSecret
{
get
{
return Setting<string>("ClientSecret");
}
}
private static T Setting<T>(string name)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
throw new Exception(String.Format("Could not find setting '{0}',", name));
}
return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
}
}
Are you ever calling set? I'm guessing it never gets called, so the private variable never gets the value from the config.
Try it this way (just retrieve the value in the get, no set needed):
public string ClientSecret
{
get { return System.Configuration.ConfigurationManager.AppSettings["ClientSecret"]; }
}
I did it this way:
myVar = System.Configuration.ConfigurationManager.AppSettings["ClientID"].ToString();
Looking at the code I assume you are using sharepoint provider hosted apps?
Best thing to do here in mvc is to ignore the web.config which is on the view level and only use the one in the root of the webapplication.
The other thing I want to mention is that its probably not a good idea to fetch configuration information from the web.config in the actual model.
Its better to move it either to :
- the constructor of the controller
- the factory/repository which returns this model

How data initialization works?

I have problem with initializing data into SQL Server Compact .sdf data file in .NET web application.
I have a data initialization class.
namespace R10491.Models
{
public class SampleData : DropCreateDatabaseAlways<LibraryEntities>
{
protected override void Seed(LibraryEntities context)
{
var categories = new List<Category>
{
new Category{Id=1, Name="Sci-fi"}
};
}
}
}
(for testing purposes I use DropCreateDatabaseAlways instead of DropCreateDatabaseIfModelChanges)
This initializer class I call in the Global.asax.cs file:
protected void Session_Start()
{
System.Data.Entity.Database.SetInitializer(new R10491.Models.SampleData());
}
(again for testing purposes I call it on every session start).
My connection string definition:
<connectionStrings>
<add name="LibraryEntities"
connectionString="Data Source=C:\Users\Administrator\Documents\Visual Studio 2012\Projects\2OBOP3_KU1\R10491\App_Data\R10491_library.sdf;"
providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>
But the initialization doesn't work - tables defined in SampleData class are not created nor data are initialized.
Looks like you're forgetting to add the just created Category to the DB table. If you don't add it to the context's table, Entity Framework won't see anything... So you must do something like this:
protected override void Seed(LibraryEntities context)
{
var categories = new List<Category>
{
new Category{Id=1, Name="Sci-fi"}
};
foreach(Category c in categories)
{
context.Categories.Add(c)
}
// Call the Save method in the Context
context.SaveChanges();
}
For the DataSource problem, try this modified connection string:
<add name="LibraryEntities"
connectionString="DataSource=|DataDirectory|R10491_library.sdf"
providerName="System.Data.SqlServerCe.4.0" />
In one of my projects I have this connection string:
<add name="FitnessCenterContext"
connectionString="DataSource=|DataDirectory|FitnessCenter.Model.FitnessCenterContext.sdf"
providerName="System.Data.SqlServerCe.4.0" />
Note above that the database name matches the namespace and Context name.
I also use Application_Start() to call the SetInitializer method in Global.asax.cs file. I see that you're calling it inside Session_Start(). Maybe this is the problem... Change your code to:
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(new R10491.Models.SampleData());
}
You can also try calling the Initialize method:
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(new R10491.Models.SampleData());
using (var context = new LibraryEntities())
{
context.Database.Initialize(true);
}
}

Custom Authentication Module inside MVC3 web app

Context:
We have an internal Asp.Net web application which is configured to use windows authentication. As part of this authentication aspect, we have an HttpModule that essentially grabs the HttpContext.Current.Identity.Name and returns a UserInfo object which get dropped into the HttpContext.Items collection.
In migrating this over MVC3, I have a base controller and OnActionExecuting, I am unable to see this UserInfo item in the collection at all. Any insight would be great. Here's my setup:
BaseController:
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
if (ctx.HttpContext.Items["UserInfo"] != null)
{
UserInfo currentUser = (UserInfo)ctx.HttpContext.Items["UserInfo"];
dynamic viewBag = ctx.Controller.ViewBag;
viewBag.CurrentUser = currentUser;
}
else
{
// Unauthorized do something
}
base.OnActionExecuting(ctx);
}
web.config:
<system.web>
<httpModules>
<add type="WFS.SIG.Client.Security.Authentication.WindowsAuthentication, WFS.SIG.Client.Security" name="AuthenticationModule"/>
</httpModules>
</system.web>....
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<add name="AuthenticationModule" type="WFS.SIG.Client.Security.Authentication.WindowsAuthentication, WFS.SIG.Client.Security" />
</modules>
</system.webServer>
I think your code should look like this:
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
if (ctx.HttpContext.Items["UserInfo"] != null)
{
UserInfo currentUser = (UserInfo)ctx.HttpContext.Items["UserInfo"];
ViewBag.CurrentUser = currentUser;
}
else
{
// Unauthorized do something
}
base.OnActionExecuting(ctx);
}
The access to HttpContext should work like this. But you can access the ViewBag directly.
Can you check whether your authenticaton module is really called and does store an object in the HttpContext? Can you set a breakpoint?

Resources