I'm trying to protect config file of .Net desktop app with ProtectedConfigurationProvider according to this page. I implement a new provider class, to protect config section, I deserialize section node to model, encrypt string value inside, then serialize encrypted model to section node and put into a new "EncryptedData" element, vice versa.
For example, I have a "appSettings" section in config file:
<appSettings>
<add key="test key" value="test value" />
</appSettings>
after encrypted:
<appSettings configProtectionProvider="customProtectionProvider">
<EncryptedData>
<appSettings>
<add key="6ZefRBry+Q" value="6ZefRB2w7OuU" />
</appSettings>
</EncryptedData>
</appSettings>
Here is the problem I have: When I try to decrypt the protected config data, Decrypt method in my custom provider will always be fired when I deserialize encrypted section xml to model, and then get into deserialize part again.
load configuration and get "appSettings" section
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection appSettingsSection =
config.GetSection("appSettings"); // fire Decrypt method here
// ......
in Decrypt method of provider
var sectionModel = ConfigurationBase.Deserialize(encryptedNode);
Deserialize
//...get custom section type by encryptedNode.Name
Type sectionType = typeof(Section.AppSettingsSection);
XmlSerializer serializer = new XmlSerializer(sectionType); // fire Decrypt method here and then infinite loops
here is my AppSettingsSection class:
[XmlRoot("appSettings")]
public class AppSettingsSection : ConfigurationBase
{
[XmlAttribute("file")]
public string File { get; set; }
[XmlElement("add")]
public List<KeyValueNode> Settings { get; set; }
protected override void encryp()
{
// ......
}
protected override void decrypt()
{
// ......
}
}
I don't know why create XmlSerializer with this type will call Decrypt method of ProtectedConfigurationProvider.
Are there any solutions?
I downloaded debug symbols of System.Xml.Serialization and step into it, then I found after 65 lines of TempAssembly class, ProtectedConfigurationProvider.Decrypt method will be called, so I checked out TempAssembly.UseLegacySerializerGeneration property, in getter it called System.Xml.Serialization.AppSettings class which trying to get value from ConfigurationManager.AppSettings config section, but I have enctypted it before, so Decrypt method is called again.
Finally the solution is avoiding encrypting appSettings cofnig section.
Related
I've spent a few weeks trying to figure this out, this is a duplicate of a question I asked previously but did not get a response to, so I am refining the question here.
I've created a custom class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Configuration;
namespace mssql_gui
{
public class TestConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
public TestConfigInstanceCollection Instances
{
get { return (TestConfigInstanceCollection)this[""]; }
set { this[""] = value; }
}
}
public class TestConfigInstanceCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new TestConfigInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((TestConfigInstanceElement)element).Key;
}
}
public class TestConfigInstanceElement : ConfigurationElement
{
[ConfigurationProperty("key", IsKey = true, IsRequired = true)]
public string Key
{
get { return (string)base["key"]; }
set { base["key"] = value; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return (string)base["value"]; }
set { base["value"] = value; }
}
}
}
I've implemented it:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<appSettings>
<add key="Data Source" value="localhost\SQLEXPRESS"/>
<add key="Initial Catalog" value="(empty)"/>
<add key="Integrated Security" value="SSPI"/>
</appSettings>
<testSection>
<add key ="testKey" value="tesValue"/>
</testSection>
</configuration>
and I have tried to access it, I am getting:
An error occurred creating the configuration section handler for testSection: Could not load type 'mssql_gui.TestConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
I understand that in the type, I should be declare an assembly dll, but I'm confused about that...because in the official instructions by MS, it says to create a new class for the handler:
Create a public class that inherits from the
System.Configuration.ConfigurationSection class.
Add code to define the section's attributes and elements.
Adding the class (at least through the visual studio interface) creates a .cs file, not a .dll assembly file, so how to I add that custom class to an assembly file in order to reference it in the <configSections> part of app.config?
If I understand correctly, you have problem with resolving what actually your Assembly is, since you are only creating .cs files that determine types that this file hold.
Assembly (in maybe not so accurate shorcut) is just the project you have in your solution. It will get compiled into its seperate assembly - the .dll you mentioned - later on.
When you add class to any .cs file in given project, on compile it will be included in project's assembly.
By default, if you won't provide assembly for configSection where its corresponding type should be found, App.config defaults to System.Configuration assembly - that's where you get your error from, since you've declared your section in your own assembly (== project).
Right click in Visual Studio on your project that holds App.config file and choose Properties to check its Assembly name:
Then add this name to your App.config section declaration. In my example its ConsoleApp1, so I will add it to configuration accordingly:
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection, ConsoleApp1"/>
</configSections>
Ensure that the type attribute of the section element matches the
manifest of the assembly (ensure that you specify both the correct
namespace and type name).
You need to add the name of the assembly (where the type relies) to the type attribute:
You'll get the name of the assembly from the AssemblyInfo.cs within the project where TestConfigSection class is defined.
<section name="testSection" type="mssql_gui.TestConfigSection, ASSEMBLYNAME"/>
Example asuming your assembly names mssql_gui
<section name="testSection" type="mssql_gui.TestConfigSection, mssql_gui"/>
You read it like this:
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
TestConfigSection mySec = (TestConfigSection)config.Sections["testSection"];
See more details at MSDN
How to: Create Custom Configuration Sections Using ConfigurationSection
For some context, I want to open a connection to a database and execute some queries.
Here's my App.config:
<dbservers>
<connectionStrings>
<add name="Cube_ConnectionString" connectionString="OLEDB; Datasource=http://cube.com; Initial Catalog=BP" />
</connectionStrings>
<queries>
<add connectionStringName="CubeConnectionString" usedBy="DataAccessor" connectionString="" />
</queries>
</dbservers>
This is how I intend to retrieve the connectionString:
System.Configuration.ConfigurationManager.ConnectionStrings["Cube_ConnectionString"].ConnectionString;
I am wondering whether GetSection or ConnectionString would be best to use. And what would be returned for both of them? How do these two methods function within nested XML such as this?
Additionally, what's the purpose of putting queries in the app.config?
Thanks in advance
I don't think that ConnectionStrings will work unless you put them in the standard section for those. If you want a custom <dbservers> section, you'll have to use GetSection instead.
This functionality is a bit awkward, but highly useful. How to: Create Custom Configuration Sections Using ConfigurationSection is a useful guide for this.
Essentially this boils down to creating a class that inherits from ConfigurationSection, and adding the proper attributes (from the above guide):
public class PageAppearanceSection : ConfigurationSection
{
// Create a "remoteOnly" attribute.
[ConfigurationProperty("remoteOnly", DefaultValue = "false", IsRequired = false)]
public Boolean RemoteOnly
{
get
{
return (Boolean)this["remoteOnly"];
}
set
{
this["remoteOnly"] = value;
}
}
// Create a "font" element.
[ConfigurationProperty("font")]
public FontElement Font
{
get
{
return (FontElement)this["font"]; }
set
{ this["font"] = value; }
}
// Create a "color element."
[ConfigurationProperty("color")]
public ColorElement Color
{
get
{
return (ColorElement)this["color"];
}
set
{ this["color"] = value; }
}
}
...and then adding a reference to your section in the app/web.config:
<configuration>
<configSections>
<section name="dbservers" type="Namespace.DbServersSection, YourAssembly"/>
</configSections>
</configuration>
I have the following method in my apiController:
public IEnumerable<something> GetData(DataProvider dataProvider)
{
return dataProvider.GetData();
}
What I need is to invoke this method from javascript and pass it a parameter of DataProvider derived type. I can handle this by passing string, e.g. "FirstProvider" and than write N number of if's in GetData() method to create an instance of proper type.
But is there some way that I can write in web.config file something like:
<DataProviders>
<type = FirstDataProvider, alias = "FirstProvider">
<type = SecondDataProvider, alias = "SecondProvider">
</DataProviders>
Change getData method to:
public IEnumerable<something> GetData(string dataProviderAlias)
{
// get provider type by it's alias from web congfig,
// then instantiated and call:
return dataProvider.GetData();
}
And then find and instantiate the type by it's alias?
Note: I accepted the answer below cause it's pointed me in a right direction, but msdn says that IConfigurationSectionHandler is deprecated.
So I used ConfigurationSection, ConfigurationElementCollection, ConfigurationElement classes instead to build custom config section.
You can store arbitrary data in web.config in the appSettings element:
<configuration>
<appSettings>
<add key="FirstAlias" value="FirstProvider" />
<add key="SecondAlias" value="SecondProvider" />
</appSettings>
</configuration>
And you can then read the values using:
String firstAlias = System.Configuration.ConfigurationManager.AppSettings["FirstAlias"];
String secondAlias = System.Configuration.ConfigurationManager.AppSettings["SecondAlias"];
It's built-in. It's supported. It's where you're supposed to store custom data.
First of all, you can only store valid xml in web.config. <type = FirstDataProvider, alias = "FirstProvider"> is not valid xml.
Second, there are a lot of moving pieces. Please follow the steps carefully -
web.config
Make sure you enter the proper namespace for DataProviders. type="YOUR_APPLICATION.DataProviders".
<configuration>
<configSections>
<section name="DataProviders" type="WebApplication2010.DataProviders"
requirePermission="false"/>
</configSections>
<DataProviders>
<Provider type="FirstDataProvider" alias="FirstProvider"/>
<Provider type="SecondDataProvider" alias="SecondProvider"/>
</DataProviders>
....
</configuration>
Code
public class DataProviders : IConfigurationSectionHandler
{
private static bool _initialized;
public static List<Provider> _providers;
public object Create(object parent, object configContext, XmlNode section)
{
XmlNodeList providers = section.SelectNodes("Provider");
_providers = new List<Provider>();
foreach (XmlNode provider in providers)
{
_providers.Add(new Provider
{
Type = provider.Attributes["type"].Value,
Alias = provider.Attributes["alias"].Value,
});
}
return null;
}
public static void Init()
{
if (!_initialized)
{
ConfigurationManager.GetSection("DataProviders");
_initialized = true;
}
}
public static IEnumerable<Provider> GetData(string dataProviderAlias)
{
return _providers.Where(p => p.Alias == dataProviderAlias);
}
}
public class Provider
{
public string Type { get; set; }
public string Alias { get; set; }
}
Global.asax
For good design practice, you want to read data from web.config only once, and store them in static variables. Therefore, you want to initialize inside Application_BeginRequest of Global.asax.
public class Global : System.Web.HttpApplication
{
void Application_BeginRequest(object sender, EventArgs e)
{
DataProviders.Init();
}
}
Usage
var providers = DataProviders.GetData("FirstProvider").ToList();
Well, I'm not sure if I understand what you want to achieve, but to implement your idea you need a custom section handler.
http://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.100).aspx
In case that you want to create a database connection for specific dataprovider, see this similar question:
ASP.NET: How to create a connection from a web.config ConnectionString?
In my case, I needed to store two byte[] variables in my Web.Config file. Since it must be valid XML data, I simply stored the contents of the arrays like so:
<appSettings>
<add key="Array1" value="0,1,2,3,4,5,6,7,8,9" />
<add key="Array2" value="0,1,2,3,4,5,6,7,8,9" />
</appSettings>
I then call a function that reads this into a C# string, split it into a string[], and parse each string element as a byte into a resulting byte[] and return it.
i need your help
i have app.config like this
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<connectionStrings>
...
</connectionStrings>
<appSettings />
<userSettings>
<MySettings>
<setting name="Precision" serializeAs="String">
<value>0</value>
</setting>
</MySettings>
</userSettings>
<applicationSettings>
...
</applicationSettings>
</configuration>
what I need is to get 'Precision' value. how to get that without looping SectionGroups, SectionCollection?
note:
I've DAL and in my DAL need this precision to format the decimal value and the precision is managed by user(client) through the Presentation Layer. I save the precision value in app.config. The problem, the app.config is in Presentiation Layer, and I cannot use Properties.MySetting.Default.Precision to get that. (Thanks to Branko & Tim to remind me about this reason)
I would consider "settings injection" here - like dependency injection, but for settings :)
Presumably your entry point configures the whole system... so get it to read all the settings from app.config, and use those when creating and configuring the DAL (and anywhere else which needs settings). The only piece of code which needs to know how to use app.config can be the entry point. Everything else can be specified via POCOs, individual constructor parameters etc.
This is good in several ways:
Your code will be easier to test: to test different settings, just pass in different constructor arguments. No need for files etc.
Your immediate problem is solved: the DAL no longer needs to look through the settings file
You're isolating your configuration storage in a single place which can change if you decide to (say) use a different XML format, or an "ini"-style configuration format
If I've understood Jon's answer correctly it looks like the following:
public interface IConfigurationWrapper
{
IDictionary<string, string> Properties { get; }
T GetSection<T>(string name) where T : ConfigurationSection;
}
public class ConfigurationWrapper : IConfigurationWrapper
{
// implementation with with ConfigurationManager.GetSection or just placeholders
}
public interface IProduct
{
string Name { get; }
}
public class Product : IProduct
{
readonly IConfigurationWrapper m_configuration;
public Product(string key, IConfigurationWrapper configuration)
{
m_configuration = configuration;
}
public string Name
{
get { // use m_configuration to get name from .config }
}
}
public class ProductFactory
{
readonly IConfigurationWrapper m_configuration;
public ProductFactory(IConfigurationWrapper configuration)
{
m_configuration = configuration;
}
public IProduct CreateProduct(string key)
{
return new Product(key, m_configuration);
}
}
and usage looks like:
var config = new ConfigurationWrapper();
var factory = new ProductFactory(config);
var product = factory.CreateProduct("myproductkey");
clients work with IProduct interface only, "products" layer works with IConfigurationWrapper, while wrapper works with whatever the configuration you have (.config or mocks, you can have mock products for testing too). Code above is heavily stripped part of larger system just to provide example don't get it too literally.
I want to load a dictionary at startup in my console app from my app.config.
I know that I could use an xml library or linq to XML to load it to parse and traverse it. My question is there a BUILT IN way of doing it.
Isn't there some way to add an application configuration section into the app.config and then have it loaded automagically using ConfigurationManager class in the System.Configuration namespace?
Any example? BTW, I am in NET20.
EDIT
Sorry, I should have clarified. I want to load the dictionary WITHOUT using AppSettings. I know how to do that already. Of course, the downside of using AppSettings is that I have to change my code to add new values to my dictionary. That is why I am looking for a way to do it automatically.
You will need to add an <appSettings> section to your app.config file. It will look something like:
<appSettings>
<add key="foo" value="fooValue" />
<add key="bar" value="barValue" />
<add key="baz" value="bazValue" />
</appSettings>
From within your app, you can grab these values with System.Configuration.ConfigurationManager.AppSettings, which is a NameValueCollection, which is essentially a dictionary from string to string.
string myFoo = System.Configuration.ConfigurationManager.AppSettings["foo"];
You can use the appSettings section the way you describe, but that section easily gets polluted with a variety of needs, and so I usually avoid it. You can make custom sections to deal with this.
Imagine you have a class called "PluginSpec," you can write code like this:
[ConfigurationCollection(typeof(PluginSpec), AddItemName = "Plugin",
CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class PluginCollection : ConfigurationElementCollection
{
//This collection is potentially modified at run-time, so
//this override prevents a "configuration is read only" exception.
public override bool IsReadOnly()
{
return false;
}
protected override ConfigurationElement CreateNewElement()
{
return new PluginSpec();
}
protected override object GetElementKey(ConfigurationElement element)
{
PluginSpec retVal = element as PluginSpec;
return retVal.Name;
}
public PluginSpec this[string name]
{
get { return base.BaseGet(name) as PluginSpec; }
}
public void Add(PluginSpec plugin){
this.BaseAdd(plugin);
}
}
The above code can be used from a member of another config class, like this:
[ConfigurationProperty("", IsDefaultCollection = true)]
public PluginCollection Plugins
{
get
{
PluginCollection subList = base[""] as PluginCollection;
return subList;
}
}
The above would be a member in a class that derives from ConfigurationElement or ConfigurationSection.