1
1
// Copyright (c) .NET Foundation. All rights reserved.
2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
+ using System . Collections . Generic ;
4
5
using System . DirectoryServices . Protocols ;
5
6
using System . Linq ;
6
7
using System . Security . Claims ;
7
8
using System . Text ;
8
9
using System . Threading . Tasks ;
10
+ using Microsoft . Extensions . Caching . Memory ;
9
11
using Microsoft . Extensions . Logging ;
10
12
11
13
namespace Microsoft . AspNetCore . Authentication . Negotiate
@@ -15,8 +17,26 @@ internal static class LdapAdapter
15
17
public static async Task RetrieveClaimsAsync ( LdapSettings settings , ClaimsIdentity identity , ILogger logger )
16
18
{
17
19
var user = identity . Name ;
18
- var userAccountName = user . Substring ( 0 , user . IndexOf ( '@' ) ) ;
20
+ var userAccountNameIndex = user . IndexOf ( '@' ) ;
21
+ var userAccountName = userAccountNameIndex == - 1 ? user : user . Substring ( 0 , userAccountNameIndex ) ;
22
+
23
+ if ( settings . ClaimsCache == null )
24
+ {
25
+ settings . ClaimsCache = new MemoryCache ( new MemoryCacheOptions { SizeLimit = settings . ClaimsCacheSize } ) ;
26
+ }
27
+
28
+ if ( settings . ClaimsCache . TryGetValue < IEnumerable < string > > ( user , out var cachedClaims ) )
29
+ {
30
+ foreach ( var claim in cachedClaims )
31
+ {
32
+ identity . AddClaim ( new Claim ( identity . RoleClaimType , claim ) ) ;
33
+ }
34
+
35
+ return ;
36
+ }
37
+
19
38
var distinguishedName = settings . Domain . Split ( '.' ) . Select ( name => $ "dc={ name } ") . Aggregate ( ( a , b ) => $ "{ a } ,{ b } ") ;
39
+ var retrievedClaims = new List < string > ( ) ;
20
40
21
41
var filter = $ "(&(objectClass=user)(sAMAccountName={ userAccountName } ))"; // This is using ldap search query language, it is looking on the server for someUser
22
42
var searchRequest = new SearchRequest ( distinguishedName , filter , SearchScope . Subtree , null ) ;
@@ -45,24 +65,38 @@ public static async Task RetrieveClaimsAsync(LdapSettings settings, ClaimsIdenti
45
65
46
66
if ( ! settings . IgnoreNestedGroups )
47
67
{
48
- GetNestedGroups ( settings . LdapConnection , identity , distinguishedName , groupCN , logger ) ;
68
+ GetNestedGroups ( settings . LdapConnection , identity , distinguishedName , groupCN , logger , retrievedClaims ) ;
49
69
}
50
70
else
51
71
{
52
- AddRole ( identity , groupCN ) ;
72
+ retrievedClaims . Add ( groupCN ) ;
53
73
}
54
74
}
75
+
76
+ var entrySize = user . Length * 2 ; //Approximate the size of stored key in memory cache.
77
+ foreach ( var claim in retrievedClaims )
78
+ {
79
+ identity . AddClaim ( new Claim ( identity . RoleClaimType , claim ) ) ;
80
+ entrySize += claim . Length * 2 ; //Approximate the size of stored value in memory cache.
81
+ }
82
+
83
+ settings . ClaimsCache . Set ( user ,
84
+ retrievedClaims ,
85
+ new MemoryCacheEntryOptions ( )
86
+ . SetSize ( entrySize )
87
+ . SetSlidingExpiration ( settings . ClaimsCacheSlidingExpiration )
88
+ . SetAbsoluteExpiration ( settings . ClaimsCacheAbsoluteExpiration ) ) ;
55
89
}
56
90
else
57
91
{
58
92
logger . LogWarning ( $ "No response received for query: { filter } with distinguished name: { distinguishedName } ") ;
59
93
}
60
94
}
61
95
62
- private static void GetNestedGroups ( LdapConnection connection , ClaimsIdentity principal , string distinguishedName , string groupCN , ILogger logger )
96
+ private static void GetNestedGroups ( LdapConnection connection , ClaimsIdentity principal , string distinguishedName , string groupCN , ILogger logger , IList < string > retrievedClaims )
63
97
{
64
98
var filter = $ "(&(objectClass=group)(sAMAccountName={ groupCN } ))"; // This is using ldap search query language, it is looking on the server for someUser
65
- var searchRequest = new SearchRequest ( distinguishedName , filter , System . DirectoryServices . Protocols . SearchScope . Subtree , null ) ;
99
+ var searchRequest = new SearchRequest ( distinguishedName , filter , SearchScope . Subtree , null ) ;
66
100
var searchResponse = ( SearchResponse ) connection . SendRequest ( searchRequest ) ;
67
101
68
102
if ( searchResponse . Entries . Count > 0 )
@@ -74,7 +108,7 @@ private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity pr
74
108
75
109
var group = searchResponse . Entries [ 0 ] ; //Get the object that was found on ldap
76
110
string name = group . DistinguishedName ;
77
- AddRole ( principal , name ) ;
111
+ retrievedClaims . Add ( name ) ;
78
112
79
113
var memberof = group . Attributes [ "memberof" ] ; // You can access ldap Attributes with Attributes property
80
114
if ( memberof != null )
@@ -83,15 +117,10 @@ private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity pr
83
117
{
84
118
var groupDN = $ "{ Encoding . UTF8 . GetString ( ( byte [ ] ) member ) } ";
85
119
var nestedGroupCN = groupDN . Split ( ',' ) [ 0 ] . Substring ( "CN=" . Length ) ;
86
- GetNestedGroups ( connection , principal , distinguishedName , nestedGroupCN , logger ) ;
120
+ GetNestedGroups ( connection , principal , distinguishedName , nestedGroupCN , logger , retrievedClaims ) ;
87
121
}
88
122
}
89
123
}
90
124
}
91
-
92
- private static void AddRole ( ClaimsIdentity identity , string role )
93
- {
94
- identity . AddClaim ( new Claim ( identity . RoleClaimType , role ) ) ;
95
- }
96
125
}
97
126
}
0 commit comments