mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 08:14:02 +00:00
Simplify and clarify comment-related TableRLSManager code
This commit is contained in:
parent
f65c64c9c7
commit
349ac6e9fc
2 changed files with 19 additions and 29 deletions
|
|
@ -82,9 +82,9 @@ class TableRLSManager implements RLSPolicyManager
|
||||||
$table = str($table)->afterLast('.')->toString();
|
$table = str($table)->afterLast('.')->toString();
|
||||||
|
|
||||||
// For each table, we get a list of all foreign key columns
|
// For each table, we get a list of all foreign key columns
|
||||||
$foreignKeys = collect($builder->getForeignKeys($table))->merge($this->getCommentConstraints($table))->map(function ($foreign) use ($table) {
|
$foreignKeys = collect($builder->getForeignKeys($table))
|
||||||
return $this->formatForeignKey($foreign, $table);
|
->merge($this->getCommentConstraints($table))
|
||||||
});
|
->map(fn ($foreign) => $this->formatForeignKey($foreign, $table));
|
||||||
|
|
||||||
// We loop through each foreign key column and find
|
// We loop through each foreign key column and find
|
||||||
// all possible paths that lead to the tenants table
|
// all possible paths that lead to the tenants table
|
||||||
|
|
@ -149,56 +149,48 @@ class TableRLSManager implements RLSPolicyManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve comment-based constraints for a table. These are columns with comments in the format:
|
* Retrieve table's comment-based constraints. These are columns with comments
|
||||||
* "rls <foreign_table>.<foreign_column>"
|
* formatted like "rls <foreign_table>.<foreign_column>".
|
||||||
*
|
*
|
||||||
|
* Returns the constraints as unformatted foreign key arrays, ready to be passed to $this->formatForeignKey().
|
||||||
|
* *
|
||||||
* Throws an exception if the comment is formatted incorrectly or if the referenced table/column does not exist.
|
* Throws an exception if the comment is formatted incorrectly or if the referenced table/column does not exist.
|
||||||
*/
|
*/
|
||||||
protected function getCommentConstraints(string $tableName): array
|
protected function getCommentConstraints(string $tableName): array
|
||||||
{
|
{
|
||||||
$columns = $this->database->getSchemaBuilder()->getColumns($tableName);
|
$builder = $this->database->getSchemaBuilder();
|
||||||
$schemaBuilder = $this->database->getSchemaBuilder();
|
$commentConstraintColumns = array_filter($builder->getColumns($tableName), function ($column) {
|
||||||
|
|
||||||
$commentConstraints = array_filter($columns, function ($column) {
|
|
||||||
return (isset($column['comment']) && is_string($column['comment']))
|
return (isset($column['comment']) && is_string($column['comment']))
|
||||||
&& Str::startsWith($column['comment'], 'rls ');
|
&& Str::startsWith($column['comment'], 'rls ');
|
||||||
});
|
});
|
||||||
|
|
||||||
return array_map(function ($commentConstraint) use ($schemaBuilder, $tableName) {
|
return array_map(function ($column) use ($builder, $tableName) {
|
||||||
$comment = $commentConstraint['comment'];
|
$constraint = explode('.', Str::after($column['comment'], 'rls '));
|
||||||
$constraintString = Str::after($comment, 'rls ');
|
|
||||||
$constraint = explode('.', $constraintString);
|
|
||||||
|
|
||||||
// Validate comment constraint format
|
// Validate comment constraint format
|
||||||
if (count($constraint) !== 2 || empty($constraint[0]) || empty($constraint[1])) {
|
if (count($constraint) !== 2 || empty($constraint[0]) || empty($constraint[1])) {
|
||||||
throw new RLSCommentConstraintException("Incorrectly formatted comment constraint on {$tableName}.{$commentConstraint['name']}: '{$comment}'");
|
throw new RLSCommentConstraintException("Incorrectly formatted comment constraint on {$tableName}.{$column['name']}: '{$column['comment']}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
$foreignTable = $constraint[0];
|
$foreignTable = $constraint[0];
|
||||||
$foreignColumn = $constraint[1];
|
$foreignColumn = $constraint[1];
|
||||||
|
|
||||||
// Validate table existence
|
// Validate table existence
|
||||||
$allTables = array_map(function ($table) {
|
if (! $builder->hasTable($foreignTable)) {
|
||||||
return str($table)->afterLast('.')->toString();
|
throw new RLSCommentConstraintException("Comment constraint on {$tableName}.{$column['name']} references non-existent table '{$foreignTable}'");
|
||||||
}, $schemaBuilder->getTableListing(schema: $this->database->getConfig('search_path')));
|
|
||||||
|
|
||||||
if (! in_array($foreignTable, $allTables, true)) {
|
|
||||||
throw new RLSCommentConstraintException("Comment constraint on {$tableName}.{$commentConstraint['name']} references non-existent table '{$foreignTable}'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate column existence
|
// Validate column existence
|
||||||
$foreignColumns = $schemaBuilder->getColumns($foreignTable);
|
if (! $builder->hasColumn($foreignTable, $foreignColumn)) {
|
||||||
$foreignColumnNames = array_column($foreignColumns, 'name');
|
throw new RLSCommentConstraintException("Comment constraint on {$tableName}.{$column['name']} references non-existent column '{$foreignTable}.{$foreignColumn}'");
|
||||||
if (! in_array($foreignColumn, $foreignColumnNames, true)) {
|
|
||||||
throw new RLSCommentConstraintException("Comment constraint on {$tableName}.{$commentConstraint['name']} references non-existent column '{$foreignTable}.{$foreignColumn}'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'foreign_table' => $foreignTable,
|
'foreign_table' => $foreignTable,
|
||||||
'foreign_columns' => [$foreignColumn],
|
'foreign_columns' => [$foreignColumn],
|
||||||
'columns' => [$commentConstraint['name']],
|
'columns' => [$column['name']],
|
||||||
];
|
];
|
||||||
}, $commentConstraints);
|
}, $commentConstraintColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get tree's non-nullable paths. */
|
/** Get tree's non-nullable paths. */
|
||||||
|
|
@ -266,7 +258,7 @@ class TableRLSManager implements RLSPolicyManager
|
||||||
*/
|
*/
|
||||||
protected function formatForeignKey(array $foreignKey, string $table): array
|
protected function formatForeignKey(array $foreignKey, string $table): array
|
||||||
{
|
{
|
||||||
// $foreignKey is one of the foreign keys retrieved by $this->database->getSchemaBuilder()->getForeignKeys($table)
|
// $foreignKey is an unformatted foreign key retrieved by $this->database->getSchemaBuilder()->getForeignKeys($table)
|
||||||
return [
|
return [
|
||||||
'foreignKey' => $foreignKeyName = $foreignKey['columns'][0],
|
'foreignKey' => $foreignKeyName = $foreignKey['columns'][0],
|
||||||
'foreignTable' => $foreignKey['foreign_table'],
|
'foreignTable' => $foreignKey['foreign_table'],
|
||||||
|
|
|
||||||
|
|
@ -703,8 +703,6 @@ test('table manager can generate paths leading through non-constrained foreign k
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'non_constrained_users' => [
|
'non_constrained_users' => [
|
||||||
// Category tree gets excluded because the category table is related to the tenant table
|
|
||||||
// only through a column with the 'no-rls' comment
|
|
||||||
'tenant_id' => [
|
'tenant_id' => [
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue