PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Providers/XmlProvider/XmlRoleProvider.cs

#
C# | 551 lines | 314 code | 71 blank | 166 comment | 43 complexity | a56f2f350503438025c1ee2301e5f75d MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. namespace BlogEngine.Core.Providers
  2. {
  3. // Written by: Roman D. Clarkson
  4. // http://www.romanclarkson.com inspirit@romanclarkson.com
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Collections.Specialized;
  8. using System.Configuration.Provider;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Security.Permissions;
  12. using System.Web;
  13. using System.Web.Hosting;
  14. using System.Web.Security;
  15. using System.Web.Configuration;
  16. using System.Xml;
  17. using System.IO;
  18. /// <summary>
  19. /// The xml role provider.
  20. /// </summary>
  21. public class XmlRoleProvider : RoleProvider
  22. {
  23. #region Constants and Fields
  24. /// <summary>
  25. /// The default roles to add.
  26. /// </summary>
  27. private readonly string[] defaultRolesToAdd = new[] { BlogConfig.AdministratorRole, BlogConfig.AnonymousRole };
  28. /// <summary>
  29. /// The roles.
  30. /// </summary>
  31. private readonly Dictionary<Guid, List<Role>> roles = new Dictionary<Guid, List<Role>>();
  32. /// <summary>
  33. /// The user names.
  34. /// </summary>
  35. private List<string> userNames;
  36. /// <summary>
  37. /// The xml file name.
  38. /// </summary>
  39. private string xmlFileName;
  40. #endregion
  41. #region Properties
  42. private string XmlFullyQualifiedPath
  43. {
  44. get
  45. {
  46. string location = Blog.CurrentInstance.StorageLocation;
  47. string fullyQualifiedPath =
  48. HostingEnvironment.MapPath(
  49. VirtualPathUtility.Combine(
  50. VirtualPathUtility.AppendTrailingSlash(HttpRuntime.AppDomainAppVirtualPath), location + xmlFileName));
  51. return fullyQualifiedPath;
  52. }
  53. }
  54. /// <summary>
  55. /// Gets or sets the name of the application to store and retrieve role information for.
  56. /// </summary>
  57. /// <returns>
  58. /// The name of the application to store and retrieve role information for.
  59. /// </returns>
  60. public override string ApplicationName
  61. {
  62. get
  63. {
  64. return "BlogEngine.NET";
  65. }
  66. set
  67. {
  68. }
  69. }
  70. #endregion
  71. #region Public Methods
  72. /// <summary>
  73. /// Adds the specified user names to the specified roles for the configured applicationName.
  74. /// </summary>
  75. /// <param name="usernames">
  76. /// A string array of user names to be added to the specified roles.
  77. /// </param>
  78. /// <param name="roleNames">
  79. /// A string array of the role names to add the specified user names to.
  80. /// </param>
  81. public override void AddUsersToRoles(string[] usernames, string[] roleNames)
  82. {
  83. ReadRoleDataStore();
  84. var currentRoles = new List<string>(this.GetAllRoles());
  85. if (usernames.Length != 0 && roleNames.Length != 0)
  86. {
  87. foreach (var rolename in roleNames.Where(rolename => !currentRoles.Contains(rolename) && !rolename.Equals(BlogConfig.AnonymousRole, StringComparison.OrdinalIgnoreCase)))
  88. {
  89. this.roles[Blog.CurrentInstance.Id].Add(new Role(rolename, new List<string>(usernames)));
  90. }
  91. foreach (var role in this.roles[Blog.CurrentInstance.Id])
  92. {
  93. var role1 = role;
  94. foreach (var s in from name in roleNames
  95. where role1.Name.Equals(name, StringComparison.OrdinalIgnoreCase)
  96. from s in usernames
  97. where !role1.Users.Contains(s)
  98. select s)
  99. {
  100. role.Users.Add(s);
  101. }
  102. }
  103. }
  104. this.Save();
  105. }
  106. /// <summary>
  107. /// Adds a new role to the data source for the configured applicationName.
  108. /// </summary>
  109. /// <param name="roleName">
  110. /// The name of the role to create.
  111. /// </param>
  112. public override void CreateRole(string roleName)
  113. {
  114. ReadRoleDataStore();
  115. // This needs to be fixed. This will always return false.
  116. if (this.roles[Blog.CurrentInstance.Id].Contains(new Role(roleName)))
  117. {
  118. return;
  119. }
  120. this.roles[Blog.CurrentInstance.Id].Add(new Role(roleName));
  121. this.Save();
  122. }
  123. /// <summary>
  124. /// Removes a role from the data source for the configured applicationName.
  125. /// </summary>
  126. /// <returns>
  127. /// true if the role was successfully deleted; otherwise, false.
  128. /// </returns>
  129. /// <param name="roleName">
  130. /// The name of the role to delete.
  131. /// </param>
  132. /// <param name="throwOnPopulatedRole">
  133. /// If true, throw an exception if roleName has one or more members and do not delete roleName.
  134. /// </param>
  135. public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
  136. {
  137. if (Security.IsSystemRole(roleName))
  138. return false;
  139. ReadRoleDataStore();
  140. var blogRoles = roles[Blog.CurrentInstance.Id];
  141. if (blogRoles == null || blogRoles.Count == 0)
  142. return false;
  143. for (var i = 0; i < blogRoles.Count; i++)
  144. {
  145. if (blogRoles[i].Name == roleName)
  146. {
  147. roles[Blog.CurrentInstance.Id].RemoveAt(i);
  148. Save();
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. /// <summary>
  155. /// Gets an array of user names in a role where the user name contains the specified user name to match.
  156. /// </summary>
  157. /// <returns>
  158. /// A string array containing the names of all the users where the user name matches usernameToMatch and the user is a member of the specified role.
  159. /// </returns>
  160. /// <param name="roleName">
  161. /// The role to search in.
  162. /// </param>
  163. /// <param name="usernameToMatch">
  164. /// The user name to search for.
  165. /// </param>
  166. public override string[] FindUsersInRole(string roleName, string usernameToMatch)
  167. {
  168. var usersInRole = new List<string>();
  169. if (this.IsUserInRole(usernameToMatch, roleName))
  170. {
  171. usersInRole.AddRange(this.userNames);
  172. }
  173. return usersInRole.ToArray();
  174. }
  175. /// <summary>
  176. /// Gets a list of all the roles for the configured applicationName.
  177. /// </summary>
  178. /// <returns>
  179. /// A string array containing the names of all the roles stored in the data source for the configured applicationName.
  180. /// </returns>
  181. public override string[] GetAllRoles()
  182. {
  183. ReadRoleDataStore();
  184. return this.roles[Blog.CurrentInstance.Id].Select(role => role.Name).ToArray();
  185. }
  186. /// <summary>
  187. /// Gets a list of the roles that a specified user is in for the configured applicationName.
  188. /// </summary>
  189. /// <returns>
  190. /// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
  191. /// </returns>
  192. /// <param name="username">
  193. /// The user to return a list of roles for.
  194. /// </param>
  195. public override string[] GetRolesForUser(string username)
  196. {
  197. ReadRoleDataStore();
  198. return (from role in this.roles[Blog.CurrentInstance.Id]
  199. from user in role.Users
  200. where user.Equals(username, StringComparison.OrdinalIgnoreCase)
  201. select role.Name).ToArray();
  202. }
  203. /// <summary>
  204. /// Gets a list of users in the specified role for the configured applicationName.
  205. /// </summary>
  206. /// <returns>
  207. /// A string array containing the names of all the users who are members of the specified role for the configured applicationName.
  208. /// </returns>
  209. /// <param name="roleName">
  210. /// The name of the role to get the list of users for.
  211. /// </param>
  212. public override string[] GetUsersInRole(string roleName)
  213. {
  214. ReadRoleDataStore();
  215. return (from role in this.roles[Blog.CurrentInstance.Id]
  216. where role.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase)
  217. from user in role.Users
  218. select user.ToLowerInvariant()).ToArray();
  219. }
  220. /// <summary>
  221. /// Initializes the provider.
  222. /// </summary>
  223. /// <param name="name">
  224. /// The friendly name of the provider.
  225. /// </param>
  226. /// <param name="config">
  227. /// A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.
  228. /// </param>
  229. /// <exception cref="T:System.ArgumentNullException">
  230. /// The name of the provider is null.
  231. /// </exception>
  232. /// <exception cref="T:System.ArgumentException">
  233. /// The name of the provider has a length of zero.
  234. /// </exception>
  235. /// <exception cref="T:System.InvalidOperationException">
  236. /// An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.string,System.Collections.Specialized.NameValueCollection)"/> on a provider after the provider has already been initialized.
  237. /// </exception>
  238. public override void Initialize(string name, NameValueCollection config)
  239. {
  240. if (config == null)
  241. {
  242. throw new ArgumentNullException("config");
  243. }
  244. if (string.IsNullOrEmpty(name))
  245. {
  246. name = "XmlMembershipProvider";
  247. }
  248. if (string.IsNullOrEmpty(config["description"]))
  249. {
  250. config.Remove("description");
  251. config.Add("description", "XML role provider");
  252. }
  253. base.Initialize(name, config);
  254. // Initialize _XmlFileName and make sure the path
  255. // is app-relative
  256. var filename = config["xmlFileName"];
  257. if (string.IsNullOrEmpty(filename))
  258. {
  259. filename = "roles.xml";
  260. }
  261. else
  262. {
  263. if (!VirtualPathUtility.IsAppRelative(filename))
  264. {
  265. throw new ArgumentException("xmlFileName must be app-relative");
  266. }
  267. }
  268. this.xmlFileName = filename;
  269. config.Remove("xmlFileName");
  270. // Make sure we have permission to read the XML data source and
  271. // throw an exception if we don't
  272. var permission = new FileIOPermission(FileIOPermissionAccess.Write, this.XmlFullyQualifiedPath);
  273. permission.Demand();
  274. this.ReadMembershipDataStore();
  275. if (!File.Exists(this.XmlFullyQualifiedPath))
  276. {
  277. this.AddUsersToRoles(this.userNames.ToArray(), this.defaultRolesToAdd);
  278. }
  279. // Now that we know a xml file exists we can call it.
  280. this.ReadRoleDataStore();
  281. // Throw an exception if unrecognized attributes remain
  282. if (config.Count > 0)
  283. {
  284. var attr = config.GetKey(0);
  285. if (!string.IsNullOrEmpty(attr))
  286. {
  287. throw new ProviderException(string.Format("Unrecognized attribute: {0}", attr));
  288. }
  289. }
  290. }
  291. /// <summary>
  292. /// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
  293. /// </summary>
  294. /// <returns>
  295. /// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
  296. /// </returns>
  297. /// <param name="username">
  298. /// The user name to search for.
  299. /// </param>
  300. /// <param name="roleName">
  301. /// The role to search in.
  302. /// </param>
  303. public override bool IsUserInRole(string username, string roleName)
  304. {
  305. ReadRoleDataStore();
  306. return
  307. this.roles[Blog.CurrentInstance.Id].Where(role => role.Name.Equals(roleName, StringComparison.OrdinalIgnoreCase)).SelectMany(
  308. role => role.Users).Any(user => user.Equals(username, StringComparison.OrdinalIgnoreCase));
  309. }
  310. /// <summary>
  311. /// Removes the specified user names from the specified roles for the configured applicationName.
  312. /// </summary>
  313. /// <param name="usernames">
  314. /// A string array of user names to be removed from the specified roles.
  315. /// </param>
  316. /// <param name="roleNames">
  317. /// A string array of role names to remove the specified user names from.
  318. /// </param>
  319. public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
  320. {
  321. ReadRoleDataStore();
  322. if (usernames.Length != 0 && roleNames.Length != 0)
  323. {
  324. foreach (var role in this.roles[Blog.CurrentInstance.Id])
  325. {
  326. var role1 = role;
  327. if (!roleNames.Any(name => role1.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
  328. {
  329. continue;
  330. }
  331. foreach (var user in usernames)
  332. {
  333. if (role.Name.Equals(
  334. BlogConfig.AdministratorRole, StringComparison.OrdinalIgnoreCase))
  335. {
  336. if (role.Users.Count != 1)
  337. {
  338. // if (role.Users.Contains(user))
  339. // role.Users.Remove(user);
  340. RemoveItemFromList(role.Users, user);
  341. }
  342. }
  343. else
  344. {
  345. // if (role.Users.Contains(user))
  346. // role.Users.Remove(user);
  347. RemoveItemFromList(role.Users, user);
  348. }
  349. }
  350. }
  351. }
  352. this.Save();
  353. }
  354. /// <summary>
  355. /// Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName.
  356. /// </summary>
  357. /// <returns>
  358. /// true if the role name already exists in the data source for the configured applicationName; otherwise, false.
  359. /// </returns>
  360. /// <param name="roleName">
  361. /// The name of the role to search for in the data source.
  362. /// </param>
  363. public override bool RoleExists(string roleName)
  364. {
  365. if (Utils.StringIsNullOrWhitespace(roleName))
  366. {
  367. throw new ArgumentNullException("roleName");
  368. }
  369. else
  370. {
  371. // Role names are not supposed to be case sensitive. This needs to be kept consistent with
  372. // other RoleProvider classes.
  373. return (this.GetAllRoles().Contains(roleName, StringComparer.OrdinalIgnoreCase));
  374. }
  375. }
  376. /// <summary>
  377. /// Saves this instance.
  378. /// </summary>
  379. public void Save()
  380. {
  381. ReadRoleDataStore();
  382. var settings = new XmlWriterSettings { Indent = true };
  383. using (var writer = XmlWriter.Create(this.XmlFullyQualifiedPath, settings))
  384. {
  385. writer.WriteStartDocument(true);
  386. writer.WriteStartElement("roles");
  387. foreach (var role in this.roles[Blog.CurrentInstance.Id])
  388. {
  389. writer.WriteStartElement("role");
  390. writer.WriteElementString("name", role.Name);
  391. writer.WriteStartElement("users");
  392. foreach (var username in role.Users)
  393. {
  394. writer.WriteElementString("user", username);
  395. }
  396. writer.WriteEndElement(); // closes users
  397. writer.WriteEndElement(); // closes role
  398. }
  399. }
  400. // This needs to be called in order to keep the Right class in sync.
  401. Right.RefreshAllRights();
  402. }
  403. #endregion
  404. #region Methods
  405. /// <summary>
  406. /// The remove item from list.
  407. /// </summary>
  408. /// <param name="list">
  409. /// The list of string.
  410. /// </param>
  411. /// <param name="item">
  412. /// The item of string.
  413. /// </param>
  414. private static void RemoveItemFromList(ICollection<string> list, string item)
  415. {
  416. if (list == null || string.IsNullOrEmpty(item) || list.Count == 0)
  417. {
  418. return;
  419. }
  420. var usersToRemove = list.Where(u => u.Equals(item, StringComparison.OrdinalIgnoreCase)).ToList();
  421. foreach (var u in usersToRemove)
  422. {
  423. list.Remove(u);
  424. }
  425. }
  426. /// <summary>
  427. /// Only so we can add users to the adminstrators role.
  428. /// </summary>
  429. private void ReadMembershipDataStore()
  430. {
  431. this.userNames = new List<string>();
  432. foreach (MembershipUser user in Membership.GetAllUsers())
  433. {
  434. this.userNames.Add(user.UserName);
  435. }
  436. }
  437. /// <summary>
  438. /// Builds the internal cache of users.
  439. /// </summary>
  440. private void ReadRoleDataStore()
  441. {
  442. lock (this)
  443. {
  444. if (!roles.ContainsKey(Blog.CurrentInstance.Id))
  445. {
  446. roles[Blog.CurrentInstance.Id] = new List<Role>();
  447. var doc = new XmlDocument();
  448. try
  449. {
  450. doc.Load(this.XmlFullyQualifiedPath);
  451. var nodes = doc.GetElementsByTagName("role");
  452. foreach (XmlNode roleNode in nodes)
  453. {
  454. var name = roleNode.SelectSingleNode("name");
  455. if (name == null)
  456. {
  457. continue;
  458. }
  459. var tempRole = new Role(name.InnerText);
  460. var user = roleNode.SelectNodes("users/user");
  461. if (user != null)
  462. {
  463. foreach (XmlNode userNode in user)
  464. {
  465. tempRole.Users.Add(userNode.InnerText);
  466. }
  467. }
  468. this.roles[Blog.CurrentInstance.Id].Add(tempRole);
  469. }
  470. }
  471. catch (XmlException)
  472. {
  473. this.AddUsersToRoles(this.userNames.ToArray(), this.defaultRolesToAdd);
  474. }
  475. }
  476. }
  477. }
  478. #endregion
  479. }
  480. }