1: using System;
2: using System.Collections.Generic;
3: using System.Collections.Specialized;
4: using System.Configuration;
5: using System.Configuration.Provider;
6: using System.DirectoryServices;
7: using System.Text;
8: using System.Web;
9: using System.Web.Caching;
10: using System.Web.Configuration;
11: using EPiServer.Security;
12:
13: namespace MyWebSite.Security
14: {
15: public class AdsiDataFactory : DirectoryDataFactory
16: {
17: // Fields
18: private string _baseConnectionString;
19: private const string _cacheKeyEntryPrefix = "EPiServer:DirectoryServiceEntry:";
20: private const string _cacheKeyFindAllPrefix = "EPiServer:DirectoryServiceFindAll:";
21: private const string _cacheKeyFindOnePrefix = "EPiServer:DirectoryServiceFindOne:";
22: private TimeSpan _cacheTimeout;
23: private AuthenticationTypes _connectionProtection;
24: private string _connectionString;
25: private const string _distingushedNameAttribute = "distinguishedName";
26: private const string _objectClassAttribute = "objectClass";
27: private string _password;
28: private List<string> _propertiesToLoad;
29: private const string _reservedCharacters = "\r\n+\\\"<>;/";
30: private const string _rootCacheKey = "EPiServer:DirectoryServiceRoot";
31: private string _username;
32:
33: // Methods
34: public AdsiDataFactory()
35: {
36: }
37:
38: public AdsiDataFactory(string connectionString, string username, string password, AuthenticationTypes connectionProtection, TimeSpan absoluteCacheTimeout)
39: {
40: this._connectionString = connectionString;
41: this._username = username;
42: this._password = password;
43: this._connectionProtection = connectionProtection;
44: this._cacheTimeout = absoluteCacheTimeout;
45: this.Initialize();
46: }
47:
48: public override void AddPropertyToLoad(string propertyName)
49: {
50: if (!this._propertiesToLoad.Contains(propertyName))
51: {
52: this._propertiesToLoad.Add(propertyName);
53: this.ClearCache();
54: }
55: }
56:
57: public void ClearCache()
58: {
59: HttpRuntime.Cache.Remove("EPiServer:DirectoryServiceRoot");
60: }
61:
62: protected DirectoryData CreateDirectoryDataFromDirectoryEntry(DirectoryEntry entry, string distinguishedName)
63: {
64: if (entry == null)
65: {
66: return null;
67: }
68: Dictionary<string, string[]> properties = new Dictionary<string, string[]>(this._propertiesToLoad.Count);
69: foreach (string str in this._propertiesToLoad)
70: {
71: try
72: {
73: if (entry.Properties.Contains(str))
74: {
75: PropertyValueCollection values = entry.Properties[str];
76: string[] strArray = new string[values.Count];
77: for (int i = 0; i < values.Count; i++)
78: {
79: strArray[i] = values[i].ToString();
80: }
81: properties.Add(str, strArray);
82: }
83: }
84: catch (DirectoryServicesCOMException ex)
85: {
86: StringBuilder sb = new StringBuilder();
87: foreach (string s in this._propertiesToLoad)
88: {
89: sb.Append(" | " + s);
90: }
91:
92: throw new Exception("Error in CreateDirectoryDataFromDirectoryEntry!" + Environment.NewLine +
93: "Value of last str: " + str + Environment.NewLine +
94: "All Properties: " + sb.ToString() + Environment.NewLine +
95: "distinguishedName: " + distinguishedName, ex);
96: }
97: }
98: return new DirectoryData(this.DistinguishedName(properties), entry.SchemaClassName, properties);
99: }
100:
101: protected DirectoryData CreateDirectoryDataFromSearchResult(SearchResult result)
102: {
103: if (result == null)
104: {
105: return null;
106: }
107: Dictionary<string, string[]> properties = new Dictionary<string, string[]>(this._propertiesToLoad.Count);
108: foreach (string str in this._propertiesToLoad)
109: {
110: if (result.Properties.Contains(str))
111: {
112: ResultPropertyValueCollection values = result.Properties[str];
113: string[] strArray = new string[values.Count];
114: for (int i = 0; i < values.Count; i++)
115: {
116: strArray[i] = values[i].ToString();
117: }
118: properties.Add(str, strArray);
119: }
120: }
121: return new DirectoryData(this.DistinguishedName(properties), this.SchemaClassName(properties), properties);
122: }
123:
124: protected DirectoryEntry CreateDirectoryEntry()
125: {
126: return new DirectoryEntry(this._connectionString, this._username, this._password, this._connectionProtection);
127: }
128:
129: protected DirectoryEntry CreateDirectoryEntry(string rootDistinguishedName)
130: {
131: if (!base.IsWithinSubtree(rootDistinguishedName))
132: {
133: return null;
134: }
135: return new DirectoryEntry(this._baseConnectionString + this.EscapeDistinguishedName(rootDistinguishedName), this._username, this._password, this._connectionProtection);
136: }
137:
138: protected string DistinguishedName(Dictionary<string, string[]> properties)
139: {
140: return properties["distinguishedName"][0];
141: }
142:
143: protected string EscapeDistinguishedName(string distinguishedName)
144: {
145: StringBuilder builder = new StringBuilder(distinguishedName.Length);
146: foreach (char ch in distinguishedName)
147: {
148: if (_reservedCharacters.IndexOf(ch) >= 0)
149: {
150: builder.Append('\\');
151: }
152: builder.Append(ch);
153: }
154: return builder.ToString();
155: }
156:
157: public override IList<DirectoryData> FindAll(string filter, SearchScope scope, string sortByProperty)
158: {
159: string cacheKey = "EPiServer:DirectoryServiceFindAll:" + filter + scope.ToString();
160: IList<DirectoryData> values = (IList<DirectoryData>)HttpRuntime.Cache[cacheKey];
161: if (values == null)
162: {
163: using (DirectorySearcher searcher = new DirectorySearcher(this.CreateDirectoryEntry(), filter, this._propertiesToLoad.ToArray(), scope))
164: {
165: using (SearchResultCollection results = searcher.FindAll())
166: {
167: if (results == null)
168: {
169: return null;
170: }
171: if (sortByProperty == null)
172: {
173: values = new List<DirectoryData>(results.Count);
174: foreach (SearchResult result in results)
175: {
176: values.Add(this.CreateDirectoryDataFromSearchResult(result));
177: }
178: }
179: else
180: {
181: SortedList<string, DirectoryData> list2 = new SortedList<string, DirectoryData>(results.Count);
182: foreach (SearchResult result2 in results)
183: {
184: DirectoryData data = this.CreateDirectoryDataFromSearchResult(result2);
185: list2.Add(data.GetFirstPropertyValue(sortByProperty), data);
186: }
187: values = list2.Values;
188: }
189: }
190: }
191: this.StoreInCache(cacheKey, values);
192: }
193: return values;
194: }
195:
196: public override DirectoryData FindOne(string filter, SearchScope scope)
197: {
198: string cacheKey = "EPiServer:DirectoryServiceFindOne:" + filter + scope.ToString();
199: DirectoryData data = (DirectoryData)HttpRuntime.Cache[cacheKey];
200: if (data == null)
201: {
202: using (DirectorySearcher searcher = new DirectorySearcher(this.CreateDirectoryEntry(), filter, this._propertiesToLoad.ToArray(), scope))
203: {
204: data = this.CreateDirectoryDataFromSearchResult(searcher.FindOne());
205: if (data == null)
206: {
207: return null;
208: }
209: }
210: this.StoreInCache(cacheKey, data);
211: }
212: return data;
213: }
214:
215: public override DirectoryData GetEntry(string distinguishedName)
216: {
217: string cacheKey = "EPiServer:DirectoryServiceEntry:" + distinguishedName;
218: DirectoryData data = (DirectoryData)HttpRuntime.Cache[cacheKey];
219: if (data == null)
220: {
221: using (DirectoryEntry entry = this.CreateDirectoryEntry(distinguishedName))
222: {
223: data = this.CreateDirectoryDataFromDirectoryEntry(entry, distinguishedName);
224: }
225: if (data != null)
226: {
227: this.StoreInCache(cacheKey, data);
228: }
229: }
230: return data;
231: }
232:
233: private void GetParametersFromConfig(NameValueCollection config)
234: {
235: string str;
236: string str2;
237: string str3;
238: if (!this.TryGetDestructive(config, "connectionStringName", out str))
239: {
240: throw new ProviderException("Required attribute connectionStringName not supplied.");
241: }
242: ConnectionStringSettings settings = WebConfigurationManager.ConnectionStrings[str];
243: if (settings == null)
244: {
245: throw new ProviderException(string.Format("Connection string {0} not found.", str));
246: }
247: this._connectionString = settings.ConnectionString;
248: if (string.IsNullOrEmpty(this._connectionString))
249: {
250: throw new ProviderException(string.Format("Connection string {0} is empty.", str));
251: }
252: if (!this.TryGetDestructive(config, "connectionUsername", out this._username))
253: {
254: throw new ProviderException("Required attribute connectionUsername not supplied.");
255: }
256: if (!this.TryGetDestructive(config, "connectionPassword", out this._password))
257: {
258: throw new ProviderException("Required attribute connectionPassword not supplied.");
259: }
260: this._connectionProtection = AuthenticationTypes.Secure;
261: if (this.TryGetDestructive(config, "connectionProtection", out str2))
262: {
263: try
264: {
265: this._connectionProtection = (AuthenticationTypes)Enum.Parse(typeof(AuthenticationTypes), str2, true);
266: }
267: catch (ArgumentException)
268: {
269: throw new ProviderException(string.Format("Attribute connectionProtection has illegal value {0}, supported values are {1}.", str2, string.Join(", ", Enum.GetNames(typeof(AuthenticationTypes)))));
270: }
271: }
272: if (this.TryGetDestructive(config, "cacheTimeout", out str3))
273: {
274: if (!TimeSpan.TryParse(str3, out this._cacheTimeout))
275: {
276: throw new ProviderException(string.Format("Attribute cacheTimeout has illegal value {0}, should be formatted as \"hours:minutes:seconds\"", str3));
277: }
278: }
279: else
280: {
281: this._cacheTimeout = new TimeSpan(0, 10, 0);
282: }
283: }
284:
285: private void Initialize()
286: {
287: this._propertiesToLoad = new List<string>(5);
288: this._propertiesToLoad.Add("distinguishedName");
289: this._propertiesToLoad.Add("objectClass");
290: int index = this._connectionString.IndexOf("://");
291: if (index < 0)
292: {
293: throw new ProviderException(string.Format("Protocol specification missing from connection string {0}", this._connectionString));
294: }
295: int num2 = this._connectionString.IndexOf("/", (int)(index + 3));
296: if (num2 < 0)
297: {
298: this._baseConnectionString = this._connectionString + "/";
299: }
300: else if ((num2 + 1) < this._connectionString.Length)
301: {
302: this._baseConnectionString = this._connectionString.Remove(num2 + 1);
303: }
304: else
305: {
306: this._baseConnectionString = this._connectionString;
307: }
308: using (DirectoryEntry entry = this.CreateDirectoryEntry())
309: {
310: base.RootDistinguishedName = entry.Properties["distinguishedName"][0].ToString();
311: }
312: }
313:
314: public override void Initialize(NameValueCollection config)
315: {
316: this.GetParametersFromConfig(config);
317: this.Initialize();
318: }
319:
320: protected string SchemaClassName(Dictionary<string, string[]> properties)
321: {
322: string str = string.Empty;
323: string[] strArray = properties["objectClass"];
324: if (strArray != null)
325: {
326: str = strArray[strArray.Length - 1];
327: }
328: return str;
329: }
330:
331: protected void StoreInCache(string cacheKey, object data)
332: {
333: if (HttpRuntime.Cache["EPiServer:DirectoryServiceRoot"] == null)
334: {
335: HttpRuntime.Cache.Insert("EPiServer:DirectoryServiceRoot", new object());
336: }
337: HttpRuntime.Cache.Insert(cacheKey, data, new CacheDependency(null, new string[] { "EPiServer:DirectoryServiceRoot" }), DateTime.Now.Add(this._cacheTimeout), Cache.NoSlidingExpiration);
338: }
339:
340: protected bool TryGetDestructive(NameValueCollection config, string name, out string value)
341: {
342: value = config[name];
343: if (value != null)
344: {
345: config.Remove(name);
346: }
347: if (string.IsNullOrEmpty(value))
348: {
349: value = null;
350: return false;
351: }
352: return true;
353: }
354:
355: // Properties
356: public AuthenticationTypes ConnectionProtection
357: {
358: get
359: {
360: return this._connectionProtection;
361: }
362: protected set
363: {
364: this._connectionProtection = value;
365: }
366: }
367:
368: public string ConnectionString
369: {
370: get
371: {
372: return this._connectionString;
373: }
374: protected set
375: {
376: this._connectionString = value;
377: }
378: }
379:
380: public string Password
381: {
382: get
383: {
384: return this._password;
385: }
386: protected set
387: {
388: this._password = value;
389: }
390: }
391:
392: public string Username
393: {
394: get
395: {
396: return this._username;
397: }
398: protected set
399: {
400: this._username = value;
401: }
402: }
403: }
404:
405: }