Skip to content

Commit 067b93b

Browse files
committed
fix #430
1 parent 91b1ac9 commit 067b93b

File tree

12 files changed

+77
-28
lines changed

12 files changed

+77
-28
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,18 @@ Then the server will return a '422' HTTP status code and nice error message:
681681

682682
You can parse this output to make form fields show up with a red border and their appropriate error message.
683683

684+
### Multi Tenancy support
685+
686+
You may use the "multiTenancy" middleware when you have a multi-tenant database.
687+
If your tenants are identified by the "customer_id" column you can use the following handler:
688+
689+
'multiTenancy.handler' => function ($operation, $tableName) {
690+
return ['customer_id' => 12];
691+
},
692+
693+
This construct adds a filter requiring "customer_id" to be "12" to every operation (except for "create").
694+
It also sets the column "customer_id" on "create" to "12" and removes the column from any other write operation.
695+
684696
## OpenAPI specification
685697

686698
On the "/openapi" end-point the OpenAPI 3.0 (formerly called "Swagger") specification is served.

src/Tqdev/PhpCrudApi/Api.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Tqdev\PhpCrudApi\Middleware\CorsMiddleware;
1616
use Tqdev\PhpCrudApi\Middleware\FirewallMiddleware;
1717
use Tqdev\PhpCrudApi\Middleware\JwtAuthMiddleware;
18+
use Tqdev\PhpCrudApi\Middleware\MultiTenancyMiddleware;
1819
use Tqdev\PhpCrudApi\Middleware\Router\SimpleRouter;
1920
use Tqdev\PhpCrudApi\Middleware\SanitationMiddleware;
2021
use Tqdev\PhpCrudApi\Middleware\ValidationMiddleware;
@@ -62,6 +63,9 @@ public function __construct(Config $config)
6263
case 'sanitation':
6364
new SanitationMiddleware($router, $responder, $properties, $reflection);
6465
break;
66+
case 'multiTenancy':
67+
new MultiTenancyMiddleware($router, $responder, $properties, $reflection);
68+
break;
6569
case 'authorization':
6670
new AuthorizationMiddleware($router, $responder, $properties, $reflection);
6771
break;

src/Tqdev/PhpCrudApi/Column/ReflectionService.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,4 @@ public function removeTable(String $tableName): bool
8888
return $this->database->removeTable($tableName);
8989
}
9090

91-
public function removeColumn(String $tableName, String $columnName): bool
92-
{
93-
return $this->getTable($tableName)->removeColumn($columnName);
94-
}
9591
}

src/Tqdev/PhpCrudApi/Database/GenericDB.php

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
55
use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
6-
use Tqdev\PhpCrudApi\Record\Condition\AndCondition;
76
use Tqdev\PhpCrudApi\Record\Condition\ColumnCondition;
87
use Tqdev\PhpCrudApi\Record\Condition\Condition;
98

@@ -98,10 +97,17 @@ public function definition(): GenericDefinition
9897
return $this->definition;
9998
}
10099

101-
private function addAuthorizationCondition(String $tableName, Condition $condition2): Condition
100+
private function addMiddlewareConditions(String $tableName, Condition $condition): Condition
102101
{
103102
$condition1 = VariableStore::get("authorization.conditions.$tableName");
104-
return $condition1 ? AndCondition::fromArray([$condition1, $condition2]) : $condition2;
103+
if ($condition1) {
104+
$condition = $condition->_and($condition1);
105+
}
106+
$condition2 = VariableStore::get("multiTenancy.conditions.$tableName");
107+
if ($condition2) {
108+
$condition = $condition->_and($condition2);
109+
}
110+
return $condition;
105111
}
106112

