Skip to content

Commit

Permalink
Merge pull request #343 from utopia-php/feat-authorization-v2
Browse files Browse the repository at this point in the history
Feat: Make authorization per-instance
  • Loading branch information
abnegate authored Jan 25, 2024
2 parents f55b13d + 6f8b4a7 commit b64caf4
Show file tree
Hide file tree
Showing 18 changed files with 2,079 additions and 1,963 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A list of the utopia/php concepts and their relevant equivalent using the differ
- **Document** - A simple JSON object that will be stored in one of the utopia/database collections. For SQL-based adapters, this will be equivalent to a row. For a No-SQL adapter, this will equivalent to a native document.
- **Attribute** - A simple document attribute. For SQL-based adapters, this will be equivalent to a column. For a No-SQL adapter, this will equivalent to a native document field.
- **Index** - A simple collection index used to improve the performance of your database queries.
- **Permissions** - Using permissions, you can decide which roles have read, create, update and delete access for a specific document. The special attribute `$permissions` is used to store permission metadata for each document in the collection. A permission role can be any string you want. You can use `Authorization::setRole()` to delegate new roles to your users, once obtained a new role a user would gain read, create, update or delete access to a relevant document.
- **Permissions** - Using permissions, you can decide which roles have read, create, update and delete access for a specific document. The special attribute `$permissions` is used to store permission metadata for each document in the collection. A permission role can be any string you want. You can use `$authorization->addRole()` to delegate new roles to your users, once obtained a new role a user would gain read, create, update or delete access to a relevant document.

### Filters

Expand Down
15 changes: 8 additions & 7 deletions bin/tasks/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use Utopia\Http\Validator\Numeric;
use Utopia\Http\Validator\Text;

$authorization = new Authorization();

/**
* @Example
* docker compose exec tests bin/load --adapter=mariadb --limit=1000 --name=testing
Expand Down Expand Up @@ -61,7 +63,7 @@
$database->setNamespace($namespace);

// Outline collection schema
createSchema($database);
$createSchema($database);

// reclaim resources
$database = null;
Expand Down Expand Up @@ -121,7 +123,7 @@
$database->setNamespace($namespace);

// Outline collection schema
createSchema($database);
$createSchema($database);

// reclaim resources
$database = null;
Expand Down Expand Up @@ -183,7 +185,7 @@
$database->setNamespace($namespace);

// Outline collection schema
createSchema($database);
$createSchema($database);

// Fill DB
$faker = Factory::create();
Expand Down Expand Up @@ -226,14 +228,13 @@
});


function createSchema(Database $database): void
{
$createSchema = function (Database $database) use ($authorization): void {
if ($database->exists($database->getDatabase())) {
$database->delete($database->getDatabase());
}
$database->create();

Authorization::setRole(Role::any()->toString());
$authorization->addRole(Role::any()->toString());

$database->createCollection('articles', permissions: [
Permission::create(Role::any()),
Expand All @@ -247,7 +248,7 @@ function createSchema(Database $database): void
$database->createAttribute('articles', 'views', Database::VAR_INTEGER, 0, true);
$database->createAttribute('articles', 'tags', Database::VAR_STRING, 0, true, array: true);
$database->createIndex('articles', 'text', Database::INDEX_FULLTEXT, ['text']);
}
};

function createDocument($database, Generator $faker): void
{
Expand Down
20 changes: 9 additions & 11 deletions bin/tasks/query.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
use Utopia\Database\Query;
use Utopia\Database\Adapter\Mongo;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Validator\Authorization;
use Utopia\Http\Validator\Numeric;
use Utopia\Http\Validator\Text;

Expand Down Expand Up @@ -84,35 +83,35 @@

$report = [];

$count = setRoles($faker, 1);
$count = $setRoles($faker, 1);
Console::info("\n{$count} roles:");
$report[] = [
'roles' => $count,
'results' => runQueries($database, $limit)
];

$count = setRoles($faker, 100);
$count = $setRoles($faker, 100);
Console::info("\n{$count} roles:");
$report[] = [
'roles' => $count,
'results' => runQueries($database, $limit)
];

$count = setRoles($faker, 400);
$count = $setRoles($faker, 400);
Console::info("\n{$count} roles:");
$report[] = [
'roles' => $count,
'results' => runQueries($database, $limit)
];

$count = setRoles($faker, 500);
$count = $setRoles($faker, 500);
Console::info("\n{$count} roles:");
$report[] = [
'roles' => $count,
'results' => runQueries($database, $limit)
];

$count = setRoles($faker, 1000);
$count = $setRoles($faker, 1000);
Console::info("\n{$count} roles:");
$report[] = [
'roles' => $count,
Expand All @@ -136,13 +135,12 @@
Console::error($error->getMessage());
});

function setRoles($faker, $count): int
{
$setRoles = function ($faker, $count) use ($authorization): int {
for ($i = 0; $i < $count; $i++) {
Authorization::setRole($faker->numerify('user####'));
$authorization->addRole($faker->numerify('user####'));
}
return \count(Authorization::getRoles());
}
return \count($authorization->getRoles());
};

function runQueries(Database $database, int $limit): array
{
Expand Down
10 changes: 5 additions & 5 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Exception;
use Utopia\Database\Exception as DatabaseException;
use Utopia\Database\Validator\Authorization;

abstract class Adapter
{
Expand Down Expand Up @@ -32,6 +33,23 @@ abstract class Adapter
*/
protected array $metadata = [];

