History

Renamed folders and separated GSF.Media and GSF.Net into separate projects.

StephenCWills committed 14 years ago

Commit ac1ef661 parent 45bce55628 files · +733 −461Browse files

Showing the changes to Source/Libraries/GSF.Security/UserDataCache.cs (1 of 628 files). Show all

Unified · Split

Source/Libraries/GSF.Security/UserDataCache.cs+322−0
@@ -0,0 +1,322 @@
1//******************************************************************************************************
2// UserDataCache.cs - Gbtc
3//
4// Copyright © 2010, Grid Protection Alliance. All Rights Reserved.
5//
6// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
9// not use this file except in compliance with the License. You may obtain a copy of the License at:
10//
11// http://www.opensource.org/licenses/eclipse-1.0.php
12//
13// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15// License for the specific language governing permissions and limitations.
16//
17// Code Modification History:
18// ----------------------------------------------------------------------------------------------------
19// 04/06/2011 - J. Ritchie Carroll
20// Generated original version of source code.
21// 04/14/2011 - Pinal C. Patel
22// Updated to use new serialization and deserialization methods in GSF.Serialization class.
23// 06/10/2011 - Pinal C. Patel
24// Renamed RetryDelayInterval and MaximumRetryAttempts settings persisted to the config file
25// to CacheRetryDelayInterval and CacheMaximumRetryAttempts for clarity.
26// 08/12/2011 - J. Ritchie Carroll
27// Modifed static GetCurrentCache to accept settings category of host security provider
28// implementation in case the category has been changed from the default value by the consumer.
29// 08/16/2011 - Pinal C. Patel
30// Modified GetCurrentCache() to just set the FileName property and not the RetryDelayInterval,
31// MaximumRetryAttempts, ReloadOnChange and AutoSave properties.
32// 10/8/2012 - Danyelle Gilliam
33// Modified Header
34//
35//******************************************************************************************************
36
37
38
39#region [ Contributor License Agreements ]
40
41//******************************************************************************************************
42//
43// Copyright © 2011, Grid Protection Alliance. All Rights Reserved.
44//
45// The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
46// not use this file except in compliance with the License. You may obtain a copy of the License at:
47//
48// http://www.opensource.org/licenses/eclipse-1.0.php
49//
50// Unless agreed to in writing, the subject software distributed under the License is distributed on an
51// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
52// License for the specific language governing permissions and limitations.
53//
54//******************************************************************************************************
55
56#endregion
57
58using GSF.Collections;
59using GSF.IO;
60using GSF.Security.Cryptography;
61using GSF.Threading;
62using System;
63using System.Collections.Generic;
64using System.IO;
65using System.Security.Cryptography;
66
67namespace GSF.Security
68{
69 /// <summary>
70 /// Represents a secured interprocess cache for a <see cref="Dictionary{T1,T2}"/> of serialized <see cref="UserData"/>.
71 /// </summary>
72 public class UserDataCache : InterprocessCache
73 {
74 #region [ Members ]
75
76 // Constants
77
78 // Default key and initialization vector cache file name
79 private const string DefaultCacheFileName = "UserDataCache.bin";
80
81 // Fields
82 private Dictionary<string, UserData> m_userDataTable; // Internal dictionary of serialized user data
83 private object m_userDataTableLock; // Lock object
84 private int m_providerID; // Unique provider ID used to distinguish cached user data that may be different based on provider
85
86 #endregion
87
88 /// <summary>
89 /// Creates a new instance of the <see cref="UserDataCache"/>.
90 /// </summary>
91 /// <param name="providerID">Unique provider ID used to distinguish cached user data that may be different based on provider.</param>
92 public UserDataCache(int providerID = LdapSecurityProvider.ProviderID)
93 : this(providerID, InterprocessReaderWriterLock.DefaultMaximumConcurrentLocks)
94 {
95 }
96
97 /// <summary>
98 /// Creates a new instance of the <see cref="UserDataCache"/> with the specified number of <paramref name="maximumConcurrentLocks"/>.
99 /// </summary>
100 /// <param name="maximumConcurrentLocks">Maximum concurrent reader locks to allow.</param>
101 /// <param name="providerID">Unique provider ID used to distinguish cached user data that may be different based on provider.</param>
102 public UserDataCache(int providerID, int maximumConcurrentLocks)
103 : base(maximumConcurrentLocks)
104 {
105 m_providerID = providerID;
106 m_userDataTable = new Dictionary<string, UserData>();
107 m_userDataTableLock = new object();
108 }
109
110 #region [ Properties ]
111
112 /// <summary>
113 /// Gets or sets <see cref="UserData"/> for given <paramref name="loginID"/>.
114 /// </summary>
115 /// <param name="loginID">Login ID of associated <see cref="UserData"/> to load or save.</param>
116 /// <returns>Reference to <see cref="UserData"/> for given <paramref name="loginID"/> if found; otherwise <c>null</c>.</returns>
117 public UserData this[string loginID]
118 {
119 get
120 {
121 UserData userData;
122 TryGetUserData(loginID, out userData);
123 return userData;
124 }
125 set
126 {
127 SaveUserData(loginID, value);
128 }
129 }
130
131 /// <summary>
132 /// Gets ot sets unique provider ID used to distinguish cached user data that may be different based on provider.
133 /// </summary>
134 public int ProviderID
135 {
136 get
137 {
138 return m_providerID;
139 }
140 set
141 {
142 m_providerID = value;
143 }
144 }
145
146 #endregion
147
148 #region [ Methods ]
149
150 /// <summary>
151 /// Attempts to retrieve <see cref="UserData"/> for given <paramref name="loginID"/>.
152 /// </summary>
153 /// <param name="loginID">Login ID of associated <see cref="UserData"/> to retrieve.</param>
154 /// <param name="userData">Reference to <see cref="UserData"/> object to populate if found.</param>
155 /// <returns><c>true</c> if <see cref="UserData"/> for given <paramref name="loginID"/> was retrieved; otherwise <c>false</c>.</returns>
156 public bool TryGetUserData(string loginID, out UserData userData)
157 {
158 string hash = HashLoginID(loginID);
159 bool result;
160
161 // We wait until the cache is loaded before attempting to access it
162 WaitForDataReady();
163
164 // Wait for thread level lock on user data table
165 lock (m_userDataTableLock)
166 {
167 // Attempt to lookup persisted user data based on hash of login ID
168 result = m_userDataTable.TryGetValue(hash, out userData);
169 }
170
171 return result;
172 }
173
174 /// <summary>
175 /// Serializes the <paramref name="userData"/> for the given <paramref name="loginID"/> into the <see cref="UserDataCache"/>.
176 /// </summary>
177 /// <param name="loginID">Login ID of associated <see cref="UserData"/> to retrieve.</param>
178 /// <param name="userData">Reference to <see cref="UserData"/> object to serialize into <see cref="UserDataCache"/>.</param>
179 /// <remarks>
180 /// <para>
181 /// This will add an entry into the user data cache for <paramref name="loginID"/> if it doesn't exist;
182 /// otherwise existing entry will be updated.
183 /// </para>
184 /// <para>
185 /// Updates are automatically queued up for serialization so user does not need to call <see cref="Save"/>.
186 /// </para>
187 /// </remarks>
188 public void SaveUserData(string loginID, UserData userData)
189 {
190 string hash = HashLoginID(loginID);
191
192 // We wait until the cache is loaded before attempting to access it
193 WaitForDataReady();
194
195 // Wait for thread level lock on user data table
196 lock (m_userDataTableLock)
197 {
198 // Assign new user information to user data table
199 m_userDataTable[hash] = userData;
200 }
201
202 // Queue up a serialization for this new user information
203 Save();
204 }
205
206 /// <summary>
207 /// Initiates interprocess synchronized save of user data cache.
208 /// </summary>
209 public override void Save()
210 {
211 byte[] serializedUserDataTable;
212
213 // Wait for thread level lock on key table
214 lock (m_userDataTableLock)
215 {
216 serializedUserDataTable = Serialization.Serialize(m_userDataTable, SerializationFormat.Binary);
217 }
218
219 // File data is the serialized user data table, assigmnent will initiate auto-save if needed
220 FileData = serializedUserDataTable;
221 }
222
223 /// <summary>
224 /// Handles serialization of file to disk; virtual method allows customization (e.g., pre-save encryption and/or data merge).
225 /// </summary>
226 /// <param name="fileStream"><see cref="FileStream"/> used to serialize data.</param>
227 /// <param name="fileData">File data to be serialized.</param>
228 /// <remarks>
229 /// Consumers overriding this method should not directly call <see cref="InterprocessCache.FileData"/> property to avoid potential dead-locks.
230 /// </remarks>
231 protected override void SaveFileData(FileStream fileStream, byte[] fileData)
232 {
233 // Encrypt data local to this machine (this way user cannot copy user data cache to another machine)
234 base.SaveFileData(fileStream, ProtectedData.Protect(fileData, null, DataProtectionScope.LocalMachine));
235 }
236
237 /// <summary>
238 /// Handles deserialization of file from disk; virtual method allows customization (e.g., pre-load decryption and/or data merge).
239 /// </summary>
240 /// <param name="fileStream"><see cref="FileStream"/> used to deserialize data.</param>
241 /// <returns>Deserialized file data.</returns>
242 /// <remarks>
243 /// Consumers overriding this method should not directly call <see cref="InterprocessCache.FileData"/> property to avoid potential dead-locks.
244 /// </remarks>
245 protected override byte[] LoadFileData(FileStream fileStream)
246 {
247 // Decrypt data that was encrypted local to this machine
248 byte[] serializedUserDataTable = ProtectedData.Unprotect(fileStream.ReadStream(), null, DataProtectionScope.LocalMachine);
249 Dictionary<string, UserData> userDataTable = Serialization.Deserialize<Dictionary<string, UserData>>(serializedUserDataTable, SerializationFormat.Binary);
250
251 // Wait for thread level lock on user data table
252 lock (m_userDataTableLock)
253 {
254 // Merge new and existing key tables since new user information may have been queued for serialization, but not saved yet
255 m_userDataTable = userDataTable.Merge(m_userDataTable);
256 }
257
258 return serializedUserDataTable;
259 }
260
261 /// <summary>
262 /// Calculates the hash of the <paramref name="loginID"/> used as the key for the user data cache.
263 /// </summary>
264 /// <param name="loginID">Login ID to hash.</param>
265 /// <returns>The Base64 encoded calculated SHA-2 hash of the <paramref name="loginID"/> used as the key for the user data cache.</returns>
266 /// <remarks>
267 /// For added security, a hash of the <paramref name="loginID"/> is used as the key for <see cref="UserData"/> in the
268 /// user data cache instead of the actual <paramref name="loginID"/>. This method allows the
269 /// consumer to properly calculate this hash when directly using the user data cache.
270 /// </remarks>
271 protected string HashLoginID(string loginID)
272 {
273 return Cipher.GetPasswordHash(loginID.ToLower(), m_providerID);
274 }
275
276 // Waits until the cache is loaded before attempting to access it
277 private void WaitForDataReady()
278 {
279 try
280 {
281 // Just wrapping this method to provide a more detailed exception message if there is an issue loading cache
282 WaitForLoad();
283 }
284 catch (Exception ex)
285 {
286 throw new UnauthorizedAccessException("User data access failure: timeout while attempting to load user data cache.", ex);
287 }
288 }
289
290 #endregion
291
292 #region [ Static ]
293
294 // Static Methods
295
296 /// <summary>
297 /// Loads the <see cref="UserDataCache"/> for the current local user.
298 /// </summary>
299 /// <param name="providerID">Unique security provider ID used to distinguish cached user data that may be different based on provider.</param>
300 /// <returns>Loaded instance of the <see cref="UserDataCache"/>.</returns>
301 public static UserDataCache GetCurrentCache(int providerID)
302 {
303 // By default user data cache is stored in a path where user will have rights
304 UserDataCache userDataCache;
305 string userCacheFolder = FilePath.GetApplicationDataFolder();
306 string userCacheFileName = Path.Combine(userCacheFolder, FilePath.GetFileName(DefaultCacheFileName));
307
308 // Make sure user directory exists
309 if (!Directory.Exists(userCacheFolder))
310 Directory.CreateDirectory(userCacheFolder);
311
312 // Initialize user data cache for current local user
313 userDataCache = new UserDataCache(providerID);
314 userDataCache.FileName = userCacheFileName;
315
316 return userDataCache;
317 }
318
319 #endregion
320
321 }
322}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t