injecting changing view engine list - asp.net-mvc-4

I'm trying to develop a multi-tenancy project wherein each client can have their own specific view engines.
Specifically I'd like to compile the views to a DLL (using RazorEngine) and have an individual RazorViewEngine for each client but also provide a fallback to the standard RazorViewEngine if no matching views are found, just as the MVC framework does if you have multiple view engines specified.
I have found I can inject view engines using autofac in the Global.asax of my MVC 4 project using:
ViewEngines.Engines.Clear();
var builder = new ContainerBuilder();
builder.RegisterType<WebFormViewEngine>().As<IViewEngine>();
Now I also want to provide tenant specific overrides as mentioned above which I can do with the following code:
var mtc = new MultitenantContainer(tenantIdStrategy, builder.Build());
mtc.ConfigureTenant("Client1", b => b.RegisterType<RazorViewEngine>().As<IViewEngine>());
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
In this example code I just wanted to see if I could set the WebFormsViewEngine as a fallback and then enable the RazorViewEngine for a specific tenant.
Upon loading and browsing to a non-tenant url, mvc will resolve just the WebFormsViewEngine as expected (through calling the DependencyResolver and in turn Autofac), this works as expected however when I then visit a url that would also include the RazorViewEngine no views are found, even though a valid razor view exists.
Conversely if I stop IISExpress or do something to generate an app pool recycle and visit a tenantable url first both view engines are registered.
From what I can tell MVC caches the list of view engines retrieved after the first call to the MultiServiceResolver.
internal IViewEngine[] CombinedItems
{
get
{
IViewEngine[] combinedItems = _combinedItems;
if (combinedItems == null)
{
combinedItems = MultiServiceResolver.GetCombined<IViewEngine>(Items, _dependencyResolver);
_combinedItems = combinedItems;
}
return combinedItems;
}
}
Is there a way to override this caching or another better way to achieve what I'm trying to get here?

Related

Customize Identity Server 4 Login UI for Hosted Blazor WebAssembly

