Skip to content

Commit

Permalink
feat: Add mutations: lockOrganizationContact, unlockOrganizationConta…
Browse files Browse the repository at this point in the history
…ct (#28)

feat: Add checking of xapi:my_organization:edit permission to the mutation updateOrganization
  • Loading branch information
akak1977 authored Oct 12, 2022
1 parent 2fa8d1c commit 4a56536
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,15 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext
{
var currentUser = await userManager.FindByIdAsync(currentUserId);
result = currentContact.Organizations.Contains(inviteUserCommand.OrganizationId) && currentUser.StoreId.EqualsInvariant(inviteUserCommand.StoreId);
}
}
else if (context.Resource is LockOrganizationContactCommand lockOrganizationContact)
{
result = await HasSameOrganizationAsync(currentContact, lockOrganizationContact.UserId, userManager);
}
else if (context.Resource is UnlockOrganizationContactCommand unlockOrganizationContact)
{
result = await HasSameOrganizationAsync(currentContact, unlockOrganizationContact.UserId, userManager);
}
if (result)
{
context.Succeed(requirement);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.ExperienceApiModule.Core.Infrastructure;
using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Contact;

namespace VirtoCommerce.ProfileExperienceApiModule.Data.Commands
{
public class LockOrganizationContactCommand : ICommand<ContactAggregate>
{
public string UserId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Contact;

namespace VirtoCommerce.ProfileExperienceApiModule.Data.Commands
{
public class LockOrganizationContactCommandHandler : IRequestHandler<LockOrganizationContactCommand, ContactAggregate>
{
private readonly IContactAggregateRepository _contactAggregateRepository;

public LockOrganizationContactCommandHandler(IContactAggregateRepository contactAggregateRepository)
{
_contactAggregateRepository = contactAggregateRepository;
}

public async Task<ContactAggregate> Handle(LockOrganizationContactCommand request, CancellationToken cancellationToken)
{
var contactAggregate = await _contactAggregateRepository.GetMemberAggregateRootByIdAsync<ContactAggregate>(request.UserId);

contactAggregate.Contact.Status = ModuleConstants.ContactStatuses.Locked;

await _contactAggregateRepository.SaveAsync(contactAggregate);

return contactAggregate;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using FluentValidation.Results;
using MediatR;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.CustomerModule.Core;
using CustomerCore = VirtoCommerce.CustomerModule.Core;
using VirtoCommerce.CustomerModule.Core.Model;
using VirtoCommerce.CustomerModule.Core.Services;
using VirtoCommerce.ExperienceApiModule.Core.Models;
Expand Down Expand Up @@ -145,7 +145,7 @@ private async Task<RegisterOrganizationResult> ProcessRequestAsync(RegisterReque
await SetDynamicPropertiesAsync(request.Organization.DynamicProperties, organization);
var organizationStatus = store
.Settings
.GetSettingValue<string>(ModuleConstants.Settings.General.OrganizationDefaultStatus.Name, null);
.GetSettingValue<string>(CustomerCore.ModuleConstants.Settings.General.OrganizationDefaultStatus.Name, null);
organization.CreatedBy = Creator;
organization.Status = organizationStatus;
organization.OwnerId = contact.Id;
Expand All @@ -157,7 +157,7 @@ private async Task<RegisterOrganizationResult> ProcessRequestAsync(RegisterReque
}

var contactStatus = store.Settings
.GetSettingValue<string>(ModuleConstants.Settings.General.ContactDefaultStatus.Name, null);
.GetSettingValue<string>(CustomerCore.ModuleConstants.Settings.General.ContactDefaultStatus.Name, null);

contact.Status = contactStatus;
contact.CreatedBy = Creator;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.ExperienceApiModule.Core.Infrastructure;
using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Contact;

namespace VirtoCommerce.ProfileExperienceApiModule.Data.Commands
{
public class UnlockOrganizationContactCommand : ICommand<ContactAggregate>
{
public string UserId { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Contact;

namespace VirtoCommerce.ProfileExperienceApiModule.Data.Commands
{
public class UnlockOrganizationContactCommandHandler : IRequestHandler<UnlockOrganizationContactCommand, ContactAggregate>
{
private readonly IContactAggregateRepository _contactAggregateRepository;

public UnlockOrganizationContactCommandHandler(IContactAggregateRepository contactAggregateRepository)
{
_contactAggregateRepository = contactAggregateRepository;
}

public async Task<ContactAggregate> Handle(UnlockOrganizationContactCommand request, CancellationToken cancellationToken)
{
var contactAggregate = await _contactAggregateRepository.GetMemberAggregateRootByIdAsync<ContactAggregate>(request.UserId);

contactAggregate.Contact.Status = ModuleConstants.ContactStatuses.Approved;

await _contactAggregateRepository.SaveAsync(contactAggregate);

return contactAggregate;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VirtoCommerce.ProfileExperienceApiModule.Data
{
public static class ModuleConstants
{
public static class Security
{
public static class Permissions
{
public const string MyOrganizationEdit = "xapi:my_organization:edit";

public static string[] AllPermissions { get; } = { MyOrganizationEdit };
}
}

public static class ContactStatuses
{
public const string Locked = "Locked";
public const string Approved = "Approved";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GraphQL.Types;

namespace VirtoCommerce.ProfileExperienceApiModule.Data.Schemas
{
public class InputLockUnlockOrganizationContactType : InputObjectGraphType
{
public InputLockUnlockOrganizationContactType()
{
Field<StringGraphType>("UserId");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using VirtoCommerce.CustomerModule.Core.Model;
using VirtoCommerce.CustomerModule.Core.Services;
using VirtoCommerce.ExperienceApiModule.Core.Extensions;
using VirtoCommerce.ExperienceApiModule.Core.Helpers;
using VirtoCommerce.ExperienceApiModule.Core.Infrastructure;
Expand Down Expand Up @@ -40,22 +38,19 @@ public class ProfileSchema : ISchemaBuilder
private readonly IAuthorizationService _authorizationService;
private readonly Func<SignInManager<ApplicationUser>> _signInManagerFactory;
private readonly IMemberAggregateFactory _factory;
private readonly IMemberService _memberService;
private readonly ILogger<ProfileSchema> _logger;

public ProfileSchema(
IMediator mediator,
IAuthorizationService authorizationService,
Func<SignInManager<ApplicationUser>> signInManagerFactory,
IMemberAggregateFactory factory,
IMemberService memberService,
ILogger<ProfileSchema> logger)
{
_mediator = mediator;
_authorizationService = authorizationService;
_signInManagerFactory = signInManagerFactory;
_factory = factory;
_memberService = memberService;
_logger = logger;
}

Expand Down Expand Up @@ -133,8 +128,8 @@ totalCount items {id firstName}
return new PagedConnection<OrganizationAggregate>(response.Results.Select(x => _factory.Create<OrganizationAggregate>(x)), query.Skip, query.Take, response.TotalCount);
});

schema.Query.AddField(organizationsConnectionBuilder.FieldType);

schema.Query.AddField(organizationsConnectionBuilder.FieldType);

#region contact query
/// <example>
#pragma warning disable S125 // Sections of code should not be commented out
Expand All @@ -145,10 +140,10 @@ firstName memberType organizationIds organizations { id businessCategory descrip
addresses { line1 phone }
}
}
*/
*/
#pragma warning restore S125 // Sections of code should not be commented out
/// </example>


#endregion
schema.Query.AddField(new FieldType
{
Expand Down Expand Up @@ -286,10 +281,10 @@ firstName memberType organizationIds organizations { id businessCategory descrip

return result;
})
});

});

#region updateAddressMutation


/// sample code for updating addresses:
#pragma warning disable S125 // Sections of code should not be commented out
/*
Expand All @@ -308,9 +303,9 @@ mutation updateMemberAddresses($command: UpdateMemberAddressesCommand!){
}]
}
}
*/
*/
#pragma warning restore S125 // Sections of code should not be commented out


#endregion
_ = schema.Mutation.AddField(FieldBuilder.Create<object, MemberAggregateRootBase>(GraphTypeExtenstionHelper.GetActualType<MemberType>())
.Name("updateMemberAddresses")
Expand Down Expand Up @@ -343,7 +338,8 @@ mutation updateMemberAddresses($command: UpdateMemberAddressesCommand!){
{
var type = GenericTypeHelper.GetActualType<UpdateOrganizationCommand>();
var command = (UpdateOrganizationCommand)context.GetArgument(type, _commandName);
await CheckAuthAsync(context.GetCurrentUserId(), command, CustomerModule.Core.ModuleConstants.Security.Permissions.Update);
await CheckAuthAsync(context.GetCurrentUserId(), command,
ModuleConstants.Security.Permissions.MyOrganizationEdit);
return await _mediator.Send(command);
})
.FieldType);
Expand Down Expand Up @@ -463,7 +459,32 @@ mutation updateMemberAddresses($command: UpdateMemberAddressesCommand!){

return await _mediator.Send(command);
})
.FieldType);
.FieldType);


_ = schema.Mutation.AddField(FieldBuilder.Create<ContactAggregate, ContactAggregate>(GraphTypeExtenstionHelper.GetActualType<ContactType>())
.Name("lockOrganizationContact")
.Argument(GraphTypeExtenstionHelper.GetActualComplexType<NonNullGraphType<InputLockUnlockOrganizationContactType>>(), _commandName)
.ResolveAsync(async context =>
{
var type = GenericTypeHelper.GetActualType<LockOrganizationContactCommand>();
var command = (LockOrganizationContactCommand)context.GetArgument(type, _commandName);
await CheckAuthAsync(context.GetCurrentUserId(), command, ModuleConstants.Security.Permissions.MyOrganizationEdit);
return await _mediator.Send(command);
})
.FieldType);

_ = schema.Mutation.AddField(FieldBuilder.Create<ContactAggregate, ContactAggregate>(GraphTypeExtenstionHelper.GetActualType<ContactType>())
.Name("unlockOrganizationContact")
.Argument(GraphTypeExtenstionHelper.GetActualComplexType<NonNullGraphType<InputLockUnlockOrganizationContactType>>(), _commandName)
.ResolveAsync(async context =>
{
var type = GenericTypeHelper.GetActualType<UnlockOrganizationContactCommand>();
var command = (UnlockOrganizationContactCommand)context.GetArgument(type, _commandName);
await CheckAuthAsync(context.GetCurrentUserId(), command, ModuleConstants.Security.Permissions.MyOrganizationEdit);
return await _mediator.Send(command);
})
.FieldType);

// Security API fields

Expand Down Expand Up @@ -734,12 +755,13 @@ private async Task CheckAuthAsync(string userId, object resource, params string[
{
var user = await signInManager.UserManager.FindByIdAsync(userId) ?? new ApplicationUser
{
Id = userId, UserName = ExperienceApiModule.Core.AnonymousUser.UserName,
Id = userId,
UserName = ExperienceApiModule.Core.AnonymousUser.UserName,
};

var userPrincipal = await signInManager.CreateUserPrincipalAsync(user);

if (!await CanExecuteWithoutPermissionAsync(user, resource) && !permissions.IsNullOrEmpty())
if (!CanExecuteWithoutPermissionAsync(user, resource) && !permissions.IsNullOrEmpty())
{
foreach (var permission in permissions)
{
Expand Down Expand Up @@ -773,19 +795,14 @@ private async Task CheckAuthAsync(string userId, object resource, params string[
}
}

private async Task<bool> CanExecuteWithoutPermissionAsync(ApplicationUser user, object resource)
private bool CanExecuteWithoutPermissionAsync(ApplicationUser user, object resource)
{
var result = false;

if (resource is UpdateMemberDynamicPropertiesCommand updateMemberDynamicPropertiesCommand)
{
result = updateMemberDynamicPropertiesCommand.MemberId == user.MemberId;
}
else if (resource is UpdateOrganizationCommand updateOrganizationCommand && !string.IsNullOrEmpty(user.MemberId))
{
var member = await _memberService.GetByIdAsync(user.MemberId) as Contact;
result = member?.Organizations.Any(x => x.EqualsInvariant(updateOrganizationCommand.Id)) ?? false;
}

return result;
}
Expand Down
9 changes: 7 additions & 2 deletions src/VirtoCommerce.ProfileExperienceApiModule.Web/Module.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Linq;
using AutoMapper;
using GraphQL.Server;
using MediatR;
Expand All @@ -10,6 +11,7 @@
using VirtoCommerce.ExperienceApiModule.Core.Pipelines;
using VirtoCommerce.MarketingModule.Core.Model.Promotions;
using VirtoCommerce.Platform.Core.Modularity;
using VirtoCommerce.Platform.Core.Security;
using VirtoCommerce.PricingModule.Core.Model;
using VirtoCommerce.ProfileExperienceApiModule.Data;
using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates;
Expand Down Expand Up @@ -66,12 +68,15 @@ public void Initialize(IServiceCollection serviceCollection)
}

public void PostInitialize(IApplicationBuilder appBuilder)
{
{
var permissionsProvider = appBuilder.ApplicationServices.GetRequiredService<IPermissionsRegistrar>();
permissionsProvider.RegisterPermissions(ModuleConstants.Security.Permissions.AllPermissions.Select(x =>
new Permission() { GroupName = "Xapi", Name = x }).ToArray());
}

public void Uninstall()
{
// do nothing in here
// Nothing to do there
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<PackageReference Include="AutoFixture" Version="4.11.0" />
<PackageReference Include="Bogus" Version="29.0.2" />
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.0" />
Expand Down

0 comments on commit 4a56536

Please sign in to comment.