On IIS, I have a site on which I wish to edit the SslFlags.
I want to have these flags being set in the web.config at the site level instead of applicationHost.config.
I managed to have the UI of IIS to behave as expected by declaring the access section in the web.config, and allowing the override of the access section by editing applicationHost.config with the following element:
<section name="access" overrideModeDefault="Allow" />
Editing the SslFlags through the UI will edit the web.config file as expected. The section is not locked and the overridden value is considered.
However, when using the Microsoft.Web.Administration assembly to read and edit these flags by using the following code, the values which are considered are the ones of applicationHost.config, both when reading and editing.
In that first example, I used GetWebConfiguration to get the Configuration.
var serverManager = ServerManager.OpenRemote(serverName);
// Try with GetWebConfiguration
Configuration config = serverManager.GetWebConfiguration(sitename);
ConfigurationSection accessSection = config.GetSection(
"system.webServer/security/access",
sitename);
also, same applies if I retrieve the configuration with GetApplicationHostConfiguration:
config = serverManager.GetApplicationHostConfiguration();
accessSection = config.GetSection(
"system.webServer/security/access",
sitename);
I feel like I'm missing something obvious here, but I can't seem to access the values of the SslFlags in Web.config, how can I achieve that?
The first thing I would recommend is to only unlock sections for the specific web site or application that you want to allow overriding the values. For that you can do it quite easily using AppCmd.exe, for example:
C:\Windows\System32\inetsrv\appcmd.exe unlock config "Default Web Site/" /section:system.webServer/security/access -commit:apphost
Once you do that, then you can use the following code:
using(ServerManager serverManager = new ServerManager()) {
Configuration config = serverManager.GetWebConfiguration("Default Web Site");
ConfigurationSection accessSection = config.GetSection("system.webServer/security/access");
accessSection["sslFlags"] = #"SslRequireCert";
serverManager.CommitChanges();
}
Related
I want to assign Active Directory group name dynamically as an attribute to authorize filter.
Currently we have 2 Active Directory groups, one is for DEV and other is for Prod. However if I have access to dev while debugging in local, I need to have access to the action method. If the AD group is prod, I should not have have access to the action method.
I tried using constants in static class classA
public const string DevActiveDirectoryName = "DevdirectoryName";
public const string ProdActiveDirectoryName = "ProddirectoryName";
My action method is like this:
[Authorize(Roles = ClassA.DevActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
The only problem with above solution is if I want to deploy to prod, I need to change code and deploy it. Instead if I can pass value dynamically to attribute that would solve my problem. I have tried many ways. Please suggest the best solution for this case. Is there any workaround for this kind of problem? I appreciate your help.
In order to be able to change the group name for different environments, you need to use configuration settings instead of constants. There are many options on how to provide configuration settings to an ASP.NET Core application. This link gives an overview.
If your application uses the default host builder, it will read configuration settings from a variety of sources, e.g. appsettings.json. You can add a setting to this file (if it does not exist yet, add it to the project), e.g.:
{
"ADGroupName": "ProddirectoryName"
}
For your dev-environment there is a dedicated file appsettings.dev.json that you can use to hold your dev settings:
{
"ADGroupName": "DevdirectoryName"
}
When protecting the controller with an Authorize attribute, you need to provide a constant value to the constructor. As the configuration setting can be changed later, it is (obviously) not constant.
Therefore, you have to set up a policy with a constant name in the ConfigureServices method in Startup.cs:
var adGroupName = Configuration.GetValue<string>("ADGroupName");
services.AddAuthorization(options =>
{
options.AddPolicy("ADGroupPolicy", policy =>
{
// This requirement checks for the group
policy.RequireRole(adGroupName);
});
});
For the controller, you need to add the policy name to the Authorize attribute:
[Authorize("ADGroupPolicy")]
public async task<Iactionresult>GetMemberID()
{
}
You can add an entry in your <appSettings> of your web.Config file and use ConfigurationManager to look up the value that should be assigned to a variable ActiveDirectoryName.
<appSettings>
<add key="ActiveDirectoryName" value="DevdirectoryName" />
... // other keys
</appSettings>
and in your code, you could look up what you have in your web.Config file (Dev for development and Prod for production servers (you dont need to deploy new web.config when deploying new code unless you make changes to it.
public const string ActiveDirectoryName = ConfigurationManager.AppSettings["ActiveDirectoryName"];
If you are using Visual Studio, web.config have two different configs (web.debug.config / web.release.config). You can use debug for development and Release that works on production.
This will stay constant and only your config files are changed,
[Authorize(Roles = ClassA.ActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
Let's say I have the following in my config:
<configSections>
<section name="interestingThings" type="Test.InterestingThingsSection, Test" />
<section name="moreInterestingThings" type="Test.InterestingThingsSection, Test" />
</configSections>
<interestingThings>
<add name="Thing1" value="Seuss" />
</interestingThings>
<moreInterestingThings>
<add name="Thing2" value="Seuss" />
</moreInterestingThings>
If I want to get either section, I can get them by name pretty easily:
InterestingThingsSection interesting = (InterestingThingsSection)ConfigurationManager.GetSection("interestingThings");
InterestingThingsSection more = (InterestingThingsSection)ConfigurationManager.GetSection("moreInterestingThings");
However, this relies on my code knowing how the section is named in the config - and it could be named anything. What I'd prefer is the ability to pull all sections of type InterestingThingsSection from the config, regardless of name. How can I go about this in a flexible way (so, supports both app configs and web configs)?
EDIT: If you have the Configuration already, getting the actual sections isn't too difficult:
public static IEnumerable<T> SectionsOfType<T>(this Configuration configuration)
where T : ConfigurationSection
{
return configuration.Sections.OfType<T>().Union(
configuration.SectionGroups.SectionsOfType<T>());
}
public static IEnumerable<T> SectionsOfType<T>(this ConfigurationSectionGroupCollection collection)
where T : ConfigurationSection
{
var sections = new List<T>();
foreach (ConfigurationSectionGroup group in collection)
{
sections.AddRange(group.Sections.OfType<T>());
sections.AddRange(group.SectionGroups.SectionsOfType<T>());
}
return sections;
}
However, how do I get the Configuration instance in a generally-applicable way? Or, how do I know if I should use ConfigurationManager or WebConfigurationManager?
So far, this appears to be the best way:
var config = HostingEnvironment.IsHosted
? WebConfigurationManager.OpenWebConfiguration(null) // Web app.
: ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); // Desktop app.
Try to use ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) method. It opens the configuration file for the current application as a Configuration object.
MSDN documentation: https://msdn.microsoft.com/en-us/library/ms134265%28v=vs.110%29.aspx
Maybe not the best way to do it but you can read your configuration file as a normal xml and then parse the sections you want. For example if it were a web application:
XmlDocument myConfig= new XmlDocument();
myConfig.Load(Server.MapPath("~/Web.config"));
XmlNode xnodes = myConfig.SelectSingleNode("/configSections");
Now you can see the nodes that you care about discovering the names at runtime and then access the specific node of your configuration file.
Another solution is:
Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
If this returns "web.config", it is probably a web application.
However, HostingEnvironment.IsHosted is intended to indicate whether an appdomain is configured to run under ASP.NET so it is not sure that yours is a web application.
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.
I'm accessing a config file thusly:
var map = new ConfigurationFileMap(configPath);
var config = ConfigurationManager.OpenMappedMachineConfiguration(map);
config.AppSettings.Settings.Add("SomeSetting", "SomeValue");
It works fine for any .exe.config file, but not for any web.config.
Note: I am not trying to access the web.config of the current application, I am attempting to modify the web.config in an arbitrary path.
(I've tried WebConfigurationManager instead of ConfigurationManager, but it gives identical results)
The exception is thrown by the AppSettings property accessor - trying to GetSection("appSettings") and cast it to an AppSettingsSection of course gievs the same exception. Either way, here it is:
System.InvalidCastException: Unable to cast object of type 'System.Configuration.DefaultSection' to type 'System.Configuration.AppSettingsSection'.
I have obviously searched around, but have found only people accessing the web.config for the 'current web app' or using XmlDocument/XDocument.
My guess is .exe.config files automatically get some configSections-type information inferred which means it correctly knows how to deal with appSettings. However I have no idea why, based on the filename, it wouldn't work with web.config.
Ah. For app.config I'm using OpenExeConfiguration:
// works fine for .exe.config
var config = ConfigurationManager.OpenExeConfiguration("some-assembly-here.exe");
config.AppSettings.Settings.Add("SomeSetting", "SomeValue");
config.Save();
Here I'm using OpenMappedMachineConfiguration which appears to be for machine.config, however I can't see another way of opening an arbitrary web.config file - anyone?
My mistake - I can use OpenMappedExeConfiguration just fine when opening web.config files:
var map = new ExeConfigurationFileMap();
map.ExeConfigFilename = configPath;
var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
I can create an http redirect in my IIS by using the following code. However, it only creates a redirect for the main site "MySite" itself. How do I programatically add a redirect to a virtual directory? Is there some setting I need to set when I create the VD? Thanks!
ServerManager iisManager = new ServerManager();
Configuration config = iisManager.GetWebConfiguration("MySite");
ConfigurationSection httpRedirectSection = config.GetSection("system.webServer/httpRedirect");
httpRedirectSection["enabled"] = true;
httpRedirectSection["destination"] = #"http://www.google.com";
httpRedirectSection["exactDestination"] = false;
httpRedirectSection["httpResponseStatus"] = #"Found";
iisManager.CommitChanges();
Sean,
What your code is doing is essentially modifying the web.config file for your site. The virtual directory is likely configured as an application so it would have its own web.config file. Did you try doing the same thing but simply changing:
Configuration config = iisManager.GetWebConfiguration("MySite/VirtDirName");
Also, the virtual directory, since it is a child application may already be inheriting the httpRedirect setting from the parent site, I'd check first that this is not the case.
http://www.iis.net/ConfigReference/system.webServer/httpRedirect
http://msdn.microsoft.com/en-us/library/ms178685.aspx
If your hosting it in IIS 7+ then you could use web.config.
Place a web.config in the directory and then add
<?xml version="1.0"?>
<configuration>
<system.webServer>
<httpRedirect enabled="true" destination="/[PathFromRoot]/" childOnly="true" httpResponseStatus="Temporary" />
</system.webServer>
</configuration>
You can read up on the properties here