Control output of MiniProfiler for .NET in web app - c#

I followed the setup instructions here https://miniprofiler.com/dotnet/AspDotNetCore and got Mini Profiler working with my ASP.NET core web app. I pushed the code up to my staging site and now see the output on every request.
Previously local only access was documented here https://miniprofiler.com/
using StackExchange.Profiling;
...
protected void Application_BeginRequest()
{
if (Request.IsLocal)
{
MiniProfiler.Start();
}
}
How can I restrict miniprofiler to only show for local requests in ASP.NET core

From the documentation:
services.AddMiniProfiler(options =>
{
// (Optional) To control authorization, you can use the Func<HttpRequest, bool> options:
// (default is everyone can access profilers)
options.ResultsAuthorize = request => CanAccessMiniProfiler(request);
options.ResultsListAuthorize = request => CanAccessMiniProfiler(request);
}
You can implement it any way you like:
private bool CanAccessMiniProfiler(HttpRequest request)
{
// Add your logic here, e.g. check for local requests and certain roles
}

Related

Extend IPrincipal in .NET Core 2.1 with Windows Authentication

I'm trying to extend IPrincipal in .NET Core with Windows Authentication.
In a previous project (using .NET Framework 4.6.1) on Application_Start() I added the code below to extend IPrincipal:
protected void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs e)
{
if (e.Identity != null && e.Identity.IsAuthenticated)
{
NgUser opPrincipal = CustomStaticMethod.GetUserAD();
HttpContext.Current.User = opPrincipal;
}
}
And this is my custom class
public class NgUser : IPrincipal
{
// code removed for abbr ...
}
Then every time in a controller casting HttpContext.Current.User as CustomPrincipal I'd have access to the custom properties without having to use claims or using static extensions or storing an object in session.
Now in .NET Core I've seen you can customize claims transformation and I've also read this and they basically extended IPrincipal.
I'd prefer to extend IPrincipal with my custom class, register it in Startup.cs and be able to access it in my controllers.
Surely this is doable, question is how?
I hope this is clear and some one can help me.
Many thanks
It's very doable. The easiest way is to add a piece of middleware, which behaves similar to OnAuthenticate, to the Configure() pipeline in .NET Core. Here you'll swap out the IPrincipal. Note, this only works if the web app is set to run with Windows Authentication only in IIS/IIS Express. Anonymous authentication adds extra overhead.
If you have this simple Win auth setup, in your Startup.cs put this near the top of your Configure(IApplicationBuilder app) method:
// Because IIS automatically captures the user login, by the time the app is touched
// in any request, the context (ctx) User is already present.
app.Use(async (ctx, next) =>
{
if (ctx.User?.Identity?.IsAuthenticated == true)
{
NgUser opPrincipal = CustomStaticMethod.GetUserAD();
ctx.User = opPrincipal;
}
// be sure to continue the rest of the pipeline!
await next.Invoke();
});
```

Exposing hangfire without auth

Is there a way to expose Hangfire in IIS without having to configure authorization?
In this specific case the dashboard should be open, but when accessing it (not in debug) it returns a 401 code.
I think you should be able to write a custom implementation of IDashboardAuthorizationFilter as described in the documentation. Be aware that the default is only local requests to the dashboard are allowed. It's also recommended that you really use authorization and do not publish unauthorized dashboards as it contains sensitive information.
If you still want to do it, try:
Custom DashboardAuthorizationFilter
public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
return true;
}
}
Use it in the configuration of hangfire
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new [] { new MyAuthorizationFilter() }
});

Define NServiceBus instance-mapping in code

I'm writing an asp.net core application and wanna send a message to an NServiceBus endpoint.
As we have different environments I need to configure an endpoint address for each environment. I do that in the app settings.Env.json.
I like to do the same with the instance mappings. The only ways I know is to have a different instance-mapping.xml file for every environment or add it to the app.config that I don't have. Is there a way to set the instance machine in code? I don't wanna have different XML files.
I use NServiceBus 6.3.4
I added a feature to the endpoint configuration:
endpointConfiguration.EnableFeature<MyFeature>();
public class MyFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
var endpointInstances = context.Settings.Get<EndpointInstances>();
endpointInstances.AddOrReplaceInstances("InstanceMapping",
new List<EndpointInstance>
{
new EndpointInstance("MyEndpoint").AtMachine("VM-1")
});
}
}
Check docs here and here

Azure ad b2c authentication challenge loses session object

I am using the github example active-directory-b2c-dotnet-webapp-and-webapi.
I configured my own b2c tenant and got the MVC sample to work successfully.
However, I need this to work with a WebForms app. I created a new Webforms app, checked that the references were pointing to the same versions of the dlls as the working sample and adjusted the code to work in WebForms.
Everything works up until the time that I issue the Authentication.Challenge and process the AuthorizationCodeReceivedNotification in the OnAuthorizationCodeReceived function.
The Session object in notification.OwinContext.Environment["System.Web.HttpContextBase"] is null whereas at this point in the MVC sample the session object in notification.OwinContext.Environment["System.Web.HttpContextBase"].Session is a System.Web.HttpSessionStateWrapper with a valid SessionId.
In WebForms, HttpContext.GetOwinContext throws an error as not being available, so I then tried HttpContext.Current.GetOwinContext which returned an HttpContext object instead of a HttpContextBase object. So I finally used
if (!Request.IsAuthenticated)
{
HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
context.GetOwinContext().Authentication.Challenge();
return;
}
instead of the following which is used in the MVC sample
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge();
return;
}
At this point in the code, in both MVC and WebForms the Authentication object has a valid Session object in its OwinContext.Environment prior to the Challenge being issued. The problem is that whilst in the MVC version the resulting notification includes the session object, the WebForms version of the notification has a null object.
The problem finally surfaces when getting the TokenCache by calling the MSALSessionCache which uses the httpContext.Session object (MVC works; Webforms throws Null reference exception)
I am aware that MVC and Webforms treat the session differently but cannot work out how to solve this problem.
EDIT
Whilst this worked it did not address the problem regarding the session id. I added
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
MessageReceived = OnMessageReceived,
SecurityTokenReceived=OnSecurityTokenReceived,
SecurityTokenValidated= OnSecurityTokenValidated
},
to your code with the appropriate stubs for these functions. MessageReceived was invoked as was SecurityTokenReceived and Validated. However, AuthorizationCodeReceived was never invoked. When the first three were invoked, I examined the notification.OwinContext.Environment["System.Web.HttpContextBase"] and in all cases the Session object is null - which is what my problem was to start with.
EDIT (includes answer)
Thanks to #Ramakrishna the solution is to add A RequireAspNetSession helper function at the beginning of ConfigureAuth. The revised code snippet for his sample is as follows:
public void ConfigureAuth(IAppBuilder app)
{
RequireAspNetSession(app);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// Configure OpenID Connect middleware for each policy
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfilePolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}
public static void RequireAspNetSession(IAppBuilder app)
{
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
// To make sure the above `Use` is in the correct position:
app.UseStageMarker(PipelineStage.MapHandler);
}
you will need to add the following references:
using System.Web.SessionState;
using Microsoft.Owin.Extensions;
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
}
Try this in webforms.
EDIT
Sample Application hosted at https://github.com/Zen3InfoSolutions/B2CSamples/tree/master/B2C-WebForms
Zero configuration required to run and verify the policies.

User.IsInRole is always false when using IIS Express / Kestrel in ASP.Net 5 with Windows Authentication

I have an ASP.Net 5 application using version 1.0.0-rc1-update1 with Windows Authentication. I've implemented a custom policy in my Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
// ... other configuration code
var viewCharts = new AuthorizationPolicyBuilder()
.AddRequirements(new ViewChartsRequirement())
.Build();
collection.AddAuthorization(opts =>
{
opts.AddPolicy(Policy.ViewCharts, viewCharts);
});
collection.AddTransient<IAuthorizationHandler, ViewChartsHandler>();
// ... other configuration code
}
The ViewChartsHandler's Handle method is as follows:
protected override void Handle(
AuthorizationContext context,
T requirement)
{
var identities = _securityRepo.GetIdentitiesForPolicy(_policy);
// this returns a result when using a web listener, but
// never finds a match when using IIS Express
var matchingIdentity = identities.FirstOrDefault(role => context.User.IsInRole(role));
if (!string.IsNullOrWhiteSpace(matchingIdentity))
{
context.Succeed(requirement);
}
}
When using a web listener as shown in this answer, the code above works. However, when using IIS Express it never finds a matchingIdentity.
Things to note:
My IIS Express is configured to use Windows Authentication, and deny Anonymous Authentication. The bug related to IIS Express and this was fixed in RC1.
The username from Windows is always resolving correctly.
In the Handle code above, context.User is an instance of System.Security.Principal.WindowsPrincipal when using a web listener, but when using IIS Express it is a System.Security.Claims.ClaimsPrincipal.
I have forwardWindowsAuthToken="true" set in my web.config.
I think this is a role provider problem, but I am at a loss as to how to correct it.
I'm using the following code to successfully check for role membership on RC1, however note that it does not resolve group names (probably due to the problems referred to by #blowdart), I have to supply the group names as SIDs:
// Set up authorisation policies from the configured AD group lists.
// NOTE: Currently this only works with SIDs if hosting using Kestrel
// behind IIS, not friendly group names.
var viewerGroups = Configuration.GetSection("Groups:Viewer").Value.Split(',');
var adminGroups = Configuration.GetSection("Groups:Admin").Value.Split(',');
services.AddAuthorization(auth =>
{
auth.AddPolicy("viewer", policy =>
{
policy.RequireRole(viewerGroups);
});
auth.AddPolicy("admin", policy =>
{
policy.RequireRole(adminGroups);
});
});
I don't know if this will help you, but I thought I'd offer it up in case!
Did you add the IISPlatformHandler middleware?
app.UseIISPlatformHandler();
This needs to be ran before app.UseMvc(), app.UseStaticFiles() or any other authentication middleware.
It turns out that User.IsInRole does not function correctly in RC1. (See #blowdart's comments.) This will be fixed in RC2.
The original code will work so long as the line below returns the SIDs for each AD group, as opposed to the friendly names.
// This line must return SIDs instead of friendly names
var identities = _securityRepo.GetIdentitiesForPolicy(_policy);
Update
This was fixed in RC2. The original code works!

Categories