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