From b8971f0dd1545349dc14e314cf9755ee3a5e9bcd Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 31 Jul 2017 18:15:30 +1000 Subject: [PATCH] decouple user repo from the membershi providers, lets see what happens with unit tests on the server --- .../Repositories/UserRepository.cs | 147 ++++++++++-------- .../Persistence/RepositoryFactory.cs | 11 +- 2 files changed, 89 insertions(+), 69 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 3582038d04..204e17aff7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -26,12 +26,23 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class UserRepository : PetaPocoRepositoryBase, IUserRepository { - //private readonly CacheHelper _cacheHelper; - - public UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax) + private readonly IDictionary _passwordConfiguration; + + /// + /// Constructor + /// + /// + /// + /// + /// + /// + /// A dictionary specifying the configuration for user passwords. If this is null then no password configuration will be persisted or read. + /// + public UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax, + IDictionary passwordConfiguration = null) : base(work, cacheHelper, logger, sqlSyntax) { - //_cacheHelper = cacheHelper; + _passwordConfiguration = passwordConfiguration; } #region Overrides of RepositoryBase @@ -46,8 +57,8 @@ namespace Umbraco.Core.Persistence.Repositories .OrderBy(d => d.Id, SqlSyntax); var dto = Database.Fetch(new UserGroupRelator().Map, sql) - .FirstOrDefault(); - + .FirstOrDefault(); + if (dto == null) return null; @@ -127,7 +138,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = GetBaseQuery("umbracoUser.*"); sql.Where(GetBaseWhereClause(), new { Id = id }); - dto = Database.FirstOrDefault(sql); + dto = Database.FirstOrDefault(sql); } if (dto == null) @@ -139,14 +150,14 @@ namespace Umbraco.Core.Persistence.Repositories public IProfile GetProfile(string username) { - var sql = GetBaseQuery(false).Where(userDto => userDto.UserName == username, SqlSyntax); - + var sql = GetBaseQuery(false).Where(userDto => userDto.UserName == username, SqlSyntax); + var dto = Database.Fetch(sql) .FirstOrDefault(); if (dto == null) - return null; - + return null; + return new UserProfile(dto.Id, dto.UserName); } @@ -176,8 +187,8 @@ UNION SELECT '5CountOfInvited' AS colName, COUNT(id) AS num FROM umbracoUser WHERE lastLoginDate IS NULL AND userDisabled = 1 AND invitedDate IS NOT NULL ORDER BY colName"; - var result = Database.Fetch(sql); - + var result = Database.Fetch(sql); + return new Dictionary { {UserState.All, result[0].num}, @@ -193,7 +204,7 @@ ORDER BY colName"; var sql = GetQueryWithGroups(); if (ids.Any()) { - sql.Where("umbracoUser.id in (@ids)", new {ids = ids}); + sql.Where("umbracoUser.id in (@ids)", new { ids = ids }); } sql //must be included for relator to work .OrderBy(d => d.Id, SqlSyntax) @@ -201,11 +212,11 @@ ORDER BY colName"; .OrderBy(d => d.Id, SqlSyntax); var users = ConvertFromDtos(Database.Fetch(new UserGroupRelator().Map, sql)) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. - + .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + return users; - } - + } + protected override IEnumerable PerformGetByQuery(IQuery query) { var sqlClause = GetQueryWithGroups(); @@ -220,15 +231,15 @@ ORDER BY colName"; .DistinctBy(x => x.Id); var users = ConvertFromDtos(dtos) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. - + .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + return users; - } - + } + #endregion - + #region Overrides of PetaPocoRepositoryBase - + protected override Sql GetBaseQuery(bool isCount) { var sql = new Sql(); @@ -298,26 +309,25 @@ ORDER BY colName"; protected override Guid NodeObjectTypeId { get { throw new NotImplementedException(); } - } - + } + protected override void PersistNewItem(IUser entity) { ((User)entity).AddingEntity(); - - //ensure security stamp if non + + //ensure security stamp if non if (entity.SecurityStamp.IsNullOrWhiteSpace()) { entity.SecurityStamp = Guid.NewGuid().ToString(); - } - + } + var userDto = UserFactory.BuildDto(entity); //Check if we have a known config, we only want to store config for hashing - //TODO: This logic will need to be updated when we do http://issues.umbraco.org/issue/U4-10089 - var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); - if (membershipProvider.PasswordFormat == MembershipPasswordFormat.Hashed) + //TODO: This logic will need to be updated when we do http://issues.umbraco.org/issue/U4-10089 + if (_passwordConfiguration != null && _passwordConfiguration.Count > 0) { - var json = JsonConvert.SerializeObject(new { hashAlgorithm = Membership.HashAlgorithmType }); + var json = JsonConvert.SerializeObject(_passwordConfiguration); userDto.PasswordConfig = json; } @@ -341,8 +351,8 @@ ORDER BY colName"; //lookup all assigned var assigned = entity.Groups == null || entity.Groups.Any() == false ? new List() - : Database.Fetch("SELECT * FROM umbracoUserGroup WHERE userGroupAlias IN (@aliases)", new { aliases = entity.Groups.Select(x => x.Alias) }); - + : Database.Fetch("SELECT * FROM umbracoUserGroup WHERE userGroupAlias IN (@aliases)", new { aliases = entity.Groups.Select(x => x.Alias) }); + foreach (var groupDto in assigned) { var dto = new User2UserGroupDto @@ -360,18 +370,18 @@ ORDER BY colName"; protected override void PersistUpdatedItem(IUser entity) { //Updates Modified date - ((User)entity).UpdatingEntity(); - - //ensure security stamp if non + ((User)entity).UpdatingEntity(); + + //ensure security stamp if non if (entity.SecurityStamp.IsNullOrWhiteSpace()) { entity.SecurityStamp = Guid.NewGuid().ToString(); } - var userDto = UserFactory.BuildDto(entity); - - //build list of columns to check for saving - we don't want to save the password if it hasn't changed! - //List the columns to save, NOTE: would be nice to not have hard coded strings here but no real good way around that + var userDto = UserFactory.BuildDto(entity); + + //build list of columns to check for saving - we don't want to save the password if it hasn't changed! + //List the columns to save, NOTE: would be nice to not have hard coded strings here but no real good way around that var colsToSave = new Dictionary() { {"userDisabled", "IsApproved"}, @@ -379,8 +389,8 @@ ORDER BY colName"; {"startStructureID", "StartContentId"}, {"startMediaID", "StartMediaId"}, {"userName", "Name"}, - {"userLogin", "Username"}, - {"userEmail", "Email"}, + {"userLogin", "Username"}, + {"userEmail", "Email"}, {"userLanguage", "Language"}, {"securityStampToken", "SecurityStamp"}, {"lastLockoutDate", "LastLockoutDate"}, @@ -416,11 +426,12 @@ ORDER BY colName"; //Check if we have a known config, we only want to store config for hashing //TODO: This logic will need to be updated when we do http://issues.umbraco.org/issue/U4-10089 - var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); - if (membershipProvider.PasswordFormat == MembershipPasswordFormat.Hashed) + if (_passwordConfiguration != null && _passwordConfiguration.Count > 0) { - var json = JsonConvert.SerializeObject(new { hashAlgorithm = Membership.HashAlgorithmType }); + var json = JsonConvert.SerializeObject(_passwordConfiguration); userDto.PasswordConfig = json; + + changedCols.Add("passwordConfig"); } } @@ -475,7 +486,7 @@ ORDER BY colName"; //remove the ones not assigned to the entity var toDelete = assignedIds.Except(entityStartIds).ToArray(); if (toDelete.Length > 0) - Database.Delete("WHERE UserId = @UserId AND startNode IN (@startNodes)", new {UserId = entity.Id, startNodes = toDelete}); + Database.Delete("WHERE UserId = @UserId AND startNode IN (@startNodes)", new { UserId = entity.Id, startNodes = toDelete }); //add the ones not currently in the db var toAdd = entityStartIds.Except(assignedIds).ToArray(); foreach (var i in toAdd) @@ -515,8 +526,8 @@ ORDER BY colName"; .Where(x => x.UserName == username); return Database.ExecuteScalar(sql) > 0; - } - + } + /// /// Gets a list of objects associated with a given group /// @@ -566,8 +577,8 @@ ORDER BY colName"; var mappedField = mapper.Map(expressionMember.Name); if (mappedField.IsNullOrWhiteSpace()) - throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); - + throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); + long tr; var results = GetPagedResultsByQuery(query, Convert.ToInt64(pageIndex), pageSize, out tr, mappedField, Direction.Ascending); totalRecords = Convert.ToInt32(tr); @@ -603,13 +614,13 @@ ORDER BY colName"; throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, mappedField, orderDirection, userGroups, userState, filter); - } - - - - private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, - string[] userGroups = null, - UserState[] userState = null, + } + + + + private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, + string[] userGroups = null, + UserState[] userState = null, IQuery filter = null) { if (string.IsNullOrWhiteSpace(orderBy)) throw new ArgumentException("Value cannot be null or whitespace.", "orderBy"); @@ -633,7 +644,7 @@ ORDER BY colName"; INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId WHERE umbracoUserGroup.userGroupAlias IN (@userGroups)))"; - filterSql.Append(subQuery, new {userGroups= userGroups}); + filterSql.Append(subQuery, new { userGroups = userGroups }); } if (userState != null && userState.Length > 0) { @@ -671,12 +682,12 @@ ORDER BY colName"; } // Get base query for returning IDs - var sqlBaseIds = GetBaseQuery("id"); - + var sqlBaseIds = GetBaseQuery("id"); + if (query == null) query = new Query(); var translatorIds = new SqlTranslator(sqlBaseIds, query); var sqlQueryIds = translatorIds.Translate(); - + //get sorted and filtered sql var sqlNodeIdsWithSort = GetSortedSqlForPagedResults( GetFilteredSqlForPagedResults(sqlQueryIds, filterSql), @@ -696,11 +707,11 @@ ORDER BY colName"; string sqlStringCount, sqlStringPage; Database.BuildPageQueries(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage); - var sqlQueryFull = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); - + var sqlQueryFull = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); + var fullQueryWithPagedInnerJoin = sqlQueryFull - .Append("INNER JOIN (") - //join the paged query with the paged query arguments + .Append("INNER JOIN (") + //join the paged query with the paged query arguments .Append(sqlStringPage, args) .Append(") temp ") .Append("ON umbracoUser.id = temp.id"); diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index d976a53cd1..34a4b1a612 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -1,6 +1,7 @@ using Umbraco.Core.Configuration; using System; using System.ComponentModel; +using System.Web.Security; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -9,6 +10,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Security; namespace Umbraco.Core.Persistence { @@ -314,11 +316,18 @@ namespace Umbraco.Core.Persistence public virtual IUserRepository CreateUserRepository(IScopeUnitOfWork uow) { + var userMembershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); + var passwordConfig = userMembershipProvider == null || userMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed + ? null + : new System.Collections.Generic.Dictionary {{"hashAlgorithm", Membership.HashAlgorithmType}}; + return new UserRepository( uow, //Need to cache users - we look up user information more than anything in the back office! _cacheHelper, - _logger, _sqlSyntax); + _logger, + _sqlSyntax, + passwordConfig); } internal virtual IMacroRepository CreateMacroRepository(IScopeUnitOfWork uow)