Setting up developer-specific configuration with .NET Core

I’m working on a project that I will likely make a public repo on GitHub. However, it has credentials for an SMTP server. I would like to set it up so that the code that is checked-in, is generic and safe – I want to exclude those credentials. Then, the developer can have their own credentials in their own configuration file, on their workstation.

Starting with App.config

OK, I can create an app.config in my .NET Core project, but how do you read it? After a little research, I saw a few references to the “new” .NET Core configuration libraries found here. Turns out, this is a pretty cool way to handle configuration!

In short, at the start of your application, you load up JSON files (from various places if you’d like), and you can see the contents of configuration via this one, combined class.

Never mind. Let’s start with appSettings.json!

OK, using the reference above, I found I could easily accomplish what wanted. I can store information (sensitive, and non-sensitive) in difference places, but view those settings in-aggregate as if they came from the same place. Like this:

image

I can access “setting1” and “username” the same way, even though one is public (stored in GitHub) and the other is sensitive (stored in my user profile on my local machine). To get started, just create an appSettings.json file in the root of your project, which has some settings, like above for example.

How does it work?

First, you need NuGet packages:

  1. Microsoft.Extensions.Configuration
  2. Microsoft.Extensions.Configuration.Json

Next, you need to have an IConfiguration object stored somewhere. Depending on the architecture of your application, you could:

  • Create an instance of IConfiguration once on the startup of your application, and make it globally available – like in a public static read-only property.
  • Create an instance of IConfiguration once on the startup of your application, and pass it around (“inject” it) to all of the classes that need configuration.

In my case, so far, I’m just passing around the IConfiguration to the classes that need it, via constructor injection. Then, in code, you can access your JSON values – for example:

Sample appSettings.json:

{
  "smtp": {
    "server": "smtp.gmail.com",
    "port": "587",
    "username": "USERNAME@gmail.com",
    "password": "PASSWORD"
  },
  "from": {
    "address": "USERNAME@gmail.com",
    "name": "Tests, Unit"
  },
  "to": {
    "address": "OTHERUSER@outlook.com",
    "name": "OTHER USER"
  }
}

Sample of reading those values:

IConfiguration configuration; // TODO: How do we create this?

String smtpServer = configuration["smtp:server"];
String toAddress = configruation["to:address"];

And again, if you are pointing to multiple files, it doesn’t matter, it overlays all of the graphs into one “view”, that is access like above. Note that you step into each level of your JSON with a “:” colon separator.

But how do you get that IConfiguration?

Setting up a JSON Configuration object:

I looks like there are a few providers supported, but since we all use JSON for a lot of stuff, that’s a good a format as any for your configuration settings. So, how do we point to an appSettings.json in the root of the current project?

String projectConfigFile = Path.Combine(
    Directory.GetCurrentDirectory(), "..\\..\\..", "appSettings.json");

var builder = new ConfigurationBuilder()
    .AddJsonFile(projectConfigFile);

IConfiguration configuration = builder.Build();

So, it’s this sort of code that you need to put somewhere where your application starts up. You can either publicly expose it someplace or just pass the IConfiguration object around to any objects that need to refer to the configuration file.

Including developer-specific config:

My original goal was that I wanted to put some non-sensitive config with the project, which would be stored with the GitHub project. However, other configuration with sensitive information (passwords, etc.) I wanted to have in the user profile. To do that, you can just add an additional JSON file, when you are building your configuration. For example:

String projectConfigFile = Path.Combine(
    Directory.GetCurrentDirectory(), "..\\..\\..", "appSettings.json");

var builder = new ConfigurationBuilder()
    .AddJsonFile(projectConfigFile)
    .SetBasePath(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile))
    .AddJsonFile(Path.Combine("Configs", "ProjectNameGoesHere", "appsettings.json"));

IConfiguration configuration = builder.Build();

I also tried to write this in an OS-agnostic way. In theory, if the .NET Core libraries are doing their job correctly, the Environment.SpecialFolder.UserProfile will point to %UserProfile% on a Windows machine and the “~” folder on Linux and macOS. I didn’t test that out, though.

What about new developers?

If a new developer rolls onto the project, they may not know about this requirement. So, what I did (and the code in it’s final form) is throw an exception which explains what the developer needs to do:

String projectConfigFile = Path.Combine(
    Directory.GetCurrentDirectory(), "..\\..\\..", "appSettings.json");

String developerConfigFile = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
    "Configs", "ProjectNameGoesHere", "appsettings.json");

if (!File.Exists(developerConfigFile))
{
    String message = $"Please see the project documentation. You should "
        + "create a workstation-specific configuration file to store "
        + "sensitive app settings. The file should be \"{developerConfigFile}\". "
        + "For the contents, see the developer documentation.";

    throw new FileNotFoundException(message, developerConfigFile);
}

IConfigurationBuilder builder = new ConfigurationBuilder()
    .AddJsonFile(projectConfigFile)
    .AddJsonFile(developerConfigFile);

Configuration = builder.Build();

Bottom Line:

For anyone who has ever dug into the System.Configuration design, it definitely had it’s flaws and was very limited. Conversely, I really like this new approach! It’s fast, easy, and lightweight. Even better, it took me longer to write this post than it did to find the answer and implement this. But, my brain won’t remember these details, so I’m doing Future Robert a favor by making a quick blog post about this!

Posted in .NET Core, Best-practices, Computers and Internet, General, Security

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 9 other followers

%d bloggers like this: