Skip to content

Commit 6311f5f

Browse files
jonkjetiloyeJon Kjetil Øye
andauthored
UserProfile Cache improvement (#1227)
#1222 - UserProfile lookups are now cached on both UserId and PersonId (SSN) - DelegationContextHandler now use the same cached implementation from ContextHandler base class Co-authored-by: Jon Kjetil Øye <acn-joye@ai-dev.no>
1 parent cf7c1d2 commit 6311f5f

File tree

6 files changed

+148
-17
lines changed

6 files changed

+148
-17
lines changed

src/Authorization/Services/Implementation/ContextHandler.cs

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ namespace Altinn.Platform.Authorization.Services.Implementation
3232
/// </summary>
3333
public class ContextHandler : IContextHandler
3434
{
35+
private readonly string _uidUserProfileCacheKeyPrefix = "profile:uid:";
36+
private readonly string _pidUserProfileCacheKeyPrefix = "profile:pid:";
37+
3538
#pragma warning disable SA1401 // Fields should be private
3639
#pragma warning disable SA1600 // Elements should be documented
3740
protected readonly IInstanceMetadataRepository _policyInformationRepository;
@@ -403,8 +406,7 @@ protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string
403406

404407
if (!string.IsNullOrEmpty(subjectSsn))
405408
{
406-
UserProfile subjectProfile = await _profileWrapper.GetUserProfileByPersonId(subjectSsn);
407-
409+
UserProfile subjectProfile = await GetUserProfileByPersonId(subjectSsn);
408410
if (subjectProfile != null)
409411
{
410412
subjectUserId = subjectProfile.UserId;
@@ -439,7 +441,7 @@ protected async Task EnrichSubjectAttributes(XacmlContextRequest request, string
439441
{
440442
if (string.IsNullOrEmpty(subjectSsn))
441443
{
442-
subjectSsn = await GetSsnForUser(subjectUserId);
444+
subjectSsn = await GetPersonIdForUser(subjectUserId);
443445
}
444446

445447
string resourceSsn = await GetSSnForParty(resourcePartyId);
@@ -667,31 +669,77 @@ protected async Task<List<OedRoleAssignment>> GetOedRoleAssignments(string from,
667669
return oedRoles;
668670
}
669671

670-
private string GetCacheKey(int userId, int partyId)
672+
/// <summary>
673+
/// Method that fetches the user profile for a given user id
674+
/// </summary>
675+
/// <param name="userId">The user id</param>
676+
/// <param name="cancellationToken">The <see cref="CancellationToken"/></param>
677+
/// <returns>The user profile</returns>
678+
protected async Task<UserProfile> GetUserProfileByUserId(int userId, CancellationToken cancellationToken = default)
671679
{
672-
return "rolelist_" + userId + "_" + partyId;
673-
}
680+
string uidCacheKey = $"{_uidUserProfileCacheKeyPrefix}{userId}";
674681

675-
private string GetOedRoleassignmentCacheKey(string from, string to)
676-
{
677-
return $"oed{from}_{to}";
682+
if (!_memoryCache.TryGetValue(uidCacheKey, out UserProfile userProfile))
683+
{
684+
userProfile = await _profileWrapper.GetUserProfile(userId, cancellationToken);
685+
686+
var cacheEntryOptions = new MemoryCacheEntryOptions()
687+
.SetPriority(CacheItemPriority.High)
688+
.SetAbsoluteExpiration(new TimeSpan(0, _generalSettings.RoleCacheTimeout, 0));
689+
690+
_memoryCache.Set(uidCacheKey, userProfile, cacheEntryOptions);
691+
692+
if (!string.IsNullOrWhiteSpace(userProfile?.Party?.SSN))
693+
{
694+
_memoryCache.Set($"{_pidUserProfileCacheKeyPrefix}{userProfile.Party.SSN}", userProfile, cacheEntryOptions);
695+
}
696+
}
697+
698+
return userProfile;
678699
}
679700

680-
private async Task<string> GetSsnForUser(int userId)
701+
/// <summary>
702+
/// Method that fetches the user profile for a given person id (aka national identity number or ssn)
703+
/// </summary>
704+
/// <param name="personId">The person id</param>
705+
/// <param name="cancellationToken">The <see cref="CancellationToken"/></param>
706+
/// <returns>The user profile</returns>
707+
protected async Task<UserProfile> GetUserProfileByPersonId(string personId, CancellationToken cancellationToken = default)
681708
{
682-
string cacheKey = $"uid:{userId}";
709+
string pidCacheKey = $"{_pidUserProfileCacheKeyPrefix}{personId}";
683710

684-
if (!_memoryCache.TryGetValue(cacheKey, out UserProfile userProfile))
711+
if (!_memoryCache.TryGetValue(pidCacheKey, out UserProfile userProfile))
685712
{
686-
userProfile = await _profileWrapper.GetUserProfile(userId);
713+
userProfile = await _profileWrapper.GetUserProfileByPersonId(personId, cancellationToken);
687714

688715
var cacheEntryOptions = new MemoryCacheEntryOptions()
689716
.SetPriority(CacheItemPriority.High)
690717
.SetAbsoluteExpiration(new TimeSpan(0, _generalSettings.RoleCacheTimeout, 0));
691718

692-
_memoryCache.Set(cacheKey, userProfile, cacheEntryOptions);
719+
_memoryCache.Set(pidCacheKey, userProfile, cacheEntryOptions);
720+
721+
if (userProfile?.UserId > 0)
722+
{
723+
_memoryCache.Set($"{_uidUserProfileCacheKeyPrefix}{userProfile.UserId}", userProfile, cacheEntryOptions);
724+
}
693725
}
694726

727+
return userProfile;
728+
}
729+
730+
private string GetCacheKey(int userId, int partyId)
731+
{
732+
return "rolelist_" + userId + "_" + partyId;
733+
}
734+
735+
private string GetOedRoleassignmentCacheKey(string from, string to)
736+
{
737+
return $"oed{from}_{to}";
738+
}
739+
740+
private async Task<string> GetPersonIdForUser(int userId, CancellationToken cancellationToken = default)
741+
{
742+
UserProfile userProfile = await GetUserProfileByUserId(userId, cancellationToken);
695743
return userProfile?.Party?.SSN;
696744
}
697745

src/Authorization/Services/Implementation/DelegationContextHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public async Task EnrichRequestSubjectAttributes(XacmlContextAttributes requestS
5656
if (isInstanceAccessRequest)
5757
{
5858
// Instance delegation policies use uuid as subject, meaning the request needs to be enriched with the users uuid
59-
UserProfile userProfile = await _profileWrapper.GetUserProfile(subjectUserId, cancellationToken);
59+
UserProfile userProfile = await GetUserProfileByUserId(subjectUserId, cancellationToken);
6060
if (userProfile != null && userProfile.Party != null &&
6161
userProfile.Party.PartyTypeName == PartyType.Person && userProfile.Party.PartyUuid.HasValue)
6262
{

test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistryMulti0012Request.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@
1010
"Value": "01039012345"
1111
}
1212
]
13+
},
14+
{
15+
"Id": "s2",
16+
"Attribute": [
17+
{
18+
"AttributeId": "urn:altinn:userid",
19+
"Value": "1337"
20+
}
21+
]
1322
}
1423
],
1524
"Action": [
@@ -70,6 +79,26 @@
7079
"IncludeInResult": true
7180
}
7281
]
82+
},
83+
{
84+
"Id": "r4",
85+
"Attribute": [
86+
{
87+
"AttributeId": "urn:altinn:resource",
88+
"Value": "ttd-externalpdp-resource3",
89+
"IncludeInResult": true
90+
},
91+
{
92+
"AttributeId": "urn:altinn:resource:instance-id",
93+
"Value": "4c1b880e-f425-4050-9a46-cd1b3e56bf94",
94+
"IncludeInResult": true
95+
},
96+
{
97+
"AttributeId": "urn:altinn:organization:identifier-no",
98+
"Value": "910459880",
99+
"IncludeInResult": true
100+
}
101+
]
73102
}
74103
],
75104
"MultiRequests": {
@@ -94,6 +123,13 @@
94123
"a1",
95124
"r3"
96125
]
126+
},
127+
{
128+
"ReferenceId": [
129+
"s2",
130+
"a1",
131+
"r4"
132+
]
97133
}
98134
]
99135
}

