Retrieve the Session in the GlimpseSecurityPolicy RuntimeEvent.ExecuteResource - glimpse

Using glimpse I'm able to access the session information accept when using the RuntimeEvent.ExecuteResource. Without this the axd file is exposed and I'd rather have it disabled unless specific users are logged in. The session will be null in both examples below. Also I've tried having the class implement IRequiresSessionState but that didn't help either.
namespace Glimpse
{
public class GlimpseSecurityPolicy:IRuntimePolicy
{
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
try
{
var name = HttpContext.Current.Session["username"];
var name2 = policyContext.GetHttpContext().Session["username"];
}
catch (Exception)
{
}
// You can perform a check like the one below to control Glimpse's permissions within your application.
// More information about RuntimePolicies can be found at http://getglimpse.com/Help/Custom-Runtime-Policy
// var httpContext = policyContext.GetHttpContext();
// if (!httpContext.User.IsInRole("Administrator"))
// {
// return RuntimePolicy.Off;
// }
return RuntimePolicy.On;
}
public RuntimeEvent ExecuteOn
{
// The RuntimeEvent.ExecuteResource is only needed in case you create a security policy
// Have a look at http://blog.getglimpse.com/2013/12/09/protect-glimpse-axd-with-your-custom-runtime-policy/ for more details
get { return RuntimeEvent.EndRequest | RuntimeEvent.ExecuteResource; }
}
}
}

The reason for this is that the Glimpse HttpHandler which processes the requests for Glimpse.axd does not implement the IRequireSessionState interface.
It is that HttpHandler that will eventually execute all IRuntimePolicy instances that have RuntimeEvent.ExecuteResource configured as part of the ExecuteOn property value.
I think the easiest solution for you is to create your own IHttpHandler that implements the IRequireSessionState interface and forwards all calls to the Glimpse HttpHandler as shown below.
public class SessionAwareGlimpseHttpHandler : IHttpHandler, IRequiresSessionState
{
private readonly HttpHandler _glimpseHttpHandler =
new Glimpse.AspNet.HttpHandler();
public void ProcessRequest(HttpContext context)
{
_glimpseHttpHandler.ProcessRequest(context);
}
public bool IsReusable
{
get { return _glimpseHttpHandler.IsReusable; }
}
}
Don't forget to update your web.config to use that handler instead of the original one:
...
<system.webServer>
...
<handlers>
<add name="Glimpse" path="glimpse.axd" verb="GET" type="YourNamespace.SessionAwareGlimpseHttpHandler, YourAssembly" preCondition="integratedMode" />
</handlers>
...
</system.webServer>
...
Once all this is in place, you should be able to access the Session inside your IRuntimePolicy.

Related

How to make FluentFTP to log to log4net log?

I couldn't find any information on how to do it. Basically FluentFTP is using System.Diagnostics to log their messages.
FluentFtp expose the following static method:
FtpTrace.AddListener(TraceListener listener);
However I don't know if there is any way to implement (or use existing implementation, which?) TraceListener in the way it relays everything to log4net engine.
Any hints or ideas?
Thanks, Radek
You can attach a listener to the OnLogEvent method that FluentFTP exposes.
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static void UploadFTP(FileInfo localFile, string remoteFileLocation, string remoteServer, NetworkCredential credentials)
{
FtpClient client = new FtpClient(remoteServer, credentials);
client.RetryAttempts = 3;
client.OnLogEvent = OnFTPLogEvent;
client.Connect();
if (!client.UploadFile(localFile.FullName, remoteFileLocation, FtpExists.Overwrite, false, FtpVerify.Retry | FtpVerify.Throw))
{
throw new Exception($"Could not Upload File {localFile.Name}. See Logs for more information");
}
}
private static void OnFTPLogEvent(FtpTraceLevel ftpTraceLevel, string logMessage)
{
switch (ftpTraceLevel)
{
case FtpTraceLevel.Error:
Log.Error(logMessage);
break;
case FtpTraceLevel.Verbose:
Log.Debug(logMessage);
break;
case FtpTraceLevel.Warn:
Log.Warn(logMessage);
break;
case FtpTraceLevel.Info:
default:
Log.Info(logMessage);
break;
}
}
The method OnFTPLogEvent will be called every-time the OnLogEvent action will be called allowing you to extend any logging you have already built into your application.
Basically FluentFTP is using System.Diagnostics.TraceListener so in order to make it logging to your log4net log you need to write your own simple class that would redirect logs to log4net logger. Like the following:
using System.Diagnostics;
using log4net;
namespace YourApp.Logging
{
public class Log4NetTraceListener : TraceListener
{
private readonly ILog _log;
public Log4NetTraceListener(string provider)
{
_log = LogManager.GetLogger(provider);
}
public override void Write(string message)
{
if(_log == null)
return;
if(!string.IsNullOrWhiteSpace(message))
_log.Info(message);
}
public override void WriteLine(string message)
{
if(_log == null)
return;
if (!string.IsNullOrWhiteSpace(message))
_log.Info(message);
}
}
}
Then, in your app.config file add the following entry:
<system.diagnostics>
<trace autoflush="true"></trace>
<sources>
<source name="FluentFTP">
<listeners>
<clear />
<add name="FluentLog" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="FluentLog" type="YourApp.Logging.Log4NetTraceListener, YourApp" initializeData="FluentLog" />
</sharedListeners>
</system.diagnostics>
That should enable FluentFtp logs and merge it with your application log4net log.

