Mastering Security checks easily with the power of Bitmasks
You know, when you are developing a System for more than one user, you will be faced with the Endboss... Access rights.
I saw several code implementations like If else constructs that were multiple rows long, sometimes encapsulated into a single function but hevy to maintain. But there is a simpler solution that will I show you in this blog post.
Some definitions about security
There are to definitions of security
- Authentication
- Authorization
They are very similar but have different meanings
First of all Authentication. This defines the access to the system itself, so when you enter a username and password it will check against an Identity Provider (IdP) your credentials if you have general access. It will send you back an answer (like a cookie, JWT-Token, or whatever) if the user is authenticated or not.
Authorization is another form, this will happen after the Authentication. Because it will check the Assigned Access rights and it will get information if the user has access or not.
Implementing Access rights
So in most cases, I saw in several implementation acorss the developerverse that you get a large if els construct. like this
public bool CheckIfHasWriteAcess(){
if (Accesslevel.contains("Admin"))
{
return true
}
if (Accesslevel.contains("Reader"))
{
return false
}
if (Accesslevel.contains("Writer"))
{
return true
}
return false
}
So that is simple for this case, but what if when your system generally get a new Acceslevel like a "Guest" Role? Then you must apply this to all access checks and modify the if else construct. But This can be faulty and breaks the system completely in the worst-case scenario. It will also make the access level check more complex.
Bitmask to the rescue
To avoid the complexity of this, you can use the bitmask implementation. Now I can see your reaction
But this is not hard to understand. So let's take the following example, you have four different access rights
- Reader
- Writer
- ExternalViewer
- Admin
Let's assume you map this to a bit of representation like this:
- Reader => Bit 1
- Writer => Bit 2
- ExternalViewer => Bit 3
- Admin => Bit 4
Now you have the following representation
0 0 0 0
Reader Writer ExternalViewer Admin
So every access right will be represented as a bit. When you give one person the reader and writer access the bit representation looks like this
1 1 0 0
Reader Writer ExternalViewer Admin
So it changes the first both bits to 1. The huge advantage for this ist that you can store only one number into a database. Because the bit combination will completely unique.
Cool eh?
How I can extend with new rights?
That's very easy, just add a new bit at the last position. Let's assume you want a new external contributor access right. Then you can add it to the end like this
1 1 0 0 0
Reader Writer ExternalViewer Admin ExternalContributor
You'll see, that when you add this new role, the previous behavior won't change anything, but you will extend the role possibilities for now with a new role.
Now, if you can set these rights, you must do something to get a smooth check. So here comes the
Checking the Access rights against a given set
Technically we get one access level to check, so how we check it against the level?
technically we make a bitwise AND check against the existing access rights to the accesslevel to check. So let me explain
You as a user have the access level above
11000
Now you want to check if the access right contains the right writer. For this you will bitwise AND the to check right
11000 &
10000
_____
10000
The result will be the checked access. So when you make an AND combination with the desired access level to check and the given access you will get the at minimum the same right back otherwise you can guarantee that the access level is not given.
You don't believe me? So let's check against two other levels
11000 &
10100
-----
10000
for this example, we checked if the user has Reader and ExternalViewer rights. It will result only on the Reader access level. The ExernalViewer access level will not result, because the user won't have it right now.
So far to the theory, but how does it looks in c#?
So let's look into the code
The code
In c# we use an enumeration like this
[Flags]
public enum Rights
{
Reader=1,
Writer=2
ExternalViewer=4,
Admin=8
}
>Pleases note, that you set the Flags-attribute to the enum. This helps the compiler to know that it will be used asflags.
Now lets get the code to set the access level to a property, vor example we will set the access level to Reader and Writer.
public Rights rights= Rights.Reader | Rights.Writer;
Now the code to check it against an access level
public bool HasAccess(Rights rightsToCheck, Rights requiredLevel)
{
return (rightsToCheck & requiredLevel) == requiredLevel;
}
bool hasAccess= HasAccess(rights, Rights.Reader);
The result will be true in the hasAccess variable because we set it before.
When you need more access level, you add it to the enum and please notice, that you must assign a value powered by 2.
Final words
So in fact you can master this Scenario very easily, just use one enumeration and use the power of the binary calculation. So I don't think in my past, that it was very important in my life. So feel free to try it out and use it for your own code and authorization check.