I have a solution based on the Visual Studio template that is successfully using IdentityServer4.
What think I understand is:
The IS4 integration implements endpoints at /authentication/{action}, e.g. authentication/login
IS4 routes that request to host/Identity/Account/Login.cshtml. I have scaffolded identity so I can see that .cshtml file in my project.
The code-behind Login.cshtml.cs takes care of speaking with SignInManager, etc.
In the client project the "LoginDisplay.razor" component redirects to /authentication/login when I click the login button. I presume that this is where step 1 above is invoked, which redirects to the location in step 2.
Fine. Now, I want to customise the whole login UI.
From instructions at: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-5.0 I can see that:
I can configure authentication paths to be anything I want. For example:
builder.Services.AddApiAuthorization(options => {
options.AuthenticationPaths.LogInPath = "security/login";
})
So, I have created a razor component to handle security/login:
#page "/security/{action}"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="#Action">
<LoggingIn>
This is the new login page
</LoggingIn>
</RemoteAuthenticatorView>
#code{
[Parameter] public string Action { get; set; }
}
My expectation was that after clicking "Login", I would be routed to the above and presented with a page that just said:
"This is the new login page"
and thought that from there, I would:
Customise the UI within the <LoggingIn> fragment.
Make a call to an API that would then replicate the logic in the scaffolded login.cshtml file that actually logs the user in.
That line of code from the login.cshtml.cs file looks like this:
var result = await _signInManager.PasswordSignInAsync(
Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
However, it seems that the razor component I created for /security/login is simply a 'transient' message that appears before being routed to the scaffolded login.csthml file.
So, to me, it seems that I am unable to actually change the physical page used to collect the user's credentials. I am only able to change the transient screen that appears before the originally scaffolded login page is shown.
Does this mean that if I want a customised UI for logging in I need to directly edit the scaffolded page as opposed to creating a whole new UI in the WebAssembly project that calls an APIController that I create to take care of using SignInManager?
It would actually be a lot less work to do that than to take the approach of creating a Client-side UI that calls an API that I create, etc. so, with hindsight, editing the scaffolded cshtml file is probably the best way to go. But am still confused as to what value is really being brought by being able to configure options.AuthenticationPaths.LogInPath.

TYPO3 Plugin - One action public access and one private

I'm developing an extension that have both, a list of records (action show) and a form to send a new record (action new).
The list must be for public access, but the form must require a login form (I'm using the login form content type that comes with TYPO3).
I have tried using the Access Tab for the plugin selecting Show at any Login but it applies to the entire plugin not for each action.
Currently, this is how the page looks like:
How could I get to display the login form only when someone tries to create a new record?
Note: The extension is based on Extbase and Fluid. The target version is TYPO3 6.2.
The easiest would be to split the actions in different "views" with switchableControllerActions in your flexform. Then you would need to place separate plugins on two different pages, that way you can have different access configuration for the plugins. If you don't know how to adjust the flexform, you can post the content of it here.
The other way would be to make a check inside the controller, but i would only use it if you have a lot of different roles you need to check.
if ($this->loginUser === null && $GLOBALS['TSFE']->loginUser && !empty($GLOBALS['TSFE']->fe_user->user['uid'])) {
// the user is logged in
} else {
// return '' as action content
return '';
}

Hook up Auth0AccountController in Umbraco 7

Has anyone successfully integrated Auth0 with Umbraco 7 for member (front-end public users, not CMS back-end users) authentication?
I've successfully integrated with owin startup and dealing with signin-auth0 response. However I'm stuck on hooking up the Auth0AccountController to work with Umbraco (I'm getting a 404). Any suggestions?
I've tried adding ~/Auth0Account to the "umbracoReservedPaths" appSetting, but I just get a slightly different looking 404 (not controlled by Umbraco by looks of it).
I've also tried mapping the route in Startup.cs using
RouteTable.Routes.MapRoute(
"Auth0Account",
"Auth0Account/{action}",
new
{
controller = "Auth0Account"
}
);
but that results in a "No route in the route table matches the supplied values" error.
Any ideas?
Mapping the Auth0Account route in Startup.cs was correct:
RouteTable.Routes.MapRoute(
"Auth0Account",
"Auth0Account/{action}",
new
{
controller = "Auth0Account"
}
);
Turns out my problem was with the default redirect RedirectToLocal method in the Auth0AccountController controller. It was doing a
return RedirectToAction("Index", "Home");
which I didn't have a controller hooked up for. I replaced this with an Umbraco compatible redirect instead.
Also, instead of Auth0AccountController inheriting from Controller it might be useful to inherit from Umbraco.Web.Mvc.SurfaceController or Umbraco.Web.Mvc.RenderMvcController to expose useful Umbraco methods to the code.

ImageResizer and Images delivered as byte stream

We have an umbraco MVC website with which we are trying to use ImageResizer. In order to keep uploaded files out of a publicly accessible area, images are stored outside of the webroot and we use a controller to deliver them to browswer (e.g. an image url on a page would be something like <img src="/umbraco/surface/AttachmentSurface/ShowImage/999?" alt="Here there be an image, yarrr!">)
The ShowImage action is something like:
public ActionResult ShowImage(int id)
{
using (DBContext db = new DBContext())
{
Attachment a = db.Attachments.FirstOrDefault(x.ID == id);
if (a != null)
{
byte[] file = System.IO.File.ReadAllBytes(System.IO.Path.Combine(System.Configuration.ConfigurationManager.AppSettings["UploadPath"], a.Path, a.FileName));
return this.File(file, a.MimeType);
}
}
}
If we use the URL API on a publicly accessible image (e.g. <img src="/images/kitty.jpg?width=33&height=33&mode=crop" alt="Here there be kitties, yarrr!">), then everything works as expected.
However, we've tried using the URL API to resize our images delivered by the controller to no avail (e.g. <img src="/umbraco/surface/AttachmentSurface/ShowImage/999?width=33&height=33&mode=crop" alt="Here there be an image, yarrr!">).
Is the only way around this to use the Managed API and resize the image before delivering? I seem to remember the documentation saying something to the effect that it's a bad idea to use it from within an MVC Action...
Thanks for any help.
ImageResizer doesn't operate as middleware, unfortunately, as middleware can't access the right resources in order to do a good job. It controls the request from beginning to end, and tries to integrate smoothly with existing authentication, URL rewrite, and CMS modules. That's why it doesn't work on an MVC action. It wouldn't be able to cache to disk, and it would have a severe performance overhead from double-buffering.
If you're just trying to block access to original files, you could do that in a variety of ways without moving the files outside of the root (UrlAuthorization, AuthorizeRequest event, disabling handlers for that folder, etc).
If you want to control where the images come from, you should implement an IVirtualImageProvider class. ImageResizer does include an IVirtualImageProvider plugin (named VirtualFolder) that can provide access to external/off root files. If your needs are basic, give that a try, and drop the MVC action.

MVC Area Routing not working for one specific area in my web application....Returning 500.19 with wrong phsyical path

I have an asp.net mvc web application with multiple areas.
There are about 15 areas within this application all of these route correctly except for one area.
This area blows up when I go directly to the index page with a 500.19 error.
The routing, controller, and views are essentially very similar for all areas involved.
The one thing that I have noticed is that the error provided shows the incorrect area path (physical path) for the specific view that is being requested. This can be seen in the image provided, but it is showing this as the physical path to the robin index view. :
C:\Projects\mvc\Test\BatmanProject\Index
Whereas it should be showing something like this. :
C:\Projects\mvc\Test\BatmanProject\Areas\Robin\Views\Home\Index
As you can see below it is attempting to find the index view directly in the batmanproject versus looking in the robin area. Whereas all the other areas (Riddler, Joker, etc etc) are finding the index directly in their corresponding area without throwing a 500.19 error.
I know it is not web.config related because all the other areas are working. I have tried numerous things such as. :
wiping out the host config and temp aspnet files
clearing out all the applications in my iis
running aspnet_regiis -r
numerous other fixes
I am just confused as to why it would occur for this specific area and not the others any one else seen an error like this?
***Route having issue
http://localhost/BatmanProject/Robin/Home/Index
***AreaRegistration
public override string AreaName
{
get
{
return "Robin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Robin_default",
"Robin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}