11// Copyright (c) .NET Foundation. All rights reserved.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
4+ using System . Collections . Generic ;
45using System . DirectoryServices . Protocols ;
56using System . Linq ;
67using System . Security . Claims ;
78using System . Text ;
89using System . Threading . Tasks ;
10+ using Microsoft . Extensions . Caching . Memory ;
911using Microsoft . Extensions . Logging ;
1012
1113namespace Microsoft . AspNetCore . Authentication . Negotiate
@@ -15,8 +17,26 @@ internal static class LdapAdapter
1517 public static async Task RetrieveClaimsAsync ( LdapSettings settings , ClaimsIdentity identity , ILogger logger )
1618 {
1719 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+
1938 var distinguishedName = settings . Domain . Split ( '.' ) . Select ( name => $ "dc={ name } ") . Aggregate ( ( a , b ) => $ "{ a } ,{ b } ") ;
39+ var retrievedClaims = new List < string > ( ) ;
2040
2141 var filter = $ "(&(objectClass=user)(sAMAccountName={ userAccountName } ))"; // This is using ldap search query language, it is looking on the server for someUser
2242 var searchRequest = new SearchRequest ( distinguishedName , filter , SearchScope . Subtree , null ) ;
@@ -45,24 +65,38 @@ public static async Task RetrieveClaimsAsync(LdapSettings settings, ClaimsIdenti
4565
4666 if ( ! settings . IgnoreNestedGroups )
4767 {
48- GetNestedGroups ( settings . LdapConnection , identity , distinguishedName , groupCN , logger ) ;
68+ GetNestedGroups ( settings . LdapConnection , identity , distinguishedName , groupCN , logger , retrievedClaims ) ;
4969 }
5070 else
5171 {
52- AddRole ( identity , groupCN ) ;
72+ retrievedClaims . Add ( groupCN ) ;
5373 }
5474 }
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 ) ) ;
5589 }
5690 else
5791 {
5892 logger . LogWarning ( $ "No response received for query: { filter } with distinguished name: { distinguishedName } ") ;
5993 }
6094 }
6195
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 )
6397 {
6498 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 ) ;
66100 var searchResponse = ( SearchResponse ) connection . SendRequest ( searchRequest ) ;
67101
68102 if ( searchResponse . Entries . Count > 0 )
@@ -74,7 +108,7 @@ private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity pr
74108
75109 var group = searchResponse . Entries [ 0 ] ; //Get the object that was found on ldap
76110 string name = group . DistinguishedName ;
77- AddRole ( principal , name ) ;
111+ retrievedClaims . Add ( name ) ;
78112
79113 var memberof = group . Attributes [ "memberof" ] ; // You can access ldap Attributes with Attributes property
80114 if ( memberof != null )
@@ -83,15 +117,10 @@ private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity pr
83117 {
84118 var groupDN = $ "{ Encoding . UTF8 . GetString ( ( byte [ ] ) member ) } ";
85119 var nestedGroupCN = groupDN . Split ( ',' ) [ 0 ] . Substring ( "CN=" . Length ) ;
86- GetNestedGroups ( connection , principal , distinguishedName , nestedGroupCN , logger ) ;
120+ GetNestedGroups ( connection , principal , distinguishedName , nestedGroupCN , logger , retrievedClaims ) ;
87121 }
88122 }
89123 }
90124 }
91-
92- private static void AddRole ( ClaimsIdentity identity , string role )
93- {
94- identity . AddClaim ( new Claim ( identity . RoleClaimType , role ) ) ;
95- }
96125 }
97126}
0 commit comments