fix identity stuff + add validator extensions + localizer updates

This commit is contained in:
2022-12-12 15:06:29 +01:00
parent 1909746405
commit 430de2ae54
16 changed files with 133 additions and 80 deletions
-1
View File
@@ -17,6 +17,5 @@ public static partial class Constants
public static partial class Database
{
public const string ReservationPrefix = "zero.";
public const string Expires = Raven.Client.Constants.Documents.Metadata.Expires;
}
}
+3 -1
View File
@@ -21,7 +21,9 @@ public class ZeroIdentityConstants
public static readonly string IsZero = ClaimPrefix + ".iszero";
public static readonly string UserId = ClaimPrefix + ".userid";
public static readonly string UserName = ClaimPrefix + ".username";
public static readonly string Username = ClaimPrefix + ".username";
public static readonly string Name = ClaimPrefix + ".name";
public static readonly string Nickname = ClaimPrefix + ".nickname";
public static readonly string Role = ClaimPrefix + ".rolealias";
public static readonly string SecurityStamp = ClaimPrefix + ".securitystamp";
public static readonly string Email = ClaimPrefix + ".email";
+1 -1
View File
@@ -106,7 +106,7 @@ public static class ZeroIdentityExtensions
services.Configure<IdentityOptions>(opts =>
{
opts.ClaimsIdentity.UserIdClaimType = ZeroIdentityConstants.Claims.UserId;
opts.ClaimsIdentity.UserNameClaimType = ZeroIdentityConstants.Claims.UserName;
opts.ClaimsIdentity.UserNameClaimType = ZeroIdentityConstants.Claims.Username;
opts.ClaimsIdentity.RoleClaimType = ZeroIdentityConstants.Claims.Role;
opts.ClaimsIdentity.SecurityStampClaimType = ZeroIdentityConstants.Claims.SecurityStamp;
opts.ClaimsIdentity.EmailClaimType = ZeroIdentityConstants.Claims.Email;
+6 -23
View File
@@ -1,7 +1,4 @@
using Microsoft.AspNetCore.Identity;
using Raven.Client.Documents;
using Raven.Client.Documents.Linq;
using Raven.Client.Exceptions;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using zero.Identity;
@@ -71,18 +68,11 @@ public class ZeroRoleStore<TRole> :
/// <inheritdoc/>
public async Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken)
{
try
{
Result<TRole> result = await Db.Update(role);
Result<TRole> result = await Db.Update(role);
if (!result.IsSuccess)
{
return Fail(result);
}
}
catch (ConcurrencyException)
if (!result.IsSuccess)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
return Fail(result);
}
return IdentityResult.Success;
}
@@ -91,18 +81,11 @@ public class ZeroRoleStore<TRole> :
/// <inheritdoc/>
public async Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken)
{
try
{
Result<TRole> result = await Db.Delete(role);
Result<TRole> result = await Db.Delete(role);
if (!result.IsSuccess)
{
return Fail(result);
}
}
catch (ConcurrencyException)
if (!result.IsSuccess)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
return Fail(result);
}
return IdentityResult.Success;
}
+3 -13
View File
@@ -1,10 +1,7 @@
using FluentValidation.Results;
using Microsoft.AspNetCore.Identity;
using Raven.Client.Documents;
using Raven.Client.Documents.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using Raven.Client.Exceptions;
using zero.Identity;
namespace zero.Identity;
@@ -94,18 +91,11 @@ public partial class ZeroUserStore<TUser> :
/// <inheritdoc />
public async Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken)
{
try
{
Result<TUser> result = await Db.Delete(user);
Result<TUser> result = await Db.Delete(user);
if (!result.IsSuccess)
{
return Fail(result);
}
}
catch (ConcurrencyException)
if (!result.IsSuccess)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
return Fail(result);
}
return IdentityResult.Success;
}
+1 -3
View File
@@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Identity;
using Raven.Client.Documents;
using Raven.Client.Documents.Linq;
using zero.Identity;
namespace zero.Identity;
@@ -31,7 +29,7 @@ public partial class ZeroUserStore<TUser, TRole> : ZeroUserStore<TUser>,
/// <inheritdoc />
public async Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
{
return await Db.FindAll<TUser>(x => roleName.In(x.RoleIds), cancellationToken);
return await Db.FindAll<TUser>(x => x.RoleIds.Contains(roleName), cancellationToken);
}
@@ -1,6 +1,5 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
-2
View File
@@ -1,7 +1,5 @@
using FluentValidation;
using Microsoft.Extensions.Logging;
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using System.Globalization;
using System.Security.Claims;
+1 -9
View File
@@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System.Diagnostics;
using System.Diagnostics;
namespace zero.Models;
@@ -65,12 +64,5 @@ public class ZeroEntity : ZeroIdEntity, ISupportsDbConventions, ISupportsRouting
/// Additional properties for this entity
/// </summary>
public Dictionary<string, string> Properties { get; set; } = new();
/// <summary>
/// [Warning] This field is always empty when bound to the database.
/// It is only filled in the app-code for routing.
/// </summary>
[JsonIgnore]
public string Url { get; set; }
}
+15
View File
@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace zero.Mvc;
public class ZeroMvcModule : ZeroModule
{
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddRouting(opts => opts.LowercaseUrls = true);
}
}
+20
View File
@@ -0,0 +1,20 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
namespace zero.Mvc;
public abstract class ZeroPageModel : PageModel
{
/// <summary>
/// Get access to the zero context for this request
/// </summary>
public IZeroContext ZeroContext => _zeroContext ?? (_zeroContext = HttpContext?.RequestServices?.GetService<IZeroContext>());
IZeroContext _zeroContext;
/// <summary>
/// Get access to the localizer
/// </summary>
public ILocalizer Localizer => _localizer ?? (_localizer = HttpContext?.RequestServices?.GetService<ILocalizer>());
ILocalizer _localizer;
}
+5 -5
View File
@@ -1,6 +1,6 @@
using Newtonsoft.Json;
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Reflection;
using System.Text.Json;
namespace zero.Utils;
@@ -16,20 +16,20 @@ public class ObjectCopycat
public static T Clone<T>(T obj)
{
Type type = obj.GetType();
return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(obj), type);
return (T)JsonSerializer.Deserialize(JsonSerializer.Serialize(obj), type);
}
public static T CloneSpecific<T>(T obj)
{
Type type = obj.GetType();
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj));
}
public static bool ContentEquals<T>(T obj1, T obj2)
{
return (obj1 == null && obj2 == null) || (obj1 != null && obj2 != null && JsonConvert.SerializeObject(obj1) == JsonConvert.SerializeObject(obj2));
return (obj1 == null && obj2 == null) || (obj1 != null && obj2 != null && JsonSerializer.Serialize(obj1) == JsonSerializer.Serialize(obj2));
}
@@ -0,0 +1,56 @@
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace zero.Validation;
public static class ModelStateDictionaryExtensions
{
const char DOT = '.';
public static void AddToModelState(this ValidationResult result, ModelStateDictionary modelState, bool convertUnresolvedPropertiesToModel = true, string prefix = "Form", Dictionary<string, string> aliases = default, IEnumerable<string> removePrefixes = default)
{
if (result == null || result.IsValid)
{
return;
}
string[] formProperties = modelState.Keys.ToArray();
foreach (var error in result.Errors)
{
string propertyName = error.PropertyName;
if (propertyName != null && removePrefixes != null && removePrefixes.Any())
{
foreach (var removePrefix in removePrefixes)
{
if (propertyName.StartsWith(removePrefix, StringComparison.InvariantCultureIgnoreCase))
{
propertyName = propertyName.Substring(removePrefix.Length);
break;
}
}
}
string key = prefix.IsNullOrEmpty() ? propertyName : (propertyName.IsNullOrEmpty() ? String.Empty : prefix + DOT + propertyName);
string message = error.ErrorMessage;
bool found = formProperties.Contains(key, StringComparer.InvariantCultureIgnoreCase);
// find property by alias
if (!found && aliases != null && aliases.ContainsKey(propertyName))
{
key = aliases[propertyName];
key = prefix.IsNullOrEmpty() ? key : (key.IsNullOrEmpty() ? String.Empty : prefix + DOT + key);
found = formProperties.Contains(key, StringComparer.InvariantCultureIgnoreCase);
}
// move property errors to model if they are not part of the form
if (!found && convertUnresolvedPropertiesToModel)
{
key = String.Empty;
}
modelState.AddModelError(key, message);
}
}
}
+20 -20
View File
@@ -1,6 +1,6 @@
using FluentValidation;
using Raven.Client.Documents;
using System.Globalization;
using System.Net.Mail;
namespace zero.Validation;
@@ -38,22 +38,7 @@ public static class ValidatorExtensions
/// </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.forms.email_invalid");
return ruleBuilder.Must((root, value, context) => ValidateEmail(value)).WithMessage("@errors.forms.email_invalid");
}
@@ -78,9 +63,7 @@ public static class ValidatorExtensions
foreach (string mail in mails)
{
int index = mail.IndexOf(KLAMMERAFFE);
if (index < 0 || index == mail.Length - 1 || index != mail.LastIndexOf(KLAMMERAFFE))
if (!ValidateEmail(mail))
{
return false;
}
@@ -91,6 +74,23 @@ public static class ValidatorExtensions
}
private static bool ValidateEmail(string value)
{
if (value == null)
{
return false;
}
int index = value.IndexOf(KLAMMERAFFE);
return
index > 0 &&
index != value.Length - 1 &&
index == value.LastIndexOf(KLAMMERAFFE) &&
MailAddress.TryCreate(value, out _);
}
/// <summary>
/// Validates a culture identifier
/// </summary>
+2
View File
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using zero.Mvc;
namespace zero;
@@ -42,6 +43,7 @@ public class ZeroBuilder
new AssemblyDiscovery(Mvc).Execute(_startupOptions.AssemblyDiscoveryRules);
Modules.Add<ZeroCommunicationModule>();
Modules.Add<ZeroMvcModule>();
Modules.Add<ZeroConfigurationModule>();
Modules.Add<ZeroValidationModule>();
Modules.Add<ZeroContextModule>();
-1
View File
@@ -16,7 +16,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="RavenDB.Client" Version="5.4.5" />
<PackageReference Include="FluentValidation" Version="11.4.0" />
<PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.2" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />