TestSystem is a library which provides functionality for creating/run educational tests for students and lectors. I pursued the following goals:
- Use of EntityFramework and LINQ for queries for a database abstraction.
- The library should work independently or be able to integrate easily with the target application.
For the first goal, I considered that using code first approach will be a good idea. But unfortunately, I was disappointed. Every database has some types/features which can’t be abstracted. PostgreSQL doesn’t support code first and has scheme “public” versus “dbo” in SQLServer. We will not be able to run our library on PostgreSQL cause it’s forbidden code first(as I understood this is available in v.10 which is currently in beta). Type datetime2 exists just in SQLServer, and it's preferable than datetime, but code first will create the column with datetime type. And of course, some of the databases don't support inheritance.
For the second goal, I met two problems – authorization and extensibility. We should provide some authorization mechanism to work independently. .NET way is a role-based authorization, which means that we should manage users and roles internally. Imagine that you have already running asp.net project which has users and roles. How will you synchronize roles and authorization rights between your project and TestSystem library? It goes me thinking that role-based authorization is a very bad approach in libraries where roles will be managed internally. Fortunately, I found a way and it’s so-called permission-based authorization or someone calls it claims-based authorization.
The idea is that our rights are based on permissions. You can buy a beer when your age is more than 18 years old, or you can enter in the male toilet if you are a man. First one example was the age permission, the second one was the gender permission. The most powerful in the permission-based authorization is that roles are the particular case of the claim.
In .NET everyone thread can be associated with some principal. Authorization rights are represented by the principal and can be accessed via Thread.CurrentPrincipal
. To access properties and methods you should cast it to ClaimsPrincipal
. When you make authorization it associates roles with current running thread. As Thread
is the static class you can access it from any part of the application. In such way, for example, works AspNetIdentity.
All methods can be accessed from TestSystemService
class, and they are pure in the sense that doesn’t provide logging, authorization, caching, etc. If the library is running independently than should be used TestSystemServiceProxy
class. It internally checks the current thread for ActionPermission
claims. Values for claim type are placed in ActionPermissionValues
class. For caching and logging you could implement decorator pattern.