1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System
.ServiceModel
.Security
.Tokens
7 using System
.Collections
;
8 using System
.Collections
.Generic
;
9 using System
.Collections
.ObjectModel
;
10 using System
.IdentityModel
.Policy
;
11 using System
.IdentityModel
.Selectors
;
12 using System
.IdentityModel
.Tokens
;
13 using System
.Security
.Cryptography
;
16 public interface ILogonTokenCacheManager
18 bool RemoveCachedLogonToken(string username
);
19 void FlushLogonTokenCache();
22 class LogonTokenCache
: TimeBoundedCache
24 const int lowWaterMarkFactor
= 75;
25 const int saltSize
= 256;
27 TimeSpan cachedLogonTokenLifetime
;
28 RNGCryptoServiceProvider random
;
30 public LogonTokenCache(int maxCachedLogonTokens
, TimeSpan cachedLogonTokenLifetime
)
31 : base((maxCachedLogonTokens
* lowWaterMarkFactor
) / 100, maxCachedLogonTokens
, StringComparer
.OrdinalIgnoreCase
, PurgingMode
.TimerBasedPurge
, TimeSpan
.FromTicks(cachedLogonTokenLifetime
.Ticks
>> 2), true)
33 this.cachedLogonTokenLifetime
= cachedLogonTokenLifetime
;
34 this.random
= new RNGCryptoServiceProvider();
37 public bool TryGetTokenCache(string userName
, out LogonToken token
)
39 token
= (LogonToken
)GetItem(userName
);
43 public bool TryAddTokenCache(string userName
, string password
, ReadOnlyCollection
<IAuthorizationPolicy
> authorizationPolicies
)
45 byte[] salt
= new byte[saltSize
];
46 this.random
.GetBytes(salt
);
47 LogonToken token
= new LogonToken(userName
, password
, salt
, authorizationPolicies
);
48 DateTime expirationTime
= DateTime
.UtcNow
.Add(this.cachedLogonTokenLifetime
);
49 return TryAddItem(userName
, token
, expirationTime
, true);
52 // Remove those about to expire
53 protected override ArrayList
OnQuotaReached(Hashtable cacheTable
)
55 List
<IExpirableItem
> items
= new List
<IExpirableItem
>(cacheTable
.Count
);
56 foreach (IExpirableItem
value in cacheTable
.Values
)
60 // Those expired soon in front
61 items
.Sort(ExpirableItemComparer
.Default
);
62 int pruningAmount
= (items
.Count
* (100 - lowWaterMarkFactor
)) / 100;
64 pruningAmount
= pruningAmount
<= 0 ? items
.Count
: pruningAmount
;
65 ArrayList keys
= new ArrayList(pruningAmount
);
66 for (int i
= 0; i
< pruningAmount
; ++i
)
68 LogonToken token
= (LogonToken
)ExtractItem(items
[i
]);
69 keys
.Add(token
.UserName
);
75 public bool TryRemoveTokenCache(string userName
)
77 return this.TryRemoveItem(userName
);
85 protected override void OnRemove(object item
)
87 ((LogonToken
)item
).Dispose();
92 class LogonToken
: IDisposable
97 ReadOnlyCollection
<IAuthorizationPolicy
> authorizationPolicies
;
99 public LogonToken(string userName
, string password
, byte[] salt
, ReadOnlyCollection
<IAuthorizationPolicy
> authorizationPolicies
)
101 this.userName
= userName
;
102 this.passwordHash
= ComputeHMACSHA256Hash(password
, salt
);
104 this.authorizationPolicies
= System
.IdentityModel
.SecurityUtils
.CloneAuthorizationPoliciesIfNecessary(authorizationPolicies
);
107 public bool PasswordEquals(string password
)
109 byte[] passwordHash
= ComputeHMACSHA256Hash(password
, this.salt
);
110 return CryptoHelper
.IsEqual(this.passwordHash
, passwordHash
);
113 public string UserName
115 get { return this.userName; }
118 public ReadOnlyCollection
<IAuthorizationPolicy
> GetAuthorizationPolicies()
120 return System
.IdentityModel
.SecurityUtils
.CloneAuthorizationPoliciesIfNecessary(this.authorizationPolicies
);
123 public void Dispose()
125 System
.IdentityModel
.SecurityUtils
.DisposeAuthorizationPoliciesIfNecessary(this.authorizationPolicies
);
128 static byte[] ComputeHMACSHA256Hash(string password
, byte[] key
)
130 using (HMACSHA256 hmac
= new HMACSHA256(key
))
132 return hmac
.ComputeHash(Encoding
.Unicode
.GetBytes(password
));
137 class WindowsUserNameCachingSecurityTokenAuthenticator
: WindowsUserNameSecurityTokenAuthenticator
, ILogonTokenCacheManager
, IDisposable
139 LogonTokenCache logonTokenCache
;
141 public WindowsUserNameCachingSecurityTokenAuthenticator(bool includeWindowsGroups
, int maxCachedLogonTokens
, TimeSpan cachedLogonTokenLifetime
)
142 : base(includeWindowsGroups
)
144 this.logonTokenCache
= new LogonTokenCache(maxCachedLogonTokens
, cachedLogonTokenLifetime
);
147 public void Dispose()
149 FlushLogonTokenCache();
152 protected override ReadOnlyCollection
<IAuthorizationPolicy
> ValidateUserNamePasswordCore(string userName
, string password
)
155 if (this.logonTokenCache
.TryGetTokenCache(userName
, out token
))
157 if (token
.PasswordEquals(password
))
159 return token
.GetAuthorizationPolicies();
163 // this prevents logon with old password.
164 this.logonTokenCache
.TryRemoveTokenCache(userName
);
168 ReadOnlyCollection
<IAuthorizationPolicy
> authorizationPolicies
= base.ValidateUserNamePasswordCore(userName
, password
);
169 this.logonTokenCache
.TryAddTokenCache(userName
, password
, authorizationPolicies
);
170 return authorizationPolicies
;
173 public bool RemoveCachedLogonToken(string username
)
175 if (this.logonTokenCache
== null)
178 return this.logonTokenCache
.TryRemoveTokenCache(username
);
181 public void FlushLogonTokenCache()
183 if (this.logonTokenCache
!= null)
184 this.logonTokenCache
.Flush();