diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs index 69413bc7..418d8c22 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.DirectoryServices.ActiveDirectory; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; using WebsitePanel.Providers.FTP.IIs70; @@ -99,6 +100,20 @@ namespace WebsitePanel.Providers.FTP { get { return ProviderSettings["ADGroupsOU"]; } } + + protected string AdFtpRoot + { + get { return ProviderSettings["AdFtpRoot"]; } + } + + protected Mode UserIsolationMode + { + get + { + var site = GetSite(ProviderSettings["SiteId"]); + return (Mode)Enum.Parse(typeof(Mode), site["UserIsolationMode"]); + } + } #endregion #region IFtpServer Members @@ -291,134 +306,275 @@ namespace WebsitePanel.Providers.FTP this.ftpSitesService.DeleteSite(siteId); } - /// - /// Checks whether account with given name exists. - /// - /// Account name to check. - /// true - if it exists; false - otherwise. + /// + /// Checks whether account with given name exists. + /// + /// Account name to check. + /// true - if it exists; false - otherwise. public bool AccountExists(string accountName) { if (String.IsNullOrEmpty(accountName)) { - return false; + return false; } - // check acocunt on FTP server - bool ftpExists = this.ftpSitesService.VirtualDirectoryExists(this.SiteId, accountName); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + return SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); - // check account in the system - bool systemExists = SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); - return (ftpExists || systemExists); + default: + // check acocunt on FTP server + bool ftpExists = this.ftpSitesService.VirtualDirectoryExists(this.SiteId, accountName); + + // check account in the system + bool systemExists = SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); + return (ftpExists || systemExists); + } } - /// + /// /// Gets available ftp accounts. /// /// List of avaialble accounts. public FtpAccount[] GetAccounts() { - List accounts = new List(); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + return SecurityUtils.GetUsers(ServerSettings, UsersOU).Select(GetAccount).ToArray(); + default: + List accounts = new List(); - foreach (string directory in this.ftpSitesService.GetVirtualDirectoriesNames(this.SiteId)) - { - // Skip root virtual directory - if (String.Equals(directory, "/")) - continue; - // - accounts.Add(this.GetAccount(directory.Substring(1))); - } + foreach (string directory in this.ftpSitesService.GetVirtualDirectoriesNames(this.SiteId)) + { + // Skip root virtual directory + if (String.Equals(directory, "/")) + continue; + // + accounts.Add(this.GetAccount(directory.Substring(1))); + } - return accounts.ToArray(); + return accounts.ToArray(); + } } - /// - /// Gets account with given name. - /// - /// Account's name to get. - /// Ftp account. + /// + /// Gets account with given name. + /// + /// Account's name to get. + /// Ftp account. public FtpAccount GetAccount(string accountName) { - FtpAccount acc = new FtpAccount(); - acc.Name = accountName; - this.FillFtpAccountFromIis(acc); - return acc; + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var user = SecurityUtils.GetUser(accountName, ServerSettings, UsersOU); + + var path = Path.Combine(user.MsIIS_FTPRoot, user.MsIIS_FTPDir); + var permission = GetUserPermission(accountName, path); + var account = new FtpAccount() + { + CanRead = permission.Read, + CanWrite = permission.Write, + CreatedDate = user.CreatedDate, + Enabled = !user.AccountDisabled, + Folder = path, + GroupName = user.GroupName, + Name = user.Name + }; + + return account; + default: + FtpAccount acc = new FtpAccount(); + acc.Name = accountName; + this.FillFtpAccountFromIis(acc); + return acc; + } } - /// + protected UserPermission GetUserPermission(string accountName, string folder) + { + var userPermission = new UserPermission {AccountName = accountName}; + return SecurityUtils.GetGroupNtfsPermissions(folder, new[] {userPermission}, ServerSettings, UsersOU, GroupsOU)[0]; + } + + + /// /// Creates ftp account under root ftp site. /// /// Ftp account to create. public void CreateAccount(FtpAccount account) { - // Create user account. - SystemUser user = new SystemUser(); - user.Name = account.Name; - user.FullName = account.Name; - user.Description = "WebsitePanel System Account"; - user.MemberOf = new string[] { FtpGroupName }; - user.Password = account.Password; - user.PasswordCantChange = true; - user.PasswordNeverExpires = true; - user.AccountDisabled = !account.Enabled; - user.System = true; + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + SecurityUtils.EnsureOrganizationalUnitsExist(ServerSettings, UsersOU, GroupsOU); - // Create in the operating system. - if (SecurityUtils.UserExists(user.Name, ServerSettings, UsersOU)) - { - SecurityUtils.DeleteUser(user.Name, ServerSettings, UsersOU); - } - SecurityUtils.CreateUser(user, ServerSettings, UsersOU, GroupsOU); + var systemUser = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); - // Prepare account's home folder. - this.EnsureUserHomeFolderExists(account.Folder, account.Name, account.CanRead, account.CanWrite); + if (systemUser == null) + { + systemUser = new SystemUser + { + Name = account.Name, + FullName = account.Name, + Password = account.Password, + PasswordCantChange = true, + PasswordNeverExpires = true, + System = true + }; - // Future account will be given virtual directory under default ftp web site. - this.ftpSitesService.CreateFtpAccount(this.SiteId, account); - // - this.ftpSitesService.ConfigureConnectAs(account.Folder, this.SiteId, account.VirtualPath, - this.GetQualifiedAccountName(account.Name), account.Password, true); - } + SecurityUtils.CreateUser(systemUser, ServerSettings, UsersOU, GroupsOU); + } - /// - /// Updates ftp account. - /// - /// Accoun to update. + UpdateAccount(account); + + break; + + default: + // Create user account. + SystemUser user = new SystemUser(); + user.Name = account.Name; + user.FullName = account.Name; + user.Description = "WebsitePanel System Account"; + user.MemberOf = new string[] {FtpGroupName}; + user.Password = account.Password; + user.PasswordCantChange = true; + user.PasswordNeverExpires = true; + user.AccountDisabled = !account.Enabled; + user.System = true; + + // Create in the operating system. + if (SecurityUtils.UserExists(user.Name, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(user.Name, ServerSettings, UsersOU); + } + SecurityUtils.CreateUser(user, ServerSettings, UsersOU, GroupsOU); + + // Prepare account's home folder. + this.EnsureUserHomeFolderExists(account.Folder, account.Name, account.CanRead, account.CanWrite); + + // Future account will be given virtual directory under default ftp web site. + this.ftpSitesService.CreateFtpAccount(this.SiteId, account); + // + this.ftpSitesService.ConfigureConnectAs(account.Folder, this.SiteId, account.VirtualPath, + this.GetQualifiedAccountName(account.Name), account.Password, true); + break; + } + } + + /// + /// Updates ftp account. + /// + /// Accoun to update. public void UpdateAccount(FtpAccount account) { - // Change user account state and password (if required). - SystemUser user = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); - user.Password = account.Password; - user.AccountDisabled = !account.Enabled; - SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); - // Update iis configuration. - this.FillIisFromFtpAccount(account); + var user = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); + + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var ftpRoot = AdFtpRoot.ToLower(); + var ftpDir = account.Folder.ToLower().Replace(ftpRoot, ""); + + var oldDir = user.MsIIS_FTPDir; + + user.Password = account.Password; + user.PasswordCantChange = true; + user.PasswordNeverExpires = true; + user.Description = "WebsitePanel FTP Account with AD User Isolation"; + user.MemberOf = new[] {FtpGroupName}; + user.AccountDisabled = !account.Enabled; + user.MsIIS_FTPRoot = ftpRoot; + user.MsIIS_FTPDir = ftpDir; + user.System = true; + + SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); + + // Set NTFS permissions + var userPermission = GetUserPermission(account.Name, account.Folder); + + // Do we need to change the NTFS permissions? i.e. is users home dir changed or are permissions changed? + if (oldDir != ftpDir || account.CanRead != userPermission.Read || account.CanWrite != userPermission.Write) + { + // First get sid of user account + var sid = SecurityUtils.GetAccountSid(account.Name, ServerSettings, UsersOU, GroupsOU); + + // Remove the permissions set for this account on previous folder + SecurityUtils.RemoveNtfsPermissionsBySid(Path.Combine(ftpRoot, oldDir), sid); + + // If no permissions is to be set, exit + if (!account.CanRead && !account.CanWrite) + { + return; + } + + // Add the new permissions + var ntfsPermissions = account.CanRead ? NTFSPermission.Read : NTFSPermission.Write; + if (account.CanRead && account.CanWrite) + { + ntfsPermissions = NTFSPermission.Modify; + } + + SecurityUtils.GrantNtfsPermissionsBySid(account.Folder, sid, ntfsPermissions, true, true); + } + break; + + default: + + // Change user account state and password (if required). + user.Password = account.Password; + user.AccountDisabled = !account.Enabled; + SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); + // Update iis configuration. + this.FillIisFromFtpAccount(account); + break; + } } - /// - /// Deletes account with given name. - /// - /// Account's name to be deleted. + /// + /// Deletes account with given name. + /// + /// Account's name to be deleted. public void DeleteAccount(string accountName) { - string virtualDirectory = String.Format("/{0}", accountName); - string currentPhysicalPath = this.ftpSitesService.GetSitePhysicalPath(this.SiteId, virtualDirectory); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var account = GetAccount(accountName); - // Delete virtual directory - this.ftpSitesService.DeleteFtpAccount(this.SiteId, virtualDirectory); + // Remove the NTFS permissions first + SecurityUtils.RemoveNtfsPermissions(account.Folder, account.Name, ServerSettings, UsersOU, GroupsOU); - this.ftpSitesService.CommitChanges(); + if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); + } + break; - // Remove permissions - RemoveFtpFolderPermissions(currentPhysicalPath, accountName); + default: + string virtualDirectory = String.Format("/{0}", accountName); + string currentPhysicalPath = this.ftpSitesService.GetSitePhysicalPath(this.SiteId, virtualDirectory); - // Delete system user account - if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) - { - SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); - } + // Delete virtual directory + this.ftpSitesService.DeleteFtpAccount(this.SiteId, virtualDirectory); + + this.ftpSitesService.CommitChanges(); + + // Remove permissions + RemoveFtpFolderPermissions(currentPhysicalPath, accountName); + + // Delete system user account + if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); + } + break; + } } - /// + /// /// Fills iis configuration from ftp account. /// /// Ftp account to fill from. @@ -519,6 +675,7 @@ namespace WebsitePanel.Providers.FTP ftpSite.AllowAnonymous = iisFtpSite.Security.Authentication.AnonymousAuthentication.Enabled; ftpSite.AnonymousUsername = iisFtpSite.Security.Authentication.AnonymousAuthentication.UserName; ftpSite.AnonymousUserPassword = iisFtpSite.Security.Authentication.AnonymousAuthentication.Password; + ftpSite["UserIsolationMode"] = iisFtpSite.UserIsolation.Mode.ToString(); // Logging settings. ftpSite[FtpSite.MSFTP7_SITE_ID] = iisFtpSite.SiteServiceId; if (iisFtpSite.LogFile.Enabled) diff --git a/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs b/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs index a2fcd1a9..27ddaa75 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs +++ b/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs @@ -511,6 +511,8 @@ namespace WebsitePanel.Providers.Utils user.PasswordCantChange = ((userFlags & ADAccountOptions.UF_PASSWD_CANT_CHANGE) != 0); user.PasswordNeverExpires = ((userFlags & ADAccountOptions.UF_DONT_EXPIRE_PASSWD) != 0); user.AccountDisabled = ((userFlags & ADAccountOptions.UF_ACCOUNTDISABLE) != 0); + user.MsIIS_FTPDir = GetObjectProperty(objUser, "msIIS-FTPDir").ToString(); + user.MsIIS_FTPRoot = GetObjectProperty(objUser, "msIIS-FTPRoot").ToString(); // get user groups user.MemberOf = GetUserGroups(objUser); @@ -727,6 +729,12 @@ namespace WebsitePanel.Providers.Utils objUser.Properties["description"].Value = String.IsNullOrEmpty(user.Description) ? "WebsitePanel System Account" : user.Description; + if (user.MsIIS_FTPDir != string.Empty) + { + SetObjectProperty(objUser, "msIIS-FTPDir", user.MsIIS_FTPDir); + SetObjectProperty(objUser, "msIIS-FTPRoot", user.MsIIS_FTPRoot); + } + ADAccountOptions userFlags = ADAccountOptions.UF_NORMAL_ACCOUNT; if (user.PasswordCantChange) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx index d6149f97..4021bbe7 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx @@ -112,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Yes @@ -138,4 +138,7 @@ <Select FTP Site> + + FTP RootDir: + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx index 831772d8..bb35b384 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx @@ -1,45 +1,61 @@ <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MSFTP70_Settings.ascx.cs" Inherits="WebsitePanel.Portal.ProviderControls.MSFTP70_Settings" %> <%@ Register Src="Common_ActiveDirectoryIntegration.ascx" TagName="ActiveDirectoryIntegration" TagPrefix="uc1" %> <%@ Register Src="../UserControls/SelectIPAddress.ascx" TagName="SelectIPAddress" TagPrefix="uc1" %> - - - - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs index 9caf61df..71c18d09 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs @@ -27,10 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; +using System.Collections.Generic; using System.Data; using System.Configuration; using System.Collections; using System.Collections.Specialized; +using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; @@ -51,7 +53,8 @@ namespace WebsitePanel.Portal.ProviderControls { int selectedAddressid = this.FindAddressByText(settings["SharedIP"]); ipAddress.AddressId = (selectedAddressid > 0) ? selectedAddressid : 0; - txtSiteId.Text = settings["SiteId"]; + BindSiteId(settings); + txtAdFtpRoot.Text = settings["AdFtpRoot"]; txtFtpGroupName.Text = settings["FtpGroupName"]; chkBuildUncFilesPath.Checked = Utils.ParseBool(settings["BuildUncFilesPath"], false); ActiveDirectoryIntegration.BindSettings(settings); @@ -75,7 +78,11 @@ namespace WebsitePanel.Portal.ProviderControls { settings["SharedIP"] = String.Empty; } - settings["SiteId"] = txtSiteId.Text.Trim(); + settings["SiteId"] = ddlSite.SelectedValue; + if (!string.IsNullOrWhiteSpace(txtAdFtpRoot.Text)) + { + settings["AdFtpRoot"] = txtAdFtpRoot.Text.Trim(); + } settings["FtpGroupName"] = txtFtpGroupName.Text.Trim(); settings["BuildUncFilesPath"] = chkBuildUncFilesPath.Checked.ToString(); ActiveDirectoryIntegration.SaveSettings(settings); @@ -92,5 +99,31 @@ namespace WebsitePanel.Portal.ProviderControls } return 0; } + + private void BindSiteId(StringDictionary settings) + { + var sites = ES.Services.FtpServers.GetFtpSites(PanelRequest.ServiceId); + + foreach (var site in sites) + { + var item = new ListItem(site.Name + " (User Isolation Mode: " + site["UserIsolationMode"] + ")", site.Name); + + if (item.Value == settings["SiteId"]) + { + item.Selected = true; + } + + ddlSite.Items.Add(item); + } + + ddlSite_SelectedIndexChanged(this, null); + } + + protected void ddlSite_SelectedIndexChanged(object sender, EventArgs e) + { + var isActiveDirectoryUserIsolated = ddlSite.SelectedItem.Text.Contains("ActiveDirectory"); + FtpRootRow.Visible = isActiveDirectoryUserIsolated; + txtAdFtpRootReqValidator.Enabled= isActiveDirectoryUserIsolated; + } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs index 596afc56..b4f88c2a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs @@ -1,22 +1,15 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.1378 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ namespace WebsitePanel.Portal.ProviderControls { - /// - /// MSFTP70_Settings class. - /// - /// - /// Auto-generated class. - /// public partial class MSFTP70_Settings { /// @@ -47,13 +40,49 @@ namespace WebsitePanel.Portal.ProviderControls { protected global::System.Web.UI.WebControls.Label lblSite; /// - /// txtSiteId control. + /// ddlSite control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.TextBox txtSiteId; + protected global::System.Web.UI.WebControls.DropDownList ddlSite; + + /// + /// FtpRootRow control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlTableRow FtpRootRow; + + /// + /// lblAdFtpRoot control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAdFtpRoot; + + /// + /// txtAdFtpRoot control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtAdFtpRoot; + + /// + /// txtAdFtpRootReqValidator control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator txtAdFtpRootReqValidator; /// /// lblGroupName control.