I have a Web Role that is using the ASP.NET SQL Membership provider. Currently the configuration is in the Web.Config file. I would like to configure the connection string as a Web Role setting instead of having it in the Web.Config. The main reason that I wan this is so that I can set up configurations on the azure project to publish to different hosted services (dev, qa, etc.) Right now, I have to manually edit the web.config each time I want to publish to a different service.
I have a couple ideas on how I might be able to accomplish this.
Write a custom membership provider that wraps the SQL provider and provides custom configuration to it.
Put something in the Web Role OnStart method to change the connection string in the Web.config file.
Has anyone done something like this before, or have recommendations on which option might be best, or have another idea on how to accomplish this?
The Windows Azure SDK allows you to have multiple service configurations per environment. But web.config modifications are a bit harder. In your case I would suggest you write some code (or a startup task) that reads the connection string from the service configuration and writes it to the web.config.
Andy's blog post 'Programmatically modify web.config on WebRole Startup' explains exactly how you can do this:
public override bool OnStart()
{
using (var server = new ServerManager())
{
// get the site's web configuration
var siteNameFromServiceModel = "Web"; // TODO: update this site name for your site.
var siteName =
string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
var siteConfig = server.Sites[siteName].GetWebConfiguration();
// get the appSettings section
var appSettings = siteConfig.GetSection("appSettings").GetCollection();
AddElement(appSettings, "deploymentId", RoleEnvironment.DeploymentId);
AddElement(appSettings, "internalEndpointPort", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints
.First(t=>t.Key=="InternalEndpoint1").Value
.IPEndpoint.Port.ToString());
server.CommitChanges();
}
return base.OnStart();
}
Related
I have a query regarding BTDF SSO config setting. I am beginner with BizTalk.
I am looking for SSO storage where credentials are stored and retrieved from SSO. I have built-in app located at C:\Program Files (x86)\Deployment Framework for BizTalk 6.0\Framework\DeployToolsork\DeployTools
Could anyone tell me how to store and retrieve from existing SSO config like SSOSettingsEditor which is the default provided by BTDF.
Using BTDF, you can store your configurations as provided in SettingsFileGenerator.xml in BizTalk SSODB. BTDF automatically store your configuration if IncludeSSO property is set to true in btdfproj file.
If you have provided your credential details in SettingsFileGenerator.xml file then only you will find them in SSODB.
You should use SSOSettingsEditor to retrieve or make changes to the configurations. In SSOSettingsEditor, type in your application name and press enter.
Refer to link: BTDF IncludeSSO
BTDF provides a library for modifying SSO Settings that it uses. The method is uses is slightly different from the default Microsoft sample SSO client, so take care regarding which one you're using.
Per that link, the class provides these methods:
namespace SSOSettingsFileManager
{
public static class SSOSettingsManager
{
public static void WriteSetting(string affiliateApplication, string propertyName, string propertyValue);
}
}
It should be fairly straightforward to call that method once you've added a reference to the SSOSettingsFileReader.dll in whatever C# project you have generating your password or updating it, i.e.
string newPassword = GenerateMyPassword();
SSOSettingsFileManager.SSOSettingsManager.WriteSetting("MyApplicationName", "Password", newPassword;);
You could also look at the source of how he's doing it if you want to implement the method yourself.
I got a basic Silverlight Ria Services solution with 2 projects (Silverlight Client and Asp.net Host) with a Domain Service Class in the Host project.
I can create a new Domain Service object in another class inside the host project, and use the methods generated by Visual Studio.
The query methods of this Domain object works fine retrieving data, but insert methods don't apply to the database, probably cause I didn't submit te operation, as the method "SubmitChanges" does in client project.
Question is: how can I apply insert, delete and update operations with this object in server-side since I'm not coding in Domain Service class, but only using an object of this type?
I've found the method DomainService.Submit, but it requires a ChangeSet that don't know how to provide.
EDIT:
//Client Project (Silverlight):
MyDomain domain = new MyDomain();
domain.Products.Add(new Product());
domain.SubmitChanges(); //sucessfull DB insertion
//Host Project, any new asp.net WebPage:
MyDomain domain = new MyDomain();
domain.InsertProduct(new Product()); //nothing happens in DB
domain.SubmitChanges(); //don't exist
domain.Submit(ChangeSet); //don't know how to provide a ChangeSet
It's done a little bit differently on the server vs. the client. On the server, from your domain service class, you can use Me.ObjectContext to add and save your entities, e.g.
Me.ObjectContext.Products.AddObject(new Product)
Me.ObjectContext.SaveChanges()
I just created a cron job like job using Quartz.net. For the test, it execute a simple request to the database. It simply adds a field.
I have a dbcontext:
private TotoContext db = new TotoContext();
In my job I have:
var totos = from u in db.totos where u.name == name select u;
Toto[] totoArray = totos.ToArray();
In my web.config, I have a special field with my specific connectionstring and so on ("TotoContext").
But when I create a new dbContext it seems that it uses doesn't use the good connectionString. In the watch the connectionString is not linked with "TotoContext".
I initialize my job in:
public override bool OnStart()
And I have a specific Web.toto.config file with the connectionString for the build.
Why it doesn't use the good connectionString ?!
Thanks a lot !
Edit: if I set manually the connectionString in my db.Database.Connection.ConnectionString, it works. But why it doesn't use the web.config ConnectionString.
If you use full IIS mode (default configuration for web role), web.config will be ignored in the role entry point. So it is recommended to put all ASP.NET specific initialization tasks in Global.asax's Application_Start method. Role entry point is used to do something before ASP.NET application starts up, for example, modify IIS configuration. Inside Global.asax, web.config (and config transform) is respected.
I just found why it's not using the Web.config: https://stackoverflow.com/a/10153375/1396323
But the next question is how to store different connectionString depending on the build config (Debug, Release etc...) and where ?
Is it possible to read the IIS re-write rules from the Azure ServiceConfiguration file instead of the web.config?
The problem that is arising is that we have friendly urls to certain weekly updated pages that are content managed, so a new url is created every week. The old ones are stored in a newslist archive so overwriting is not an option.
We would like to try and avoid having to upload the Azure site files every week, and want to be able to respond quickly (immediately) to possible link changes by altering values in the serviceconfig.
Anyone have any idea if this is possible or wether there is another solution?
Thanks
Yes, you can change your role to modify the web.config at runtime using the Configuration editor classes in the IIS Admin api. I haven't tried this, but it should enable you to load the settings from Azure config during startup then apply to the runtime instance of your role. So you would set this likely in your Application_start section of the web role's global.asax.
Alternatively, you could programitically build up the web.config at role start time using a Startup Task.
For the 1st approach:
Do some research at iis.net and then read this IIS forum post:
http://forums.iis.net/t/1150481.aspx
Take a sample from user ruslany (give credit where due, but pasting so you see it):
using(ServerManager serverManager = new ServerManager()) {
Configuration config = serverManager.GetWebConfiguration("Default Web Site");
ConfigurationSection rulesSection = config.GetSection("system.webServer/rewrite/rules");
ConfigurationElementCollection rulesCollection = rulesSection.GetCollection();
ConfigurationElement ruleElement = rulesCollection.CreateElement("rule");
ruleElement["name"] = #"MyTestRule";
ruleElement["stopProcessing"] = true;
ConfigurationElement matchElement = ruleElement.GetChildElement("match");
matchElement["url"] = #"foo\.asp";
ConfigurationElement conditionsElement = ruleElement.GetChildElement("conditions");
ConfigurationElementCollection conditionsCollection = conditionsElement.GetCollection();
ConfigurationElement addElement = conditionsCollection.CreateElement("add");
addElement["input"] = #"{HTTP_HOST}";
addElement["pattern"] = #"www\.foo\.com";
conditionsCollection.Add(addElement);
ConfigurationElement actionElement = ruleElement.GetChildElement("action");
actionElement["type"] = #"Rewrite";
actionElement["url"] = #"bar.asp";
rulesCollection.Add(ruleElement);
serverManager.CommitChanges();
}
I have a classic ASP page - written in JScript - that's using Scripting.FileSystemObject to save files to a network share - and it's not working. ("Permission denied")
The ASP page is running under IIS using Windows authentication, with impersonation enabled.
If I run the following block of code locally via CScript.exe:
var objNet = new ActiveXObject("WScript.Network");
WScript.Echo(objNet.ComputerName);
WScript.Echo(objNet.UserName);
WScript.Echo(objNet.UserDomain);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var path = "\\\\myserver\\my_share\\some_path";
if (fso.FolderExists(path)) {
WScript.Echo("Yes");
} else {
WScript.Echo("No");
}
I get the (expected) output:
MY_COMPUTER
dylan.beattie
MYDOMAIN
Yes
If I run the same code as part of a .ASP page, substituting Response.Write for WScript.Echo I get this output:
MY_COMPUTER
dylan.beattie
MYDOMAIN
No
Now - my understanding is that the WScript.Network object will retrieve the current security credentials of the thread that's actually running the code. If this is correct - then why is the same user, on the same domain, getting different results from CScript.exe vs ASP? If my ASP code is running as dylan.beattie, then why can't I see the network share? And if it's not running as dylan.beattie, why does WScript.Network think it is?
Your problem is clear. In the current implementation you have only impersonation of users and no delegation. I don't want to repeat information already written by Stephen Martin. I only want to add at least three solutions. The classical way of delegation which Stephen Martin suggests is only one way. You can read some more ways here: http://msdn.microsoft.com/en-us/library/ff647404.aspx#paght000023_delegation. I see three practical ways of you solving your problem:
Convert the impersonation token of the user to a token with delegation level of impersonation or to a new primary token. You can do this with respect of DuplicateToken or DuplicateTokenEx.
Use S4U2Self (see http://msdn.microsoft.com/en-us/magazine/cc188757.aspx and http://msdn.microsoft.com/en-us/library/ms998355.aspx) to receive a new token from the old one with respect of one simple .NET statement WindowsIdentity wi = new WindowsIdentity(identity);
You can access another server with respect of one fixed account. It can be a computer account on an account of the application pool of the IIS. It can be another fixed defined account which one will only use for access to the file system.
It is important to know which version of Windows Server you have on the server where IIS is running and which Domain Function Level you have in Active Directory for your Domain (you see this in "Active Directory Domain and Trusts" tool if you select your domain and choose "Raise Domain Functional Level"). It is also interesting to know under which account the application pool of the IIS runs.
The first and the third way will always work. The third way can be bad for your environment and for the current permission in the file system. The second one is very elegant. It allows control of which servers (file server) are accessed from IIS. This way has some restrictions and it needs some work to be done in Active Directory.
Because you use classic ASP, a small scriptable software component must be created to support your implementation.
Which way do you prefer?
UPDATED based on the question from comment: Because you use classic ASP you can not use a Win32 API directly, but you can write a small COM component in VB6 or in .NET which use APIs which you need. As an example you can use code from http://support.microsoft.com/kb/248187/en. But you should do some other things inside. So I explain now which Win32 API can help you to do everything what you need with tokens and impersonation.
First of all a small explanation about impersonation. Everything works very easy. There are always one primary token under which the process runs. To any thread another token (thread token) can be assigned. To do this one needs to have a token of a user hUserToken and call API ImpersonateLoggedOnUser(hUserToken);.
To go back to the original process token (for the current thread only) you can call RevertToSelf() function. The token of user will be received and already impersonated for you by IIS, because you so configured your Web Site. To go back to the original process token you should implement calling of the function RevertToSelf() in your custom COM component. Probably, if you need to do nothing more in the ASP page, it will be enough, but I recommend you be more careful and save current users token in a variable before operation with files. Then you make all operations with file system and at the end reassign users token back to the current thread. You can assign an impersonation token to a thread with respect of SetThreadToken(NULL,hUserToken);. To give (save) current thread token (user token in your case) you can use OpenThreadToken API. It must work.
UPDATED 2: Probably the usage of RevertToSelf() function at the end of one ASP page would be already OK for you. The corresponding C# code can be so:
Create a new Project in C# of the type "Class Library" with the name LoginAdmin. Paste the following code inside
using System;
using System.Runtime.InteropServices;
namespace LoginAdmin {
[InterfaceTypeAttribute (ComInterfaceType.InterfaceIsDual)]
public interface IUserImpersonate {
[DispId(1)]
bool RevertToSelf ();
}
internal static class NativeMethods {
[DllImport ("advapi32.dll", SetLastError = true)]
internal static extern bool RevertToSelf ();
}
[ClassInterface (ClassInterfaceType.AutoDual)]
public class UserImpersonate : IUserImpersonate {
public UserImpersonate () { }
public bool RevertToSelf () {
return NativeMethods.RevertToSelf();
}
}
}
Check in project properties in "Build" part "Register for COM interop". In "Signing" part of the project check Sign the assembly and in "Choose a strong name key file" choose <New...>, then type any filename and password (or check off "protect my key..."). At the end you should modify a line from AssemblyInfo.cs in Properties part of the project:
[assembly: ComVisible (true)]
After compiling this project you get two files, LoginAdmin.dll and LoginAdmin.tlb. The DLL is already registered on the current computer. To register if on the other computer use RegAsm.exe.
To test this COM DLL on a ASP page you can do following
<%# Language="javascript" %>
<html><body>
<% var objNet = Server.CreateObject("WScript.Network");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var objLoginAdmin = Server.CreateObject("LoginAdmin.UserImpersonate");
var isOK = objLoginAdmin.RevertToSelf();
if (isOK)
Response.Write("RevertToSelf return true<br/>");
else
Response.Write("RevertToSelf return false<br/>");
Response.Write("One more time after RevertToSelf()<br/>");
Response.Write("Current user: ");Response.Write(objNet.UserName);Response.Write("<br/>");
Response.Write("Current user's domain: ");Response.Write(objNet.UserDomain);Response.Write("<br/>");
var fso = Server.CreateObject("Scripting.FileSystemObject");
var path = "\\\\mk01\\C\\Oleg";
if (fso.FolderExists(path)) {
Response.Write("Yes");
} else {
Response.Write("No");
}%>
</body></html>
If the account used to run the IIS application pool has access to the corresponding network share, the output will be look like following
Current user: Oleg
Current user's domain: WORKGROUP
RevertToSelf return true
One more time after RevertToSelf()
Current user: DefaultAppPool
Current user's domain: WORKGROUP
Yes
Under impersonation you can only access securable resources on the local computer you cannot access anything over the network.
On Windows when you are running as an impersonated user you are running under what is called a Network token. This token has the user's credentials for local computer access but has no credentials for remote access. So when you access the network share you are actually accessing it as the Anonymous user.
When you are running a process on your desktop (like CScript.exe) then you are running under an Interactive User token. This token has full credentials for both local and remote access, so you are able to access the network share.
In order to access remote resources while impersonating a Windows user you must use Delegation rather then Impersonation. This will involve some changes to your Active directory to allow delegation for the computer and/or the users in your domain. This can be a security risk so it should be reviewed carefully.