(refactor): dataprovider iqueriable and selector
This commit is contained in:
parent
7033f00664
commit
9e59ca7f30
@ -9,7 +9,6 @@ namespace Core.Abstractions.Models {
|
||||
|
||||
|
||||
public abstract class RequestModelBase<T> : ModelBase, IValidatableObject {
|
||||
public abstract T ToDomainObject();
|
||||
public abstract IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,14 +6,16 @@ using MongoDB.Driver.GridFS;
|
||||
|
||||
using DomainResults.Common;
|
||||
using Extensions;
|
||||
using DataProviders.Buckets;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
namespace DataProviders.Abstractions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
|
||||
|
||||
private readonly GridFSBucket _bucket;
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@ using MongoDB.Driver;
|
||||
using DomainResults.Common;
|
||||
|
||||
using DomainObjects.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DataProviders.Abstractions {
|
||||
|
||||
@ -35,9 +37,13 @@ namespace DataProviders.Abstractions {
|
||||
ISessionService sessionService,
|
||||
string databaseName,
|
||||
string collectionName
|
||||
) : base (logger, client, databaseName) {
|
||||
) : base(logger, client, databaseName) {
|
||||
_idGenerator = idGenerator;
|
||||
_sessionService = sessionService;
|
||||
|
||||
if (!_database.ListCollectionNames().ToList().Contains(collectionName))
|
||||
_database.CreateCollection(collectionName);
|
||||
|
||||
_collection = _database.GetCollection<T>(collectionName);
|
||||
}
|
||||
|
||||
@ -54,7 +60,7 @@ namespace DataProviders.Abstractions {
|
||||
public Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid sessionId) =>
|
||||
InsertAsync(obj, sessionId);
|
||||
|
||||
private async Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid? sessionId) {
|
||||
protected virtual async Task<(Guid?, IDomainResult)> InsertAsync(T obj, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj);
|
||||
@ -83,7 +89,7 @@ namespace DataProviders.Abstractions {
|
||||
private protected Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid sessionId) =>
|
||||
InsertManyAsync(objList, sessionId);
|
||||
|
||||
private async Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid? sessionId) {
|
||||
protected virtual async Task<(List<Guid>?, IDomainResult)> InsertManyAsync(List<T> objList, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList);
|
||||
@ -99,26 +105,91 @@ namespace DataProviders.Abstractions {
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
GetWithPredicate(predicate, 0, 0);
|
||||
#region Count
|
||||
private protected (int?, IDomainResult) CountWithPredicate(Expression<Func<T, bool>> predicate) =>
|
||||
CountWithPredicate(new List<Expression<Func<T, bool>>> { predicate });
|
||||
|
||||
private protected (List<T>?, IDomainResult) GetWithPredicate(Expression<Func<T, bool>> predicate, int skip, int limit) {
|
||||
private protected (int?, IDomainResult) CountWithPredicate(List<Expression<Func<T, bool>>> predicates) {
|
||||
try {
|
||||
var result = _collection
|
||||
.Find(predicate).Skip(skip).Limit(limit).ToList();
|
||||
var query = GetWithPredicate(predicates);
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<T>?>();
|
||||
var result = query.Count();
|
||||
|
||||
return IDomainResult.Success(result);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<T>?>();
|
||||
return IDomainResult.Failed<int?>();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicate"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicate }, selector, null, null);
|
||||
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(Expression<Func<T, bool>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit) =>
|
||||
GetWithPredicate(new List<Expression<Func<T, bool>>> { predicates }, selector, skip, limit);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="predicates"></param>
|
||||
/// <param name="selector"></param>
|
||||
/// <param name="skip"></param>
|
||||
/// <param name="limit"></param>
|
||||
/// <returns></returns>
|
||||
private protected (List<TResult>?, IDomainResult) GetWithPredicate<TResult>(List<Expression<Func<T, bool>>> predicates, Expression<Func<T, TResult>> selector, int? skip, int? limit) {
|
||||
try {
|
||||
var query = GetWithPredicate(predicates).Select(selector);
|
||||
|
||||
if (skip != null)
|
||||
query = query.Skip(skip.Value);
|
||||
|
||||
if (limit != null)
|
||||
query = query.Take(limit.Value);
|
||||
|
||||
var result = query.ToList();
|
||||
|
||||
return result != null && result.Count > 0
|
||||
? IDomainResult.Success(result)
|
||||
: IDomainResult.NotFound<List<TResult>?>();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
_logger.LogError(ex, "Data provider error");
|
||||
return IDomainResult.Failed<List<TResult>?>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="predicates"></param>
|
||||
/// <returns></returns>
|
||||
private protected IQueryable<T> GetWithPredicate(List<Expression<Func<T, bool>>> predicates) {
|
||||
var query = GetQuery();
|
||||
|
||||
foreach (var predicate in predicates)
|
||||
query = query.Where(predicate);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IQueryable<T> GetQuery() => _collection.AsQueryable();
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression<Func<T, bool>> predicate) =>
|
||||
UpdateWithPredicateAsync(obj, predicate).Result;
|
||||
@ -132,7 +203,7 @@ namespace DataProviders.Abstractions {
|
||||
private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
UpdateWithPredicateAsync(obj, predicate, sessionId);
|
||||
|
||||
private async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
protected virtual async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
|
||||
|
||||
@ -163,7 +234,7 @@ namespace DataProviders.Abstractions {
|
||||
private protected Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
private async Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
protected virtual async Task<IDomainResult> DeleteWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
if (sessionId != null)
|
||||
await _collection.DeleteOneAsync<T>(_sessionService.GetSession(sessionId.Value), predicate);
|
||||
@ -192,7 +263,7 @@ namespace DataProviders.Abstractions {
|
||||
private protected Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid sessionId) =>
|
||||
DeleteManyWithPredicateAsync(predicate, sessionId);
|
||||
|
||||
private async Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
protected virtual async Task<IDomainResult> DeleteManyWithPredicateAsync(Expression<Func<T, bool>> predicate, Guid? sessionId) {
|
||||
try {
|
||||
|
||||
if (sessionId != null)
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders {
|
||||
public class BucketFile {
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
public Guid SiteId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
public DateTime? Published { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
public byte[] Bytes { get; private set; }
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, DateTime? published, string name, byte[] bytes, string contentType) {
|
||||
Id = id;
|
||||
SiteId = siteId;
|
||||
UserId = userId;
|
||||
|
||||
Published = published;
|
||||
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, string name, byte[] bytes, string contentType)
|
||||
: this(id, siteId, userId, null, name, bytes, contentType) { }
|
||||
|
||||
}
|
||||
}
|
||||
39
src/DataProviders/Buckets/BucketFile.cs
Normal file
39
src/DataProviders/Buckets/BucketFile.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders.Buckets
|
||||
{
|
||||
public class BucketFile
|
||||
{
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
public Guid SiteId { get; private set; }
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
public DateTime? Published { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
public byte[] Bytes { get; private set; }
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, DateTime? published, string name, byte[] bytes, string contentType)
|
||||
{
|
||||
Id = id;
|
||||
SiteId = siteId;
|
||||
UserId = userId;
|
||||
|
||||
Published = published;
|
||||
|
||||
Name = name;
|
||||
Bytes = bytes;
|
||||
ContentType = contentType;
|
||||
}
|
||||
|
||||
public BucketFile(Guid id, Guid siteId, Guid userId, string name, byte[] bytes, string contentType)
|
||||
: this(id, siteId, userId, null, name, bytes, contentType) { }
|
||||
|
||||
}
|
||||
}
|
||||
@ -7,12 +7,13 @@ using MongoDB.Driver;
|
||||
using DataProviders.Abstractions;
|
||||
using MongoDB.Driver.GridFS;
|
||||
|
||||
namespace DataProviders.Buckets {
|
||||
namespace DataProviders.Buckets
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IImageBucketDataProvider {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IImageBucketDataProvider {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@ -36,7 +36,7 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (BlogDocument?, IDomainResult) Get(Guid siteId, Guid blogId) {
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == blogId);
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == blogId, x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
@ -54,13 +54,13 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (List<BlogDocument>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)));
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), x => x);
|
||||
|
||||
public (List<BlogDocument>?, IDomainResult) GetMany(Guid siteId, List<Guid> blogIds) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && blogIds.Contains(x.Id));
|
||||
GetWithPredicate(x => x.SiteId == siteId && blogIds.Contains(x.Id), x => x);
|
||||
|
||||
public (List<BlogDocument>?, IDomainResult) GetAll(Guid siteId, int skip, int take) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId);
|
||||
GetWithPredicate(x => x.SiteId == siteId, x => x);
|
||||
|
||||
public (Guid?, IDomainResult) Update(BlogDocument blogItem) =>
|
||||
UpdateWithPredicate(blogItem, x => x.Id == blogItem.Id);
|
||||
|
||||
@ -37,7 +37,7 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (Guid?, IDomainResult) CreateDefault(Guid siteId) {
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => y.Slug == "default"));
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => y.Slug == "default"), x => x);
|
||||
|
||||
if (result.IsSuccess && list != null)
|
||||
return (list.First().Id, result);
|
||||
@ -55,7 +55,7 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (CategoryDocument?, IDomainResult) Get(Guid siteId, Guid categoryId) {
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == categoryId);
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == categoryId, x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
@ -64,7 +64,7 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (List<CategoryDocument>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && categoryIds.Contains(x.Id));
|
||||
GetWithPredicate(x => x.SiteId == siteId && categoryIds.Contains(x.Id), x => x);
|
||||
|
||||
public (CategoryDocument?, IDomainResult) GetBySlug(Guid siteId, string slug) {
|
||||
var (list, result) = GetBySlugs(siteId, new List<string> { slug });
|
||||
@ -76,10 +76,10 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (List<CategoryDocument>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)));
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), x => x);
|
||||
|
||||
public (List<CategoryDocument>?, IDomainResult) GetAll(Guid siteId) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId);
|
||||
GetWithPredicate(x => x.SiteId == siteId, x => x);
|
||||
|
||||
public (Guid?, IDomainResult) Update(CategoryDocument obj) =>
|
||||
UpdateWithPredicate(obj, x => x.Id == obj.Id);
|
||||
|
||||
30
src/DataProviders/Collections/CompositeDataProvider.cs
Normal file
30
src/DataProviders/Collections/CompositeDataProvider.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using DomainObjects.Documents.Users;
|
||||
using DomainResults.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataProviders.Collections {
|
||||
|
||||
public interface ICompositeDataProvider {
|
||||
}
|
||||
|
||||
public class CompositeDataProvider : ICompositeDataProvider {
|
||||
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
|
||||
public CompositeDataProvider(
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) {
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_userDataProvider = userDataProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,6 @@ namespace DataProviders.Collections {
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) {
|
||||
}
|
||||
|
||||
public (List<ContentDocument>?, IDomainResult) Get(Guid siteId) => GetWithPredicate(x => x.SiteId == siteId);
|
||||
public (List<ContentDocument>?, IDomainResult) Get(Guid siteId) => GetWithPredicate(x => x.SiteId == siteId, x => x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,10 +33,10 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (List<ShopCartDocument>?, IDomainResult) GetAll(Guid siteId, Guid userId) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.UserId == userId, 0, 0);
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.UserId == userId, x => x, 0, 0);
|
||||
|
||||
public (ShopCartDocument?, IDomainResult) Get(Guid siteId, Guid userId, string sku) {
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.UserId == userId && x.Sku == sku, 0, 0);
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.UserId == userId && x.Sku == sku, x => x, 0, 0);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
@ -33,7 +33,7 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (ShopDocument?, IDomainResult) Get(Guid siteId, string sku) {
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Sku == sku);
|
||||
var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Sku == sku, x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
@ -51,10 +51,10 @@ namespace DataProviders.Collections {
|
||||
}
|
||||
|
||||
public (List<ShopDocument>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)));
|
||||
GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), x => x);
|
||||
|
||||
public (List<ShopDocument>?, IDomainResult) GetAll(Guid siteId, int skip, int take) =>
|
||||
GetWithPredicate(x => x.SiteId == siteId, skip, take);
|
||||
GetWithPredicate(x => x.SiteId == siteId, x => x, skip, take);
|
||||
|
||||
public (Guid?, IDomainResult) Update(ShopDocument shopCart) =>
|
||||
UpdateWithPredicate(shopCart, x => x.Id == shopCart.Id);
|
||||
|
||||
43
src/DataProviders/Collections/SiteDataProvider.cs
Normal file
43
src/DataProviders/Collections/SiteDataProvider.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
using DomainObjects.Documents.Sites;
|
||||
using DomainResults.Common;
|
||||
using DataProviders.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DataProviders.Collections;
|
||||
|
||||
public interface ISiteDataProvider {
|
||||
(Guid?, IDomainResult) Insert(Site site);
|
||||
|
||||
(Site?, IDomainResult) GetByHostName(string hostName);
|
||||
}
|
||||
|
||||
public class SiteDataProvider : CollectionDataProviderBase<Site>, ISiteDataProvider {
|
||||
|
||||
private const string _databaseName = "reactredux";
|
||||
private const string _collectionName = "sites";
|
||||
|
||||
public SiteDataProvider(
|
||||
ILogger<SiteDataProvider> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) { }
|
||||
|
||||
public (Site?, IDomainResult) GetByHostName(string hostName) {
|
||||
var (list, result) = GetWithPredicate(x => x.Hosts.Contains(hostName), x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
if (list.Count > 0)
|
||||
return IDomainResult.Failed<Site?>();
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,77 +4,64 @@ using DataProviders.Abstractions;
|
||||
|
||||
using DomainResults.Common;
|
||||
|
||||
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
using DomainObjects.Documents.User;
|
||||
using System.Runtime.CompilerServices;
|
||||
using DomainObjects.Documents.Users;
|
||||
|
||||
namespace DataProviders.Collections
|
||||
{
|
||||
namespace DataProviders.Collections;
|
||||
|
||||
public interface IUserDataProvider {
|
||||
(UserDocument?, IDomainResult) Get(Guid userId);
|
||||
public interface IUserDataProvider {
|
||||
(Guid?, IDomainResult) Insert(User user);
|
||||
(User?, IDomainResult) Get(Guid userId);
|
||||
|
||||
(UserDocument?, IDomainResult) GetByUserIdAndHost(Guid userId, string host);
|
||||
(User?, IDomainResult) GetByUsername(string nickName);
|
||||
|
||||
(UserDocument?, IDomainResult) GetByUsername(string nickName);
|
||||
(User?, IDomainResult) GetByRecoveryToken(string recoveryToken);
|
||||
|
||||
(UserDocument?, IDomainResult) GetByRecoveryToken(string recoveryToken);
|
||||
|
||||
(Guid?, IDomainResult) Update(UserDocument user);
|
||||
}
|
||||
|
||||
public class UserDataProvider : CollectionDataProviderBase<UserDocument>, IUserDataProvider {
|
||||
|
||||
private const string _databaseName = "reactredux";
|
||||
private const string _collectionName = "users";
|
||||
|
||||
public UserDataProvider(
|
||||
ILogger<UserDataProvider> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) {
|
||||
}
|
||||
|
||||
public (UserDocument?, IDomainResult) Get(Guid userId) {
|
||||
var (list, result) = GetWithPredicate(x => x.Id == userId);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (UserDocument?, IDomainResult) GetByUserIdAndHost(Guid userId, string host) {
|
||||
var (list, result) = GetWithPredicate(x => x.Id == userId && x.Sites.Any(x => x.Hosts.Contains(host)));
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (UserDocument?, IDomainResult) GetByUsername(string username) {
|
||||
var (list, result) = GetWithPredicate(x => x.Username == username);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (UserDocument?, IDomainResult) GetByRecoveryToken(string recoveryToken) {
|
||||
var (list, result) = GetWithPredicate(x => x.Authentication.Password != null && x.Authentication.Password.RecoveryTokens.Any(y => !(DateTime.UtcNow > y.Expires) && y.Value == recoveryToken));
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (Guid?, IDomainResult) Update(UserDocument user) =>
|
||||
UpdateWithPredicate(user, x => x.Id == user.Id);
|
||||
|
||||
}
|
||||
(Guid?, IDomainResult) Update(User user);
|
||||
}
|
||||
|
||||
public class UserDataProvider : CollectionDataProviderBase<User>, IUserDataProvider {
|
||||
|
||||
private const string _databaseName = "reactredux";
|
||||
private const string _collectionName = "users";
|
||||
|
||||
public UserDataProvider(
|
||||
ILogger<UserDataProvider> logger,
|
||||
IMongoClient client,
|
||||
IIdGenerator idGenerator,
|
||||
ISessionService sessionService) : base(logger, client, idGenerator, sessionService, _databaseName, _collectionName) {
|
||||
}
|
||||
|
||||
public (User?, IDomainResult) Get(Guid userId) {
|
||||
var (list, result) = GetWithPredicate(x => x.Id == userId, x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (User?, IDomainResult) GetByUsername(string username) {
|
||||
var (list, result) = GetWithPredicate(x => x.Username == username, x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (User?, IDomainResult) GetByRecoveryToken(string recoveryToken) {
|
||||
var (list, result) = GetWithPredicate(x => x.Authentication.Password != null && x.Authentication.Password.RecoveryTokens.Any(y => !(DateTime.UtcNow > y.Expires) && y.Value == recoveryToken), x => x);
|
||||
|
||||
if (!result.IsSuccess || list == null)
|
||||
return (null, result);
|
||||
|
||||
return (list.First(), result);
|
||||
}
|
||||
|
||||
public (Guid?, IDomainResult) Update(User user) =>
|
||||
UpdateWithPredicate(user, x => x.Id == user.Id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -28,9 +28,14 @@ namespace DataProviders.Extensions
|
||||
services.AddSingleton<IShopCartDataProvider, ShopCartDataProvider>();
|
||||
services.AddSingleton<ICategoryDataProvider, CategoryDataProvider>();
|
||||
services.AddSingleton<IBlogCatalogDataProvider, BlogCatalogDataProvider>();
|
||||
services.AddSingleton<ISiteDataProvider, SiteDataProvider>();
|
||||
services.AddSingleton<IUserDataProvider, UserDataProvider>();
|
||||
#endregion
|
||||
|
||||
#region Composite Collections
|
||||
services.AddSingleton<ICompositeDataProvider, CompositeDataProvider>();
|
||||
#endregion
|
||||
|
||||
#region Buckets
|
||||
services.AddSingleton<IImageBucketDataProvider, ImageBucketDataProvider>();
|
||||
#endregion
|
||||
|
||||
@ -10,7 +10,8 @@ using DomainObjects.L10n;
|
||||
using DomainObjects.Enumerations;
|
||||
using DomainObjects.Pages;
|
||||
using DomainObjects.Documents;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using DomainObjects.Documents.Sites;
|
||||
|
||||
namespace DataProviders
|
||||
{
|
||||
@ -335,8 +336,8 @@ namespace DataProviders
|
||||
#endregion
|
||||
|
||||
#region User
|
||||
if (!BsonClassMap.IsClassMapRegistered(typeof(Site))) {
|
||||
BsonClassMap.RegisterClassMap<Site>(cm => {
|
||||
if (!BsonClassMap.IsClassMapRegistered(typeof(SiteRole))) {
|
||||
BsonClassMap.RegisterClassMap<SiteRole>(cm => {
|
||||
cm.AutoMap();
|
||||
|
||||
cm.GetMemberMap(c => c.Role)
|
||||
@ -377,8 +378,8 @@ namespace DataProviders
|
||||
});
|
||||
}
|
||||
|
||||
if (!BsonClassMap.IsClassMapRegistered(typeof(UserDocument))) {
|
||||
BsonClassMap.RegisterClassMap<UserDocument>(cm => {
|
||||
if (!BsonClassMap.IsClassMapRegistered(typeof(User))) {
|
||||
BsonClassMap.RegisterClassMap<User>(cm => {
|
||||
cm.AutoMap();
|
||||
});
|
||||
}
|
||||
|
||||
9
src/DomainObjects/Abstractions/Token.cs
Normal file
9
src/DomainObjects/Abstractions/Token.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public abstract class Token<T> : DomainObjectBase<T> {
|
||||
public string Value { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
}
|
||||
@ -9,6 +9,12 @@ public class Contact : DomainObjectBase<Contact> {
|
||||
public bool Confirmed { get; set; }
|
||||
public bool? Primary { get; set; }
|
||||
|
||||
public Contact(ContactTypes type, string value) {
|
||||
Type = type;
|
||||
Value = value;
|
||||
Confirmed = false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
18
src/DomainObjects/Documents/Sites/Site.cs
Normal file
18
src/DomainObjects/Documents/Sites/Site.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Sites {
|
||||
public class Site : DomainObjectDocumentBase<Site> {
|
||||
|
||||
public string Name { get; set; }
|
||||
public List<string> Hosts { get; set; }
|
||||
|
||||
public Site(string name, List<string> hosts) {
|
||||
Name = name;
|
||||
Hosts = hosts;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
|
||||
public class AuthenticationToken : DomainObjectBase<AuthenticationToken> {
|
||||
public string Value { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
using CryptoProvider;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
public class PasswordRecoveryToken {
|
||||
public string Value { get; set; }
|
||||
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public PasswordRecoveryToken() {
|
||||
Value = RandomService.SecureRandomString(20);
|
||||
Created = DateTime.UtcNow;
|
||||
Expires = Created.AddMinutes(5);
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.User
|
||||
{
|
||||
public class Site : DomainObjectBase<Site> {
|
||||
public Guid SiteId { get; set; }
|
||||
|
||||
public List<string> Hosts { get; set; }
|
||||
|
||||
public Roles Role { get; set; }
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class Address
|
||||
{
|
||||
@ -1,13 +1,13 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class Authentication : DomainObjectBase<Authentication> {
|
||||
|
||||
/// <summary>
|
||||
/// Password object
|
||||
/// </summary>
|
||||
public Password? Password { get; set; }
|
||||
public Password Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Password expiration optons
|
||||
10
src/DomainObjects/Documents/Users/AuthenticationToken.cs
Normal file
10
src/DomainObjects/Documents/Users/AuthenticationToken.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class AuthenticationToken : Token<AuthenticationToken> {
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
using CryptoProvider;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class Password : DomainObjectBase<Password> {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using DomainObjects.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class PasswordExpiration : DomainObjectBase<PasswordExpiration> {
|
||||
public bool Enabled { get; set; }
|
||||
15
src/DomainObjects/Documents/Users/PasswordRecoveryToken.cs
Normal file
15
src/DomainObjects/Documents/Users/PasswordRecoveryToken.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using CryptoProvider;
|
||||
|
||||
namespace DomainObjects.Documents.Users;
|
||||
public class PasswordRecoveryToken : Token<PasswordRecoveryToken> {
|
||||
|
||||
public PasswordRecoveryToken() {
|
||||
Value = RandomService.SecureRandomString(20);
|
||||
Created = DateTime.UtcNow;
|
||||
Expires = Created.AddMinutes(5);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using Core.Abstractions;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class Roles : Enumeration
|
||||
{
|
||||
25
src/DomainObjects/Documents/Users/SiteRole.cs
Normal file
25
src/DomainObjects/Documents/Users/SiteRole.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using DomainObjects.Abstractions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DomainObjects.Documents.Users {
|
||||
|
||||
public class SiteRole : DomainObjectBase<SiteRole> {
|
||||
public Guid SiteId { get; set; }
|
||||
|
||||
public Roles Role { get; set; }
|
||||
|
||||
public SiteRole(Guid siteId, Roles role) {
|
||||
SiteId = siteId;
|
||||
Role = role;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
using CryptoProvider;
|
||||
using DomainObjects.Abstractions;
|
||||
using DomainObjects.Documents.Sites;
|
||||
using DomainObjects.Enumerations;
|
||||
|
||||
namespace DomainObjects.Documents.User;
|
||||
namespace DomainObjects.Documents.Users;
|
||||
|
||||
public class UserDocument : DomainObjectDocumentBase<UserDocument> {
|
||||
public class User : DomainObjectDocumentBase<User> {
|
||||
|
||||
public List<Site> Sites { get; set; }
|
||||
public List<SiteRole> SiteRoles { get; set; }
|
||||
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
@ -13,18 +15,43 @@ public class UserDocument : DomainObjectDocumentBase<UserDocument> {
|
||||
|
||||
public Authentication Authentication { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string LastName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
|
||||
public List<Contact> Contacts { get; set; }
|
||||
|
||||
public Address BillingAddress { get; set; }
|
||||
public Address? BillingAddress { get; set; }
|
||||
|
||||
public Address ShippingAddress { get; set; }
|
||||
public Address? ShippingAddress { get; set; }
|
||||
|
||||
public List<AuthenticationToken> Tokens { get; set; }
|
||||
|
||||
|
||||
public User(List<SiteRole> siteRoles, string userName, string email, string password) {
|
||||
SiteRoles = siteRoles;
|
||||
Created = DateTime.UtcNow;
|
||||
Username = userName;
|
||||
|
||||
Authentication = new Authentication {
|
||||
Password = new Password(password),
|
||||
Expiration = new PasswordExpiration {
|
||||
Enabled = true,
|
||||
Days = 180
|
||||
},
|
||||
Expired = new List<Password>()
|
||||
};
|
||||
|
||||
Contacts = new List<Contact> {
|
||||
new Contact(ContactTypes.Email, email) {
|
||||
Primary = true
|
||||
}
|
||||
};
|
||||
|
||||
Tokens = new List<AuthenticationToken>();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// By providing encryption parameters and token, system checks if this token is whitelisted
|
||||
/// </summary>
|
||||
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using WeatherForecast.Models.Account.Requests;
|
||||
using WeatherForecast.Policies;
|
||||
using WeatherForecast.Services;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
@ -36,8 +36,6 @@ public class AccountController : ControllerBase {
|
||||
_userDataProvider = userDataProvider;
|
||||
}
|
||||
|
||||
#region Authless methods
|
||||
|
||||
/// <summary>
|
||||
/// By providing username and password user obtains jwt token
|
||||
/// </summary>
|
||||
@ -50,10 +48,13 @@ public class AccountController : ControllerBase {
|
||||
}
|
||||
|
||||
[HttpPost("[action]")]
|
||||
public IActionResult Create() {
|
||||
public IActionResult Register([FromBody] RegisterRequestModel requestData) {
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Password
|
||||
/// <summary>
|
||||
/// Passing the Username in the request body is a more secure alternative to passing it as a GET param
|
||||
/// </summary>
|
||||
@ -78,7 +79,6 @@ public class AccountController : ControllerBase {
|
||||
var result = _accountService.PasswordReset(requestData);
|
||||
return result.ToActionResult();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// For authenticated users that want to change their password the PUT request can be performed immediately without the email
|
||||
@ -94,7 +94,7 @@ public class AccountController : ControllerBase {
|
||||
if (!getUserResult.IsSuccess || user == null)
|
||||
return BadRequest();
|
||||
|
||||
if ((await _authorizationService.AuthorizeAsync(User, new List<UserDocument> { user }, new PasswordChangeRequirement {
|
||||
if ((await _authorizationService.AuthorizeAsync(User, new List<User> { user }, new PasswordChangeRequirement {
|
||||
OldPassword = requestData.OldPassword
|
||||
})).Succeeded) {
|
||||
var result = _accountService.PasswordChange(user, requestData.OldPassword, requestData.NewPassword);
|
||||
@ -103,4 +103,5 @@ public class AccountController : ControllerBase {
|
||||
|
||||
return Unauthorized();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -65,14 +65,11 @@ public class BlogItemController : ControllerBase {
|
||||
[HttpPost("{siteId}")]
|
||||
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromBody] BlogItemRequestModel requestData) {
|
||||
|
||||
var blogItem = requestData.ToDomainObject();
|
||||
blogItem.SiteId = siteId;
|
||||
|
||||
var userId = User?.Identity?.Name?.ToNullableGuid();
|
||||
if (userId == null)
|
||||
return IDomainResult.Failed().ToActionResult();
|
||||
|
||||
blogItem.Author = userId.Value;
|
||||
var blogItem = requestData.ToDomainObject(userId.Value, siteId);
|
||||
|
||||
if ((await _authorizationService.AuthorizeAsync(User, new List<BlogDocument> { blogItem }, new BlogAuthorizationRequirement {
|
||||
Action = CrudActions.Create
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using Core.Enumerations;
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DomainResults.Common;
|
||||
using DomainResults.Mvc;
|
||||
|
||||
27
src/WeatherForecast/Controllers/InitializationController.cs
Normal file
27
src/WeatherForecast/Controllers/InitializationController.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using DomainResults.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WeatherForecast.Models.Initialization.Requests;
|
||||
using WeatherForecast.Services;
|
||||
|
||||
namespace WeatherForecast.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class InitializationController : ControllerBase {
|
||||
|
||||
private readonly IInitializationService _initializationService;
|
||||
|
||||
public InitializationController(
|
||||
IInitializationService initializationService
|
||||
) {
|
||||
_initializationService = initializationService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult InitializeSystem([FromBody] InitializeSystemRequestModel requestData) {
|
||||
var result = _initializationService.InitializeSystem(requestData);
|
||||
return result.ToActionResult();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -64,16 +64,12 @@ public class ShopItemController : ControllerBase {
|
||||
/// <returns></returns>
|
||||
[HttpPost("{siteId}/{sku}")]
|
||||
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) {
|
||||
var shopItem = requestData.ToDomainObject();
|
||||
|
||||
shopItem.SiteId = siteId;
|
||||
shopItem.Sku = sku;
|
||||
|
||||
var userId = User?.Identity?.Name?.ToNullableGuid();
|
||||
if (userId == null)
|
||||
return IDomainResult.Failed().ToActionResult();
|
||||
|
||||
shopItem.Author = userId.Value;
|
||||
var shopItem = requestData.ToDomainObject(sku, userId.Value, siteId);
|
||||
|
||||
if ((await _authorizationService.AuthorizeAsync(User, new List<ShopDocument> { shopItem }, new ShopAuthorizationRequirement {
|
||||
Action = CrudActions.Create
|
||||
|
||||
@ -42,7 +42,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override MediaAttachmentL10n ToDomainObject() {
|
||||
public MediaAttachmentL10n ToDomainObject() {
|
||||
if (HasValidationErrors()) throw new ValidationException();
|
||||
|
||||
return new MediaAttachmentL10n() {
|
||||
|
||||
@ -33,7 +33,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Requests {
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public override MediaAttachment ToDomainObject() {
|
||||
public MediaAttachment ToDomainObject() {
|
||||
|
||||
return new MediaAttachment {
|
||||
Src = Src,
|
||||
|
||||
@ -63,7 +63,7 @@ namespace WeatherForecast.Models.Abstractions.PostItem.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override PostItemL10n ToDomainObject() {
|
||||
public PostItemL10n ToDomainObject() {
|
||||
if (HasValidationErrors()) throw new ValidationException();
|
||||
|
||||
return new PostItemL10n() {
|
||||
|
||||
@ -3,12 +3,25 @@ using Core.Enumerations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class PasswordResetRequestModel : RequestModelBase {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string RePassword { get; set; }
|
||||
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
namespace WeatherForecast.Models.Account.Requests {
|
||||
public class RegisterRequestModel {
|
||||
}
|
||||
}
|
||||
@ -25,21 +25,22 @@ namespace WeatherForecast.Models.Blog.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override BlogDocument ToDomainObject() {
|
||||
return new BlogDocument() {
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList(),
|
||||
public BlogDocument ToDomainObject(Guid userId, Guid siteId) => new () {
|
||||
SiteId = siteId,
|
||||
|
||||
// Images
|
||||
// Author
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList(),
|
||||
|
||||
Tags = Tags,
|
||||
Categories = Categories,
|
||||
FamilyFriendly = FamilyFriendly,
|
||||
// Images
|
||||
Author = userId,
|
||||
|
||||
Tags = Tags,
|
||||
Categories = Categories,
|
||||
FamilyFriendly = FamilyFriendly,
|
||||
|
||||
ReadTime = ReadTime,
|
||||
Likes = Likes
|
||||
};
|
||||
|
||||
ReadTime = ReadTime,
|
||||
Likes = Likes
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@ -20,12 +20,12 @@ namespace WeatherForecast.Models.Category.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override CategoryDocument ToDomainObject() {
|
||||
public CategoryDocument ToDomainObject(Guid siteId) => new CategoryDocument() {
|
||||
SiteId = siteId,
|
||||
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList()
|
||||
};
|
||||
|
||||
return new CategoryDocument() {
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@ -32,7 +32,7 @@ namespace WeatherForecast.Models.Category.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override CategoryL10n ToDomainObject() {
|
||||
public CategoryL10n ToDomainObject() {
|
||||
if (HasValidationErrors()) throw new ValidationException();
|
||||
|
||||
return new CategoryL10n() {
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
using Core.Abstractions.Models;
|
||||
using Core.Enumerations;
|
||||
using DomainObjects.Documents;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace WeatherForecast.Models.Initialization.Requests {
|
||||
public class InitializeSystemRequestModel : RequestModelBase<InitializeSystemRequestModel> {
|
||||
|
||||
public string SiteName { get; set; }
|
||||
|
||||
public string Host { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string RePassword { get; set; }
|
||||
|
||||
public InitializeSystemRequestModel ToDomainObject() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||
|
||||
if (string.IsNullOrWhiteSpace(SiteName))
|
||||
yield return new ValidationResult($"{nameof(SiteName)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Host))
|
||||
yield return new ValidationResult($"{nameof(Host)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Username))
|
||||
yield return new ValidationResult($"{nameof(Username)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Email))
|
||||
yield return new ValidationResult($"{nameof(Email)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Password))
|
||||
yield return new ValidationResult($"{nameof(Password)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RePassword))
|
||||
yield return new ValidationResult($"{nameof(RePassword)} ${Errors.NullOrEmpty}");
|
||||
|
||||
if (string.Compare(Password, RePassword) != 0)
|
||||
yield return new ValidationResult($"{nameof(Password)} and {nameof(RePassword)} ${Errors.NotMatched}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,9 +21,13 @@ namespace WeatherForecast.Models.Shop.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override ShopCartDocument ToDomainObject() {
|
||||
public ShopCartDocument ToDomainObject(string sku, Guid userId, Guid siteId) {
|
||||
|
||||
return new ShopCartDocument() {
|
||||
Sku = sku,
|
||||
UserId = userId,
|
||||
SiteId = siteId,
|
||||
|
||||
Quantity = Quantity.Value,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
@ -41,23 +41,24 @@ namespace WeatherForecast.Models.Requests {
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override ShopDocument ToDomainObject() {
|
||||
public ShopDocument ToDomainObject(string sku, Guid userId, Guid siteId) => new ShopDocument() {
|
||||
Sku = sku,
|
||||
SiteId = siteId,
|
||||
|
||||
return new ShopDocument() {
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList(),
|
||||
MediaAttachments = MediaAttachments?.Select(x => x.ToDomainObject()).ToList(),
|
||||
// Author
|
||||
Created = DateTime.UtcNow,
|
||||
Tags = Tags,
|
||||
FamilyFriendly = FamilyFriendly,
|
||||
L10n = L10n.Select(x => x.ToDomainObject()).ToList(),
|
||||
MediaAttachments = MediaAttachments?.Select(x => x.ToDomainObject()).ToList(),
|
||||
Author = userId,
|
||||
Created = DateTime.UtcNow,
|
||||
Tags = Tags,
|
||||
FamilyFriendly = FamilyFriendly,
|
||||
|
||||
BrandName = BrandName,
|
||||
Rating = Rating,
|
||||
Price = Price.Value,
|
||||
NewPrice = NewPrice,
|
||||
Quantity = Quantity.Value
|
||||
};
|
||||
|
||||
BrandName = BrandName,
|
||||
Rating = Rating,
|
||||
Price = Price.Value,
|
||||
NewPrice = NewPrice,
|
||||
Quantity = Quantity.Value
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
||||
@ -3,11 +3,12 @@ using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using DataProviders.Collections;
|
||||
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
|
||||
|
||||
using CryptoProvider;
|
||||
using Extensions;
|
||||
using DomainObjects.Documents.Sites;
|
||||
|
||||
namespace WeatherForecast.Policies.Abstractions;
|
||||
|
||||
@ -22,12 +23,13 @@ public static class AuthorisationHandlerHelper {
|
||||
/// <param name="context"></param>
|
||||
/// <param name="aesKey"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static UserDocument? GetUser(AuthorizationHandlerContext context, IAesKey? aesKey, IHttpContextAccessor contextAccessor, IUserDataProvider userDataProvider) {
|
||||
/// <returns>User document</returns>
|
||||
public static (Site?, User?) GetUser(AuthorizationHandlerContext context, IAesKey? aesKey, IHttpContextAccessor contextAccessor, ISiteDataProvider siteDataProvider, IUserDataProvider userDataProvider) {
|
||||
|
||||
if (context == null || aesKey?.IV == null || aesKey?.Key == null)
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
var userId = context.User?.Identity?.Name?.ToNullableGuid();
|
||||
|
||||
@ -36,16 +38,25 @@ public static class AuthorisationHandlerHelper {
|
||||
var bearerToken = request?.Headers.Authorization.FirstOrDefault();
|
||||
|
||||
if (userId == null || host == null || bearerToken == null)
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
var (user, getUserResult) = userDataProvider.GetByUserIdAndHost(userId.Value, host);
|
||||
#region Validate if user belongs to site
|
||||
var (site, getSiteResult) = siteDataProvider.GetByHostName(host);
|
||||
if (!getSiteResult.IsSuccess || site == null)
|
||||
return (null, null);
|
||||
|
||||
var (user, getUserResult) = userDataProvider.Get(userId.Value);
|
||||
if (!getUserResult.IsSuccess || user == null)
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
if (user.SiteRoles.Any(x => x.SiteId == site.Id))
|
||||
return (null, null);
|
||||
#endregion
|
||||
|
||||
if (!user.ValidateToken(aesKey.IV, aesKey.Key, bearerToken))
|
||||
return null;
|
||||
return (null, null);
|
||||
|
||||
return user;
|
||||
return (site, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -54,11 +65,8 @@ public static class AuthorisationHandlerHelper {
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
public static Roles? GetRole(IHttpContextAccessor contextAccessor, UserDocument user) {
|
||||
var request = contextAccessor.HttpContext?.Request;
|
||||
var host = request?.Headers["X-Forwarded-Host"].FirstOrDefault();
|
||||
var bearerToken = request?.Headers.Authorization.FirstOrDefault();
|
||||
return user.Sites.Single(x => x.Hosts.Any(x => x == host)).Role;
|
||||
public static Roles? GetRole(Site site, User user) {
|
||||
return user.SiteRoles.SingleOrDefault(x => x.SiteId == site.Id)?.Role;
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +79,7 @@ public abstract class AuthorizationHandlerBase<TRequirement> : AuthorizationHand
|
||||
|
||||
private readonly IAesKey? _aesKey;
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
|
||||
/// <summary>
|
||||
@ -78,14 +87,17 @@ public abstract class AuthorizationHandlerBase<TRequirement> : AuthorizationHand
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public AuthorizationHandlerBase(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) {
|
||||
_aesKey = configuration.Value.JwtTokenEncryption;
|
||||
_contextAccessor = contextAccessor;
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_userDataProvider = userDataProvider;
|
||||
}
|
||||
|
||||
@ -94,11 +106,11 @@ public abstract class AuthorizationHandlerBase<TRequirement> : AuthorizationHand
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected UserDocument? GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _userDataProvider);
|
||||
protected (Site?, User?) GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _siteDataProvider, _userDataProvider);
|
||||
|
||||
protected Roles? GetRole(UserDocument user) =>
|
||||
AuthorisationHandlerHelper.GetRole(_contextAccessor, user);
|
||||
protected Roles? GetRole(Site site, User user) =>
|
||||
AuthorisationHandlerHelper.GetRole(site, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,6 +122,7 @@ public abstract class AuthorizationHandlerBase<TRequirement, TResource> : Author
|
||||
|
||||
private readonly IAesKey? _aesKey;
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
|
||||
/// <summary>
|
||||
@ -117,14 +130,17 @@ public abstract class AuthorizationHandlerBase<TRequirement, TResource> : Author
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public AuthorizationHandlerBase(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) {
|
||||
_aesKey = configuration.Value.JwtTokenEncryption;
|
||||
_contextAccessor = contextAccessor;
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_userDataProvider = userDataProvider;
|
||||
}
|
||||
|
||||
@ -133,9 +149,9 @@ public abstract class AuthorizationHandlerBase<TRequirement, TResource> : Author
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected UserDocument? GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _userDataProvider);
|
||||
protected (Site?, User?) GetUser(AuthorizationHandlerContext context) =>
|
||||
AuthorisationHandlerHelper.GetUser(context, _aesKey, _contextAccessor, _siteDataProvider, _userDataProvider);
|
||||
|
||||
protected Roles? GetRole(UserDocument user) =>
|
||||
AuthorisationHandlerHelper.GetRole(_contextAccessor, user);
|
||||
protected Roles? GetRole(Site site, User user) =>
|
||||
AuthorisationHandlerHelper.GetRole(site, user);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ using DataProviders.Collections;
|
||||
|
||||
using WeatherForecast.Policies.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
|
||||
namespace WeatherForecast.Policies;
|
||||
|
||||
@ -21,12 +21,14 @@ public class BlogAuthorizationHandler : AuthorizationHandlerBase<BlogAuthorizati
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public BlogAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(configuration, contextAccessor, userDataProvider) { }
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -37,11 +39,11 @@ public class BlogAuthorizationHandler : AuthorizationHandlerBase<BlogAuthorizati
|
||||
/// <returns></returns>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlogAuthorizationRequirement requirement, List<BlogDocument> resource) {
|
||||
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
// Can only Admin, Editor, Author, Contributor (cannot set publish date)
|
||||
if (requirement.Action == CrudActions.Create
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using Core.Enumerations;
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Policies.Abstractions;
|
||||
@ -16,13 +16,16 @@ public class CategoryAuthorizationHandler : AuthorizationHandlerBase<CategoryAut
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public CategoryAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(configuration, contextAccessor, userDataProvider) { }
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -32,11 +35,11 @@ public class CategoryAuthorizationHandler : AuthorizationHandlerBase<CategoryAut
|
||||
/// <returns></returns>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CategoryAuthorizationRequirement requirement) {
|
||||
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
// Can Admin, Editor, Shop manager
|
||||
if ((requirement.Action == CrudActions.Create || requirement.Action == CrudActions.Update)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using Core.Enumerations;
|
||||
using DataProviders;
|
||||
using DataProviders.Buckets;
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using FileSecurityService;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
@ -26,9 +26,10 @@ public class FileAuthorisationHandler : AuthorizationHandlerBase<FileAuthorisati
|
||||
public FileAuthorisationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider,
|
||||
IFileSecurityService fileSecurityService
|
||||
) : base(configuration, contextAccessor, userDataProvider) {
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) {
|
||||
_fileSecurityService = fileSecurityService;
|
||||
}
|
||||
|
||||
@ -42,11 +43,11 @@ public class FileAuthorisationHandler : AuthorizationHandlerBase<FileAuthorisati
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FileAuthorisationRequirement requirement, List<BucketFile> resource) {
|
||||
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
if (resource.Any(x => {
|
||||
var (fileCategory, signatureResult) = _fileSecurityService.CheckFileSignature(x.Name, x.Bytes, x.ContentType);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using CryptoProvider;
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Policies.Abstractions;
|
||||
@ -11,18 +11,21 @@ namespace WeatherForecast.Policies;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class PasswordChangeAuthorizationHandler : AuthorizationHandlerBase<PasswordChangeRequirement, List<UserDocument>> {
|
||||
public class PasswordChangeAuthorizationHandler : AuthorizationHandlerBase<PasswordChangeRequirement, List<User>> {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public PasswordChangeAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(configuration, contextAccessor, userDataProvider) { }
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -31,27 +34,16 @@ public class PasswordChangeAuthorizationHandler : AuthorizationHandlerBase<Passw
|
||||
/// <param name="requirement"></param>
|
||||
/// <param name="resource"></param>
|
||||
/// <returns></returns>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PasswordChangeRequirement requirement, List<UserDocument> resource) {
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PasswordChangeRequirement requirement, List<User> resource) {
|
||||
|
||||
// User from token
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
|
||||
if (userRole != Roles.Admin && resource.Any(x => x.Id != user.Id))
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (resource.Count() > 0 && resource.Any(x => x.Id == user.Id))
|
||||
return Task.CompletedTask;
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
if (resource.All(x => x.Id == user.Id)) {
|
||||
|
||||
if (user.Authentication.Password == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (!HashService.ValidateHash(requirement.OldPassword, user.Authentication.Password.Salt, user.Authentication.Password.Hash))
|
||||
if (user.Authentication.Password != null && user.Authentication.Password.Validate(requirement.OldPassword))
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
using Core.Enumerations;
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Policies.Abstractions;
|
||||
@ -18,12 +18,14 @@ namespace WeatherForecast.Policies {
|
||||
/// </summary>
|
||||
/// <param name="configuration"></param>
|
||||
/// <param name="contextAccessor"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public ShopAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(configuration, contextAccessor, userDataProvider) { }
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -33,11 +35,12 @@ namespace WeatherForecast.Policies {
|
||||
/// <param name="resource"></param>
|
||||
/// <returns></returns>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ShopAuthorizationRequirement requirement, List<ShopDocument> resource) {
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
// Can Admin, Shop manager
|
||||
if (requirement.Action == CrudActions.Create
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Policies.Abstractions;
|
||||
@ -21,8 +21,9 @@ namespace WeatherForecast.Policies {
|
||||
public ShopCartAuthorizationHandler(
|
||||
IOptions<Configuration> configuration,
|
||||
IHttpContextAccessor contextAccessor,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(configuration, contextAccessor, userDataProvider) { }
|
||||
) : base(configuration, contextAccessor, siteDataProvider, userDataProvider) { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@ -32,11 +33,12 @@ namespace WeatherForecast.Policies {
|
||||
/// <param name="resource"></param>
|
||||
/// <returns></returns>
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ShopCartAuthorizationRequirement requirement, List<ShopCartDocument> resource) {
|
||||
var user = GetUser(context);
|
||||
if (user == null)
|
||||
|
||||
var (site, user) = GetUser(context);
|
||||
if (site == null || user == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
var userRole = GetRole(user);
|
||||
var userRole = GetRole(site, user);
|
||||
|
||||
// Leave only admin to manage others carts
|
||||
if (userRole != Roles.Admin && resource.Any(x => x.UserId != user.Id))
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
using Core.Abstractions;
|
||||
using DomainObjects;
|
||||
using CryptoProvider;
|
||||
using DataProviders.Collections;
|
||||
using DomainResults.Common;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Models.Account.Requests;
|
||||
using DomainObjects.Documents.User;
|
||||
using DomainObjects.Documents.Users;
|
||||
using EmailProvider;
|
||||
using DomainObjects.Enumerations;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Extensions;
|
||||
using DomainObjects.Documents.Sites;
|
||||
|
||||
namespace WeatherForecast.Services
|
||||
{
|
||||
namespace WeatherForecast.Services {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IAccountService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public interface IAccountService {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -48,7 +48,7 @@ namespace WeatherForecast.Services
|
||||
/// <param name="oldPassword"></param>
|
||||
/// <param name="newPassword"></param>
|
||||
/// <returns></returns>
|
||||
(Guid?, IDomainResult) PasswordChange(UserDocument user, string oldPassword, string newPassword);
|
||||
(Guid?, IDomainResult) PasswordChange(User user, string oldPassword, string newPassword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -108,6 +108,17 @@ namespace WeatherForecast.Services
|
||||
return IDomainResult.Success(token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Passing the Username in the request body is a more secure alternative to passing it as a GET param
|
||||
/// </summary>
|
||||
@ -137,10 +148,12 @@ namespace WeatherForecast.Services
|
||||
|
||||
emailBuilder.AddHtmlBody(template);
|
||||
|
||||
// TODO: save inside mongo file bucket
|
||||
emailBuilder.DkimSign("maks-it.com", "default", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dkim", "maks-it.com"));
|
||||
|
||||
using var smtpService = new SMTPService();
|
||||
|
||||
// TODO: change email password and manage configs inside database
|
||||
smtpService.Connect("smtp.ionos.it", 465, true);
|
||||
smtpService.Authenticate("commercial@maks-it.com", "E23{R#<X&#Lyz");
|
||||
|
||||
@ -159,7 +172,7 @@ namespace WeatherForecast.Services
|
||||
public IDomainResult PasswordReset(PasswordResetRequestModel requestData) {
|
||||
var (user, getUserResult) = _userDataProvider.GetByRecoveryToken(requestData.Token);
|
||||
|
||||
if(!getUserResult.IsSuccess || user == null)
|
||||
if (!getUserResult.IsSuccess || user == null)
|
||||
return IDomainResult.Failed();
|
||||
|
||||
user.Authentication.PasswordReset(requestData.Token, requestData.Password);
|
||||
@ -175,7 +188,7 @@ namespace WeatherForecast.Services
|
||||
/// <param name="user"></param>
|
||||
/// <param name="newPassword"></param>
|
||||
/// <returns></returns>
|
||||
public (Guid?, IDomainResult) PasswordChange(UserDocument user, string oldPassword, string newPassword) {
|
||||
public (Guid?, IDomainResult) PasswordChange(User user, string oldPassword, string newPassword) {
|
||||
|
||||
user.Authentication.PasswordChange(oldPassword, newPassword);
|
||||
|
||||
|
||||
@ -137,12 +137,9 @@ namespace WeatherForecast.Services {
|
||||
public (Guid?, IDomainResult) Update(BlogDocument blogItem, BlogItemRequestModel requestData) {
|
||||
|
||||
// construct domain object from model
|
||||
var newItem = requestData.ToDomainObject();
|
||||
var newItem = requestData.ToDomainObject(blogItem.Author, blogItem.SiteId);
|
||||
newItem.Id = blogItem.Id;
|
||||
newItem.SiteId = blogItem.SiteId;
|
||||
|
||||
newItem.Created = blogItem.Created;
|
||||
newItem.Author = blogItem.Author;
|
||||
|
||||
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(blogItem.SiteId, requestData.Categories);
|
||||
if (!addCategoriesResult.IsSuccess || categories == null)
|
||||
|
||||
@ -85,8 +85,7 @@ namespace WeatherForecast.Services {
|
||||
/// <returns></returns>
|
||||
public (Guid?, IDomainResult) Post(Guid siteId, CategoryItemRequestModel requestModel) {
|
||||
try {
|
||||
var item = requestModel.ToDomainObject();
|
||||
item.SiteId = siteId;
|
||||
var item = requestModel.ToDomainObject(siteId);
|
||||
|
||||
var (_, getResult) = _categoryDataProvider.GetBySlugs(item.SiteId, item.L10n.Select(x => x.Slug).ToList());
|
||||
if (getResult.IsSuccess)
|
||||
@ -161,9 +160,8 @@ namespace WeatherForecast.Services {
|
||||
return (null, result);
|
||||
|
||||
// construct domain object from model
|
||||
var newItem = requestData.ToDomainObject();
|
||||
var newItem = requestData.ToDomainObject(item.SiteId);
|
||||
newItem.Id = item.Id;
|
||||
newItem.SiteId = item.SiteId;
|
||||
|
||||
if (!item.Equals(newItem)) {
|
||||
var (id, updateResult) = _categoryDataProvider.Update(newItem);
|
||||
|
||||
75
src/WeatherForecast/Services/InitializationService.cs
Normal file
75
src/WeatherForecast/Services/InitializationService.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using Core.Abstractions;
|
||||
using CryptoProvider;
|
||||
using DataProviders.Collections;
|
||||
using DomainObjects.Documents.Sites;
|
||||
using DomainObjects.Documents.Users;
|
||||
using DomainObjects.Enumerations;
|
||||
using DomainObjects;
|
||||
using DomainResults.Common;
|
||||
using Extensions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WeatherForecast.Models.Initialization.Requests;
|
||||
|
||||
namespace WeatherForecast.Services;
|
||||
|
||||
public interface IInitializationService {
|
||||
IDomainResult InitializeSystem(InitializeSystemRequestModel requestData);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class InitializationService : ServiceBase<InitializationService>, IInitializationService {
|
||||
|
||||
private readonly IAesKey? _aesKey;
|
||||
private readonly IJwtConfig? _jwtConfig;
|
||||
|
||||
private readonly ISiteDataProvider _siteDataProvider;
|
||||
private readonly IUserDataProvider _userDataProvider;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="siteDataProvider"></param>
|
||||
/// <param name="userDataProvider"></param>
|
||||
public InitializationService(
|
||||
ILogger<InitializationService> logger,
|
||||
IOptions<Configuration> options,
|
||||
ISiteDataProvider siteDataProvider,
|
||||
IUserDataProvider userDataProvider
|
||||
) : base(logger) {
|
||||
_aesKey = options.Value.JwtTokenEncryption;
|
||||
_jwtConfig = options.Value.JwtConfig;
|
||||
_siteDataProvider = siteDataProvider;
|
||||
_userDataProvider = userDataProvider;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="requestData"></param>
|
||||
/// <returns></returns>
|
||||
public IDomainResult InitializeSystem(InitializeSystemRequestModel requestData) {
|
||||
|
||||
var userId = "fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60".ToGuid();
|
||||
var siteId = "404c8232-9048-4519-bfba-6e78dc7005ca".ToGuid();
|
||||
|
||||
var (_, siteInsetResult) = _siteDataProvider.Insert(new Site(requestData.SiteName, new List<string> { requestData.Host }) {
|
||||
Id = siteId
|
||||
});
|
||||
|
||||
var user = new User(new List<SiteRole> { new SiteRole(siteId, Roles.Admin) }, requestData.Username, requestData.Email, requestData.Password) {
|
||||
Id = userId
|
||||
};
|
||||
|
||||
user.Contacts.ForEach(x => x.Confirmed = true);
|
||||
|
||||
var (_, userInsertResult) = _userDataProvider.Insert(user);
|
||||
|
||||
return IDomainResult.Success();
|
||||
}
|
||||
}
|
||||
@ -84,11 +84,7 @@ public class ShopCartItemService : ServiceBase<ShopCartItemService>, IShopCartIt
|
||||
if (getResult.IsSuccess)
|
||||
return IDomainResult.Failed<Guid?>();
|
||||
|
||||
var item = requestModel.ToDomainObject();
|
||||
|
||||
item.SiteId = siteId;
|
||||
item.UserId = userId;
|
||||
item.Sku = sku;
|
||||
var item = requestModel.ToDomainObject(sku, userId, siteId);
|
||||
|
||||
var (id, insertResult) = _shopCartDataProvider.Insert(item);
|
||||
|
||||
@ -131,11 +127,8 @@ public class ShopCartItemService : ServiceBase<ShopCartItemService>, IShopCartIt
|
||||
public (Guid?, IDomainResult) Update(ShopCartDocument cartItem, ShopCartItemRequestModel requestData) {
|
||||
|
||||
// construct domain object from model
|
||||
var newItem = requestData.ToDomainObject();
|
||||
var newItem = requestData.ToDomainObject(cartItem.Sku, cartItem.UserId, cartItem.SiteId);
|
||||
newItem.Id = cartItem.Id;
|
||||
newItem.SiteId = cartItem.SiteId;
|
||||
newItem.UserId = cartItem.UserId;
|
||||
newItem.Sku = cartItem.Sku;
|
||||
newItem.Created = cartItem.Created;
|
||||
|
||||
if (!cartItem.Equals(newItem)) {
|
||||
|
||||
@ -153,12 +153,9 @@ namespace WeatherForecast.Services {
|
||||
return (null, getResult);
|
||||
|
||||
// construct domain object from model
|
||||
var newItem = requestData.ToDomainObject();
|
||||
var newItem = requestData.ToDomainObject(sku, item.Author, siteId);
|
||||
newItem.Id = item.Id;
|
||||
newItem.SiteId = siteId;
|
||||
newItem.Sku = sku;
|
||||
newItem.Created = item.Created;
|
||||
newItem.Author = item.Author;
|
||||
|
||||
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
|
||||
if (!addCategoriesResult.IsSuccess || categories == null)
|
||||
|
||||
@ -94,6 +94,7 @@ namespace WeatherForecast {
|
||||
services.AddScoped<ICategoryItemsService, CategoryItemsService>();
|
||||
services.AddScoped<IContentService, ContentService>();
|
||||
services.AddScoped<IImageService, ImageService>();
|
||||
services.AddScoped<IInitializationService, InitializationService>();
|
||||
services.AddScoped<IShopCartItemService, ShopCartItemService>();
|
||||
services.AddScoped<IShopCartItemsService, ShopCartItemsService>();
|
||||
services.AddScoped<IShopItemService, ShopItemService>();
|
||||
|
||||
@ -3,4 +3,4 @@ WiredTiger 10.0.2: (December 21, 2021)
|
||||
WiredTiger version
|
||||
major=10,minor=0,patch=2
|
||||
file:WiredTiger.wt
|
||||
access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.78531=(addr="018781e4a527c68d8881e43cd827ac8981e4935a7dbb808080e3022fc0e2dfc0",order=78531,time=1670018018,size=69632,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=3,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=235943,run_write_gen=235628)),checkpoint_backup_info=,checkpoint_lsn=(29,118272)
|
||||
access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.87032=(addr="018781e4072edf558b81e49885698c8c81e48750482f808080e301bfc0e2dfc0",order=87032,time=1671750241,size=69632,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=442,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=261469,run_write_gen=260980)),checkpoint_backup_info=,checkpoint_lsn=(33,135936)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user