I have answered more than thirty questions on the different forums about the same subject. “How to Implement a flexible security module” , well the title differs from post to post but the question is the as the answer is the same. The Following post is the trigger or the valiant to the writing of this article.
Introduction
I have answered more than thirty questions on the different forums about the same subject. “How to Implement a flexible security module” , well the title differs from post to post but the question is the as the answer is the same. The Following post is the trigger or the valiant to the writing of this article.
Background
The first thing i would like to say is that, this is not my idea and I did not invent this idea. Let me tell you a story where does this permission module come from.
Before I met Sheo I was only a registered member for CodeProject and I was writing articles in CodeProject and other forums. Few years ago; I wrote an article with the title
"How to be an Expert in your Field". I was young when i wrote that article and it made sense to a lot of people and they liked it. Someone noticed my article and loved it. I reluctantly meet the man and since that day I never regretted to have met my fan(J). This man is one of the people who wrote the legacy systems that we are trying to rewrite today. He understood ,used the technology at his disposal in those days and he finally learned the .Net world through me. I have learned a lot of things from him and he has been like a brother to me since the day I met him.
The First time we met, it was at a McDonalds’ in Pretoria ,South Africa. He travelled from another city to my city to meet me in person. I just can’t remember the year. One of the things he talked about , was the "Permission Module". his idea was implemented in the legacy systems he wrote decades and decades before I was born. He was amazed or surprised by today’s lack of quality in development. He probably never saw any .net code rather let me say the .net programmer of today implementing a permission module as he did. I got this idea from Emmanuel Musabayana.
Using the code
We are going to use C# as our language, as you know I am doing a lot of Silverlight these days, so the examples will be in Silverlight, but as you can see the title says or Asp.net, I will just explain how you will implement it in Asp.net.
Database Structure
The Database Structure is straight forward.
This is an easy view created in SQL. We have a login table (TBL_LOGIN). When a user first register in system or website. He must fill in the username and password and the email. There is another table that works hand in hand with the (TBL_LOGIN), which is (TBL_USERS). This table contains more info about the user, this info can be filled in later when the user has already have access to the system. There is no rule in this. You can decide to require all the information in the TBL_USERS tables.
There is another table called TBL_GROUPS, this table contains the group information. A user must belong to one or more than one group to be able to do anything on the system. The groups are used to categorize the type of users in the system and the type of operations they can perform.
The next table is the Permission or operations table TBL_PERMISSIONS. This table contains the permissions. A User cannot be directly assigned a permission, but a user must belong to a group and a group must assigned permissions. Now this means that for a user to acquire certain rights or permissions , he will need to be a member of a certain group.
Now those were the base tables of our System. But we cannot go and run a query that will join all those tables every time we query data about permissions and users. We can have a Many to Many table that will contain will the information for us in one place , but there are some values that we will need to retrieve from the base tables . This is better than rejoining the tables again.
Let us start from the back. We have a many to many table named “TBL_GROUPPERMISSIONS”. This table contains a relationship between the group and the permissions. This table has the permissions that are assigned to one or more group. That means Group1 can appear many times as long as it appears with different permissions. This is the table that will be queried to find the permissions that are linked to a certain group.
The last table is named “TBL_USERGROUPS”. This table carries the relationship between the user and the groups. This table tells us the groups that the user is assigned to. Basically this means that we will if we want to find the permissions that the user have , we will need to query this table to find the groups that are assigned the user and go further and query the table that contains the relationship of the Group and the Permissions and get the permission from there.
I think I explained all the functions for the tables. Now let us see how we can apply this logic in our application.
Application Example
In your application before you load the page, you can check if the user is logged in , if the user is logged in , then continue to check what group does the user belongs to , and what permissions does the group posses. Now I have said a lot of things ,one might think it’s a lot of calls to the server. Now I have a smart way of doing this. When you validate the user , you must return an object that carries information about the user and his permissions , if the user is not valid , then in the object you must have a property that will be used as a flag to tell you if the user is valid or not. This will be only one call , and with that call we can do a lot of things.
NB: Due to time and things that are busy with, I will provide the example project later. What I will share is a code that you will need to do this.
In your Silverlight , WPF, ASP.NET . you will check if the user is logged in and the user is logged in you will enable the menu items based on the permission.
I will share with you my example data layer that I have in one of my applications. I have a model defined like this
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace eCashDatalayer
{
public class UserModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _iUserid;
private string _sFirstName;
private string _sLastname;
private string _sUsername;
private string _sPassword;
private string _sEmail;
private List<string> _sPermissions;
private bool _bIsValid;
private bool _bIsconfirmed;
private bool _bIsactive;
private bool _bPermitted;
/// When the Property is changed from the PL this gets Fired
/// </summary>
/// <param name="property"></param>
private void OnPropertyChanged(String property)
{
// DAL objConverter = new DAL();
// List<RegistrationModel> Model = new List<RegistrationModel>();
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
//objConverter.Updatedata();
}
}
public int iUserid
{
get
{
return _iUserid;
}
set
{
_iUserid = value;
OnPropertyChanged("iUserid");
}
}
public string sUsername
{
get
{
return _sUsername;
}
set
{
_sUsername = value;
OnPropertyChanged("sUsername");
}
}
public string sPassword
{
get
{
return _sPassword;
}
set
{
_sPassword = value;
OnPropertyChanged("sPassword");
}
}
public string sFirstName
{
get
{
return _sFirstName;
}
set
{
_sFirstName = value;
OnPropertyChanged("sFirstName");
}
}
public string sLastname
{
get
{
return _sLastname;
}
set
{
_sLastname = value;
OnPropertyChanged("sLastname");
}
}
public string sEmailAddress
{
get
{
return _sEmail;
}
set
{
_sEmail = value;
OnPropertyChanged("sEmailAddress");
}
}
public List<string> sPermissions
{
get
{
return _sPermissions;
}
set
{
_sPermissions = value;
OnPropertyChanged("sPermissions");
}
}
public Boolean bIsValid
{
get
{
return _bIsValid;
}
set
{
_bIsValid = value;
OnPropertyChanged("bIsValid");
}
}
public Boolean bIsconfirmed
{
get
{
return _bIsconfirmed;
}
set
{
_bIsconfirmed = value;
OnPropertyChanged("bIsconfirmed");
}
}
public Boolean bIsactive
{
get
{
return _bIsactive;
}
set
{
_bIsactive = value;
OnPropertyChanged("bIsactive");
}
}
public Boolean bPermitted
{
get
{
return _bPermitted;
}
set
{
_bPermitted = value;
OnPropertyChanged("bPermitted");
}
}
}
}
As you can see the permission property is being returned as a list. This list is only retrieved one. So when the user logs in , I will check the permissions that are for the groups that the user is assigned to and add them to a list , and my data layer function that is using this model looks like this
public UserModel AuthonticateUser(String _sUsername, String _sPassword)
{
con = new SqlConnection(strCon);
cmdselect = new SqlCommand();
cmdselect.CommandText = "sp_Authonticateuser";
cmdselect.CommandType = CommandType.StoredProcedure;
cmdselect.Connection = con;
cmdselect.Parameters.Add("@USERNAME", SqlDbType.VarChar).Value = _sUsername;
cmdselect.Parameters.Add("@PASSWORD", SqlDbType.VarChar).Value = _sPassword;
cmdselect.Parameters.Add("@RESULTS", SqlDbType.Int);
cmdselect.Parameters["@RESULTS"].Direction = ParameterDirection.Output;
int isValid = 0;
UserModel m = new UserModel();
try
{
con.Open();
cmdselect.ExecuteNonQuery();
isValid = (int)cmdselect.Parameters["@RESULTS"].Value;
if(isValid > 0)
{
m = GetPermissions(_sUsername);
}
else
{
m.bIsValid = false;
}
}
catch (SqlException ex)
{
throw ex;
}
finally
{
con.Close();
}
return m;
}
And the GetPermission function is a private function defined in the datalayer like this
private UserModel GetPermissions(String Username)
{
con = new SqlConnection(strCon);
cmdselect = new SqlCommand();
cmdselect.CommandText = "sp_GetPagePermission";
cmdselect.CommandType = CommandType.StoredProcedure;
cmdselect.Connection = con;
da = new SqlDataAdapter();
cmdselect.Parameters.Add("@USERNAME", SqlDbType.VarChar).Value = Username;
UserModel m = new UserModel();
DataTable dtpermissions = new DataTable();
da.SelectCommand = cmdselect;
List<string> LstPermissions = new List<string>();
try
{
con.Open();
da.Fill(dtpermissions);
if (dtpermissions.Rows.Count > 0)
{
for (int i = 0; i < dtpermissions.Rows.Count; i++)
{
m.iUserid = Convert.ToInt32(dtpermissions.Rows[i][0]);
m.sUsername = dtpermissions.Rows[i][1].ToString();
m.sFirstName = dtpermissions.Rows[i][2].ToString();
m.sLastname = dtpermissions.Rows[i][3].ToString();
m.sEmailAddress = dtpermissions.Rows[i][4].ToString();
m.bIsactive = Convert.ToBoolean(dtpermissions.Rows[i][6]);
m.bIsconfirmed = Convert.ToBoolean(dtpermissions.Rows[i][7]);
}
foreach (DataRow row in dtpermissions.Rows)
{
//get List of Permissions
LstPermissions.Add(row["PERMISSIONNAME"].ToString());
}
}
m.bIsValid = true; //this will confirm to the BLL Function that this login is Valid
m.sPermissions = LstPermissions;
}
catch (SqlException ex)
{
throw ex;
}
finally
{
con.Close();
}
return m;
}
As you can see this adds the permission to a list and add the list to a property. This is passed to the Business logic Layer and the same functions are consumed by your app. My example is Silverlight and it looks like this
void business_AuthonticateUserCompleted(object sender, BusinessLayer.AuthonticateUserCompletedEventArgs e)
{
lblError.Visibility = System.Windows.Visibility.Collapsed;
if (e.Error == null)
{
UserModel m = new UserModel();
m = e.Result;
if (m.bIsValid == true) //CHECK IF THE USERNAME AND PASSWORD ARE CORRECT
{
if (m.bIsactive == true)
{
//CHECK IF THE USER IS CONFIRM
if (m.bIsconfirmed == true)
{
//get the Permissions that the User belongs to
LoginCanvas.Visibility = System.Windows.Visibility.Collapsed;
GenericMethods.GenericMethods.SetCookie("Username", txtusername.Text);
GenericMethods.GenericMethods.SetCookie("UserID", m.iUserid.ToString());
itemlogout.Visibility = System.Windows.Visibility.Visible;
//Get Permissions
for (int i = 0; i < m.sPermissions.Count; i++)
{
if (m.sPermissions[i] == "CanteenSolutions")
{
itemcanteensolutions.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "Linkmykids")
{
itemlinkmykids.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "UploadMoney")
{
itemuploadmoney.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "MealPlan")
{
itemmealplan.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "Orderonline")
{
itemorderonline.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "PARENTACCOUNT")
{
itemAccount.Visibility = System.Windows.Visibility.Visible;
}
if (m.sPermissions[i] == "TuckShopAccount")
{
itemTuckshopAccount.Visibility = System.Windows.Visibility.Visible;
}
}
this.MainFrame.Navigate(new Uri("/Home", UriKind.Relative));
}
else if (m.bIsValid == false)
{
//Invalid Loggin
lblError.Content = "Your Registration is not Confirmed";
lblError.Visibility = System.Windows.Visibility.Visible;
}
}
else
{
//Invalid Loggin
lblError.Content = "Your Username is not Active, please contact Administrator";
lblError.Visibility = System.Windows.Visibility.Visible;
}
}
else
{
//Invalid Loggin
lblError.Content = "Invalid Login";
lblError.Visibility = System.Windows.Visibility.Visible;
}
}
}
as you can see the permissions are hardcoded here , so each menu item is linked to a certain permission. Initially my menu items have visibility set to collapse and now on the above if statement , I set the visibility to “visible” based on the available permissions.
One might ask a question? What if you want to assign different permissions to different users, then you need to add a group with a different permission as one users can be a member of many groups.
Conclusion
I had a of things to do , that is why I delayed with this article. Thank you for understand . I will write you good example projects for asp.net and Silverlight.
Thank you again for Visiting Dotnetfunda.
Kind Regards
Vuyiswa Maseko