start new user store
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
{
|
||||
public class BackofficeConfiguration : IBackofficeConfiguration
|
||||
{
|
||||
public string DefaultLanguage { get; set; }
|
||||
|
||||
|
||||
public string SystemUserId { get; set; }
|
||||
|
||||
public FoldersConfig Folders { get; set; }
|
||||
@@ -13,6 +16,9 @@
|
||||
|
||||
public interface IBackofficeConfiguration
|
||||
{
|
||||
string DefaultLanguage { get; set; }
|
||||
|
||||
|
||||
string SystemUserId { get; set; }
|
||||
|
||||
FoldersConfig Folders { get; set; }
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace zero.Core.Entities
|
||||
public List<string> RoleIds { get; set; } = new List<string>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<IBackofficeUserClaim> Claims { get; set; } = new List<IBackofficeUserClaim>();
|
||||
public List<BackofficeUserClaim> Claims { get; set; } = new List<BackofficeUserClaim>();
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace zero.Core.Entities
|
||||
/// <summary>
|
||||
/// The user's claims, for use in claims-based authentication.
|
||||
/// </summary>
|
||||
List<IBackofficeUserClaim> Claims { get; set; }
|
||||
List<BackofficeUserClaim> Claims { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace zero.Core.Entities
|
||||
public Claim ToClaim() => new Claim(Type, Value);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IBackofficeUserClaim FromClaim(Claim other)
|
||||
public BackofficeUserClaim FromClaim(Claim other)
|
||||
{
|
||||
Type = other?.Type;
|
||||
Value = other?.Value;
|
||||
@@ -39,7 +39,7 @@ namespace zero.Core.Entities
|
||||
/// <summary>
|
||||
/// Constructs a new claim with the type and value
|
||||
/// </summary>
|
||||
IBackofficeUserClaim FromClaim(Claim other);
|
||||
BackofficeUserClaim FromClaim(Claim other);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes by copying ClaimType and ClaimValue from the other claim
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace zero.Core.Entities
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<IBackofficeUserClaim> Claims { get; set; } = new List<IBackofficeUserClaim>();
|
||||
public List<BackofficeUserClaim> Claims { get; set; } = new List<BackofficeUserClaim>();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,6 @@ namespace zero.Core.Entities
|
||||
/// <summary>
|
||||
/// The user's claims, for use in claims-based authentication.
|
||||
/// </summary>
|
||||
List<IBackofficeUserClaim> Claims { get; set; }
|
||||
List<BackofficeUserClaim> Claims { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using FluentValidation.Results;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace zero.Core.Entities
|
||||
{
|
||||
[DataContract(Name = "result", Namespace = "")]
|
||||
public class EntityChangeResult<T>
|
||||
{
|
||||
[DataMember(Name = "model")]
|
||||
public T Model { get; set; }
|
||||
|
||||
[DataMember(Name = "success")]
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
[DataMember(Name = "errors")]
|
||||
public IList<EntityChangeResultError> Errors { get; set; } = new List<EntityChangeResultError>();
|
||||
|
||||
public EntityChangeResult() { }
|
||||
|
||||
public EntityChangeResult(ValidationResult validation)
|
||||
{
|
||||
IsSuccess = validation.IsValid;
|
||||
Errors = validation.Errors.Select(x => new EntityChangeResultError()
|
||||
{
|
||||
Property = x.PropertyName,
|
||||
Message = x.ErrorMessage
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public void AddError(string property, string message)
|
||||
{
|
||||
IsSuccess = false;
|
||||
Errors.Add(new EntityChangeResultError()
|
||||
{
|
||||
Property = property,
|
||||
Message = message
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public EntityChangeResult<T> Combine(EntityChangeResult<T> with)
|
||||
{
|
||||
if (IsSuccess && !with.IsSuccess)
|
||||
{
|
||||
IsSuccess = false;
|
||||
}
|
||||
foreach (EntityChangeResultError error in with.Errors)
|
||||
{
|
||||
Errors.Add(error);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public static EntityChangeResult<T> Success() => new EntityChangeResult<T>() { IsSuccess = true };
|
||||
|
||||
public static EntityChangeResult<T> Success(T model) => new EntityChangeResult<T>() { IsSuccess = true, Model = model };
|
||||
|
||||
public static EntityChangeResult<T> Fail() => new EntityChangeResult<T>() { };
|
||||
|
||||
public static EntityChangeResult<T> Fail(string property, string message)
|
||||
{
|
||||
EntityChangeResult<T> result = new EntityChangeResult<T>();
|
||||
result.AddError(property, message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static EntityChangeResult<T> Fail(ValidationResult validation) => new EntityChangeResult<T>(validation);
|
||||
|
||||
public static EntityChangeResult<T> Fail(EntityChangeResultError error) => Fail(error.Property, error.Message);
|
||||
}
|
||||
|
||||
[DataContract(Name = "resultError", Namespace = "")]
|
||||
public class EntityChangeResultError
|
||||
{
|
||||
[DataMember(Name = "property")]
|
||||
public string Property { get; set; }
|
||||
|
||||
[DataMember(Name = "message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using FluentValidation;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace zero.Core.Extensions
|
||||
{
|
||||
public static class ValidatorExtensions
|
||||
{
|
||||
private const char DOT = '.';
|
||||
|
||||
private const char KLAMMERAFFE = '@';
|
||||
|
||||
private static string HEX_REGEX = "#[0-9a-fA-F]{3,8}";
|
||||
|
||||
/// <summary>
|
||||
/// Validate a color input as HEX (#aabbccdd or #aabbcc or #abc)
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Hex<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.Matches(HEX_REGEX).WithMessage("@errors/format_hex");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validate an email
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Email<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.Must((root, value, context) =>
|
||||
{
|
||||
if (value.IsNullOrWhiteSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int index = value.IndexOf(KLAMMERAFFE);
|
||||
|
||||
if (index < 0 || index == value.Length - 1 || index != value.LastIndexOf(KLAMMERAFFE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).WithMessage("@errors/invalid_mail");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validate one or multiple emails
|
||||
/// </summary>
|
||||
public static IRuleBuilderOptions<T, string> Emails<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.Must((root, value, context) =>
|
||||
{
|
||||
if (value.IsNullOrWhiteSpace())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string[] mails = value.Split(',', ';').Select(x => x.Trim()).Where(x => !x.IsNullOrWhiteSpace()).ToArray();
|
||||
|
||||
if (!mails.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string mail in mails)
|
||||
{
|
||||
int index = value.IndexOf(KLAMMERAFFE);
|
||||
|
||||
if (index < 0 || index == value.Length - 1 || index != value.LastIndexOf(KLAMMERAFFE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}).WithMessage("@errors/invalid_mails");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using FluentValidation.Results;
|
||||
using Raven.Client.Documents;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Validation;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Identity
|
||||
{
|
||||
public class BackofficeUserStore<TUser, TRole>
|
||||
where TUser : IBackofficeUser, new()
|
||||
where TRole : IBackofficeUserRole, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
protected IDocumentStore Raven { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration
|
||||
/// </summary>
|
||||
protected IBackofficeConfiguration Config { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently logged-in user
|
||||
/// </summary>
|
||||
protected IBackofficeUser Current { get; private set; }
|
||||
|
||||
|
||||
public BackofficeUserStore(IDocumentStore raven, IBackofficeConfiguration config, IBackofficeUser currentUser)
|
||||
{
|
||||
Raven = raven;
|
||||
Config = config;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new backoffice user
|
||||
/// </summary>
|
||||
public async Task<EntityChangeResult<TUser>> Add(TUser model, CancellationToken token = default)
|
||||
{
|
||||
// set default language
|
||||
if (model.LanguageId.IsNullOrEmpty())
|
||||
{
|
||||
model.LanguageId = Config.DefaultLanguage;
|
||||
}
|
||||
|
||||
// validate model
|
||||
ValidationResult validation = await new BackofficeUserValidator(isCreate: true).ValidateAsync(model);
|
||||
|
||||
if (!validation.IsValid)
|
||||
{
|
||||
return EntityChangeResult<TUser>.Fail(validation);
|
||||
}
|
||||
|
||||
return EntityChangeResult<TUser>.Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentValidation;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Validation
|
||||
{
|
||||
public class BackofficeUserValidator : AbstractValidator<IBackofficeUser>
|
||||
{
|
||||
public BackofficeUserValidator(bool isCreate = false)
|
||||
{
|
||||
RuleFor(x => x.Name).NotEmpty();
|
||||
RuleFor(x => x.Email).Email();
|
||||
RuleFor(x => x.PasswordHash).NotEmpty();
|
||||
RuleFor(x => x.LanguageId).NotEmpty(); // TODO only allow available languages
|
||||
RuleFor(x => x.RoleIds).NotEmpty();
|
||||
|
||||
if (isCreate)
|
||||
{
|
||||
RuleFor(x => x.IsSuper).Equals(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" />
|
||||
<PackageReference Include="RavenDB.Client" Version="4.2.101" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user