LINQ to SQL Role Provider

This is following my previous post on the Membership Provider. I decided I might as well add the RoleProvider to go along with it.

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Configuration;
using System.Configuration.Provider;
using Osiris.Models;

namespace Osiris.Providers
{
	public sealed class OsirisRoleProvider : RoleProvider
	{

		/*************************************************************************
		 * General settings
		 *************************************************************************/

		private string _applicationName;
		public override string ApplicationName
		{
			get { return _applicationName; }
			set { _applicationName = value; }
		}


		/*************************************************************************
		 * Retrieval methods
		 *************************************************************************/

		/// <summary>
		/// Gets all available user roles
		/// </summary>
		/// <returns>Array of all available roles</returns>
		public override string[] GetAllRoles()
		{
			string[] roles = null;
			using (OsirisDataContext db = new OsirisDataContext())
			{
				roles = (from groups in db.Groups
						 select groups.Title).ToArray();
			}
			return roles;
		}

		/// <summary>
		/// Gets the assigned roles for a particular user.
		/// </summary>
		/// <param name="username">Matching username</param>
		/// <returns>Array of assigned roles</returns>
		public override string[] GetRolesForUser(string username)
		{
			string[] roles = null;
			using (OsirisDataContext db = new OsirisDataContext())
			{
				roles = (from mg in db.MembersInGroups
								  where mg.Member.Username == username
								  select mg.Group.Title).ToArray();
			}
			return roles;
		}

		/// <summary>
		/// Gets all the users in a particular role
		/// </summary>
		public override string[] GetUsersInRole(string roleName)
		{
			// Without paging, this function seems pointless to me,
			// so I didn't implement it. Should be simple enough using the previous code though.
			throw new NotImplementedException();
		}


		/*************************************************************************
		 * Create and Delete methods
		 *************************************************************************/

		/// <summary>
		/// Creates a new role
		/// </summary>
		public override void CreateRole(string roleName)
		{
			// No need to add if it already exists
			if (!RoleExists(roleName))
			{
				Group g = new Group();
				g.Title = roleName;
				g.Signup = false;

				using (OsirisDataContext db = new OsirisDataContext())
				{
					db.Groups.InsertOnSubmit(g);
					db.SubmitChanges();
				}
			}
		}

		/// <summary>
		/// Deletes a given role
		/// </summary>
		/// <param name="roleName">Role name to delete</param>
		/// <param name="throwOnPopulatedRole">Specifies whether the function should throw 
		/// if there are assigned users to this role</param>
		/// <returns>True if successful. Defaults to false</returns>
		public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
		{
			// Return status. Defaults to false.
			bool ret = false;

			// You can only delete an existing role
			if (RoleExists(roleName))
			{
				try
				{
					using (OsirisDataContext db = new OsirisDataContext())
					{
						if (throwOnPopulatedRole)
						{
							int[] users = (from mg in db.MembersInGroups
										   where mg.Group.Title == roleName
										   select mg.Member.MemberId).ToArray();

							if (users.Count() > 0)
								throw new ProviderException("Cannot delete roles with users assigned to them");
						}

						Group g = (from groups in db.Groups
								   where groups.Title == roleName
								   select groups).FirstOrDefault();

						db.Groups.DeleteOnSubmit(g);
						db.SubmitChanges();

						ret = true;
					}
				}
				catch { }
			}
			
			return ret;
		}


		/*************************************************************************
		 * Assign/Remove methods
		 *************************************************************************/
		
