-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathUpdateRoleClaims.cs
More file actions
134 lines (116 loc) · 6.33 KB
/
Copy pathUpdateRoleClaims.cs
File metadata and controls
134 lines (116 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using LantanaGroup.Link.Account.Application.Commands.AuditEvent;
using LantanaGroup.Link.Account.Application.Interfaces.Factories.Role;
using LantanaGroup.Link.Account.Application.Interfaces.Persistence;
using LantanaGroup.Link.Account.Domain.Entities;
using LantanaGroup.Link.Account.Infrastructure;
using LantanaGroup.Link.Account.Infrastructure.Logging;
using LantanaGroup.Link.Shared.Application.Interfaces;
using LantanaGroup.Link.Shared.Application.Models;
using LantanaGroup.Link.Shared.Application.Models.Configs;
using LantanaGroup.Link.Shared.Application.Models.Kafka;
using Link.Authorization.Infrastructure;
using Microsoft.Extensions.Options;
using System.Diagnostics;
using System.Security.Claims;
namespace LantanaGroup.Link.Account.Application.Commands.Role
{
public class UpdateRoleClaims : IUpdateRoleClaims
{
private readonly ILogger<UpdateRoleClaims> _logger;
private readonly IRoleRepository _roleRepository;
private readonly IUserRepository _userRepository;
private readonly ILinkRoleModelFactory _roleModelFactory;
private readonly ICreateAuditEvent _createAuditEvent;
private readonly IOptions<CacheSettings> _cacheSettings;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ICacheService _cache;
public UpdateRoleClaims(ILogger<UpdateRoleClaims> logger, IRoleRepository roleRepository, ICacheService cache, IUserRepository userRepository, ILinkRoleModelFactory roleModelFactory, ICreateAuditEvent createAuditEvent, IOptions<CacheSettings> cacheSettings, IServiceScopeFactory serviceScopeFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_roleRepository = roleRepository ?? throw new ArgumentNullException(nameof(roleRepository));
_userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
_roleModelFactory = roleModelFactory ?? throw new ArgumentNullException(nameof(roleModelFactory));
_createAuditEvent = createAuditEvent ?? throw new ArgumentNullException(nameof(createAuditEvent));
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
}
public async Task<bool> Execute(ClaimsPrincipal? requestor, Guid roleId, List<string> claims, CancellationToken cancellationToken = default)
{
using Activity? activity = ServiceActivitySource.Instance.StartActivity("UpdateClaims:Execute");
try
{
var role = await _roleRepository.GetRoleAsync(roleId, cancellationToken: cancellationToken);
if (role is null)
{
_logger.LogRoleClaimAssignmentException(roleId.ToString(), string.Join(",", claims), "Role not found");
return false;
}
if (requestor is not null)
{
role.LastModifiedBy = requestor?.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
}
var currentClaims = await _roleRepository.GetClaimsAsync(role.Id, cancellationToken);
var addedClaims = claims.Except(currentClaims.Select(c => c.Value));
var removedClaims = currentClaims.Select(c => c.Value).Except(claims);
foreach (var claim in addedClaims)
{
var newClaim = new Claim(LinkAuthorizationConstants.LinkSystemClaims.LinkPermissions, claim);
var outcome = await _roleRepository.AddClaimAsync(role.Id, newClaim, cancellationToken);
if (outcome)
{
_logger.LogRoleClaimAssignment(role.Id.ToString(), newClaim.Type, newClaim.Value, requestor?.Claims.FirstOrDefault(c => c.Type == "sub")?.Value ?? "Unknown");
}
}
foreach (var claim in removedClaims)
{
var roleClaim = currentClaims.FirstOrDefault(c => c.Value == claim);
if (roleClaim is not null)
{
var outcome = await _roleRepository.RemoveClaimAsync(role.Id, roleClaim, cancellationToken);
if (outcome)
{
_logger.LogRoleClaimAssignment(role.Id.ToString(), roleClaim.Type, roleClaim.Value, requestor?.Claims.FirstOrDefault(c => c.Type == "sub")?.Value ?? "Unknown");
}
}
}
//Capture changes
List<PropertyChangeModel> changes = [];
if (addedClaims.Any() || removedClaims.Any())
{
changes.Add(new PropertyChangeModel("Claims", string.Join(",", currentClaims), string.Join(",", claims)));
}
_logger.LogRoleUpdated(role.Name ?? string.Empty, role.LastModifiedBy ?? string.Empty, _roleModelFactory.Create(role));
//generate audit event
var auditMessage = new AuditEventMessage
{
Action = AuditEventType.Update,
EventDate = DateTime.UtcNow,
UserId = role.LastModifiedBy,
User = requestor?.Identity?.Name ?? string.Empty,
Resource = typeof(LinkRole).Name,
PropertyChanges = changes,
Notes = $"Role ({role.Id}) updated by '{role.LastModifiedBy}'."
};
_ = Task.Run(() => _createAuditEvent.Execute(auditMessage, cancellationToken));
//clear user cache for any user with the role that has changed
if (!string.IsNullOrEmpty(role.Name))
{
var users = await _userRepository.GetRoleUsersAsync(role.Name, cancellationToken);
if (users.Any())
{
foreach (var user in users)
{
var userKey = $"user:{user.Email}";
await _cache.RemoveAsync(userKey, cancellationToken);
}
}
}
return true;
}
catch (Exception)
{
activity?.SetStatus(ActivityStatusCode.Error);
throw;
}
}
}
}