Sudden 'No valid key mapping found for securityToken' error

TL;DR
Our website suddenly had the below error with no code or web.config changes. Would Azure have changed?
I have a website which has been running on Azure with no issues for a few months. Then the other day, we now have this error:
WIF10201: No valid key mapping found for securityToken: 'System.IdentityModel.Tokens.X509SecurityToken' and issuer: 'https://sts.windows.net/<guid>/'.
We have made no changes to the web.config or the values in Tenants and IssuingAuthorityKeys.
Searching SO and the web give lots of code based answers, but we haven't changed any code.
The web.config is like this:
<system.identityModel>
<identityConfiguration>
<issuerNameRegistry type="DatabaseIssuerNameRegistry, Site.Web" />
<audienceUris>
<add value="https://localhost:44301" />
<add value="https://<other urls...>" />
</audienceUris>
<securityTokenHandlers>
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
<certificateValidation certificateValidationMode="None" />
</identityConfiguration>
</system.identityModel>
The issuerNameRegistry class is as follows:
public class DatabaseIssuerNameRegistry : ValidatingIssuerNameRegistry
{
public static bool ContainsTenant(string tenantId)
{
using (DBEntities context = new DBEntities())
{
return context.Tenants
.Where(tenant => tenant.Id == tenantId)
.Any();
}
}
public static bool ContainsKey(string thumbprint)
{
using (DBEntities context = new DBEntities())
{
return context.IssuingAuthorityKeys
.Where(key => key.Id == thumbprint)
.Any();
}
}
public static void RefreshKeys(string metadataLocation)
{
IssuingAuthority issuingAuthority = ValidatingIssuerNameRegistry.GetIssuingAuthority(metadataLocation);
bool newKeys = false;
bool refreshTenant = false;
foreach (string thumbprint in issuingAuthority.Thumbprints)
{
if (!ContainsKey(thumbprint))
{
newKeys = true;
refreshTenant = true;
break;
}
}
foreach (string issuer in issuingAuthority.Issuers)
{
if (!ContainsTenant(GetIssuerId(issuer)))
{
refreshTenant = true;
break;
}
}
if (newKeys || refreshTenant)
{
using (DBEntities context = new DBEntities())
{
if (newKeys)
{
context.IssuingAuthorityKeys.RemoveRange(context.IssuingAuthorityKeys);
foreach (string thumbprint in issuingAuthority.Thumbprints)
{
context.IssuingAuthorityKeys.Add(new IssuingAuthorityKey { Id = thumbprint });
}
}
if (refreshTenant)
{
foreach (string issuer in issuingAuthority.Issuers)
{
string issuerId = GetIssuerId(issuer);
if (!ContainsTenant(issuerId))
{
context.Tenants.Add(new Tenant { Id = issuerId });
}
}
}
context.SaveChanges();
}
}
}
private static string GetIssuerId(string issuer)
{
return issuer.TrimEnd('/').Split('/').Last();
}
protected override bool IsThumbprintValid(string thumbprint, string issuer)
{
return ContainsTenant(GetIssuerId(issuer))
&& ContainsKey(thumbprint);
}
}
Judging from this Technet article, it seems that Microsoft may have updated the way they handle Tenant keys.
I had to add the following code into my Global.asax.cs file:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// ....
string configPath = AppDomain.CurrentDomain.BaseDirectory + "\\" + "Web.config";
string metadataAddress =
ConfigurationManager.AppSettings["ida:FederationMetadataLocation"];
ValidatingIssuerNameRegistry.WriteToConfig(metadataAddress, configPath);
}
Check out my answer on a similar post. It only applies to solutions created in VS2013 or later.
https://stackoverflow.com/a/38131092/5919316
Here is a copy of it:
For solutions created in VS2013 and later, the solution should contain the logic to roll over keys automatically. No need to put the value in the web.config file.
You might run into this issue when migrating your solution from local to another environment. In that case you will probably try to point your solution to a new application in Azure Active Directory. Check the following:
Make sure all urls in the web.config are pointing to the correct url not the one automatically generated when you set it up locally
Remove all enteries from IssuingAuthorityKeys table. The keys will autopopulate when you re-build the solution and run it. On the server you might need to replace the dlls manually for it to refresh
Last and most important, delete all rows from the Tenants table. On the first run on the new environment, an Admin from the owning Active Directory has to sign up and authorize the application.
If the values in both tables are still not populated automatically after these steps, check this article for steps on how to manually get the values.

