[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Security / Tokens / WindowsUserNameCachingSecurityTokenAuthenticator.cs
blobac169b1975025b7418ef05323d37923336502d0f
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;
14 using System.Text;
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);
40 return token != null;
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)
58 items.Add(value);
60 // Those expired soon in front
61 items.Sort(ExpirableItemComparer.Default);
62 int pruningAmount = (items.Count * (100 - lowWaterMarkFactor)) / 100;
63 // edge case
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);
70 OnRemove(token);
72 return keys;
75 public bool TryRemoveTokenCache(string userName)
77 return this.TryRemoveItem(userName);
80 public void Flush()
82 this.ClearItems();
85 protected override void OnRemove(object item)
87 ((LogonToken)item).Dispose();
88 base.OnRemove(item);
92 class LogonToken : IDisposable
94 string userName;
95 byte[] passwordHash;
96 byte[] salt;
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);
103 this.salt = 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)
154 LogonToken token;
155 if (this.logonTokenCache.TryGetTokenCache(userName, out token))
157 if (token.PasswordEquals(password))
159 return token.GetAuthorizationPolicies();
161 else
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)
176 return false;
178 return this.logonTokenCache.TryRemoveTokenCache(username);
181 public void FlushLogonTokenCache()
183 if (this.logonTokenCache != null)
184 this.logonTokenCache.Flush();