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

RBAC - REST APIs #2773

Merged
merged 44 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
73e1ed1
GetAllRoles and GetRoleDetails(byID) with detailed correspondant acti…
Jan 8, 2024
52fdda5
GetAllGroups and GetGroupByID fonctionnal APIs
Jan 8, 2024
5cdd36b
GetAllUsers and GetUserByID fonctionnal APIs
Jan 8, 2024
7d0218a
GetGroupDetails API (list of members)
Jan 8, 2024
3a0bb2f
some changes about the review
Jan 9, 2024
4c241c4
New migrations and some changes in entities class of RBAC Model
Jan 9, 2024
33a5ada
New migrations and some changes in entities class of RBAC Model (fixes)
Jan 9, 2024
c2d6721
Add AccessControls gestion
Jan 9, 2024
be176e0
API Roles are functional
Jan 12, 2024
fcf467b
RoleServices Changes
Jan 15, 2024
be2d3e0
delete useless comments
Jan 15, 2024
6c2c439
document RoleController methods
Jan 16, 2024
f1de5c6
forgot dependencies
Jan 16, 2024
da989f9
save before resolve cache problems
Apr 11, 2024
5d1b9e1
save
Apr 12, 2024
9e1872c
Refact and upgrade quality of Roles API traitment
Apr 12, 2024
f4ccd88
add the actions role gesture
Apr 12, 2024
f708da5
Add AccessControl Get logic
Apr 15, 2024
5ccdc7c
add Get,Create,Delete Routes for the APIs
Apr 16, 2024
0be1ec3
some change about EF dbContext and entities, Role, AccessControl and …
Apr 18, 2024
10aa2a2
Migration (add Principal and his logic in EF DbContext and DB
Apr 22, 2024
8e2cdab
Migration + refactor the namespace of RBAC Services & move PredicateB…
Apr 22, 2024
3ea3c3d
functionnal service for Users
Apr 23, 2024
d773989
functionnal basic routes for group controller
Apr 23, 2024
528c648
accessControl API Routes are functionnals (with Principal changes)
Apr 24, 2024
54a6ffa
fix mistake on delete AccessControl
Apr 24, 2024
819bc40
Improve quality of the Role service implementation (Exceptions, remov…
Apr 24, 2024
a29e721
fix some mistakes + add logger for Roles
Apr 25, 2024
279d60f
User logger
Apr 25, 2024
661a5b9
logger AccessControl & group + fix somes bugs
Apr 25, 2024
a0c0131
add accessControl in group and user details routes
Apr 26, 2024
f0b5b9b
Add GetByName for User and group + some exceptions if name already ex…
Apr 26, 2024
1b39293
Tests for all RBAC repositories + RoleController
May 13, 2024
513f288
Add tests of Groups & Users controllers
May 13, 2024
a2c057c
Tests the AccessControl's Controller
May 13, 2024
09ed998
Fix some conflicts
May 27, 2024
f1c5b60
Review fixes
Jun 4, 2024
1fd33a0
Improving the robustness of tests (rbac)
Jun 6, 2024
a2d4780
Testing RBAC Controller & Services + some modification in the corresp…
Jun 14, 2024
9f8e90f
Refactor a mistake in namespace v10
Jun 14, 2024
a530219
Some changes
Jun 18, 2024
155f6dc
use autoFixture in all unitTest instead of creating some ressource ma…
Jul 2, 2024
251a2f1
Add tests of limits in methods
Jul 15, 2024
76b069d
remove forgotten comment
TLeoDev Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/IoTHub.Portal.Application/Mappers/AccessControlProfile.cs
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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace IoTHub.Portal.Application.Mappers
using IoTHub.Portal.Domain.Entities;
using Microsoft.Azure.Devices.Shared;
using Models.v10.LoRaWAN;
using Shared.Models.v1._0;
using Shared.Models.v10;

public class ConcentratorProfile : Profile
{
Expand Down
39 changes: 39 additions & 0 deletions src/IoTHub.Portal.Application/Mappers/GroupProfile.cs
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));
}
}
}
41 changes: 41 additions & 0 deletions src/IoTHub.Portal.Application/Mappers/RoleProfile.cs
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 })));
}
}
}
45 changes: 45 additions & 0 deletions src/IoTHub.Portal.Application/Mappers/UserProfile.cs
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));
}
}
}
148 changes: 148 additions & 0 deletions src/IoTHub.Portal.Application/Services/AccessControlService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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)
{
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<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;
}

public async Task<AccessControlModel?> UpdateAccessControl(string id, AccessControlModel 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);
}
}
}
Loading