I get a 500 page instead of a ResponseStatus from exceptions within ServiceStack Service

Using ServiceStack 4.0.33 and given the following simplified DTOs...
[Route("/products")]
[Route("/products/{Id}")]
public class Product : IReturn<ProductResponse>
{
[PrimaryKey]
public string Id { get; set; }
public string Description { get; set; }
}
public class ProductResponse
{
public Product Product { get; set; }
}
with the following simplified service...
public class ProductService : Service
{
public object Post(Product product)
{
Db.Insert<Product>(product);
return new ProductResponse() { Product = product };
}
}
and calling it via this in my ProductsController
using (var productService = ResolveService<ProductService>())
{
var result = productService.Post(product);
if (result.IsErrorResponse())
return View(product);
else
return RedirectToAction("Index");
}
If I try to post a new Product with an intentional duplicate primary key, I get a 500 error HTML-style page instead of the ResponseStatus getting populated and returned...
I've seen lots of different StackOverflow posts about different reasons that ResponseStatus won't get populated, but I've tried several things to no avail. Am I missing something (hopefully simple)?
Where do you define the behavior that a duplicate key would be handled differently than HTTP 500. What other response would you expect? ResponseStatus can contain HTTP 500 which merely indicates a server-side error.
You have to specifically define that you want to handle the error differently and if so what type of error you want (which error code, which message) as explained here.
For instance:
public object Get(User request)
{
throw HttpError.NotFound("User {0} does not exist".Fmt(request.Name));
}
Check out the ServiceStack tutorial for more.
The error page you're getting is a custom ASP.NET Error page which hijacks any custom 500 HTTP error responses returned by your ASP.NET web application.
It can be disabled with:
<system.web>
...
<customErrors mode="Off" />
</system.web>
<system.webServer>
<httpErrors errorMode="Detailed" />
</system.webServer>
Here's another alternative of disabling IIS errors by setting TrySkipIisCustomErrors = true which in ServiceStack can be set with:
GlobalRequestFilters.Add((req, res, dto) =>
{
((HttpResponse)res.OriginalResponse).TrySkipIisCustomErrors = true;
});

Web Api on Azure shows no error detail using 'return InternalServerError(ex)'

