What does SetApplicationName actually do in reference to AddDataProtection? - c#

Consider this example code:
services.AddDataProtection()
.SetApplicationName("TestingApp");
The docs seem to indicate that setting the application name like this will allow two servers to know that they should use the same key for cookies (assuming that it is also persisted using something like PersistKeysToDbContext).
However, when I look at the persisted key, I don't see the shared application name anywhere in the document:
<key id="c34df9c6-bf38-49e8-97cf-dda5f8bc141c" version="1">
<creationDate>2021-03-23T21:47:20.4615267Z</creationDate>
<activationDate>2021-03-23T21:47:19.4492298Z</activationDate>
<expirationDate>2021-06-21T21:47:19.4492298Z</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=3.1.13.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey xmlns:p4="http://schemas.asp.net/2015/03/dataProtection" p4:requiresEncryption="true">
<!-- Warning: the key below is in an unencrypted form. -->
<value>eNZoh1a2DEiEi03ae1aklP9dM3z____FAKE_____Pgy8bcVpkOI+q/I9d9iELvy+ptOW54Q==</value>
</masterKey>
</descriptor>
</descriptor>
</key>
The "Friendly Name" of the key seems to be a Guid.
What does SetApplication actually cause to change in the persisted xml?

SetApplicationName does not cause any change in persisted xml. In fact, you can change the Application name at any time after the XML is generated.
Application name is used as a master Purpose string for IDataProtectionProvider.
Consider the following code example:
// Startup.cs
services.AddDataProtection().SetApplicationName( "My application name" );
// ExampleConsumer.cs
public ExampleConsumer( IDataProtectionProvider protectionProvider )
{
var dataProtector = protectionProvider.CreateProtector( "My protector" );
}
If you run this code in a debugger and check the purposes, it will look like this:
protectionProvider.Purposes: ["My application name"]
dataProtector.Purposes: ["My application name", "My protector"]
For more information on purposes, see Purpose hierarchy.

Related

Programmatically setting Application Insights instrumentation key throws error

To support Application Insights in multiple environments, we are setting the Instrumentation Key programmatically, as adviced in this post.
We have left <InstrumentationKey> in ApplicationInsights.config empty, and instead added an application setting in web.config:
<add key="ApplicationInsightsInstrumentationKey" value="1234-5678-9xxx" />
In Application_Start we do the following to set the instrumentation key:
var instrumentationKey = ConfigurationManager.AppSettings["ApplicationInsightsInstrumentationKey"];
if (string.IsNullOrWhiteSpace(instrumentationKey))
{
throw new ConfigurationErrorsException("Missing app setting 'ApplicationInsightsInstrumentationKey' used for Application Insights");
}
TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey;
new TelemetryClient().TrackEvent("Application started");
However, this produces an exception saying "TelemetryChannel must be initialized before it can send telemetry".
Googling this exception message yields nothing, but I guess there's something additional required before events can be tracked?
Removing the <InstrumentationKey /> element from ApplicationInsights.config seems to do the trick.
I've done the exact same thing, setting the iKey in Application_Start() for a web application, and before I new() up a TelemetryClient in console applications. I do not have any element in my ApplicationInsights.config, not even a blank one. I keep a comment in that config file that says the key is being set programmatically.

Change a Line of Code based on Configuration selected

In my ASP.NET MVC 3 application, I have 2 configurations setup; Play and Live.
Right now, I have to change the following code before loading my application with a configuration based on what I currently have selected:
Mailer.SendMessageTo("playEmailAddress", "MailBody");
// Mailer.SendMessageTo("liveEmailAddress", "MailBody");
So if I have Play configuration selected I'll comment out the liveEmailAddress line and vice versa
What I'd like to do is perhaps make use of the web.config file to change this code for me without manually doing it every time I load up my application with a different configuration by putting the lines of code in the config file and then reading it from the config file from within my class
You should add the "app key" in your web configuration file. Give it anyname like "OptionalEmail" and set the value accordingly.
When you send the email check the value in the configuration file like
If(ConfigurationManager.AppSettings["OptionalEmail"]=="PlayEmail")
SendEmail using PlayEmail address else SendEmail using Work emai
Address.
Hope this help. "ConfigurationManager.AppSettings[use key or index]
Please keep in mind, Config Transformations "xdt" works only when you deploy your web application.
it's generally a good idea to have a configuration parameter named "environment" (or the like).
This link explains how to read from the web.config: http://msdn.microsoft.com/en-us/library/610xe886.aspx.
one way to implement this would be:
var env = "play";
if( ConfigurationManager.AppSettings["environment"]=="live" ) env="live";
var email = env + "EmailAddress";
Mailer.SendMessageTo(email, "MailBody");
as an additional note, if you have multiple developers that each want their own "play"-Address, then you can extend the setting to include the developers machine name:
<appSettings>
<add key="environment" value="play"/>
<add key="liveEmailAddress" value="a#b.com"/>
<add key="myCoolPC-playEmailAddress" value="c#d.com"/>
<add key="otherDevPC-playEmailAddress" value="bubba#test.com"/>
</appSettings>
but then you would have to change the implementation above to prefix the hostname before getting the setting, but only if currently in play-mode.

