HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpModerate

Bitwise operators for permissions

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
forpermissionsoperatorsbitwise

Problem

I'm trying to create a read-write-execute permissions system for items using user-group-world system.

I already have the user/group system set up, just not the permissions.

This is what I wrote:

/** @param $requestedPerm the permission being requested: read write or execute - 4, 2, or 1 respectively 
* @param $level the permission level: world, group, or user
* @param $permInt the permissions checked against: 
   (like 754) which is user:read-write-exec, group:read-exec, world:read
*/
function check($requestedPerm,$level,$permInt){
    $digit = ($permInt/$level)%10;
    return ($requestedPerm & $digit)===$requestedPerm;
}

$read = 4;
$write = 2;
$exec = 1;

$world = 1;
$group = 10;
$user = 100;

$bool = check($read,$group,754);


It seems to work how I want it to, but I'm new to bitwise operations, so I'm not too confident.

In my production code, it will be object oriented, but basically it'll work the same.

Is this check() function reliable, as long as I provide the correct $requestedPerm, $level, and $permInt?

Solution

Your presentation of your problem indicates that you want to emulate the permission system as used by POSIX like systems (Unix). The three sets (user, group, and world), and three permissions (read, write, execute) are from that. Your actual implementation though is very different....

What you have will work, but is wasteful of bits, and is only a partial bitwise solution.

Consider the permissions 0777 (by the way, the 0 there is supposed to indicate that the value is Octal - did you know that? - see PHP's documentation on Integers)

The 7 permission is the read permission (4), the write permission (2), and the execute permission (1). The important thing is to look at it in binary though:

read  00000100
write 00000010
exec  00000001


If we 'OR' all those permissions, we get:

rwx   00000111


Now, the way things are supposed to work, is that the right-most 3 bits represent the permissions of the world users, the next are the group users, and then the actual user:

user rwx     00000001 11000000
group rwx    00000000 00111000
world rwx    00000000 00000111


The left-most 7 bits are also used on most systems to represent other properties about the file. For example, in Linux, the details of the bits are described in the stat system function (remember, the leading 0 on the numbers means the number is in Octal...!!!):

S_IRWXU     00700   mask for file owner permissions

       S_IRUSR     00400   owner has read permission
       S_IWUSR     00200   owner has write permission
       S_IXUSR     00100   owner has execute permission

       S_IRWXG     00070   mask for group permissions
       S_IRGRP     00040   group has read permission
       S_IWGRP     00020   group has write permission
       S_IXGRP     00010   group has execute permission

       S_IRWXO     00007   mask for permissions for others
                           (not in group)
       S_IROTH     00004   others have read permission
       S_IWOTH     00002   others have write permission
       S_IXOTH     00001   others have execute permission


The second reminder about the Octal numbers is important. Note that octal numbers go from 0 to 7. This is conveniently 3-bits in binary (and hexadecimal is conveniently 4 binary bits per hex digit).

OK, so, that's how it's normally done. Setting read-write-execute permissions for user, group, and world results in the binary:

00000001 11111111


which is 511 in decimal. Note, if I want to test whether the user has execute permission, it is a simple case of testing with:

00000000 01000000


If the permission is $perm = 0777 (octal) and the test is $userexec = 0100 (execute for user in octal), then we can do:

if (userexec & perm != 0) {
    // user has execute permission
    ....


But, what does your code do? It does:

7 * 100 + 7 * 10 + 7


which is 777 in decimal, and, unfortunately, in binary it is

00000011 00001001


Now, if I want to determine if the user has write-permissions, I have to do divisions and bit-tests to get there (which is what you do, but it's not the way it is supposed to be).

So, with all that background, what should your code look like? First, let's talk about bit shifting...

If I have the bits (in binary):

00000000 00000111


and I then shift them all 3 positions to the left, I will have:

00000000 00111000


I can left-shift bits with the left-shift operator <<. So, the first value there is 0007 (octal), and I can do:

$threebits = 0007;
$threebits = $threebits << 3;


After that shift, $threebits will have the (octal) value 0070.

Note that we need to shift permission bits 0 times for the world permissions, 3 times for group, and 6 times for user.....

$read = 4;
$write = 2;
$exec = 1;

$world = 0;
$group = 3;
$user = 6;

function check($requestedPerm, $level, $permInt){
    return ($requestedPerm << $level) & $permInt != 0;
}


Note that using the above logic we expect the $permInt to be in a different format to what you had.... so, your current code is:

$bool = check($read,$group,754);


But, that will need to change (to add the 0 to make it octal...):

$bool = check($read, $group, 0754);

Code Snippets

read  00000100
write 00000010
exec  00000001
rwx   00000111
user rwx     00000001 11000000
group rwx    00000000 00111000
world rwx    00000000 00000111
S_IRWXU     00700   mask for file owner permissions

       S_IRUSR     00400   owner has read permission
       S_IWUSR     00200   owner has write permission
       S_IXUSR     00100   owner has execute permission

       S_IRWXG     00070   mask for group permissions
       S_IRGRP     00040   group has read permission
       S_IWGRP     00020   group has write permission
       S_IXGRP     00010   group has execute permission

       S_IRWXO     00007   mask for permissions for others
                           (not in group)
       S_IROTH     00004   others have read permission
       S_IWOTH     00002   others have write permission
       S_IXOTH     00001   others have execute permission
00000001 11111111

Context

StackExchange Code Review Q#79020, answer score: 12

Revisions (0)

No revisions yet.