Cool Privilege Control System Part 1 -- asp.net MVC
Cool Privilege Control System Part 2 -- asp.net MVC with WCF
Introduction
Cool Privilege Control is a standalone access right system. It resolves many complex access right problems. such as user with different access level problem, unlimited function hierarchy and easily add function type. And also contains many common mechanisms, such as support multilingual environment, support three type logs(error log, trace log, audit log). It helps you easily and quickly create asp.net mvc demo project with powerful privilege. For details please see my last artical. In here I want to take some times to thanks who vote 5 and ask questions. Thanks again. Today I want to go more deeply into system extension. What is this and how? Please follow my mind.
Background
WCF which full name is Windows Communication Foundation. It is a framework for building service-oriented applications. I known some smart guys had guessed. Yeah. you got it! In this artical I will seperate Cool Privilege Control System into two different part. One is MVC project which is responsible for UI maintenance (Send request to Service/Respond received from Service) , the other is WCF project(Hosted by IIS) which is responsible for executing operations (Receive and perform request from caller/Send response to caller). Let's go ahead I will to How and Why use WCF to perform system extension.
Design Pattern
First of all, I want to describe the mvc design pattern. Yeah. MVC design pattern. I known many guys have more ideas and experiences than me. I don't want to fool who go further than me. But I think the advanced is come from the cons. of original one and new requirements.
Figure 3.1 MVC-Diagram.gif
There are many mvc image in google search engine. Which help me to reduce many times to draw the diagram. Thanks Google.
MVC composed of Controller, View and Model. MVC design pattern can be used in not only web application but also client application(P.S. I prefer use MVVM design pattern in client application development. I mean WPF application). But Cool Privilege Control is a web application, so I will focus on web application MVC. A web request send to the app server. A url router receive the request and select suitable controller to perform the request(P.S. I ignored many steps before url router received the reuqest. such as HttpHandler. Because my topic is not to overwrite MVC design pattern.). Controller is an intermedialy between model and view. It handles the requests, call model methods, bind data and return views. Model contains db operations, business logic. View is a presentation layer, present layout with data.
MVC Pros and Cons
Pros: |
- Easy maintenance. Designer focus on design UI and Developer focus on business logic. That said designer no longer care about the business logic and db operation, on the other hand developer need not care about the UI layer. (P.S. You know, In real case, sometimes we are all in one.).
- Easily write unit test for business logic.
- Big advantage is remove web form's terrible post back logic which contains huge viewstate.
|
Cons: |
- Cannot share business logic to different UI layer at the same time. such as WPF, Angular js website and other application written in different language. Which is the pros of the SO application(service-oriented application).
- Without WS/WCF, in Asp.net mvc project, All complied dll have to be stored in one server. If the hacker crack the server, he would find easy way to access db and do terriable things whatever you never think before.
- Without WS/WCF, if you design asp.net mvc application, you use either entity framework to perform db operation or enterprice liberary to perform db operation, Model in MVC is aware the db scheme. I thinks it is dangerous for hacker know about these.
|
Note: Above pros and cons is my personal opinions. If there are anything wrong. Please feel free and contact me.
Figure 3.2 MVC without and with WCF service flow
As pervious diagram discribed, after Controller received the request from URL Router, Controller will call wcf service instead of Model modification method. Then WCF service receive request and call Model modification method. Model perform the action and return View Model to the caller. Controller receive the responded View Model and bind to the View.
MVC With WCF Pros and Cons
Pros: |
- More Security. You can set at least one firewall before the service server and limit the caller. (i.e. specify one or two server can call the service server)
- Both View and Controller are not aware the Model. They are aware View Model only. Even if the hacker crack the web server which can be visited from outside, they don't know the database scheme and business logic.
- Support multi-UI layer.
Figure 3.3 Support Multi-UI
|
Cons: |
- Because the Web application and WCF application have not been hold by the same server. The connection between the web application and the WCF application maybe via internal network or internet. It will have network issue. The bandwidth determine the service response time. However, throughout many years development experiences, this issue does not have any impact. maybe one or two second delay. (P.S. Currently, Many new hardware technology reduce the risk. such as SSD,5G and so on.). Furthermore, you can use asynchronous method to prevent the main thread suspension and continute remaining process that help you to reduce the waiting time.
- It is easy to know, if you are developing a small project, developing and maintain too many tiers, It will increase the project complexity and maintenance time. On the other hand, If you are developing large system and contains many modules as I met, it will help you to recognize the whole view of system and assign different tier/part to corresponding colleauge.
|
After previous sections, as smart as you are, I guess you would like to use WCF to extend yours' products. Below sequence diagram illustrate the flow about "Get HR Info." in ASP.NET MVC, I hope this diagram give you some ideas when you design your complex system with cool privilege control.
Figure 3.4 Cool Privilege Control With WCF sequence diagram
Description
1. | Client user sent a request to get Employee Basic Info from HR Module. |
1.1 | HR Module redirect browser to login page. |
1.1.1 | Reponse login page to client. |
2. | Client user input login name and password, click submit button. |
2.1 | MVC controller invoke WCF authenticate method. |
2.1.1 | WCF execute validation method. |
2.1.2 | WCF Service issue token or return valication error to the caller. |
2.1.3 | Client side cache token for advance process. |
3. | Client user sent a request with token to get HR Info. |
3.1 | HR Module invoke WCF service with function key and token in order to check access right. |
3.1.1 | WCF execute validation method. |
3.1.2 | If it is successful, return success to HR Module. |
3.1.2.1 | HR Module return HR Info to client user. |
3.1.3 | If it is fail, return fail to HR Module. |
3.1.3.1 | HR Module return fail message to client user. |
There was no doubting that you can replace the UI to different technology. I just give you an example, you can control the flow by yourself via call WCF service.
Settings
For MSSQL/MYSQL database user, I publish two versions to fulfill your case. If you use MSSQL as default, please download “CoolPrivilegeControl.Community.WCF.MSSQL.zip”, otherwise, please download “CoolPrivilegeControl.Community.WCF.MYSQL.zip”. And I list my development environment for your reference.
1. Microsoft Visual Studio 2013
2. .NET Framework 4.5.1
3. MSSQL Server 2012 or MYSQL 5.6.26
4. MVC 5.2.3
5. Entity Framwork 6.0
Mandatory Step: After opened solution, please right click solution and select “Enable NuGet Package Restore”.
Figure 4.0 Enable NuGet Package Restore
Mandatory Step: Please right click solution and click "Properties", set projects "CoolPrivilegeControl" and "CoolPrivilegeServiceHost" as startup projects.
4.1 EDIT WEB.CONFIG FILE
4.1.1 For MYSQL user
Figure 4.1.1.1 Hosting Service web.config(MySql with WCF Version)
Please pay attention to the appSettings node, change DBSource/DBName/DBPort/LoginName/LoginPWD value based on mysql server settings.
Property | Description |
DBSource: | IP address of the server host |
DBName: | Database name |
DBPort: | Server TCP/IP port |
LoginName: | DB user name |
LoginPWD: | DB user password |
IsDebug: | If you set true, More details tracer info. will be shown. |
Figure 4.1.1.2 Asp.net MVC web.config(MySql with WCF Version)
Property | Description |
IsDebug: | If you set true, whatever exception will be show on the page, and vice versa. |
4.1.2 For MSSQL user
Figure 4.1.2.1 Hosting Service web.config(MSSql with WCF Version)
As the same as mysql user, change DBSource/DBName/LoginName/LoginPWD value based on mssql server settings, except DBPort. In mssql server, the port number can be specified after the server name or server ip address with comma. E.g.
WELLSCHEUNG\MSSQLSERVER2012,49287 49287 is the port.
Property | Description |
DBSource: | IP address of the server host
With Port Format: server name or ip address,port |
DBName: | Database name |
LoginName: | DB user name |
LoginPWD: | DB user password |
IsDebug: | If you set true, More details tracer info. will be shown. |
For UI web.config settings, Please refer to above section 4.1.1.2
4.1.3 Enable or Disable DB initializer
That is the feature of entity framework code first design pattern. And it is the most useful I ever heard before. When you executed the project, if database instance did not exist in the server and the flag is enabled, entity framework mechanism would help you to initialize it. For more information please refer to MSDN(https://msdn.microsoft.com/en-us/data/ee712907). If you do not want entity framework to initial database and cover your database, you can set the attribute named “disableDatabaseInitialization” on context element to true.
Figure 4.1.3 disableDatabaseInitialization flag
Alternative, you can initialize DB via sql script or database backup. I prepare sql script for mysql user to execute and database backup for mssql user to restore.
MySql Script
MSSql Database Backup
4.2 Edit Log4Net.config file
We used log4net to help us record trace info and error info. About how to use log4net, I though most of you had more experiences than me, so I do not want to spend many times on repeat. I only specified that all functions in the system enable trace log as default (i.e. Include both input and output information). That is an easy way to trace error even if we cannot run visual studio debug when onsite support. In WCF version, we can config two log4net.config. The one is for asp.net mvc, the other is for wcf service.
It is easy to turn off or change another type of info you wanted to capture. There are seven levels type which is pre-set in log4net.
The following levels are defined in order of increasing priority:
- ALL
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- OFF
You can replace the value of the attribute named “level” to what you wanted in log4net.config.
Figure 4.2.1 Log4Net.config
SysLog: Log all info of the system. |
ErrorLog: Only log exception of the system. |
In mvc project, previous setting is a global setting. If you want to disable one or any functions to log information automated. You can mark the function you wanted as “[UnTracerAction]” function. After you do like that all info will not be log.
Figure 4.2.2 UnTracerAction Function
[HttpPost]
[ValidateAntiForgeryToken]
[UnTracerAction]
public ActionResult Create(FunctionVM functionVM)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FManage_Create");
If you only want to log the main thread enter the function whether or not, except the detail information input or output. You can mark the function you wanted as “[TracerActionWithDetails(EnableTracer=false)]” function.
[HttpPost]
[ValidateAntiForgeryToken]
[TracerActionWithDetails(EnableTracer=false)]
public ActionResult Create(FunctionVM functionVM)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FManage_Create");
Figure 4.2.4 The log of tracer action without details
Figure 4.2.5 The log of tracer action with details
After previously settings. System is ready for your use. Press “F5” in Visual Studio and double check if it has any compile errors or not. If any error came out, you can send the error to me for inspection or search solution in google by yourself.
Using the code
In coming section, I want to illustrate how to call wcf service in Cool Privilege Control.
UI Operation
Here is a full view of method Index in FTManageController. which will be called when user enter selection criteria and click search button in Function Type Management.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(FunctionTypeVM selectionCriteria)
{
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FTManage");
//Declare output variable(recordCount && entityList_Result)
int recordCount = 0;
List<FunctionTypeVM> entityList_Result = new List<FunctionTypeVM>();
//Declare wcf output object;
FTSerListResult entity_FTSerListResult = null;
//Instantiate WebCommonHelper in order to call wcf service
WebCommonHelper webCommonHelper = new WebCommonHelper();
webCommonHelper.CallWCFHelper<IFunTypeMgtSer>(this, this.HttpContext, postOffice.FunTypeMgtSerPath, (entity_IFunTypeMgtSer, entity_WCFSessionVM) =>
{
entity_FTSerListResult = entity_IFunTypeMgtSer.GetListWithPaging(entity_WCFSessionVM, selectionCriteria, 1, PageSize, null, null, CustomFilter(selectionCriteria));
});
//Assign data to local variable
if (entity_FTSerListResult != null)
{
recordCount = entity_FTSerListResult.Int_TotalRecordCount;
entityList_Result = entity_FTSerListResult.EntityList_FunctionTypeVM;
}
//Set paging bar info (Total Record Count and Page Index)
StorePageInfo(recordCount, 1);
//Cache selection criteria
StoreSelectionCriteria<FunctionTypeVM>(selectionCriteria);
//Pass Error To UI
string strError = "";
if (entity_FTSerListResult.StrList_Error.Count() > 0)
strError = string.Join("<br/>", entity_FTSerListResult.StrList_Error.ToArray());
//Fail
if (entity_FTSerListResult.StrList_Error.Count > 0)
{
MsgInfo errorMsgInfo = new MsgInfo();
errorMsgInfo.MsgTitle = str_MsgBoxTitle;
errorMsgInfo.MsgDesc = strError;
errorMsgInfo.MsgType = MessageType.ValidationError;
ViewBag.ActionMessage = errorMsgInfo;
}
//Success
return View(entityList_Result);
}
Step by step code spec
- Set message box title.
//Message Box Title -- When Error occured, Message Box would be showed.
string str_MsgBoxTitle = MultilingualHelper.GetStringFromResource(languageKey, "FTManage");
- Declare variables recordCount and entityList_Result, which used to receive total record count and Function Type list after service call.
//Declare output variable(recordCount && entityList_Result)
int recordCount = 0;
List<FunctionTypeVM> entityList_Result = new List<FunctionTypeVM>();
- Declare WCF output object
//Declare wcf output object;
FTSerListResult entity_FTSerListResult = null;
- Instantiate WebCommonHelper and call service.
//Instantiate WebCommonHelper in order to call wcf service
WebCommonHelper webCommonHelper = new WebCommonHelper();
webCommonHelper.CallWCFHelper<IFunTypeMgtSer>(this, this.HttpContext, postOffice.FunTypeMgtSerPath, (entity_IFunTypeMgtSer, entity_WCFSessionVM) =>
{
entity_FTSerListResult = entity_IFunTypeMgtSer.GetListWithPaging(entity_WCFSessionVM, selectionCriteria, 1, PageSize, null, null, CustomFilter(selectionCriteria));
});
- Assign data to local variableS
//Assign data to local variable
if (entity_FTSerListResult != null)
{
recordCount = entity_FTSerListResult.Int_TotalRecordCount;
entityList_Result = entity_FTSerListResult.EntityList_FunctionTypeVM;
}
- Cache paging info in order to set paging bar.
//Set paging bar info (Total Record Count and Page Index)
StorePageInfo(recordCount, 1);
- Cache selection criteria.
//Cache selection criteria
StoreSelectionCriteria<FunctionTypeVM>(selectionCriteria);
- Return error info to UI if error occured or bind entity list to the view.
//Pass Error To UI
string strError = "";
if (entity_FTSerListResult.StrList_Error.Count() > 0)
strError = string.Join("<br/>", entity_FTSerListResult.StrList_Error.ToArray());
//Fail
if (entity_FTSerListResult.StrList_Error.Count > 0)
{
MsgInfo errorMsgInfo = new MsgInfo();
errorMsgInfo.MsgTitle = str_MsgBoxTitle;
errorMsgInfo.MsgDesc = strError;
errorMsgInfo.MsgType = MessageType.ValidationError;
ViewBag.ActionMessage = errorMsgInfo;
}
//Success
return View(entityList_Result);
Service Operation
In above code snip, I illustrated the operation of UI controller. In blew code snip, I will show you the process after wcf received a call.
Here is a full view of wcf method called by above controller
public FTSerListResult GetListWithPaging(WCFSessionVM entity_WCFSessionVM, FunctionTypeVM entity_SearchCriteria, int int_CurrentPage, int int_PageSize, string str_SortColumn, string str_SortDir, List<string> str_CustomFilter)
{
try
{
//Restore Server Session by token
RetrieveServerSideSession(entity_WCFSessionVM);
//Flag Success or Fail
bool ret = false;
//Define error list
List<string> strList_Error = new List<string>();
//Instantiate FTSerListResult
FTSerListResult returnResult = new FTSerListResult();
CoolPrivilegeControlContext dbContext = CoolPrivilegeControlContext.CreateContext();
FunctionTypeRespository entityRepos_FT = new FunctionTypeRespository(dbContext, entity_ServerSideSession.ID);
#region [ Check Privilege ]
ret = CheckAccPrivilege(entity_ServerSideSession.ID, entity_WCFSessionVM.RequestFunKey, entity_WCFSessionVM.RequestFunTypeKey, ref strList_Error);
#endregion
//Initialize FTSerListResult instance
returnResult.StrList_Error = strList_Error;
returnResult.Int_TotalRecordCount = 0;
returnResult.EntityList_FunctionTypeVM = new List<FunctionTypeVM>();
//Success
if (ret)
{
int recordCount = 0;
List<FunctionTypeVM> vmList = entityRepos_FT.GetEntityListByPage(entity_SearchCriteria, int_CurrentPage, int_PageSize, str_SortColumn, str_SortDir, out recordCount, str_CustomFilter);
//Assign data to FTSerListResult instance
returnResult.EntityList_FunctionTypeVM = vmList;
returnResult.Int_TotalRecordCount = recordCount;
}
return returnResult;
}
catch (Exception ex)
{
throw new FaultException<WCFErrorContract>(new WCFErrorContract(ex), ex.Message);
}
}
Step by step code spec
- Restore wcf session by token
//Restore Server Session by token
RetrieveServerSideSession(entity_WCFSessionVM);
- Define a flag and error list
//Flag Success or Fail
bool ret = false;
//Define error list
List<string> strList_Error = new List<string>();
- Instantiate FTSerListResult object which is used to reply the request.
//Instantiate FTSerListResult
FTSerListResult returnResult = new FTSerListResult();
- Instantiate dbcontext object and function type respository that contains data access logic and is used to get data.
CoolPrivilegeControlContext dbContext = CoolPrivilegeControlContext.CreateContext();
FunctionTypeRespository entityRepos_FT = new FunctionTypeRespository(dbContext, entity_ServerSideSession.ID);
- Check Privilege based on Function Key, Function Type Key and current user id.
#region [ Check Privilege ]
ret = CheckAccPrivilege(entity_ServerSideSession.ID, entity_WCFSessionVM.RequestFunKey, entity_WCFSessionVM.RequestFunTypeKey, ref strList_Error);
#endregion
- Initialize FTSerListResult instance
//Initialize FTSerListResult instance
returnResult.StrList_Error = strList_Error;
returnResult.Int_TotalRecordCount = 0;
returnResult.EntityList_FunctionTypeVM = new List<FunctionTypeVM>();
- Get data from service and set it to the output object
//Success
if (ret)
{
int recordCount = 0;
List<FunctionTypeVM> vmList = entityRepos_FT.GetEntityListByPage(entity_SearchCriteria, int_CurrentPage, int_PageSize, str_SortColumn, str_SortDir, out recordCount, str_CustomFilter);
//Assign data to FTSerListResult instance
returnResult.EntityList_FunctionTypeVM = vmList;
returnResult.Int_TotalRecordCount = recordCount;
}
return returnResult;
After previous recipes, assume you have basic knowledge of Cool Privilege Control. And it is hard for me to decribe all functions in the system. you can trace the program via set debug point. If you have any question about the project or find any bugs, feel free and contact me. I hope all of you can get ideas from my project which make our's world more and more beautiful.
Test-driven development
Cool Privilege Control System is using the test-driven development (TDD) approach. For many companys, TDD is a mandatory approach in repetition development cycle. Cool Privilege Control System contains 40 test cases in all Controllers, you can easily test all functions by clicking "Run All" in Test Explorer. Certainly, you can add new test case or add new conditions into the orignial test case based on your requriements. Cool Privilege Control System uses Xunit and Mock. Xunit is a test framework inject into Visual Studio, as the same as MSTest and Nunit. For more infos, you can visit the official site.
Figure 6.0.1 Test Explorer
History
2016-02-21 Initial publication
2016-03-01 Add Testing Project