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+
58+using GSF.Collections;
59+using GSF.IO;
60+using GSF.Security.Cryptography;
61+using GSF.Threading;
62+using System;
63+using System.Collections.Generic;
64+using System.IO;
65+using System.Security.Cryptography;
66+
67+namespace 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