		/// <summary>
		/// Adds a collection of users to a collection of corresponding roles
		/// </summary>
		public override void AddUsersToRoles(string[] usernames, string[] roleNames)
		{
			// Get the actual available roles
			string[] allRoles = GetAllRoles();

			// See if any of the given roles match the available roles
			IEnumerable<string> roles = allRoles.Intersect(roleNames);

			// There were some roles left after removing non-existent ones
			if (roles.Count() > 0)
			{
				// Cleanup duplicates first
				RemoveUsersFromRoles(usernames, roleNames);

				using (OsirisDataContext db = new OsirisDataContext())
				{
					// Get the user IDs
					List<int> mlist = (from members in db.Members
									   where usernames.Contains(members.Username)
									   select members.MemberId).ToList();

					// Get the group IDs
					List<int> glist = (from groups in db.Groups
									   where roleNames.Contains(groups.Title)
									   select groups.GroupId).ToList();

					// Fresh list of user-role assignments
					List<MembersInGroup> mglist = new List<MembersInGroup>();
					foreach (int m in mlist)
					{
						foreach (int g in glist)
						{
							MembersInGroup mg = new MembersInGroup();
							mg.MemberId = m;
							mg.GroupId = g;
							mglist.Add(mg);
						}
					}

					db.MembersInGroups.InsertAllOnSubmit(mglist);
					db.SubmitChanges();
				}
			}
		}

		/// <summary>
		/// Remove a collection of users from a collection of corresponding roles
		/// </summary>
		public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
		{
			// Get the actual available roles
			string[] allRoles = GetAllRoles();

			// See if any of the given roles match the available roles
			IEnumerable<string> roles = allRoles.Intersect(roleNames);

			// There were some roles left after removing non-existent ones
			if (roles.Count() > 0)
			{
				using (OsirisDataContext db = new OsirisDataContext())
				{
					List<MembersInGroup> mg = (from members in db.MembersInGroups
											   where usernames.Contains(members.Member.Username) &&
											   roleNames.Contains(members.Group.Title)
											   select members).ToList();

					db.MembersInGroups.DeleteAllOnSubmit(mg);
					db.SubmitChanges();
				}
			}
		}


		/*************************************************************************
		 * Searching methods
		 *************************************************************************/

		/// <summary>
		/// Checks if a given username is in a particular role
		/// </summary>
		public override bool IsUserInRole(string username, string roleName)
		{
			// Return status defaults to false
			bool ret = false;

			if (RoleExists(roleName))
			{
				using (OsirisDataContext db = new OsirisDataContext())
				{
					int c = (from m in db.MembersInGroups
							 where m.Member.Username == username &&
							 m.Group.Title == roleName
							 select m).Count();

					if (c > 0)
						ret = true;
				}
			}

			return ret;
		}

		/// <summary>
		/// Finds a set of users in a given role
		/// </summary>
		public override string[] FindUsersInRole(string roleName, string usernameToMatch)
		{
			// Here's another function that doesn't make sense without paging
			throw new NotImplementedException();
		}

		/// <summary>
		/// Checks if a given role already exists in the database
		/// </summary>
		/// <param name="roleName">Role name to search</param>
		/// <returns>True if the role exists. Defaults to false.</returns>
		public override bool RoleExists(string roleName)
		{
			bool ret = false;

			// If the specified role doesn't exist
			if (!GetAllRoles().Contains(roleName))
				ret = true;
			
			return ret;
		}

		/*************************************************************************
		 * Initialization
		 *************************************************************************/

		/// <summary>
		/// Initialize the RoleProvider
		/// </summary>
		public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
		{
			if (config == null)
				throw new ArgumentNullException("config");

			if (name == null || name.Length == 0)
				name = "OsirisRoleProvider";

			if (String.IsNullOrEmpty(config["description"]))
			{
				config.Remove("description");
				config.Add("description", "Osiris Role Provider");
			}

			// Initialize base class
			base.Initialize(name, config);

			_applicationName = GetConfigValue(config["applicationName"],
				System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);

		}


		/*************************************************************************
		 * Private helper methods
		 *************************************************************************/

		private string GetConfigValue(string configValue, string defaultValue)
		{
			if (String.IsNullOrEmpty(configValue))
				return defaultValue;

			return configValue;
		}
	}
}
Advertisement

3 thoughts on “LINQ to SQL Role Provider

  1. Pingback: Simple CMS with Linq to SQL « This page intentionally left ugly

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s