107113
public function createSingle(ReflectedTable $table, array $columnValues) /*: ?String*/
@@ -131,7 +137,7 @@ public function selectSingle(ReflectedTable $table, array $columnNames, String $
131137
$selectColumns = $this->columns->getSelect($table, $columnNames);
132138
$tableName = $table->getName();
133139
$condition = new ColumnCondition($table->getPk(), 'eq', $id);
134-
$condition = $this->addAuthorizationCondition($tableName, $condition);
140+
$condition = $this->addMiddlewareConditions($tableName, $condition);
135141
$parameters = array();
136142
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
137143
$sql = 'SELECT ' . $selectColumns . ' FROM "' . $tableName . '" ' . $whereClause;
@@ -153,7 +159,7 @@ public function selectMultiple(ReflectedTable $table, array $columnNames, array
153159
$selectColumns = $this->columns->getSelect($table, $columnNames);
154160
$tableName = $table->getName();
155161
$condition = new ColumnCondition($table->getPk(), 'in', implode(',', $ids));
156-
$condition = $this->addAuthorizationCondition($tableName, $condition);
162+
$condition = $this->addMiddlewareConditions($tableName, $condition);
157163
$parameters = array();
158164
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
159165
$sql = 'SELECT ' . $selectColumns . ' FROM "' . $tableName . '" ' . $whereClause;
@@ -166,7 +172,7 @@ public function selectMultiple(ReflectedTable $table, array $columnNames, array
166172
public function selectCount(ReflectedTable $table, Condition $condition): int
167173
{
168174
$tableName = $table->getName();
169-
$condition = $this->addAuthorizationCondition($tableName, $condition);
175+
$condition = $this->addMiddlewareConditions($tableName, $condition);
170176
$parameters = array();
171177
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
172178
$sql = 'SELECT COUNT(*) FROM "' . $tableName . '"' . $whereClause;
@@ -178,7 +184,7 @@ public function selectAllUnordered(ReflectedTable $table, array $columnNames, Co
178184
{
179185
$selectColumns = $this->columns->getSelect($table, $columnNames);
180186
$tableName = $table->getName();
181-
$condition = $this->addAuthorizationCondition($tableName, $condition);
187+
$condition = $this->addMiddlewareConditions($tableName, $condition);
182188
$parameters = array();
183189
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
184190
$sql = 'SELECT ' . $selectColumns . ' FROM "' . $tableName . '"' . $whereClause;
@@ -195,7 +201,7 @@ public function selectAll(ReflectedTable $table, array $columnNames, Condition $
195201
}
196202
$selectColumns = $this->columns->getSelect($table, $columnNames);
197203
$tableName = $table->getName();
198-
$condition = $this->addAuthorizationCondition($tableName, $condition);
204+
$condition = $this->addMiddlewareConditions($tableName, $condition);
199205
$parameters = array();
200206
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
201207
$orderBy = $this->columns->getOrderBy($table, $columnOrdering);
@@ -216,7 +222,7 @@ public function updateSingle(ReflectedTable $table, array $columnValues, String
216222
$updateColumns = $this->columns->getUpdate($table, $columnValues);
217223
$tableName = $table->getName();
218224
$condition = new ColumnCondition($table->getPk(), 'eq', $id);
219-
$condition = $this->addAuthorizationCondition($tableName, $condition);
225+
$condition = $this->addMiddlewareConditions($tableName, $condition);
220226
$parameters = array_values($columnValues);
221227
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
222228
$sql = 'UPDATE "' . $tableName . '" SET ' . $updateColumns . $whereClause;
@@ -228,7 +234,7 @@ public function deleteSingle(ReflectedTable $table, String $id)
228234
{
229235
$tableName = $table->getName();
230236
$condition = new ColumnCondition($table->getPk(), 'eq', $id);
231-
$condition = $this->addAuthorizationCondition($tableName, $condition);
237+
$condition = $this->addMiddlewareConditions($tableName, $condition);
232238
$parameters = array();
233239
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
234240
$sql = 'DELETE FROM "' . $tableName . '" ' . $whereClause;
@@ -245,7 +251,7 @@ public function incrementSingle(ReflectedTable $table, array $columnValues, Stri
245251
$updateColumns = $this->columns->getIncrement($table, $columnValues);
246252
$tableName = $table->getName();
247253
$condition = new ColumnCondition($table->getPk(), 'eq', $id);
248-
$condition = $this->addAuthorizationCondition($tableName, $condition);
254+
$condition = $this->addMiddlewareConditions($tableName, $condition);
249255
$parameters = array_values($columnValues);
250256
$whereClause = $this->conditions->getWhereClause($condition, $parameters);
251257
$sql = 'UPDATE "' . $tableName . '" SET ' . $updateColumns . $whereClause;

src/Tqdev/PhpCrudApi/Middleware/AuthorizationMiddleware.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private function handleColumns(String $operation, String $tableName) /*: void*/
3030
foreach ($table->columnNames() as $columnName) {
3131
$allowed = call_user_func($columnHandler, $operation, $tableName, $columnName);
3232
if (!$allowed) {
33-
$this->reflection->removeColumn($tableName, $columnName);
33+
$table->removeColumn($columnName);
3434
}
3535
}
3636
}

tests/config/base.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
'username' => 'php-crud-api',
55
'password' => 'php-crud-api',
66
'controllers' => 'records,columns,cache,openapi',
7-
'middlewares' => 'cors,jwtAuth,basicAuth,authorization,validation,sanitation',
7+
'middlewares' => 'cors,jwtAuth,basicAuth,authorization,validation,sanitation,multiTenancy',
88
'jwtAuth.time' => '1538207605',
99
'jwtAuth.secret' => 'axpIrCGNGqxzx2R9dtXLIPUSqPo778uhb8CA0F4Hx',
1010
'basicAuth.passwordFile' => __DIR__ . DIRECTORY_SEPARATOR . '.htpasswd',
@@ -23,5 +23,8 @@
2323
'validation.handler' => function ($operation, $tableName, $column, $value, $context) {
2424
return ($column['name'] == 'post_id' && !is_numeric($value)) ? 'must be numeric' : true;
2525
},
26+
'multiTenancy.handler' => function ($operation, $tableName) {
27+
return ($tableName == 'kunsthåndværk') ? ['user_id' => 1] : [];
28+
},
2629
'debug' => true,
2730
];

tests/fixtures/blog_mysql.sql

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,16 @@ DROP TABLE IF EXISTS `kunsthåndværk`;
152152
CREATE TABLE `kunsthåndværk` (
153153
`id` varchar(36) NOT NULL,
154154
`Umlauts ä_ö_ü-COUNT` int(11) NOT NULL,
155+
`user_id` int(11) NOT NULL,
155156
`invisible` varchar(36),
156157
PRIMARY KEY (`id`),
157-
CONSTRAINT `kunsthåndværk_Umlauts ä_ö_ü-COUNT_fkey` UNIQUE (`Umlauts ä_ö_ü-COUNT`)
158+
CONSTRAINT `kunsthåndværk_Umlauts ä_ö_ü-COUNT_fkey` UNIQUE (`Umlauts ä_ö_ü-COUNT`),
159+
CONSTRAINT `kunsthåndværk_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
158160
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
159161

160-
INSERT INTO `kunsthåndværk` (`id`, `Umlauts ä_ö_ü-COUNT`, `invisible`) VALUES
161-
('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, NULL);
162+
INSERT INTO `kunsthåndværk` (`id`, `Umlauts ä_ö_ü-COUNT`, `user_id`, `invisible`) VALUES
163+
('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, 1, NULL),
164+
('e31ecfe6-591f-4660-9fbd-1a232083037f', 2, 2, NULL);
162165

163166
DROP TABLE IF EXISTS `invisibles`;
164167
CREATE TABLE `invisibles` (

tests/fixtures/blog_pgsql.sql

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ CREATE TABLE barcodes (
159159
CREATE TABLE "kunsthåndværk" (
160160
id character varying(36) NOT NULL,
161161
"Umlauts ä_ö_ü-COUNT" integer NOT NULL,
162+
user_id integer NOT NULL,
162163
invisible character varying(36)
163164
);
164165

@@ -263,8 +264,9 @@ INSERT INTO "barcodes" ("product_id", "hex", "bin") VALUES
263264
-- Data for Name: kunsthåndværk; Type: TABLE DATA; Schema: public; Owner: postgres
264265
--
265266

266-
INSERT INTO "kunsthåndværk" ("id", "Umlauts ä_ö_ü-COUNT", "invisible") VALUES
267-
('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, NULL);
267+
INSERT INTO "kunsthåndværk" ("id", "Umlauts ä_ö_ü-COUNT", "user_id", "invisible") VALUES
268+
('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, 1, NULL),
269+
('e31ecfe6-591f-4660-9fbd-1a232083037f', 2, 2, NULL);
268270

269271
--
270272
-- Data for Name: invisibles; Type: TABLE DATA; Schema: public; Owner: postgres
@@ -429,6 +431,13 @@ CREATE INDEX barcodes_product_id_idx ON barcodes USING btree (product_id);
429431
CREATE INDEX "kunsthåndværk_Umlauts ä_ö_ü-COUNT_idx" ON "kunsthåndværk" USING btree ("Umlauts ä_ö_ü-COUNT");
430432

431433

434+
--
435+
-- Name: kunsthåndværk_user_id_idx; Type: INDEX; Schema: public; Owner: postgres; Tablespace:
436+
--
437+
438+
CREATE INDEX "kunsthåndværk_user_id_idx" ON "kunsthåndværk" USING btree (user_id);
439+
440+
432441
--
433442
-- Name: comments_post_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
434443
--
@@ -484,6 +493,13 @@ ALTER TABLE ONLY barcodes
484493
ALTER TABLE ONLY "kunsthåndværk"
485494
ADD CONSTRAINT "kunsthåndværk_Umlauts ä_ö_ü-COUNT_uc" UNIQUE ("Umlauts ä_ö_ü-COUNT");
486495

496+
--
497+
-- Name: kunsthåndværk_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
498+
--
499+
500+
ALTER TABLE ONLY "kunsthåndværk"
501+
ADD CONSTRAINT "kunsthåndværk_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id);
502+
487503

488504
--
489505
-- PostgreSQL database dump complete

tests/fixtures/blog_sqlsrv.sql

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ GO
224224
CREATE TABLE [kunsthåndværk](
225225
[id] [nvarchar](36),
226226
[Umlauts ä_ö_ü-COUNT] [int] NOT NULL,
227+
[user_id] [int] NOT NULL,
227228
[invisible] [nvarchar](36),
228229
CONSTRAINT [PK_kunsthåndværk]
229230
PRIMARY KEY CLUSTERED([id] ASC)
@@ -294,7 +295,9 @@ GO
294295
INSERT [barcodes] ([product_id], [hex], [bin]) VALUES (1, N'00ff01', 0x00ff01)
295296
GO
296297

297-
INSERT [kunsthåndværk] ([id], [Umlauts ä_ö_ü-COUNT], [invisible]) VALUES ('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, NULL)
298+
INSERT [kunsthåndværk] ([id], [Umlauts ä_ö_ü-COUNT], [user_id], [invisible]) VALUES ('e42c77c6-06a4-4502-816c-d112c7142e6d', 1, 1, NULL)
299+
GO
300+
INSERT [kunsthåndværk] ([id], [Umlauts ä_ö_ü-COUNT], [user_id], [invisible]) VALUES ('e31ecfe6-591f-4660-9fbd-1a232083037f', 2, 2, NULL)
298301
GO
299302

300303
INSERT [invisibles] ([id]) VALUES ('e42c77c6-06a4-4502-816c-d112c7142e6d')
@@ -341,3 +344,9 @@ GO
341344

342345
ALTER TABLE [kunsthåndværk] WITH CHECK ADD CONSTRAINT [UC_kunsthåndværk_Umlauts ä_ö_ü-COUNT] UNIQUE([Umlauts ä_ö_ü-COUNT])
343346
GO
347+
348+
ALTER TABLE [kunsthåndværk] WITH CHECK ADD CONSTRAINT [FK_kunsthåndværk_users] FOREIGN KEY([user_id])
349+
REFERENCES [users] ([id])
350+
GO
351+
ALTER TABLE [kunsthåndværk] CHECK CONSTRAINT [FK_kunsthåndværk_users]
352+
GO

tests/functional/001_records/063_list_kunsthåndværk.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ GET /records/kunsthåndværk
22
===
33
200
44
Content-Type: application/json
5-
Content-Length: 86
5+
Content-Length: 98
66

7-
{"records":[{"id":"e42c77c6-06a4-4502-816c-d112c7142e6d","Umlauts ä_ö_ü-COUNT":1}]}
7+
{"records":[{"id":"e42c77c6-06a4-4502-816c-d112c7142e6d","Umlauts ä_ö_ü-COUNT":1,"user_id":1}]}

0 commit comments

Comments
 (0)