Exemple d'approvisionnement en C#
Auteur : Walter Oliver
Vue d’ensemble
Au fil des ans, il est devenu simple pour les utilisateurs Web avancés de créer leur propre site Web. Une fois qu'un utilisateur final a inscrit son nom de domaine, il existe différents types d'hôtes à choisir. Les hôtes Web sont disponibles dans le monde entier pour répondre aux demandes des clients finaux. Mais quel que soit l'hôte choisi, le scénario d'inscription et de création d'un site Web est similaire dans tous les cas.
Au départ, un nouveau compte d'utilisateur doit être établi pour l'utilisateur. Une fois le compte configuré, l'utilisateur final décide quelles fonctionnalités et options le site doit incorporer : par exemple, la quantité d'espace disque, la fonctionnalité FTP, la création de répertoires virtuels, la nécessité ou non de bases de données, etc. Les hébergeurs créent des panneaux de configuration ou des applications de tableau de bord qui permettent aux utilisateurs finaux de créer et de gérer ces fonctionnalités.
Il existe plusieurs façons d'implémenter ces fonctionnalités dans le panneau de configuration. Dans cette section, nous examinons l'implémentation de l'aspect d'approvisionnement de ces fonctionnalités via du code managé. Voici un aperçu des fonctionnalités :
Provisionner un nouveau compte d'utilisateur
Le propriétaire du site qui gère et maintient le site a besoin d'un nouveau compte d'utilisateur. Le compte peut être un compte Active Directory® ou un compte d'utilisateur local. Les fragments de code suivants illustrent la création d'un compte local. Notez que l'espace de noms System.DirectoryServices doit être spécifié.
public static bool CreateLocalUserAccount(string userName, string password)
{
try
{
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName", "Invalid User Name.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "Invalid Password.");
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" +
Environment.MachineName + ",computer");
bool userFound = false;
try
{
if (directoryEntry.Children.Find(userName, "user") != null)
userFound = true;
}
catch
{
userFound = false;
}
if (!userFound)
{
DirectoryEntry newUser = directoryEntry.Children.Add(userName, "user");
newUser.Invoke("SetPassword", new object[] { password });
newUser.Invoke("Put", new object[] { "Description", "Application Pool User Account" });
newUser.CommitChanges();
newUser.Close();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Créer un stockage de contenu
Un site Web nécessite un emplacement sur les systèmes de fichiers vers lesquels les utilisateurs peuvent charger du contenu pour le site. La classe d'annuaires Microsoft® .NET fournit l'interface de programmation d'application (API) pour créer des répertoires sur le système de fichiers.
Directory.CreateDirectory(parentPath + "\\" + directoryName);
Le stockage de contenu nécessite des autorisations spécifiques configurées pour permettre aux utilisateurs de gérer leur propre contenu. Le fragment de code suivant montre comment définir des autorisations d'annuaire à l'aide du code managé en C# :
public static bool AddDirectorySecurity(string directoryPath, string userAccount, FileSystemRights rights, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType controlType)
{
try
{
// Create a new DirectoryInfo object.
DirectoryInfo dInfo = new DirectoryInfo(directoryPath);
// Get a DirectorySecurity object that represents the
// current security settings.
DirectorySecurity dSecurity = dInfo.GetAccessControl();
// Add the FileSystemAccessRule to the security settings.
dSecurity.AddAccessRule(new FileSystemAccessRule(userAccount, rights, inheritanceFlags, propagationFlags, controlType));
// Set the new access settings.
dInfo.SetAccessControl(dSecurity);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Le fragment de code suivant montre comment définir le quota de disque à l'aide du code managé si le quota de disque est restreint. Pour utiliser la gestion des quotas de disque, vous devez ajouter une référence au composant de gestion des quotas de disque Windows® ; elle réside sous Windows\system32\dskquota.dll.
public static bool AddUserDiskQuota(string userName, double quota, double quotaThreshold, string diskVolume)
{
try
{
DiskQuotaControlClass diskQuotaCtrl = GetDiskQuotaControl(diskVolume);
diskQuotaCtrl.UserNameResolution = UserNameResolutionConstants.dqResolveNone;
DIDiskQuotaUser diskUser = diskQuotaCtrl.AddUser(userName);
diskUser.QuotaLimit = quota;
diskUser.QuotaThreshold = quotaThreshold;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Créer un pool d'applications
Un pool d'applications définit les paramètres d'un processus de travail qui héberge une ou plusieurs applications Internet Information Services 7 (IIS 7), qui effectuent leur traitement des demandes. Le pool d'applications est une unité d'isolation des processus, car tout le traitement des demandes pour une application s'exécute dans les processus de travail de son pool d'applications.
Un pool d'applications fournit également une isolation, qui à son tour fournit une sécurité. Chaque pool d'applications peut s'exécuter avec une identité unique et peut utiliser une liste de contrôle d'accès (ACL) pour empêcher les applications d'autres pools d'accéder à ses ressources. Le fragment de code suivant illustre la création d'un pool d'applications, la définition de l'identité et la définition des propriétés.
public static bool CreateApplicationPool(string applicationPoolName, ProcessModelIdentityType identityType, string applicationPoolIdentity, string password, string managedRuntimeVersion, bool autoStart, bool enable32BitAppOnWin64,ManagedPipelineMode managedPipelineMode, long queueLength, TimeSpan idleTimeout, long periodicRestartPrivateMemory, TimeSpan periodicRestartTime)
{
try
{
if (identityType == ProcessModelIdentityType.SpecificUser)
{
if (string.IsNullOrEmpty(applicationPoolName))
throw new ArgumentNullException("applicationPoolName", "CreateApplicationPool: applicationPoolName is null or empty.");
if (string.IsNullOrEmpty(applicationPoolIdentity))
throw new ArgumentNullException("applicationPoolIdentity", "CreateApplicationPool: applicationPoolIdentity is null or empty.");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password", "CreateApplicationPool: password is null or empty.");
}
using (ServerManager mgr = new ServerManager())
{
ApplicationPool newAppPool = mgr.ApplicationPools.Add(applicationPoolName);
if (identityType == ProcessModelIdentityType.SpecificUser)
{
newAppPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
newAppPool.ProcessModel.UserName = applicationPoolIdentity;
newAppPool.ProcessModel.Password = password;
}
else
{
newAppPool.ProcessModel.IdentityType = identityType;
}
if (!string.IsNullOrEmpty(managedRuntimeVersion))
newAppPool.ManagedRuntimeVersion = managedRuntimeVersion;
newAppPool.AutoStart = autoStart;
newAppPool.Enable32BitAppOnWin64 = enable32BitAppOnWin64;
newAppPool.ManagedPipelineMode = managedPipelineMode;
if (queueLength > 0)
newAppPool.QueueLength = queueLength;
if (idleTimeout != TimeSpan.MinValue)
newAppPool.ProcessModel.IdleTimeout = idleTimeout;
if (periodicRestartPrivateMemory > 0)
newAppPool.Recycling.PeriodicRestart.PrivateMemory = periodicRestartPrivateMemory;
if (periodicRestartTime != TimeSpan.MinValue)
newAppPool.Recycling.PeriodicRestart.Time = periodicRestartTime;
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Créer un site
Un site est le conteneur logique de niveau supérieur qui spécifie la façon dont les requêtes HTTP sont reçues et traitées. Un site définit un groupe de liaisons qui déterminent la façon dont le site écoute les requêtes entrantes, et un site contient les définitions d'applications ou de répertoires virtuels qui partitionnent l'espace de noms d'URL du site dans le but de structurer le contenu de l'application.
Le fragment de code suivant montre comment créer un site. Les espaces de noms Microsoft.Web.Administration sont nécessaires lors de l'implémentation de l'objet ServerManager. Notez que la collection d'applications, la collection de sites et les objets de liaison sont tous accessibles via l'objet ServerManager.
public static bool CreateWebSite(string siteName)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "CreateWebSite: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
Site newSite = mgr.Sites.CreateElement();
//get site id
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
mgr.Sites.Add(newSite);
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}
Créer une liaison
Une liaison combine le nom du protocole et les informations de liaison spécifiques au protocole. Alors que IIS 7 prend en charge les liaisons multi-protocole (telles que Windows® Communication Foundation [WCF] SOAP-TCP et FTP), le code suivant utilise uniquement le chemin HTTP. Une liaison HTTP définit efficacement un point de terminaison HTTP qui écoute une adresse IP d'interface spécifique (ou toutes les interfaces), un numéro de port spécifique ou un en-tête d'hôte HTTP spécifique (ou tous les en-têtes d'hôte). Ainsi, vous pouvez configurer de nombreux sites sur votre serveur, qui écoutent sur différentes adresses IP, différents ports ou sur la même adresse IP ou le même port, mais avec des en-têtes d'hôte différents.
public static bool AddSiteBinding(string siteName, string ipAddress, string tcpPort, string hostHeader, string protocol)
{
try
{
if (string.IsNullOrEmpty(siteName))
{
throw new ArgumentNullException("siteName", "AddSiteBinding: siteName is null or empty.");
}
//get the server manager instance
using (ServerManager mgr = new ServerManager())
{
SiteCollection sites = mgr.Sites;
Site site = mgr.Sites[siteName];
if (site != null)
{
string bind = ipAddress + ":" + tcpPort + ":" + hostHeader;
//check the binding exists or not
foreach (Binding b in site.Bindings)
{
if (b.Protocol == protocol && b.BindingInformation == bind)
{
throw new Exception("A binding with the same ip, port and host header already exists.");
}
}
Binding newBinding = site.Bindings.CreateElement();
newBinding.Protocol = protocol;
newBinding.BindingInformation = bind;
site.Bindings.Add(newBinding);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Créer une application racine
Une application est un conteneur logique pour la fonctionnalité de site Web, ce qui vous permet de diviser l'espace de noms d'URL de site en parties distinctes et de contrôler le comportement d'exécution de chaque partie individuellement.
Par exemple, vous pouvez configurer chaque application pour qu'elle réside dans un pool d'applications distinct. Cela permet d'isoler une application des autres applications en la plaçant dans un processus distinct et, éventuellement, en faisant en sorte que ce processus s'exécute avec une identité Windows différente pour le mettre dans un sand-box.
public static bool AddApplication(string siteName, string applicationPath, string applicationPool, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddApplication: siteName is null or empty.");
if (string.IsNullOrEmpty(applicationPath))
throw new ArgumentNullException("applicationPath", "AddApplication: application path is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("PhysicalPath", "AddApplication: Invalid physical path.");
if (string.IsNullOrEmpty(applicationPool))
throw new ArgumentNullException("ApplicationPool", "AddApplication: application pool namespace is Nullable or empty.");
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPool];
if (appPool == null)
throw new Exception("Application Pool: " + applicationPool + " does not exist.");
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[applicationPath];
if (app != null)
throw new Exception("Application: " + applicationPath + " already exists.");
else
{
app = site.Applications.CreateElement();
app.Path = applicationPath;
app.ApplicationPoolName = applicationPool;
VirtualDirectory vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
site.Applications.Add(app);
mgr.CommitChanges();
return true;
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Créer un répertoire virtuel
Un répertoire virtuel mappe une partie de l'espace de noms d'URL de l'application à un emplacement physique sur le disque. Lorsqu'une requête est acheminée vers l'application, elle utilise le même algorithme pour rechercher le répertoire virtuel avec le chemin virtuel le plus long correspondant au reste du chemin absolu de la requête après le chemin d'accès de l'application.
public static bool AddVirtualDirectory(string siteName, string application, string virtualDirectoryPath, string physicalPath, string userName, string password)
{
try
{
if (string.IsNullOrEmpty(siteName))
throw new ArgumentNullException("siteName", "AddVirtualDirectory: siteName is null or empty.");
if (string.IsNullOrEmpty(application))
throw new ArgumentNullException("application", "AddVirtualDirectory: application is null or empty.");
if (string.IsNullOrEmpty(virtualDirectoryPath))
throw new ArgumentNullException("virtualDirectoryPath", "AddVirtualDirectory: virtualDirectoryPath is null or empty.");
if (string.IsNullOrEmpty(physicalPath))
throw new ArgumentNullException("physicalPath", "AddVirtualDirectory: physicalPath is null or empty.");
using (ServerManager mgr = new ServerManager())
{
Site site = mgr.Sites[siteName];
if (site != null)
{
Application app = site.Applications[application];
if (app != null)
{
VirtualDirectory vDir = app.VirtualDirectories[virtualDirectoryPath];
if (vDir != null)
{
throw new Exception("Virtual Directory: " + virtualDirectoryPath + " already exists.");
}
else
{
vDir = app.VirtualDirectories.CreateElement();
vDir.Path = virtualDirectoryPath;
vDir.PhysicalPath = physicalPath;
if (!string.IsNullOrEmpty(userName))
{
if (string.IsNullOrEmpty(password))
throw new Exception("Invalid Virtual Directory User Account Password.");
else
{
vDir.UserName = userName;
vDir.Password = password;
}
}
app.VirtualDirectories.Add(vDir);
}
mgr.CommitChanges();
return true;
}
else
throw new Exception("Application: " + application + " does not exist.");
}
else
throw new Exception("Site: " + siteName + " does not exist.");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
Créer un site FTP
Un site FTP permet aux clients de charger du contenu sur leur site Web et de déplacer des fichiers sur Internet. Le client peut gérer à la fois le contenu et l'accès. La création d'un site FTP est similaire à celle d'un site Web : un pool d'applications et un site sont créés avec une application racine et un répertoire virtuel, puis une liaison FTP est appliquée.
public static bool CreateFtpSite(string applicationPoolName,string siteName, string domainName, string userName, string password,string contentPath, string ipAddress, string tcpPort, string hostHeader)
{
try
{
//provision the application pool
using (ServerManager mgr = new ServerManager())
{
ApplicationPool appPool = mgr.ApplicationPools[applicationPoolName];
//per IIS7 team recommendation, we always create a new application pool
//create new application pool
if (appPool == null)
{
appPool = mgr.ApplicationPools.Add(applicationPoolName);
//set the application pool attribute
appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
appPool.ProcessModel.UserName = domainName + "\\" + userName;
appPool.ProcessModel.Password = password;
}
//if the appPool is null, we throw an exception. The appPool should be created or already exists.
if (appPool == null)
throw new Exception("Invalid Application Pool.");
//if the site already exists, throw an exception
if (mgr.Sites[siteName] != null)
throw new Exception("Site already exists.");
//create site
Site newSite = mgr.Sites.CreateElement();
newSite.Id = GenerateNewSiteID(mgr, siteName);
newSite.SetAttributeValue("name", siteName);
newSite.ServerAutoStart = true;
mgr.Sites.Add(newSite);
//create the default application for the site
Application newApp = newSite.Applications.CreateElement();
newApp.SetAttributeValue("path", "/"); //set to default root path
newApp.SetAttributeValue("applicationPool", applicationPoolName);
newSite.Applications.Add(newApp);
//create the default virtual directory
VirtualDirectory newVirtualDirectory = newApp.VirtualDirectories.CreateElement();
newVirtualDirectory.SetAttributeValue("path", "/");
newVirtualDirectory.SetAttributeValue("physicalPath", contentPath);
newApp.VirtualDirectories.Add(newVirtualDirectory);
//add the bindings
Binding binding = newSite.Bindings.CreateElement();
binding.SetAttributeValue("protocol", "ftp");
binding.SetAttributeValue("bindingInformation", ipAddress + ":" + tcpPort + ":" + hostHeader);
newSite.Bindings.Add(binding);
//commit the changes
mgr.CommitChanges();
}
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return true;
}