How to write and configure a custom provider for ASP.Net Healthmonitoring?

I'm looking for a walkthrough on how to create and use a custom provider for ASP.Net Healthmonitoring.
So far I've only worked with the e-mail provider that generates e-mails on errors. Basically I want to do the same, but with more flexibility:
I want to use the HealthMonitoring features (I don't want to use the Application_OnError event in the global.asax) in a way that allows me have access to an event, that gets thrown like "OnNewHealthMonitoringEntry" with all the information provided in the e-mail, to run custom code.
Edit:
Based on the source code provided here http://www.asp.net/general/videos/how-do-i-create-a-custom-provider-for-logging-health-monitoring-events I was able to build my own custom provider and implement it. Now I want to add some new attributes to configure my custom provider.
Here is what the web.config looks like:
<healthMonitoring>
<bufferModes>
<add name="Log Notification" maxBufferSize="1" maxFlushSize="1" urgentFlushThreshold="1" regularFlushInterval="Infinite" urgentFlushInterval="00:00:10"/>
</bufferModes>
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
<profiles>
<add name="Custom" minInstances="1" maxLimit="Infinite" minInterval="00:00:00"/>
</profiles>
<rules>
<add name="File Event Provider" eventName="All Errors" provider="FileEventProvider" profile="Custom"/>
</rules>
</healthMonitoring>
If I attempt to add an attribute to the provider, like this
<providers>
<add name="FileEventProvider" buffer="true" bufferMode="Log Notification" foo="bar" type="healthmonitoringtest.FileHealthMonitorEventProvider"/>
</providers>
I'll get an error saying:
An exception of type
'System.Configuration.ConfigurationErrorsException'
occurred in System.Web.dll but was not
handled in user code Additional
information: Unexpected attribute foo
in the configuration of the
FileEventProvider.
Is it possible to store configuration necessary for custom provider close to the healthMonitoring section? I guess I could include the settings into the appSettings node, but I'd like to configure it somehow with attributes (inside the healthMonitoring node). Is that possible?
Edit2:
You might take a look at this article: http://www.tomot.de/en-us/article/6/asp.net/how-to-create-a-custom-healthmonitoring-provider-that-sends-e-mails
The following series of articles will take you through the basics of using the Health Monitoring System upto creating Custom Events.
Then the following 26 minute video will take you through creating a custom provider that records events to a text-based log file.
UPDATE Based on Comment
Looking at your update and using Reflector to look at the source for the BufferedWebEventProvider class that you base your custom provider on, I have found that the Initialize method in BufferedWebEventProvider does a check at the end to see if there are any attributes that it doesn't recognize. This is done by removing values from the config NameValueCollection parameter as soon as they are assigned to the properties or fields of the BufferedWebEventProvider. Then a check is done to see if the config parameter is empty and if not that means that there are extra attributes added, which causes an exception to be thrown.
As to how to fix this problem, one option is to:
Move the call to base.Initialize to the end of the method
Remove the additional attributes as soon as you assign them to variables just like the provider does.
Something like the following would work:
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
foo = config["foo"];
if (String.IsNullOrEmpty(foo))
{
// You can set a default value for foo
}
//remove foo from the config just like BufferedWebEventProvider with the other
//attributes. Note that it doesn't matter if someone didn't proivde a foo attribute
//because the NameValueCollection remains unchanged if you call its Remove method
//and the name doesn't exist.
config.Remove("foo");
base.Initialize(name, config);
}
Hopefully this works out for you.

Get values from the web.config section in an app.config file?

