From f307d6d37e564ff029796fd95606854843cefbba Mon Sep 17 00:00:00 2001 From: Andre Rodrigues Date: Thu, 7 Apr 2011 14:28:19 -0700 Subject: [PATCH 01/12] #17691: Possible to create invalid admin account with Turkish colation --HG-- branch : 1.x --- .../Users/Services/UserServiceTests.cs | 15 +++++++++++ .../Controllers/AdminController.cs | 2 +- .../Modules/Orchard.Users/Migrations.cs | 25 ++++++++++++++++--- .../Services/MembershipService.cs | 22 ++++++++++------ .../Orchard.Users/Services/UserService.cs | 15 ++++++++--- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs index 22b00929e..eb2a0e57f 100644 --- a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs +++ b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Threading; using System.Xml.Linq; using Autofac; using Moq; @@ -122,5 +124,18 @@ namespace Orchard.Tests.Modules.Users.Services { Assert.That(username, Is.EqualTo("foo")); Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); } + + [Test] + public void VerifyUserUnicityTurkishTest() { + CultureInfo turkishCulture = new CultureInfo("tr-TR"); + Thread.CurrentThread.CurrentCulture = turkishCulture; + + // Create user lower case + _membershipService.CreateUser(new CreateUserParams("admin", "66554321", "foo@bar.com", "", "", true)); + _container.Resolve().ContentManager.Flush(); + + // Verify unicity with upper case which with turkish coallition would yeld admin with an i without the dot and therefore generate a different user name + Assert.That(_userService.VerifyUserUnicity("ADMIN", "differentfoo@bar.com"), Is.False); // should fail + } } } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs index b83896b2e..33df35610 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs @@ -245,7 +245,7 @@ namespace Orchard.Users.Controllers { _siteService.GetSiteSettings().As().SuperUser = editModel.UserName; } - user.NormalizedUserName = editModel.UserName.ToLower(); + user.NormalizedUserName = editModel.UserName.ToUpperInvariant(); } } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs index 4175a2dc5..7b041f320 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Migrations.cs @@ -1,15 +1,24 @@ -using Orchard.Data.Migration; +using System.Collections.Generic; +using Orchard.ContentManagement; +using Orchard.Data.Migration; +using Orchard.Users.Models; namespace Orchard.Users { public class UsersDataMigration : DataMigrationImpl { + public UsersDataMigration(IOrchardServices orchardServices) { + Services = orchardServices; + } + + public IOrchardServices Services { get; set; } + public int Create() { SchemaBuilder.CreateTable("UserPartRecord", table => table .ContentPartRecord() .Column("UserName") .Column("Email") - .Column("NormalizedUserName") + .Column("NormalizedUserName", c => c.Unique()) .Column("Password") .Column("PasswordFormat") .Column("HashAlgorithm") @@ -32,7 +41,17 @@ namespace Orchard.Users { .Column("EnableLostPassword", c => c.WithDefault(false)) ); - return 1; + return 2; + } + + public int UpdateFrom1() { + IEnumerable users = Services.ContentManager.Query().List(); + + foreach (UserPart user in users) { + user.NormalizedUserName = user.UserName.ToUpperInvariant(); + } + + return 2; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs index 163685cc1..44936191a 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs @@ -49,7 +49,7 @@ namespace Orchard.Users.Services { user.Record.UserName = createUserParams.Username; user.Record.Email = createUserParams.Email; - user.Record.NormalizedUserName = createUserParams.Username.ToLower(); + user.Record.NormalizedUserName = createUserParams.Username.ToUpperInvariant(); user.Record.HashAlgorithm = "SHA1"; SetPassword(user.Record, createUserParams.Password); @@ -97,18 +97,24 @@ namespace Orchard.Users.Services { } public IUser GetUser(string username) { - var lowerName = username == null ? "" : username.ToLower(); + var higherName = username == null ? "" : username.ToUpperInvariant(); - return _orchardServices.ContentManager.Query().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); + return _orchardServices.ContentManager.Query() + .Where(u => u.NormalizedUserName == higherName).List() + .FirstOrDefault(u => u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)); } public IUser ValidateUser(string userNameOrEmail, string password) { - var lowerName = userNameOrEmail == null ? "" : userNameOrEmail.ToLower(); + var higherName = userNameOrEmail == null ? "" : userNameOrEmail.ToUpperInvariant(); - var user = _orchardServices.ContentManager.Query().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault(); - - if (user == null) - user = _orchardServices.ContentManager.Query().Where(u => u.Email == lowerName).List().FirstOrDefault(); + var user = _orchardServices.ContentManager.Query() + .Where(u => + u.NormalizedUserName == higherName || + u.Email == userNameOrEmail) + .List() + .FirstOrDefault(u => + u.UserName.Equals(userNameOrEmail, StringComparison.OrdinalIgnoreCase) || + u.Email == userNameOrEmail); if ( user == null || ValidatePassword(user.As().Record, password) == false ) return null; diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs index 6b84fb063..90ad4cf19 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs @@ -37,13 +37,16 @@ namespace Orchard.Users.Services { public ILogger Logger { get; set; } public bool VerifyUserUnicity(string userName, string email) { - string normalizedUserName = userName.ToLower(); + string normalizedUserName = userName.ToUpperInvariant(); if (_contentManager.Query() .Where(user => user.NormalizedUserName == normalizedUserName || user.Email == email) - .List().Any()) { + .List() + .Any(user => + user.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase) || + user.Email == email)) { return false; } @@ -51,13 +54,17 @@ namespace Orchard.Users.Services { } public bool VerifyUserUnicity(int id, string userName, string email) { - string normalizedUserName = userName.ToLower(); + string normalizedUserName = userName.ToUpperInvariant(); if (_contentManager.Query() .Where(user => user.NormalizedUserName == normalizedUserName || user.Email == email) - .List().Any(user => user.Id != id)) { + .List() + .Any(user => + (user.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase) || + user.Email == email) && + user.Id != id)) { return false; } From a3be04bdfb995039078263ecdbf9f3ef38374dce Mon Sep 17 00:00:00 2001 From: Andre Rodrigues Date: Thu, 7 Apr 2011 15:01:06 -0700 Subject: [PATCH 02/12] #17553: Using expand to obtain screenshots for the gallery and cut HTTP requests down. --HG-- branch : 1.x --- .../Services/PackagingSourceManager.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs index 2058367c1..535611576 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs @@ -73,22 +73,15 @@ namespace Orchard.Packaging.Services { .SelectMany( source => { var galleryFeedContext = new GalleryFeedContext(new Uri(source.FeedUrl)); - IQueryable packages = galleryFeedContext.Packages; - + IQueryable packages = includeScreenshots + ? galleryFeedContext.Packages.Expand("Screenshots") + : galleryFeedContext.Packages; + if (query != null) { packages = query(packages); } - return packages.ToList().Select( - p => { - PublishedScreenshot firstScreenshot = includeScreenshots - ? galleryFeedContext.Screenshots - .Where(s => s.PublishedPackageId == p.Id && s.PublishedPackageVersion == p.Version) - .ToList() - .FirstOrDefault() - : null; - return CreatePackageEntry(p, firstScreenshot, packagingSource, galleryFeedContext.GetReadStreamUri(p)); - }); + return packages.ToList().Select(p => CreatePackageEntry(p, packagingSource, galleryFeedContext.GetReadStreamUri(p))); } ); } @@ -116,12 +109,14 @@ namespace Orchard.Packaging.Services { #endregion - private static PackagingEntry CreatePackageEntry(PublishedPackage package, PublishedScreenshot screenshot, PackagingSource source, Uri downloadUri) { + private static PackagingEntry CreatePackageEntry(PublishedPackage package, PackagingSource source, Uri downloadUri) { Uri baseUri = new Uri(string.Format("{0}://{1}:{2}/", downloadUri.Scheme, downloadUri.Host, downloadUri.Port)); + PublishedScreenshot screenshot = package.Screenshots.FirstOrDefault(); + string iconUrl = GetAbsoluteUri(package.IconUrl, baseUri); string firstScreenshot = screenshot != null ? GetAbsoluteUri(screenshot.ScreenshotUri, baseUri) : string.Empty; From 294d4fb75a10f1abc407d355d31f66c1fdea80d9 Mon Sep 17 00:00:00 2001 From: Dave Reed Date: Thu, 7 Apr 2011 15:31:11 -0700 Subject: [PATCH 03/12] #17362: Container widget produces
    without identifier of a rendered list --HG-- branch : 1.x --- src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml b/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml index 52d1f8ad9..71aaa0c3d 100644 --- a/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml @@ -1,4 +1,9 @@ -@Display(Model.ContentItems) +@{ + IEnumerable items = Model.ContentItems; + Model.ContentItems.Classes.Add("content-items"); + Model.ContentItems.Classes.Add("list-items"); +} +@Display(items) @if (Model.ShowPager) { @Display(Model.Pager) } \ No newline at end of file From b9378f546fef4ab310d0133e5cce6998362f3e8e Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 7 Apr 2011 15:37:37 -0700 Subject: [PATCH 04/12] Fixing typo in build script --HG-- branch : 1.x --- Orchard.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orchard.proj b/Orchard.proj index 5dfb5a3e7..9aac282cd 100644 --- a/Orchard.proj +++ b/Orchard.proj @@ -388,7 +388,7 @@ - + From 8c336638b3ba8f99fe7658ec8df74266559acadb Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Thu, 7 Apr 2011 15:53:35 -0700 Subject: [PATCH 05/12] Changing the HttpRequestExtensions to use request.Url.Host instead of request.Headers["Host"] ...since that's what WebPages' IsUrlLocalToHost uses for local URL checking. This became an issue in the spec tests where the header didn't match the Url property. --HG-- branch : 1.x --- .../Utility/Extensions/HttpRequestExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs b/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs index 7a6e91d07..9c1b5bb23 100644 --- a/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs +++ b/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs @@ -8,7 +8,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030 /// Prevents port number issues by using the client requested host public static string ToRootUrlString(this HttpRequestBase request) { - return string.Format("{0}://{1}", request.Url.Scheme, request.Headers["Host"]); + return string.Format("{0}://{1}", request.Url.Scheme, request.Url.Host); } /// @@ -17,7 +17,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030 /// Prevents port number issues by using the client requested host public static string ToRootUrlString(this HttpRequest request) { - return string.Format("{0}://{1}", request.Url.Scheme, request.Headers["Host"]); + return string.Format("{0}://{1}", request.Url.Scheme, request.Url.Host); } /// @@ -26,7 +26,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030/OrchardLocal /// Prevents port number issues by using the client requested host public static string ToApplicationRootUrlString(this HttpRequestBase request) { - string url = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.ApplicationPath); + string url = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Host, request.ApplicationPath); return url; } @@ -36,7 +36,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030/OrchardLocal /// Prevents port number issues by using the client requested host public static string ToApplicationRootUrlString(this HttpRequest request) { - string url = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.ApplicationPath); + string url = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Host, request.ApplicationPath); return url; } @@ -46,7 +46,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030/OrchardLocal/Admin/Blogs /// Prevents port number issues by using the client requested host public static string ToUrlString(this HttpRequestBase request) { - return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.RawUrl); + return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Host, request.RawUrl); } /// @@ -55,7 +55,7 @@ namespace Orchard.Utility.Extensions { /// http://localhost:3030/OrchardLocal/Admin/Blogs /// Prevents port number issues by using the client requested host public static string ToUrlString(this HttpRequest request) { - return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.RawUrl); + return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Host, request.RawUrl); } } } \ No newline at end of file From 62fa1da5f2a0a583ab69d4e81ad6281f88db14af Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Thu, 7 Apr 2011 15:58:30 -0700 Subject: [PATCH 06/12] Adding a specflow test for widgets (yeah, just one :|) and making some tweaks to the specflow capabilities to enable the flow required by that test... --HG-- branch : 1.x --- .../Bindings/HtmlNodeExtensions.cs | 11 ++++ src/Orchard.Specs/Bindings/WebAppHosting.cs | 27 +++++---- .../Hosting/RequestExtensions.cs | 11 +++- src/Orchard.Specs/Orchard.Specs.csproj | 1 + src/Orchard.Specs/Widgets.feature | 22 +++++++ src/Orchard.Specs/Widgets.feature.cs | 59 +++++++++++++++++++ 6 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs diff --git a/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs b/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs new file mode 100644 index 000000000..0a8b4281c --- /dev/null +++ b/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs @@ -0,0 +1,11 @@ +using HtmlAgilityPack; + +namespace Orchard.Specs.Bindings { + public static class HtmlNodeExtensions { + public static string GetOptionValue(this HtmlNode node) { + return node.Attributes.Contains("value") + ? node.GetAttributeValue("value", "") + : node.NextSibling != null && node.NextSibling.NodeType == HtmlNodeType.Text ? node.NextSibling.InnerText : ""; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index 08670ec99..a261d505c 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Web; using Castle.Core.Logging; using HtmlAgilityPack; @@ -181,21 +182,21 @@ namespace Orchard.Specs.Bindings { Host.HostName = host; Details = Host.SendRequest(urlPath); _doc = new HtmlDocument(); - _doc.Load(new StringReader(Details.ResponseText)); + _doc.Load(new StringReader(Regex.Replace(Details.ResponseText, @">\s+<", "><"))); } [When(@"I go to ""(.*)""")] public void WhenIGoTo(string urlPath) { Details = Host.SendRequest(urlPath); _doc = new HtmlDocument(); - _doc.Load(new StringReader(Details.ResponseText)); + _doc.Load(new StringReader(Regex.Replace(Details.ResponseText, @">\s+<", "><"))); } [When(@"I follow ""([^""]*)""")] public void WhenIFollow(string linkText) { var link = _doc.DocumentNode .SelectNodes("//a") - .SingleOrDefault(elt => elt.InnerText == linkText) + .SingleOrDefault(elt => elt.InnerHtml == linkText) ?? _doc.DocumentNode .SelectSingleNode(string.Format("//a[@title='{0}']", linkText)); @@ -208,7 +209,7 @@ namespace Orchard.Specs.Bindings { public void WhenIFollow(string linkText, string hrefFilter) { var link = _doc.DocumentNode .SelectNodes("//a[@href]").Where(elt => - (elt.InnerText == linkText || + (elt.InnerHtml == linkText || (elt.Attributes["title"] != null && elt.Attributes["title"].Value == linkText)) && elt.Attributes["href"].Value.IndexOf(hrefFilter, StringComparison.OrdinalIgnoreCase) != -1).SingleOrDefault(); @@ -263,9 +264,9 @@ namespace Orchard.Specs.Bindings { break; default: if (string.Equals(input.Name, "select", StringComparison.OrdinalIgnoreCase)) { - var options = input.ChildNodes; + var options = input.Descendants("option"); foreach (var option in options) { - if (option.GetAttributeValue("value", "") == row["value"]) + if (option.GetAttributeValue("value", "") == row["value"] || (option.NextSibling.NodeType == HtmlNodeType.Text && option.NextSibling.InnerText == row["value"])) option.Attributes.Add("selected", "selected"); else if (option.Attributes.Contains("selected")) option.Attributes.Remove("selected"); @@ -286,7 +287,7 @@ namespace Orchard.Specs.Bindings { .SelectSingleNode(string.Format("(//input[@type='submit'][@value='{0}']|//button[@type='submit'][text()='{0}'])", submitText)); var form = Form.LocateAround(submit); - var urlPath = form.Start.GetAttributeValue("action", Details.UrlPath); + var urlPath = HttpUtility.HtmlDecode(form.Start.GetAttributeValue("action", Details.UrlPath)); var inputs = form.Children @@ -299,18 +300,18 @@ namespace Orchard.Specs.Bindings { // select all