-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* GetAllRoles and GetRoleDetails(byID) with detailed correspondant actions APIs * GetAllGroups and GetGroupByID fonctionnal APIs * GetAllUsers and GetUserByID fonctionnal APIs * GetGroupDetails API (list of members) * some changes about the review * New migrations and some changes in entities class of RBAC Model * New migrations and some changes in entities class of RBAC Model (fixes) * Add AccessControls gestion * API Roles are functional * RoleServices Changes * delete useless comments * document RoleController methods * forgot dependencies * save before resolve cache problems * save * Refact and upgrade quality of Roles API traitment * add the actions role gesture * Add AccessControl Get logic * add Get,Create,Delete Routes for the APIs * some change about EF dbContext and entities, Role, AccessControl and User logic implemented in services * Migration (add Principal and his logic in EF DbContext and DB * Migration + refactor the namespace of RBAC Services & move PredicateBuilder Extension into Crosscuting * functionnal service for Users * functionnal basic routes for group controller * accessControl API Routes are functionnals (with Principal changes) * fix mistake on delete AccessControl * Improve quality of the Role service implementation (Exceptions, remove unused action...) * fix some mistakes + add logger for Roles * User logger * logger AccessControl & group + fix somes bugs * add accessControl in group and user details routes * Add GetByName for User and group + some exceptions if name already exist. * Tests for all RBAC repositories + RoleController * Add tests of Groups & Users controllers * Tests the AccessControl's Controller * Review fixes * Improving the robustness of tests (rbac) * Testing RBAC Controller & Services + some modification in the correspondant files * Refactor a mistake in namespace v10 * Some changes * use autoFixture in all unitTest instead of creating some ressource manually * Add tests of limits in methods * remove forgotten comment --------- Co-authored-by: Léo TUAILLON <leo.tuaillon@cgi.com>
- Loading branch information
Showing
171 changed files
with
9,599 additions
and
578 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
src/IoTHub.Portal.Application/Mappers/AccessControlProfile.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright (c) CGI France. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace IoTHub.Portal.Application.Mappers | ||
{ | ||
using AutoMapper; | ||
using IoTHub.Portal.Domain.Entities; | ||
using IoTHub.Portal.Shared.Models.v10; | ||
|
||
public class AccessControlProfile : Profile | ||
{ | ||
public AccessControlProfile() | ||
{ | ||
_ = CreateMap<AccessControl, AccessControlModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)) | ||
.ForMember(dest => dest.Scope, opts => opts.MapFrom(src => src.Scope)) | ||
.ForMember(dest => dest.Role, opts => opts.MapFrom(src => src.Role != null | ||
? new RoleModel | ||
{ | ||
Id = src.Role.Id, | ||
Name = src.Role.Name | ||
} | ||
: null)); | ||
|
||
_ = CreateMap<AccessControlModel, AccessControl>() | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)) | ||
.ForMember(dest => dest.Scope, opts => opts.MapFrom(src => src.Scope)) | ||
.ForMember(dest => dest.RoleId, opts => opts.MapFrom(src => src.Role.Id)) | ||
.ForMember(dest => dest.Role, opts => opts.Ignore()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) CGI France. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace IoTHub.Portal.Application.Mappers | ||
{ | ||
using AutoMapper; | ||
using IoTHub.Portal.Domain.Entities; | ||
using IoTHub.Portal.Shared.Models.v10; | ||
|
||
public class GroupProfile : Profile | ||
{ | ||
public GroupProfile() | ||
{ | ||
_ = CreateMap<Group, GroupModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)); | ||
|
||
_ = CreateMap<Group, GroupDetailsModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)) | ||
.ForMember(dest => dest.Description, opts => opts.MapFrom(src => src.Description)) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)); | ||
|
||
_ = CreateMap<GroupModel, Group>() | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)); | ||
|
||
_ = CreateMap<GroupDetailsModel, Group>() | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.PrincipalId, opt => opt.Ignore()) | ||
.ForMember(dest => dest.Color, opt => opt.MapFrom(src => src.Color)) | ||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright (c) CGI France. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace IoTHub.Portal.Application.Mappers | ||
{ | ||
using AutoMapper; | ||
using IoTHub.Portal.Domain.Entities; | ||
using IoTHub.Portal.Shared.Models.v10; | ||
|
||
public class RoleProfile : Profile | ||
{ | ||
public RoleProfile() | ||
{ | ||
_ = CreateMap<Role, RoleModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)); | ||
|
||
_ = CreateMap<RoleModel, Role>() | ||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)); | ||
|
||
_ = CreateMap<Role, RoleDetailsModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)) | ||
.ForMember(dest => dest.Description, opts => opts.MapFrom(src => src.Description)) | ||
.ForMember(dest => dest.Actions, opts => opts.MapFrom(src => | ||
src.Actions.Select(a => a.Name))); | ||
|
||
_ = CreateMap<RoleDetailsModel, Role>() | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Color, opts => opts.MapFrom(src => src.Color)) | ||
.ForMember(dest => dest.Description, opts => opts.MapFrom(src => src.Description)) | ||
.ForMember(dest => dest.Actions, opts => opts.MapFrom(src => | ||
src.Actions.Select(a => new Action { Name = a }))); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright (c) CGI France. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace IoTHub.Portal.Application.Mappers | ||
{ | ||
using AutoMapper; | ||
using IoTHub.Portal.Domain.Entities; | ||
using IoTHub.Portal.Shared.Models.v10; | ||
|
||
public class UserProfile : Profile | ||
{ | ||
public UserProfile() | ||
{ | ||
_ = CreateMap<User, UserModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.GivenName, opts => opts.MapFrom(src => src.GivenName)) | ||
.ForMember(dest => dest.Email, opts => opts.MapFrom(src => src.Email)) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)); | ||
|
||
_ = CreateMap<User, UserDetailsModel>() | ||
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) | ||
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Email, opts => opts.MapFrom(src => src.Email)) | ||
.ForMember(dest => dest.GivenName, opts => opts.MapFrom(src => src.GivenName)) | ||
.ForMember(dest => dest.FamilyName, opts => opts.MapFrom(src => src.FamilyName)) | ||
.ForMember(dest => dest.Avatar, opts => opts.MapFrom(src => src.Avatar)) | ||
.ForMember(dest => dest.PrincipalId, opts => opts.MapFrom(src => src.PrincipalId)); | ||
|
||
_ = CreateMap<UserDetailsModel, User>() | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) | ||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)) | ||
.ForMember(dest => dest.Avatar, opts => opts.MapFrom(src => src.Avatar)) | ||
.ForMember(dest => dest.GivenName, opt => opt.MapFrom(src => src.GivenName)) | ||
.ForMember(dest => dest.FamilyName, opt => opt.MapFrom(src => src.FamilyName)); | ||
|
||
_ = CreateMap<UserModel, User>() | ||
.ForMember(dest => dest.Id, opt => opt.Ignore()) | ||
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) | ||
.ForMember(dest => dest.GivenName, opt => opt.MapFrom(src => src.GivenName)) | ||
.ForMember(dest => dest.PrincipalId, opt => opt.MapFrom(src => src.PrincipalId)); | ||
} | ||
} | ||
} |
155 changes: 155 additions & 0 deletions
155
src/IoTHub.Portal.Application/Services/AccessControlService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// Copyright (c) CGI France. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
namespace IoTHub.Portal.Application.Services | ||
{ | ||
using System.Threading.Tasks; | ||
using AutoMapper; | ||
using IoTHub.Portal.Domain; | ||
using IoTHub.Portal.Domain.Entities; | ||
using IoTHub.Portal.Domain.Repositories; | ||
using IoTHub.Portal.Shared.Models.v10; | ||
using IoTHub.Portal.Shared.Models.v10.Filters; | ||
using IoTHub.Portal.Domain.Exceptions; | ||
using IoTHub.Portal.Crosscutting; | ||
|
||
internal class AccessControlService : IAccessControlManagementService | ||
{ | ||
private readonly IAccessControlRepository accessControlRepository; | ||
private readonly IUnitOfWork unitOfWork; | ||
private readonly IMapper mapper; | ||
private readonly IRoleRepository roleRepository; | ||
private readonly IPrincipalRepository principalRepository; | ||
|
||
public AccessControlService(IAccessControlRepository accessControlRepository, IUnitOfWork unitOfWork, IMapper mapper, IRoleRepository roleRepository, IPrincipalRepository principalRepository) | ||
{ | ||
this.accessControlRepository = accessControlRepository; | ||
this.unitOfWork = unitOfWork; | ||
this.mapper = mapper; | ||
this.roleRepository = roleRepository; | ||
this.principalRepository = principalRepository; | ||
} | ||
|
||
public async Task<AccessControlModel> GetAccessControlAsync(string Id) | ||
{ | ||
var acEntity = await this.accessControlRepository.GetByIdAsync(Id, ac => ac.Role); | ||
if (acEntity is null) | ||
{ | ||
throw new ResourceNotFoundException($"The AccessControl with the id {Id} doesn't exist"); | ||
} | ||
var acModel = this.mapper.Map<AccessControlModel>(acEntity); | ||
return acModel; | ||
} | ||
|
||
public async Task<PaginatedResult<AccessControlModel>> GetAccessControlPage( | ||
string? searchKeyword = null, | ||
int pageSize = 10, | ||
int pageNumber = 0, | ||
string[] orderBy = null, | ||
string? principalId = null | ||
) | ||
{ | ||
var acFilter = new AccessControlFilter | ||
{ | ||
Keyword = searchKeyword, | ||
PageSize = pageSize, | ||
PageNumber = pageNumber, | ||
OrderBy = orderBy | ||
}; | ||
var acPredicate = PredicateBuilder.True<AccessControl>(); | ||
if (!string.IsNullOrWhiteSpace(acFilter.Keyword)) | ||
{ | ||
acPredicate = acPredicate.And(ac => ac.Scope.ToLower().Contains(acFilter.Keyword.ToLower()) || ac.Role.Name.ToLower().Contains(acFilter.Keyword.ToLower())); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(principalId)) | ||
{ | ||
acPredicate = acPredicate.And(ac => ac.PrincipalId == principalId); | ||
} | ||
|
||
var paginatedAc = await this.accessControlRepository.GetPaginatedListAsync( | ||
pageNumber, | ||
pageSize, | ||
orderBy, | ||
acPredicate | ||
); | ||
|
||
var paginatedAcDto = new PaginatedResult<AccessControlModel> | ||
{ | ||
Data = paginatedAc.Data.Select(x => this.mapper.Map<AccessControlModel>(x)).ToList(), | ||
TotalCount = paginatedAc.TotalCount, | ||
CurrentPage = paginatedAc.CurrentPage, | ||
PageSize = pageSize | ||
}; | ||
return new PaginatedResult<AccessControlModel>(paginatedAcDto.Data, paginatedAcDto.TotalCount); | ||
} | ||
|
||
public async Task<AccessControlModel> CreateAccessControl(AccessControlModel accessControl) | ||
{ | ||
if (accessControl is null) | ||
{ | ||
throw new ArgumentNullException(nameof(accessControl)); | ||
} | ||
var principal = await this.principalRepository.GetByIdAsync(accessControl.PrincipalId); | ||
if (principal == null) | ||
{ | ||
throw new ResourceNotFoundException($"The principal with the id {accessControl.PrincipalId} does'nt exist !"); | ||
} | ||
var role = await this.roleRepository.GetByIdAsync(accessControl.Role.Id); | ||
if (role == null) | ||
{ | ||
throw new ResourceNotFoundException($"The role {accessControl.Role.Name} with the id {accessControl.Role.Id} does'nt exist !"); | ||
} | ||
var acEntity = this.mapper.Map<AccessControl>(accessControl); | ||
await this.accessControlRepository.InsertAsync(acEntity); | ||
await this.unitOfWork.SaveAsync(); | ||
|
||
var createdAc = await this.accessControlRepository.GetByIdAsync(acEntity.Id, ac => ac.Role); | ||
var createdModel = this.mapper.Map<AccessControlModel>(createdAc); | ||
return createdModel; | ||
|
||
} | ||
public async Task<AccessControlModel?> UpdateAccessControl(string id, AccessControlModel accessControl) | ||
{ | ||
if (accessControl is null) | ||
{ | ||
throw new ArgumentNullException(nameof(accessControl)); | ||
} | ||
var acEntity = await this.accessControlRepository.GetByIdAsync(id, ac => ac.Role); | ||
if (acEntity is null) | ||
{ | ||
throw new ResourceNotFoundException($"The AccessControl with the id {id} doesn't exist"); | ||
} | ||
var principal = await this.principalRepository.GetByIdAsync(accessControl.PrincipalId); | ||
if (principal is null) | ||
{ | ||
throw new ResourceNotFoundException($"The principal with the id {accessControl.PrincipalId} not found !"); | ||
} | ||
var role = await this.roleRepository.GetByIdAsync(accessControl.Role.Id); | ||
if (role is null) | ||
{ | ||
throw new ResourceNotFoundException($"Specified role with the id {accessControl.Role.Id} not found"); | ||
} | ||
acEntity.PrincipalId = accessControl.PrincipalId; | ||
acEntity.RoleId = accessControl.Role.Id; | ||
acEntity.Scope = accessControl.Scope; | ||
accessControlRepository.Update(acEntity); | ||
await this.unitOfWork.SaveAsync(); | ||
|
||
var createdAc = await this.accessControlRepository.GetByIdAsync(id, ac => ac.Role); | ||
return this.mapper.Map<AccessControlModel>(createdAc); | ||
} | ||
|
||
public async Task<bool> DeleteAccessControl(string id) | ||
{ | ||
var acEntity = await this.accessControlRepository.GetByIdAsync(id); | ||
if (acEntity is null) | ||
{ | ||
throw new ResourceNotFoundException($"The AccessControl with the id {id} doesn't exist"); | ||
} | ||
accessControlRepository.Delete(id); | ||
await this.unitOfWork.SaveAsync(); | ||
return true; | ||
} | ||
} | ||
} |
Oops, something went wrong.