test/IntegrationTests/Data/Xacml/3.0/ResourceRegistry/AltinnResourceRegistryMulti0012Response.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,46 @@
135135
}
136136

137137
]
138+
},
139+
{
140+
"Decision": "NotApplicable",
141+
"Status": {
142+
"StatusCode": {
143+
"Value": "urn:oasis:names:tc:xacml:1.0:status:ok"
144+
}
145+
},
146+
"Category": [
147+
{
148+
"CategoryId": "urn:oasis:names:tc:xacml:3.0:attribute-category:action",
149+
"Attribute": [
150+
{
151+
"AttributeId": "urn:oasis:names:tc:xacml:1.0:action:action-id",
152+
"DataType": "http://www.w3.org/2001/XMLSchema#string",
153+
"Value": "read"
154+
}
155+
]
156+
},
157+
{
158+
"CategoryId": "urn:oasis:names:tc:xacml:3.0:attribute-category:resource",
159+
"Attribute": [
160+
{
161+
"AttributeId": "urn:altinn:resource",
162+
"DataType": "http://www.w3.org/2001/XMLSchema#string",
163+
"Value": "ttd-externalpdp-resource3"
164+
},
165+
{
166+
"AttributeId": "urn:altinn:resource:instance-id",
167+
"DataType": "http://www.w3.org/2001/XMLSchema#string",
168+
"Value": "4c1b880e-f425-4050-9a46-cd1b3e56bf94"
169+
},
170+
{
171+
"AttributeId": "urn:altinn:organization:identifier-no",
172+
"DataType": "http://www.w3.org/2001/XMLSchema#string",
173+
"Value": "910459880"
174+
}
175+
]
176+
}
177+
]
138178
}
139179
]
140180
}

