From 83cae5f0a0d6a3000be2e52e2fcf8e80966005b4 Mon Sep 17 00:00:00 2001 From: Daniel Stolt Date: Sat, 18 Jul 2015 22:05:39 +0100 Subject: [PATCH] #5536: Removed dependency on IContentManager from FormsAuthenticationService. FormsAuthenticationService now uses IMembershipService instead. Also introduced a NullMembershipService implementation in Orchard.Framework, used only for dependency resolution during setup. Fixes #5536. --- .../Services/MembershipService.cs | 2 + src/Orchard/Orchard.Framework.csproj | 1 + src/Orchard/Security/NullMembershipService.cs | 30 ++++++++++++++ .../Providers/FormsAuthenticationService.cs | 40 ++++++++++--------- 4 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 src/Orchard/Security/NullMembershipService.cs diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs index 4395e0795..6d12087f2 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs @@ -15,8 +15,10 @@ using System.Collections.Generic; using Orchard.Services; using System.Web.Helpers; using Orchard.Environment.Configuration; +using Orchard.Environment.Extensions; namespace Orchard.Users.Services { + [OrchardSuppressDependency("Orchard.Security.NullMembershipService")] public class MembershipService : IMembershipService { private const string PBKDF2 = "PBKDF2"; private const string DefaultHashAlgorithm = PBKDF2; diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 42c3bfa61..765d8e6bb 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -165,6 +165,7 @@ + diff --git a/src/Orchard/Security/NullMembershipService.cs b/src/Orchard/Security/NullMembershipService.cs new file mode 100644 index 000000000..bb3b763e8 --- /dev/null +++ b/src/Orchard/Security/NullMembershipService.cs @@ -0,0 +1,30 @@ +using System; + +namespace Orchard.Security { + /// + /// Provides a default implementation of IMembershipService used only for dependency resolution + /// in a setup context. No members on this implementation will ever be called; at the time when this + /// interface is actually used in a tenant, another implementation is assumed to have suppressed it. + /// + public class NullMembershipService : IMembershipService { + public IUser CreateUser(CreateUserParams createUserParams) { + throw new NotImplementedException(); + } + + public MembershipSettings GetSettings() { + throw new NotImplementedException(); + } + + public IUser GetUser(string username) { + throw new NotImplementedException(); + } + + public void SetPassword(IUser user, string password) { + throw new NotImplementedException(); + } + + public IUser ValidateUser(string userNameOrEmail, string password) { + throw new NotImplementedException(); + } + } +} diff --git a/src/Orchard/Security/Providers/FormsAuthenticationService.cs b/src/Orchard/Security/Providers/FormsAuthenticationService.cs index ca1a82244..346e4059a 100644 --- a/src/Orchard/Security/Providers/FormsAuthenticationService.cs +++ b/src/Orchard/Security/Providers/FormsAuthenticationService.cs @@ -3,16 +3,18 @@ using System.Web; using System.Web.Security; using Orchard.Environment.Configuration; using Orchard.Logging; -using Orchard.ContentManagement; using Orchard.Mvc; using Orchard.Mvc.Extensions; using Orchard.Services; +using Orchard.Utility.Extensions; namespace Orchard.Security.Providers { public class FormsAuthenticationService : IAuthenticationService { + private const int _cookieVersion = 3; + private readonly ShellSettings _settings; private readonly IClock _clock; - private readonly IContentManager _contentManager; + private readonly IMembershipService _membershipService; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ISslSettingsProvider _sslSettingsProvider; private readonly IMembershipValidationService _membershipValidationService; @@ -23,13 +25,13 @@ namespace Orchard.Security.Providers { public FormsAuthenticationService( ShellSettings settings, IClock clock, - IContentManager contentManager, + IMembershipService membershipService, IHttpContextAccessor httpContextAccessor, ISslSettingsProvider sslSettingsProvider, IMembershipValidationService membershipValidationService) { _settings = settings; _clock = clock; - _contentManager = contentManager; + _membershipService = membershipService; _httpContextAccessor = httpContextAccessor; _sslSettingsProvider = sslSettingsProvider; _membershipValidationService = membershipValidationService; @@ -45,12 +47,13 @@ namespace Orchard.Security.Providers { public void SignIn(IUser user, bool createPersistentCookie) { var now = _clock.UtcNow.ToLocalTime(); - - // the cookie user data is {userId};{tenant} - var userData = String.Concat(Convert.ToString(user.Id), ";", _settings.Name); + + // The cookie user data is "{userName.Base64};{tenant}". + // The username is encoded to Base64 to prevent collisions with the ';' seprarator. + var userData = String.Concat(user.UserName.ToBase64(), ";", _settings.Name); var ticket = new FormsAuthenticationTicket( - 1 /*version*/, + _cookieVersion, user.UserName, now, now.Add(ExpirationTimeSpan), @@ -121,29 +124,28 @@ namespace Orchard.Security.Providers { var formsIdentity = (FormsIdentity)httpContext.User.Identity; var userData = formsIdentity.Ticket.UserData ?? ""; - // the cookie user data is {userId};{tenant} + // The cookie user data is {userName.Base64};{tenant}. var userDataSegments = userData.Split(';'); if (userDataSegments.Length < 2) { return null; } - var userDataId = userDataSegments[0]; + var userDataName = userDataSegments[0]; var userDataTenant = userDataSegments[1]; + try { + userDataName = userDataName.FromBase64(); + } + catch { + return null; + } + if (!String.Equals(userDataTenant, _settings.Name, StringComparison.Ordinal)) { return null; } - int userId; - if (!int.TryParse(userDataId, out userId)) { - Logger.Error("User id not a parsable integer"); - return null; - } - - // todo: this issues a sql query for each authenticated request - _signedInUser = _contentManager.Get(userId).As(); - + _signedInUser = _membershipService.GetUser(userDataName); if (_signedInUser == null || !_membershipValidationService.CanAuthenticateWithCookie(_signedInUser)) { return null; }