Skip to content

Commit

Permalink
Added functionality to make working with file permissions easier
Browse files Browse the repository at this point in the history
  • Loading branch information
freost committed Oct 21, 2024
1 parent a3c165e commit 1518665
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ The major version bump is due to upping the required PHP version from `8.1` to `
- Time series
- Top-k
* Added `Redis::executeCommand()` helper method that makes it possible to call commands not yet supported by the client.
* Added `Permission` enum with the following methods to make it easier to interact with file permissions:
- `Permission::calculate()`
- `Permission::hasPermissions()`
* Added `FileSystem::setPermissions()` method that accepts `Permission` enum values.
* Added `FileSystem::hasPermissions()` method.
* Added `FileInfo::hasPermissions()` method.

#### Changes

* The gatekeeper `Session::login()` and `Session::forceLogin()` methods will now return a `LoginStatus` enum instance instead of a mix of boolean and integer values.
Expand Down
8 changes: 8 additions & 0 deletions src/mako/file/FileInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,12 @@ public function validateHmac(string $hmac, #[SensitiveParameter] string $key, st
{
return hash_equals($hmac, $this->getHmac($key, $algorithm, $raw));
}

/**
* Returns TRUE if the file permissions contain the specified permissions and FALSE if not.
*/
public function hasPermissions(Permission ...$permission): bool
{
return Permission::hasPermissions($this->getPerms() & Permission::FULL->value, ...$permission);
}
}
18 changes: 18 additions & 0 deletions src/mako/file/FileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
use FilesystemIterator;
use SplFileObject;

use function chmod;
use function copy;
use function disk_free_space;
use function disk_total_space;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function fileperms;
use function filesize;
use function getcwd;
use function glob;
Expand Down Expand Up @@ -75,6 +77,22 @@ public function isEmpty(string $path): bool
return filesize($path) === 0;
}

/**
* Sets the file permissions.
*/
public function setPermissions(string $path, Permission ...$permission): bool
{
return chmod($path, Permission::calculate(...$permission));
}

/**
* Returns TRUE if the file permissions contain the specified permissions and FALSE if not.
*/
public function hasPermission(string $path, Permission ...$permission): bool
{
return Permission::hasPermissions(fileperms($path) & Permission::FULL->value, ...$permission);
}

/**
* Returns TRUE if the file is readable and FALSE if not.
*/
Expand Down
77 changes: 77 additions & 0 deletions src/mako/file/Permission.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/**
* @copyright Frederic G. Østby
* @license http://www.makoframework.com/license
*/

namespace mako\file;

use InvalidArgumentException;

/**
* File permissions.
*/
enum Permission: int
{
// No permissions

case NONE = 0o000;

// Owner permissions

case OWNER_READ = 0o400;
case OWNER_WRITE = 0o200;
case OWNER_EXECUTE = 0o100;
case OWNER_FULL = 0o700;

// Group permissions

case GROUP_READ = 0o040;
case GROUP_WRITE = 0o020;
case GROUP_EXECUTE = 0o010;
case GROUP_FULL = 0o070;

// Public permissions

case PUBLIC_READ = 0o004;
case PUBLIC_WRITE = 0o002;
case PUBLIC_EXECUTE = 0o001;
case PUBLIC_FULL = 0o007;

// Full permissions (owner, group, and public)

case FULL = 0o777;

/**
* Calculates sum of the specified permissions.
*/
public static function calculate(Permission ...$permission): int
{
$permissions = static::NONE->value;

foreach ($permission as $_permission) {
$permissions |= $_permission->value;
}

return $permissions;
}

/**
* Returns TRUE if the permissions contain the specified permissions and FALSE if not.
*/
public static function hasPermissions(int $permissions, Permission ...$permission): bool
{
if ($permissions < 0o000 || $permissions > 0o777) {
throw new InvalidArgumentException(vsprintf('The integer %s does not represent a valid octal between 0o000 and 0o777.', [$permissions]));
}

$permission = static::calculate(...$permission);

if ($permission === 0o000) {
return $permissions === 0o000;
}

return ($permissions & $permission) === $permission;
}
}
114 changes: 114 additions & 0 deletions tests/unit/file/PermissionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

/**
* @copyright Frederic G. Østby
* @license http://www.makoframework.com/license
*/

namespace mako\tests\unit\file;

use InvalidArgumentException;
use mako\file\Permission;
use mako\tests\TestCase;

/**
* @group unit
*/
class PermissionTest extends TestCase
{
/**
*
*/
public function testCalculate(): void
{
// Test individual permissions

$this->assertSame(0o000, Permission::calculate(Permission::NONE));

$this->assertSame(0o400, Permission::calculate(Permission::OWNER_READ));

$this->assertSame(0o200, Permission::calculate(Permission::OWNER_WRITE));

$this->assertSame(0o100, Permission::calculate(Permission::OWNER_EXECUTE));

$this->assertSame(0o700, Permission::calculate(Permission::OWNER_FULL));

$this->assertSame(0o040, Permission::calculate(Permission::GROUP_READ));

$this->assertSame(0o020, Permission::calculate(Permission::GROUP_WRITE));

$this->assertSame(0o010, Permission::calculate(Permission::GROUP_EXECUTE));

$this->assertSame(0o070, Permission::calculate(Permission::GROUP_FULL));

$this->assertSame(0o004, Permission::calculate(Permission::PUBLIC_READ));

$this->assertSame(0o002, Permission::calculate(Permission::PUBLIC_WRITE));

$this->assertSame(0o001, Permission::calculate(Permission::PUBLIC_EXECUTE));

$this->assertSame(0o007, Permission::calculate(Permission::PUBLIC_FULL));

// Test combinations

$this->assertSame(0o777, Permission::calculate(
Permission::OWNER_FULL,
Permission::GROUP_FULL,
Permission::PUBLIC_FULL
));

$this->assertSame(0o744, Permission::calculate(
Permission::OWNER_FULL,
Permission::GROUP_READ,
Permission::PUBLIC_READ)
);

$this->assertSame(0o755, Permission::calculate(
Permission::OWNER_FULL,
Permission::GROUP_READ,
Permission::GROUP_EXECUTE,
Permission::PUBLIC_READ,
Permission::PUBLIC_EXECUTE)
);
}

/**
*
*/
public function testHasPermissionsWithInvalidPermissions(): void
{
$this->expectException(InvalidArgumentException::class);

$this->expectExceptionMessage('The integer 1337 does not represent a valid octal between 0o000 and 0o777.');

Permission::hasPermissions(1337, Permission::NONE);
}

/**
*
*/
public function testHasPermissions(): void
{
$this->assertTrue(Permission::hasPermissions(0o777, Permission::OWNER_FULL));

$this->assertTrue(Permission::hasPermissions(0o777, Permission::OWNER_FULL, Permission::GROUP_FULL));

$this->assertTrue(Permission::hasPermissions(0o777, Permission::OWNER_FULL, Permission::GROUP_FULL, Permission::PUBLIC_FULL));

$this->assertTrue(Permission::hasPermissions(0o755, Permission::OWNER_FULL));

$this->assertFalse(Permission::hasPermissions(0o755, Permission::GROUP_WRITE));

$this->assertFalse(Permission::hasPermissions(0o755, Permission::PUBLIC_WRITE));
}

/**
*
*/
public function testHasPermissionsWithNoPermissions(): void
{
$this->assertTrue(Permission::hasPermissions(0o000, Permission::NONE));

$this->assertFalse(Permission::hasPermissions(0o777, Permission::NONE));
}
}

0 comments on commit 1518665

Please sign in to comment.