/**
* @var Authorization
*/
protected Authorization $authorization;

/**
* @param Authorization $authorization
*
* @return $this
*/
public function setAuthorization(Authorization $authorization): self
{
$this->authorization = $authorization;

return $this;
}

/**
* @param string $key
* @param mixed $value
Expand Down
13 changes: 6 additions & 7 deletions src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;

class MariaDB extends SQL
{
Expand Down Expand Up @@ -1529,7 +1528,7 @@ public function deleteDocument(string $collection, string $id): bool
public function find(string $collection, array $queries = [], ?int $limit = 25, ?int $offset = null, array $orderAttributes = [], array $orderTypes = [], array $cursor = [], string $cursorDirection = Database::CURSOR_AFTER): array
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$orders = [];

Expand Down Expand Up @@ -1613,7 +1612,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
$where[] = $conditions;
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down Expand Up @@ -1730,7 +1729,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
public function count(string $collection, array $queries = [], ?int $max = null): int
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$limit = \is_null($max) ? '' : 'LIMIT :max';

Expand All @@ -1739,7 +1738,7 @@ public function count(string $collection, array $queries = [], ?int $max = null)
$where[] = $conditions;
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down Expand Up @@ -1801,15 +1800,15 @@ public function count(string $collection, array $queries = [], ?int $max = null)
public function sum(string $collection, string $attribute, array $queries = [], ?int $max = null): int|float
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$limit = \is_null($max) ? '' : 'LIMIT :max';

foreach ($queries as $query) {
$where[] = $this->getSQLCondition($query);
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down
12 changes: 6 additions & 6 deletions src/Database/Adapter/Mongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -927,8 +927,8 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
}

// permissions
if (Authorization::$status) { // skip if authorization is disabled
$roles = \implode('|', Authorization::getRoles());
if ($this->authorization->getStatus()) { // skip if authorization is disabled
$roles = \implode('|', $this->authorization->getRoles());
$filters['_permissions']['$in'] = [new Regex("read\(\".*(?:{$roles}).*\"\)", 'i')];
}

Expand Down Expand Up @@ -1196,8 +1196,8 @@ public function count(string $collection, array $queries = [], ?int $max = null)
$filters = $this->buildFilters($queries);

// permissions
if (Authorization::$status) { // skip if authorization is disabled
$roles = \implode('|', Authorization::getRoles());
if ($this->authorization->getStatus()) { // skip if authorization is disabled
$roles = \implode('|', $this->authorization->getRoles());
$filters['_permissions']['$in'] = [new Regex("read\(\".*(?:{$roles}).*\"\)", 'i')];
}

Expand All @@ -1223,8 +1223,8 @@ public function sum(string $collection, string $attribute, array $queries = [],
$filters = $this->buildFilters($queries);

// permissions
if (Authorization::$status) { // skip if authorization is disabled
$roles = \implode('|', Authorization::getRoles());
if ($this->authorization->getStatus()) { // skip if authorization is disabled
$roles = \implode('|', $this->authorization->getRoles());
$filters['_permissions']['$in'] = [new Regex("read\(\".*(?:{$roles}).*\"\)", 'i')];
}

Expand Down
13 changes: 6 additions & 7 deletions src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Timeout;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;

class Postgres extends SQL
{
Expand Down Expand Up @@ -1515,7 +1514,7 @@ public function deleteDocument(string $collection, string $id): bool
public function find(string $collection, array $queries = [], ?int $limit = 25, ?int $offset = null, array $orderAttributes = [], array $orderTypes = [], array $cursor = [], string $cursorDirection = Database::CURSOR_AFTER): array
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$orders = [];

Expand Down Expand Up @@ -1597,7 +1596,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
$where[] = "table_main._tenant = :_tenant";
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down Expand Up @@ -1710,7 +1709,7 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
public function count(string $collection, array $queries = [], ?int $max = null): int
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$limit = \is_null($max) ? '' : 'LIMIT :max';

Expand All @@ -1723,7 +1722,7 @@ public function count(string $collection, array $queries = [], ?int $max = null)
$where[] = "table_main._tenant = :_tenant";
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down Expand Up @@ -1774,7 +1773,7 @@ public function count(string $collection, array $queries = [], ?int $max = null)
public function sum(string $collection, string $attribute, array $queries = [], ?int $max = null): int|float
{
$name = $this->filter($collection);
$roles = Authorization::getRoles();
$roles = $this->authorization->getRoles();
$where = [];
$limit = \is_null($max) ? '' : 'LIMIT :max';

Expand All @@ -1786,7 +1785,7 @@ public function sum(string $collection, string $attribute, array $queries = [],
$where[] = "table_main._tenant = :_tenant";
}

if (Authorization::$status) {
if ($this->authorization->getStatus()) {
$where[] = $this->getSQLPermissionsCondition($name, $roles);
}

Expand Down
Loading

0 comments on commit b64caf4

Please sign in to comment.