Skip to content

Commit

Permalink
Integrate Casbin authorizator (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
akadlec authored Jul 16, 2024
1 parent f7e4b3a commit 6ba7ab2
Show file tree
Hide file tree
Showing 32 changed files with 1,395 additions and 72 deletions.
4 changes: 2 additions & 2 deletions .docs/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ class SomeSessionController {
/** @var Security\User */
private Security\User $user;

/** @var Models\Tokens\TokensManager */
private Models\Tokens\TokensManager $tokensManager;
/** @var Models\Tokens\Manager */
private Models\Tokens\Manager $tokensManager;

/** @var Security\TokenBuilder */
private Security\TokenBuilder $tokenBuilder;
Expand Down
8 changes: 7 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
"require" : {
"php": ">=8.1.0",
"ext-openssl": "*",
"casbin/casbin": "^3.23",
"casbin/dbal-adapter": "^2.4",
"contributte/event-dispatcher": "^0.9",
"cweagans/composer-patches": "^1.7",
"fastybird/datetime-factory": "^0.6",
Expand Down Expand Up @@ -86,7 +88,8 @@
"autoload-dev" : {
"psr-4" : {
"FastyBird\\SimpleAuth\\Tests\\Cases\\Unit\\" : "tests/cases/unit",
"FastyBird\\SimpleAuth\\Tests\\Fixtures\\" : "tests/fixtures"
"FastyBird\\SimpleAuth\\Tests\\Fixtures\\" : "tests/fixtures",
"FastyBird\\SimpleAuth\\Tests\\Tools\\": "tests/tools"
}
},

Expand All @@ -104,6 +107,9 @@
"patches" : {
"nette/utils" : {
"Bug: Offset check with null support" : "https://raw.githubusercontent.com/FastyBird/libraries-patches/master/nette.array.offsetCheck.diff"
},
"nettrine/orm": {
"Enable connection overrides": "https://raw.githubusercontent.com/FastyBird/libraries-patches/master/nettrine-orm-src-managerregistry-php.patch"
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions resources/model.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
2 changes: 2 additions & 0 deletions src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ final class Constants

public const ROLE_ADMINISTRATOR = 'administrator';

public const USER_ANONYMOUS = 'guest';

}
125 changes: 98 additions & 27 deletions src/DI/SimpleAuthExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@

namespace FastyBird\SimpleAuth\DI;

use Casbin;
use CasbinAdapter;
use Doctrine\DBAL\Connection;
use Doctrine\Persistence;
use FastyBird\SimpleAuth;
use FastyBird\SimpleAuth\Entities;
use FastyBird\SimpleAuth\Events;
use FastyBird\SimpleAuth\Exceptions;
use FastyBird\SimpleAuth\Mapping;
use FastyBird\SimpleAuth\Middleware;
use FastyBird\SimpleAuth\Security;
use FastyBird\SimpleAuth\Subscribers;
use IPub\DoctrineCrud;
use Nette;
use Nette\Application as NetteApplication;
use Nette\DI;
Expand All @@ -32,7 +34,8 @@
use stdClass;
use Symfony\Contracts\EventDispatcher;
use function assert;
use function ucfirst;
use function is_file;
use function is_string;
use const DIRECTORY_SEPARATOR;

/**
Expand Down Expand Up @@ -69,6 +72,9 @@ public function getConfigSchema(): Schema\Schema
'mapping' => Schema\Expect::bool(false),
'models' => Schema\Expect::bool(false),
]),
'casbin' => Schema\Expect::structure([
'database' => Schema\Expect::bool(false),
]),
'nette' => Schema\Expect::structure([
'application' => Schema\Expect::bool(false),
]),
Expand All @@ -80,6 +86,24 @@ public function getConfigSchema(): Schema\Schema
'services' => Schema\Expect::structure([
'identity' => Schema\Expect::bool(false),
]),
'casbin' => Schema\Expect::structure([
'model' => Schema\Expect::string(
// phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'model.conf',
),
'policy' => Schema\Expect::string(),
]),
'database' => Schema\Expect::anyOf(
Schema\Expect::null(),
Schema\Expect::structure([
'driver' => Schema\Expect::string('pdo_mysql'),
'host' => Schema\Expect::string('127.0.0.1'),
'port' => Schema\Expect::int(3_306),
'database' => Schema\Expect::string('security'),
'user' => Schema\Expect::string('root'),
'password' => Schema\Expect::string(),
]),
),
]);
}

Expand Down Expand Up @@ -162,14 +186,19 @@ public function loadConfiguration(): void
}

if ($configuration->enable->doctrine->models) {
$builder->addDefinition($this->prefix('doctrine.tokenRepository'), new DI\Definitions\ServiceDefinition())
->setType(SimpleAuth\Models\Tokens\TokenRepository::class);
$builder->addDefinition($this->prefix('doctrine.tokensRepository'), new DI\Definitions\ServiceDefinition())
->setType(SimpleAuth\Models\Tokens\Repository::class);

$builder->addDefinition($this->prefix('doctrine.tokensManager'), new DI\Definitions\ServiceDefinition())
->setType(SimpleAuth\Models\Tokens\TokensManager::class)
->setArgument('entityCrud', '__placeholder__');
->setType(SimpleAuth\Models\Tokens\Manager::class);
}

$builder->addDefinition($this->prefix('doctrine.policiesRepository'), new DI\Definitions\ServiceDefinition())
->setType(SimpleAuth\Models\Policies\Repository::class);

$builder->addDefinition($this->prefix('doctrine.policiesManager'), new DI\Definitions\ServiceDefinition())
->setType(SimpleAuth\Models\Policies\Manager::class);

/**
* Nette application extension
*/
Expand All @@ -182,6 +211,7 @@ public function loadConfiguration(): void

/**
* @throws DI\MissingServiceException
* @throws Exceptions\Logical
*/
public function beforeCompile(): void
{
Expand Down Expand Up @@ -258,32 +288,73 @@ public function beforeCompile(): void
}
}
}
}

/**
* @throws DI\MissingServiceException
*/
public function afterCompile(PhpGenerator\ClassType $class): void
{
$builder = $this->getContainerBuilder();
$configuration = $this->getConfig();
assert($configuration instanceof stdClass);

/**
* Doctrine extension
* Casbin
*/

if ($configuration->enable->doctrine->models) {
$entityFactoryServiceName = $builder->getByType(DoctrineCrud\Crud\IEntityCrudFactory::class, true);
if ($configuration->enable->casbin->database) {
$connectionServiceName = $builder->getByType(Connection::class);

if ($connectionServiceName !== null) {
$connectionService = $builder->getDefinition($connectionServiceName);

$adapter = $builder->addDefinition(
$this->prefix('casbin.adapter'),
new DI\Definitions\ServiceDefinition(),
)
->setType(CasbinAdapter\DBAL\Adapter::class)
->setArguments([
'connection' => $connectionService,
])
->addSetup('$policyTableName', ['fb_security_policies']);
} else {
$adapter = $builder->addDefinition(
$this->prefix('casbin.adapter'),
new DI\Definitions\ServiceDefinition(),
)
->setType(CasbinAdapter\DBAL\Adapter::class)
->setArguments([
'connection' => [
'driver' => $configuration->database->driver,
'host' => $configuration->database->host,
'port' => $configuration->database->port,
'dbname' => $configuration->database->database,
'user' => $configuration->database->user,
'password' => $configuration->database->password,
'policy_table_name' => 'fb_security_policies',
],
]);
}

$tokensManagerService = $class->getMethod(
'createService' . ucfirst($this->name) . '__doctrine__tokensManager',
);
$tokensManagerService->setBody(
'return new ' . SimpleAuth\Models\Tokens\TokensManager::class
. '($this->getService(\'' . $entityFactoryServiceName . '\')->create(\'' . Entities\Tokens\Token::class . '\'));',
);
$builder->addDefinition($this->prefix('casbin.subscriber'), new DI\Definitions\ServiceDefinition())
->setType(Subscribers\Policy::class);
} else {
$policyFile = $configuration->casbin->policy;

if (!is_string($policyFile) || !is_file($policyFile)) {
throw new Exceptions\Logical('Casbin policy file is not configured');
}

$adapter = $builder->addDefinition($this->prefix('casbin.adapter'), new DI\Definitions\ServiceDefinition())
->setType(Casbin\Persist\Adapters\FileAdapter::class)
->setArguments([
'filePath' => $policyFile,
]);
}

$modelFile = $configuration->casbin->model;

if (!is_string($modelFile) || !is_file($modelFile)) {
throw new Exceptions\Logical('Casbin model file is not configured');
}

$builder->addDefinition($this->prefix('casbin.enforcer'), new DI\Definitions\ServiceDefinition())
->setType(Casbin\Enforcer::class)
->setArguments([
$modelFile,
$adapter,
]);
}

}
Loading

0 comments on commit 6ba7ab2

Please sign in to comment.