Member 2FA (#11889)

* Bugfix - Take ufprt from form data if the request has form content type, otherwise fallback to use the query

* External linking for members

* Changed migration to reuse old table

* removed unnecessary web.config files

* Cleanup

* Extracted class to own file

* Clean up

* Rollback changes to Umbraco.Web.UI.csproj

* Fixed migration for SqlCE

* Added 2fa for members

* Change notification handler to be on deleted

* Update src/Umbraco.Infrastructure/Security/MemberUserStore.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* updated snippets

* Fixed issue with errors not shown on member linking

* fixed issue with errors

* clean up

* Fix issue where external logins could not be used to upgrade Umbraco, because the externalLogin table was expected to look different. (Like after the migration)

* Fixed issue in Ignore legacy column now using result column.

* Updated 2fa for members + publish notification when 2fa is requested.

* Changed so only Members out of box supports 2fa

* Cleanup

* rollback of csproj file, that should not have been changed

* Removed confirmed flag from db. It was not used.

Handle case where a user is signed up for 2fa, but the provider do not exist anymore. Then it is just ignored until it shows up again

Reintroduced ProviderName on interface, to ensure the class can be renamed safely

* Bugfix

* Registering DeleteTwoFactorLoginsOnMemberDeletedHandler

* Rollback nuget packages added by mistake

* Update src/Umbraco.Infrastructure/Services/Implement/TwoFactorLoginService.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* Update src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TwoFactorLoginRepository.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* Added providername to snippet

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
Bjarke Berg
2022-01-21 13:10:34 +01:00
committed by GitHub
parent 52a3e285a9
commit 12abd883a9
36 changed files with 1044 additions and 57 deletions

View File

@@ -0,0 +1,33 @@
using System;
using NPoco;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
{
[TableName(TableName)]
[ExplicitColumns]
[PrimaryKey("Id")]
internal class TwoFactorLoginDto
{
public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.TwoFactorLogin;
[Column("id")]
[PrimaryKeyColumn]
public int Id { get; set; }
[Column("userOrMemberKey")]
[Index(IndexTypes.NonClustered)]
public Guid UserOrMemberKey { get; set; }
[Column("providerName")]
[Length(400)]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, ForColumns = "providerName,userOrMemberKey", Name = "IX_" + TableName + "_ProviderName")]
public string ProviderName { get; set; }
[Column("secret")]
[Length(400)]
[NullSetting(NullSetting = NullSettings.NotNull)]
public string Secret { get; set; }
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Persistence.Querying;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
internal class TwoFactorLoginRepository : EntityRepositoryBase<int, ITwoFactorLogin>, ITwoFactorLoginRepository
{
public TwoFactorLoginRepository(IScopeAccessor scopeAccessor, AppCaches cache,
ILogger<TwoFactorLoginRepository> logger)
: base(scopeAccessor, cache, logger)
{
}
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
{
var sql = SqlContext.Sql();
sql = isCount
? sql.SelectCount()
: sql.Select<TwoFactorLoginDto>();
sql.From<TwoFactorLoginDto>();
return sql;
}
protected override string GetBaseWhereClause() =>
Core.Constants.DatabaseSchema.Tables.TwoFactorLogin + ".id = @id";
protected override IEnumerable<string> GetDeleteClauses() => Enumerable.Empty<string>();
protected override ITwoFactorLogin PerformGet(int id)
{
var sql = GetBaseQuery(false).Where<TwoFactorLoginDto>(x => x.Id == id);
var dto = Database.Fetch<TwoFactorLoginDto>(sql).FirstOrDefault();
return dto == null ? null : Map(dto);
}
protected override IEnumerable<ITwoFactorLogin> PerformGetAll(params int[] ids)
{
var sql = GetBaseQuery(false).WhereIn<TwoFactorLoginDto>(x => x.Id, ids);
var dtos = Database.Fetch<TwoFactorLoginDto>(sql);
return dtos.WhereNotNull().Select(Map);
}
protected override IEnumerable<ITwoFactorLogin> PerformGetByQuery(IQuery<ITwoFactorLogin> query)
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<ITwoFactorLogin>(sqlClause, query);
var sql = translator.Translate();
return Database.Fetch<TwoFactorLoginDto>(sql).Select(Map);
}
protected override void PersistNewItem(ITwoFactorLogin entity)
{
var dto = Map(entity);
Database.Insert(dto);
}
protected override void PersistUpdatedItem(ITwoFactorLogin entity)
{
var dto = Map(entity);
Database.Update(dto);
}
private static TwoFactorLoginDto Map(ITwoFactorLogin entity)
{
if (entity == null) return null;
return new TwoFactorLoginDto
{
Id = entity.Id,
UserOrMemberKey = entity.UserOrMemberKey,
ProviderName = entity.ProviderName,
Secret = entity.Secret,
};
}
private static ITwoFactorLogin Map(TwoFactorLoginDto dto)
{
if (dto == null) return null;
return new TwoFactorLogin
{
Id = dto.Id,
UserOrMemberKey = dto.UserOrMemberKey,
ProviderName = dto.ProviderName,
Secret = dto.Secret,
};
}
public async Task<bool> DeleteUserLoginsAsync(Guid userOrMemberKey)
{
return await DeleteUserLoginsAsync(userOrMemberKey, null);
}
public async Task<bool> DeleteUserLoginsAsync(Guid userOrMemberKey, string providerName)
{
var sql = Sql()
.Delete()
.From<TwoFactorLoginDto>()
.Where<TwoFactorLoginDto>(x => x.UserOrMemberKey == userOrMemberKey);
if (providerName is not null)
{
sql = sql.Where<TwoFactorLoginDto>(x => x.ProviderName == providerName);
}
var deletedRows = await Database.ExecuteAsync(sql);
return deletedRows > 0;
}
public async Task<IEnumerable<ITwoFactorLogin>> GetByUserOrMemberKeyAsync(Guid userOrMemberKey)
{
var sql = Sql()
.Select<TwoFactorLoginDto>()
.From<TwoFactorLoginDto>()
.Where<TwoFactorLoginDto>(x => x.UserOrMemberKey == userOrMemberKey);
var dtos = await Database.FetchAsync<TwoFactorLoginDto>(sql);
return dtos.WhereNotNull().Select(Map);
}
}
}