I'm trying to unit test values that will eventually wind up in a web.config file. In my test project, I created an app.config file with a web.config section to hold the settings. In a normal situation, I would call System.Configuration.ConfigurationSettings.AppSettings, but in this case, that doesn't work. I saw this question, which is very similar, but doesn't address how to get the NameValueCollection out of the config file. Here is an example of the config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear/>
<add
name="CustomMembershipProvider"
applicationName="SettlementInfo"
enablePasswordRetrieval="false"
enablePasswordReset="false"
requiresQuestionAndAnswer="true"
writeExceptionsToEventLog="true" />
</providers>
</membership>
</system.web>
</configuration>
Has anyone dealt with this before?
I guess I'm confused here; it looks like you're trying to test that ASP.NET is using your custom membership provider appropriately. Correct?
If so, I'm 99.999% sure that you cannot unit test this using the MS framework; you must integration test it by deploying it to the webserver (or running Cassini in VS) and typing a username/password into your login page.
Now, it's possible I've misunderstood your request. If so, let me know and I'll edit my answer accordingly.
Edit:
For right now, I'm really just trying
to test the NameValue pairs coming out
of the config file, to make sure that
if the values aren't present, my
defaults are being applied. In other
words, I want to try to pull
applicationName, and verify that it
equals "SettlementInfo", and so on.
After that, I will be using
integration testing to ensure that
ASP.NET is using the custom framework
in place of the default one. Does that
make sense?
I need more than a comment to reply, so I'm editing. If I read you correctly, you are wanting to unit test your program to ensure that it deals with configuration correctly, yes? Meaning you want to ensure that your code grabs, for example, the correct AppSettings key and handles a null value therein, correct?
If that's the case, you're in luck; you don't need an app.config or web.config at all, you can set the values you need as part of your test setup.
For example:
[TestMethod]
public void Test_Configuration_Used_Correctly()
{
ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigValue");
}
[TestMethod]
public void Test_Configuration_Defaults_Used_Correctly()
{
// you don't need to set AppSettings for a non-existent value...
// ConfigurationManager.AppSettings["MyConfigName"] = "MyConfigValue";
MyClass testObject = new MyClass();
testObject.ConfigurationHandler();
Assert.AreEqual(testObject.ConfigurationItemOrDefault, "MyConfigDefaultValue");
}
I believe you only have access to the webconfig file while your application is actually beeing started up. The solution is rather easy -> "Fake" your config. Use a NameValueCollection and use that instead:
private static NameValueCollection CreateConfig()
{
NameValueCollection config = new NameValueCollection();
config.Add("applicationName", "TestApp");
config.Add("enablePasswordReset", "false");
config.Add("enablePasswordRetrieval", "true");
config.Add("maxInvalidPasswordAttempts", "5");
config.Add("minRequiredNonalphanumericCharacters", "2");
config.Add("minRequiredPasswordLength", "6");
config.Add("requiresQuestionAndAnswer", "true");
config.Add("requiresUniqueEmail", "true");
config.Add("passwordAttemptWindow", "10");
return config;
}
Now you could easily pass that collection into your class that parses data from it.
You should be able to use the ConfigurationManager.GetSection() method to pull out whatever you want.
Actually, if you are using NUnit, you can stick that in an App.config in your test project.
Then add this line to your Post-build event:
copy /Y “$(ProjectDir)App.config” “$(TargetDir)$(TargetFileName).config”
When you create the new provider in your tests, NUnit will pass the values in your app.config to the provider in the initialize method.
Why not just stick it in the web.config file?

Is there any way to programmatically set the application name in Elmah?

I need to change the app name based on what configuration I'm using in Visual Studio. For example, if I'm in Debug configuration, I want the app name to show as 'App_Debug' in the Application field in the Elmah_Error table. Does anyone have any experience with this? Or is there another way to do it?
This can now be done purely in markup. Just add an applicationName attribute to the errorLog element in the <elmah> section of the web.config file. Example:
<errorLog type="Elmah.SqlErrorLog, Elmah"
connectionStringName="connectionString" applicationName="myApp" />
I've tested this and it works both when logging an exception and when viewing the log via Elmah.axd.
In the case of the OP, one would imagine it can be set programatically too but I didn't test that. For me and I imagine for most scenarios the markup approach is sufficient.
By default, Elmah uses the AppPool's application GUID as the default application name. It uses this as the key to identify the errors in the Elmah_Error table when you look at the web interface that's created through it's HTTP Module.
I was tasked to explore this option for my company earlier this year. I couldn't find a way to manipulate this by default since Elmah pulls the application name from HttpRuntime.AppDomainAppId in the ErrorLog.cs file. You could manipulate it by whatever key you want; however, that is the AppPool's GUID.
With that said, I was able to manipulate the ErrorLog.cs file to turn Elmah into a callable framework instead of a handler based one and allow for me set the ApplicationName. What I ended up doing was modifying ErrorLog.cs to include a property that allowed me to set the name as below:
public virtual string ApplicationName
{
get
{
if (_applicationName == null) { _applicationName = HttpRuntime.AppDomainAppId; }
return _applicationName;
}
set { _applicationName = value; }
}
What you will probably need to do is adjust this differently and set the ApplicationName not to HttpRuntime.AppDomainAppId but, instead, a value pulled from the web.config. All in all, it's possible. The way I did it enhanced the ErrorLog.Log(ex) method so I could use Elmah has a callable framework beyond web applications. Looking back I wish I did the app/web.config approach instead.
One thing to keep in mind when changing the application name in Elmah. The http handler that generates the /elmah/default.aspx interface will no longer work. I'm still trying to find time to circle back around to such; however, you may need to look into creating a custom interface when implementing.

Categories