Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add store owner #81

Merged
merged 20 commits into from
Sep 1, 2020
Merged

Add store owner #81

merged 20 commits into from
Sep 1, 2020

Conversation

RayMMond
Copy link
Member

@RayMMond RayMMond commented Aug 3, 2020

This PR resolve #68 and :

  • upgrade abp to 3.0.5.
  • add store owner and store owner permission check.
  • some minor fixes.

add product tag plugin and store approval plugin.
add store owner and store owner permission check.
Copy link
Member

@gdlcf88 gdlcf88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add product tag plugin and open store approval plugin.

Hi @RayMMond, I did not see any new plugin in this PR. I noticed that the code relate to the ProductTag is added to the Products module directly, what if I do not install the ProductTag plugin?

await AuthorizationService.CheckAsync(OrdersPermissions.Orders.Manage);

// Todo: Check if current user is an admin of the store.
await AuthorizationService.CheckStoreOwnerAsync(order.StoreId, OrdersPermissions.Orders.Manage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A store side AuthorizationRequirement may be better, we can define a StoreOwnerAuthorizationRequirement to check if the current user is a owner of the store (it does not care about the permission requirement). Please refer to the practice of https://github.com/EasyAbp/FileManagement/blob/master/src/EasyAbp.FileManagement.Application/EasyAbp/FileManagement/Files/FileOperationAuthorizationHandler.cs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see an AuthorizationHandler below, but the idea of this comment still has reference significance. (keep the PR lightweight since a huge PR is really hard to review)

}
else
{
await AuthorizationService.CheckAsync(PaymentsPermissions.Payments.Manage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also have PaymentsPermissions.Payments.CrossStore permission. I think there may be many similar problems, I did not check all the code in this PR, please check it, thank you.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

var userId = context.User?.FindUserId();
if (userId.HasValue)
{
var isStoreOwner = await _storeOwnerAppService.IsStoreOwnerAsync(requirement.StoreId, userId.Value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use IStoreOwenerStore.IsOwnerAsync()

public class StoreOwnerAppService : ReadOnlyAppService<StoreOwner, StoreOwnerDto, Guid, GetStoreOwnerListDto>,
IStoreOwnerAppService
{
protected override string GetListPolicyName { get; set; } = StoresPermissions.Stores.Default;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. What about the "get" policy?
  2. Should people who have StoresPermissions.Stores.Default be allowed to get other stores' and users' StoreOwner?


MapToEntity(input, entity);

entity = await _storeManager.UpdateAsync(entity, input.OwnerIds);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best practice of domain services is only creating/updating data of entities and use the repositories to persist the data in application services.

Comment on lines 43 to 47
StoreOwners =
(await _userAppService.GetListAsync(new GetIdentityUsersInput
{ MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount })).Items
.Select(x => new SelectListItem(x.UserName, x.Id.ToString())).ToList();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there are more than 1000 users?

Copy link
Member Author

@RayMMond RayMMond Aug 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gdlcf88 I just follow the implementation of product categories, see this:

ProductTypes =
(await _productTypeAppService.GetListAsync(new PagedAndSortedResultRequestDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount})).Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();
Categories =
(await _categoryAppService.GetListAsync(new GetCategoryListDto
{MaxResultCount = LimitedResultRequestDto.MaxMaxResultCount}))?.Items
.Select(dto => new SelectListItem(dto.DisplayName, dto.Id.ToString())).ToList();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The count of ProductType and Category is expected to be less than 1000, but the count of User is easy to be more than 1000.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best practice should be selecting from a modal with filtered user list, but I have no clue how to implement filtered list in abp, do you have any examples?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe using UserId directly before we have a better way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

return store;
}

protected virtual async Task UpdateStoreOwnersAsync(Guid storeId, IEnumerable<Guid> ownerIds)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be easier to manage if the StoreOwner has its own application service and management pages (remember it is an aggregate root). Then the StoreManager can be removed.

@RayMMond RayMMond changed the title Add store owner and two plugins Add store owner Aug 4, 2020
@RayMMond RayMMond requested a review from gdlcf88 August 15, 2020 09:52
Comment on lines 93 to 100
protected virtual async Task CheckMultiStorePolicyAsync(Guid storeId, string policyName)
{
if (!await AuthorizationService.IsStoreOwnerGrantedAsync(storeId, policyName))
{
await CheckPolicyAsync(CrossStorePolicyName);
await CheckPolicyAsync(policyName);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The storeId can be nullable and check the cross store permission if it is null.

Comment on lines 70 to 78
if (input.StoreId.HasValue)
{
// Todo: Check if current user is an admin of the store.
await CheckMultiStorePolicyAsync(input.StoreId.Value, GetListPolicyName);
}
else
{
await AuthorizationService.CheckAsync(OrdersPermissions.Orders.CrossStore);
throw new AbpAuthorizationException("Authorization failed! Given policy has not granted: " +
GetListPolicyName);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use input.StoreId directly as the param of CheckMultiStorePolicyAsync() method.

{
var isStoreOwner = await _storeOwnerStore.IsStoreOwnerAsync(requirement.StoreId, userId.Value);

if (isStoreOwner)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the current user is a cross store manager?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every domain can have their own cross-store permission definition, so it should be implemented separately. And I have created base classes which contains store-owner permission and cross-store permission check, here:https://github.com/RayMMond/EShop/blob/29080d62532c90d330077e50b85ca7d7031fc174/modules/EasyAbp.EShop.Stores/src/EasyAbp.EShop.Stores.Application/EasyAbp/EShop/Stores/Stores/MultiStoreAbstractKeyCrudAppService.cs

context.Succeed(requirement);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe set failed result here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A requirement can have multiple AuthorizationHandler, and module user may register their own Handers, so I think we should not set failed here.

{
public static class StoreOwnerAuthorizationExtensions
{
public static Task<bool> IsStoreOwnerGrantedAsync(this IAuthorizationService authorizationService,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is store owner? Does it include cross store managers?

b.ConfigureByConvention();
/* Configure more properties here */

b.HasIndex(x => new {x.StoreId, x.OwnerId})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be new {x.OwnerId, x.StoreId}.

And it is better to change all the usage of x.StoreId == storeId && x.OwnerId == ownerId to x.OwnerId == ownerId && x.StoreId == storeId.

<DependentUpon>Index.cshtml</DependentUpon>
</Compile>
</ItemGroup>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused items in ItemGroup should be cleaned.


public virtual Guid StoreId { get; protected set; }

public virtual Guid OwnerId { get; protected set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OwnerUserId is a more accurate name.

<ProjectReference Include="..\..\..\EasyAbp.EShop.Products\src\EasyAbp.EShop.Products.Application.Contracts\EasyAbp.EShop.Products.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\EasyAbp.EShop.Stores\src\EasyAbp.EShop.Stores.Application\EasyAbp.EShop.Stores.Application.csproj" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not do that since the entities in Stores module can be distributed.

@gdlcf88 gdlcf88 mentioned this pull request Aug 21, 2020
@RayMMond RayMMond requested a review from gdlcf88 August 26, 2020 09:34
Copy link
Member

@gdlcf88 gdlcf88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the related cache after a user's store ownership is changed.

{
_repository = repository;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, please remove the IStoreOwnerStore injection.

remove the related cache after a user's store ownership is changed.
@RayMMond RayMMond requested a review from gdlcf88 August 31, 2020 10:01
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;

namespace EasyAbp.EShop.Products.Products
{
public class ProductAppService : CrudAppService<Product, ProductDto, Guid, GetProductListDto, CreateUpdateProductDto, CreateUpdateProductDto>,
public class ProductAppService : CrudAppService<Product, ProductDto, Guid, GetProductListDto, CreateUpdateProductDto
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ProductAppService inherit MultiStoreCrudAppService?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product don't inherit IMultiStore, so answer is no.

@gdlcf88 gdlcf88 merged commit b782323 into EasyAbp:dev Sep 1, 2020
@gdlcf88
Copy link
Member

gdlcf88 commented Sep 1, 2020

Thank you @RayMMond for your great work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add store's owner
2 participants