test/IntegrationTests/MockServices/AccessManagementWrapperMock.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ public Task<IEnumerable<DelegationChangeExternal>> GetAllDelegationChanges(Deleg
7070
DelegationChangesTestData.Default(DelegationChangesTestData.WithResourceID("app_org1_app1"), DelegationChangesTestData.WithResourceInstanceId("f8d3526c-596b-4322-a041-38a8925c2a82"), DelegationChangesTestData.WithFromUuid(UuidType.Organization, Guid.Parse("00000000-0000-0000-0005-000000005545")), DelegationChangesTestData.WithToUuid(UuidType.Organization, Guid.Parse("00000000-0000-0000-0005-000000004222"))),
7171
WithDefaultCondition("org1/app1", new AttributeMatch { Id = XacmlRequestAttribute.PartyAttribute, Value = "50005545" }, new AttributeMatch { Id = XacmlRequestAttribute.PartyAttribute, Value = "50004222" }),
7272
WithDefaultCondition("org1/app1", new AttributeMatch { Id = XacmlRequestAttribute.PartyAttribute, Value = "50005545" }, new AttributeMatch { Id = XacmlRequestAttribute.UserAttribute, Value = "20000095" })),
73+
ConditionalAdd(
74+
DelegationChangesTestData.Default(DelegationChangesTestData.WithResourceID("ttd-externalpdp-resource3"), DelegationChangesTestData.WithResourceInstanceId("4c1b880e-f425-4050-9a46-cd1b3e56bf94"), DelegationChangesTestData.WithOfferedByPartyID(50005545), DelegationChangesTestData.WithCoveredByUserID(1337)),
75+
WithDefaultCondition("ttd-externalpdp-resource3", new AttributeMatch { Id = XacmlRequestAttribute.PartyAttribute, Value = "50005545" }, new AttributeMatch { Id = XacmlRequestAttribute.UserAttribute, Value = "1337" })),
7376
};
7477

7578
var result = new List<DelegationChangeExternal>();

test/IntegrationTests/MockServices/ProfileMock.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ public Task<UserProfile> GetUserProfile(int userId, CancellationToken cancellati
1616
{
1717
userProfile = new UserProfile { Party = new Party { SSN = "13923949741" } };
1818
}
19+
else if (userId == 13371337)
20+
{
21+
userProfile = new UserProfile { UserId = userId, Party = new Party { SSN = "13371337133" } };
22+
}
1923
else if (userId == 1337)
2024
{
21-
userProfile = new UserProfile { Party = new Party { SSN = "13371337133" } };
25+
userProfile = new UserProfile { UserId = userId, Party = new Party { SSN = "01039012345", PartyId = 1337, PartyUuid = Guid.Parse("00000000-0000-0000-0005-000000001337"), PartyTypeName = Register.Enums.PartyType.Person } };
2226
}
2327
else if (userId == 20000517)
2428
{
@@ -41,7 +45,7 @@ public Task<UserProfile> GetUserProfileByPersonId(string personId, CancellationT
4145
}
4246
else if (personId == "13371337133")
4347
{
44-
userProfile = new UserProfile { Party = new Party { SSN = personId } };
48+
userProfile = new UserProfile { UserId = 13371337, Party = new Party { SSN = personId } };
4549
}
4650
else if (personId == "01039012345")
4751
{

0 commit comments

Comments
 (0)