My Web Api when run locally (in Release mode) will return any errors in this format:
{
"Message": "An error has occurred.",
"ExceptionMessage": "No text specified",
"ExceptionType": "System.Exception",
"StackTrace": null
}
But after deployment/publish to an Azure VM, only this remains:
{
"Message": "An error has occurred."
}
API code:
try
{
var msg = ...
new MessageService().SaveMessage(msg)); // <-- does some checks; may throw.
return Ok();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
I'd like it to be more detailed on Azure, like the local result.
Can this be achieved, and if so, how?
I already (temporarily) removed <compilation xdt:Transform="RemoveAttributes(debug)" /> from the <system.web> part of Web.Release.config, and then re-deployed, but that made no difference.
Or am I using the wrong approach/pattern?
Obviously technical details should be limited, but right now we get no details at all.
You could try adding the following to your Global.asax:
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Note: I wouldn't recommend that you keep this setting on in a production environment.
If instead you use
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Default;
then you can use the system.webServer error switch e.g.
<system.webServer>
<httpErrors errorMode="Detailed" existingResponse="PassThrough">
</httpErrors>
</system.webServer>
Note the existingResponse attribute to preserve the error message.
I had the same problem, the post is three years old, things have changed a little. If you setup a new Azure Mobile App with Visual Studio 2017 there is no longer a Global.asax.cs. I searched for hours, where to put this IncludeErrorDetailPolicy. It won't work without that setting.
You do it in your Startup.MobileApp.cs:
public partial class Startup
{
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
Don't forget, in your Web.config you also need to set:
<system.webServer>
<httpErrors errorMode="Detailed" existingResponse="PassThrough">
</httpErrors>
</system.webServer>
Don't use that for production environment!
For Web API 2, you can implement a custom IExceptionLogger that utilizes Azure Application Insights. Something like this:
using Microsoft.ApplicationInsights;
using System.Web.Http.ExceptionHandling;
namespace Your.Namespace.Here
{
public class TelemetryExceptionLogger : ExceptionLogger
{
private readonly TelemetryClient telemetryClient;
public TelemetryExceptionLogger(TelemetryClient telemetryClient)
{
this.telemetryClient = telemetryClient;
}
public override void Log(ExceptionLoggerContext context)
{
if (context != null && context.Exception != null)
{
telemetryClient.TrackException(context.Exception);
}
base.Log(context);
}
}
}
Then you need to register it with Web API:
using Microsoft.ApplicationInsights;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
using Your.Namespace.Here;
namespace Some.Other.Namespace.Or.The.Same
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// --- Normal Web API configuration here ---
config.Services.Add(typeof(IExceptionLogger), new TelemetryExceptionLogger(new TelemetryClient()));
}
}
}
For this to work, you will need to have set up Application Insight in Azure and for your VS project, but that is a story for another time :)
For more information, see Application Insights: Exception Telemetry
I have a scenario with the same error, and the problem was a copy&paste in the route header attribute of a method. I have the same route for two methods
[Route("test/Method1")]
public IHttpActionResult Method1(){...}
[Route("test/Method1")]
public IHttpActionResult Method2(){...}
Check the new methods and Routes added.

beginning NServiceBus and dependancy injection and instances

Im having some problems with NServiceBus, I can get the pubsub example working fine, but now I'm trying to integrate it into a production project and I cant get the thing to work!
My publisher code is exactly the same as the publisher example (I've just imported the project to rule out any other issues) but I then create a void function and call it from my WPF app and I get a "you cant call bus without creating an instance of bus" error
public void RunTest()
{
var eventMessage = new MarketPriceMessage();
eventMessage.Ticker = "IBM";
eventMessage.DataType = "Bid";
eventMessage.Value = (decimal)23.23423;
eventMessage.EventId = Guid.NewGuid();
eventMessage.Time = DateTime.Now; // > 30 ? (DateTime?)DateTime.Now : null;
eventMessage.Duration = TimeSpan.FromSeconds(99999D);
Bus.Publish(eventMessage);
}
Any ideas as to whats going on there and where I'm going wrong?
Following #Adam's comments below this is the code I'm using internally in my WPF App:
public partial class App : Application
{
public IBus bus { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
NServiceBus.Configure.With()
.Log4Net()
.SpringBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
}
}
}
and
namespace WpfApplication2
{
class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher { }
}
and
namespace WpfApplication2
{
public class SubscriptionAuthorizer : IAuthorizeSubscriptions
{
public bool AuthorizeSubscribe(string messageType, string clientEndpoint, string clientWindowsIdentity, IDictionary<string, string> headers)
{
return true;
}
public bool AuthorizeUnsubscribe(string messageType, string clientEndpoint, string clientWindowsIdentity, IDictionary<string, string> headers)
{
return true;
}
}
}
App Config
<configuration>
<configSections>
<section name="MsmqTransportConfig" type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core"/>
<section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core"/>
</configSections>
<MsmqTransportConfig
InputQueue="WpfApplication2InputQueue"
ErrorQueue="error"
NumberOfWorkerThreads="1"
MaxRetries="5"/>
<UnicastBusConfig>
<!--DistributorControlAddress="" DistributorDataAddress="" ForwardReceivedMessagesTo="">-->
<MessageEndpointMappings>
</MessageEndpointMappings>
</UnicastBusConfig>
When I'm stepping through my code I can see that bus is a null object.
I am including the references as normal
I'm not too familiar with WPF, but it looks like there is an Application.Startup event that may work. You need to "manually" configure the bus as shown here in the docs
If you're not using Autofac or some other container, the problem is you skipped the assignment to your bus variable. I normally put this in Global.asax Application_Startup, but this way should work too.
If you are using a container, and you register the class that implements your ServiceContract, you can get away with having a local IBus constructor/property injected when it's instantiated.
public IBus bus { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
bus = NServiceBus.Configure.With() // keep a reference to the returned bus.
.Log4Net()
.SpringBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
}

Resources