Skip to content

Commit 6593d38

Browse files
committed
Added dbAuth middleware
1 parent 71b9d5a commit 6593d38

File tree

5 files changed

+63
-10
lines changed

5 files changed

+63
-10
lines changed

README.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ You can tune the middleware behavior using middleware specific configuration par
173173
- "ajaxOnly.excludeMethods": The methods that do not require AJAX ("OPTIONS,GET")
174174
- "ajaxOnly.headerName": The name of the required header ("X-Requested-With")
175175
- "ajaxOnly.headerValue": The value of the required header ("XMLHttpRequest")
176+
- "dbAuth.mode": Set to "optional" if you want to allow anonymous access ("required")
177+
- "dbAuth.usersTable": The table that is used to store the users in ("users")
178+
- "dbAuth.usernameColumn": The users table column that holds usernames ("username")
179+
- "dbAuth.passwordColumn": The users table column that holds passwords ("password")
180+
- "dbAuth.returnedColumns": The columns returned on successful login, empty means 'all' ("")
176181
- "jwtAuth.mode": Set to "optional" if you want to allow anonymous access ("required")
177182
- "jwtAuth.header": Name of the header containing the JWT token ("X-Authorization")
178183
- "jwtAuth.leeway": The acceptable number of seconds of clock skew ("5")
@@ -635,13 +640,48 @@ The GeoJSON functionality is enabled by default, but can be disabled using the "
635640

636641
### Authentication
637642

638-
Authentication is done by means of sending a "Authorization" header. It identifies the user and stores this in the `$_SESSION` super global.
643+
Currently there are three types of authentication supported. They all store the authenticated user in the `$_SESSION` super global.
639644
This variable can be used in the authorization handlers to decide wether or not sombeody should have read or write access to certain tables, columns or records.
640-
Currently there are two types of authentication supported: "Basic" and "JWT". This functionality is enabled by adding the 'basicAuth' and/or 'jwtAuth' middleware.
645+
The following overview shows the kinds of authentication middleware that you can enable.
646+
647+
| Name | Middleware | Authenticated via | Users are stored in | Session variable |
648+
| -------- | ---------- | ---------------------- | ------------------- | --------------------- |
649+
| Database | dbAuth | '/login' endpoint | Database table | $_SESSION['user'] |
650+
| Basic | basicAuth | 'Authorization' header | '.htpasswd' file | $_SESSION['username'] |
651+
| JWT | jwtAuth | 'Authorization' header | identity provider | $_SESSION['claims'] |
652+
653+
Below you find more information on each of the authentication types.
654+
655+
#### Database authentication
656+
657+
The database authentication middleware defines two new routes:
658+
659+
method path - parameters - description
660+
----------------------------------------------------------------------------------------
661+
POST /login - username + password - logs a user in by username and password
662+
POST /logout - - logs out the currently logged in user
663+
664+
A user can be logged in by sending it's username and password to the login endpoint (in JSON format).
665+
The authenticated user (with all it's properties) will be stored in the `$_SESSION['user']` variable.
666+
The user can be logged out by sending a POST request with an empty body to the logout endpoint.
667+
The passwords are stored as hashes in the password column in the users table. To generate the hash value
668+
for the password 'pass2' you can run on the command line:
669+
670+
php -r 'echo password_hash("pass2", PASSWORD_DEFAULT)."\n";'
671+
672+
It is IMPORTANT to restrict access to the users table using the 'authorization' middleware, otherwise all
673+
users can freely add, modify or delete any account! The minimal configuration is shown below:
674+
675+
'middlewares' => 'dbAuth,authorization',
676+
'authorization.tableHandler' => function ($operation, $tableName) {
677+
return $tableName != 'users';
678+
},
679+
680+
Note that this middleware uses session cookies and stores the logged in state on the server.
641681

642682
#### Basic authentication
643683

644-
The Basic type supports a file that holds the users and their (hashed) passwords separated by a colon (':').
684+
The Basic type supports a file (by default '.htpasswd') that holds the users and their (hashed) passwords separated by a colon (':').
645685
When the passwords are entered in plain text they fill be automatically hashed.
646686
The authenticated username will be stored in the `$_SESSION['username']` variable.
647687
You need to send an "Authorization" header containing a base64 url encoded and colon separated username and password after the word "Basic".

src/Tqdev/PhpCrudApi/Middleware/DbAuthMiddleware.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,14 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
4545
$passwordColumnName = $this->getProperty('passwordColumn', 'password');
4646
$passwordColumn = $table->getColumn($passwordColumnName);
4747
$condition = new ColumnCondition($usernameColumn, 'eq', $username);
48-
$columnNames = $table->getColumnNames();
49-
$users = $this->db->selectAll($table, $columnNames, $condition, [], 0, -1);
48+
$returnedColumns = $this->getProperty('returnedColumns', '');
49+
if (!$returnedColumns) {
50+
$columnNames = $table->getColumnNames();
51+
} else {
52+
$columnNames = array_map('trim', explode(',', $returnedColumns));
53+
$columnNames[] = $passwordColumnName;
54+
}
55+
$users = $this->db->selectAll($table, $columnNames, $condition, [], 0, 1);
5056
foreach ($users as $user) {
5157
if (password_verify($password, $user[$passwordColumnName]) == 1) {
5258
if (!headers_sent()) {
@@ -63,7 +69,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
6369
if (isset($_SESSION['user'])) {
6470
$user = $_SESSION['user'];
6571
unset($_SESSION['user']);
66-
session_destroy();
72+
if (session_status() != PHP_SESSION_NONE) {
73+
session_destroy();
74+
}
6775
return $this->responder->success($user);
6876
}
6977
return $this->responder->error(ErrorCode::AUTHENTICATION_REQUIRED, '');

src/index.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
'username' => 'php-crud-api',
1111
'password' => 'php-crud-api',
1212
'database' => 'php-crud-api',
13+
'middlewares' => 'dbAuth,authorization',
14+
'authorization.tableHandler' => function ($operation, $tableName) {
15+
return $tableName != 'users';
16+
},
1317
]);
1418
$request = RequestFactory::fromGlobals();
1519
$api = new Api($config);

tests/config/base.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'controllers' => 'records,columns,cache,openapi,geojson',
77
'middlewares' => 'cors,dbAuth,jwtAuth,basicAuth,authorization,validation,ipAddress,sanitation,multiTenancy,pageLimits,joinLimits,customization',
88
'dbAuth.mode' => 'optional',
9+
'dbAuth.returnedColumns' => 'id,username,password',
910
'jwtAuth.mode' => 'optional',
1011
'jwtAuth.time' => '1538207605',
1112
'jwtAuth.secret' => 'axpIrCGNGqxzx2R9dtXLIPUSqPo778uhb8CA0F4Hx',

tests/functional/002_auth/003_db_auth.log

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ Content-Type: application/json
1313
===
1414
200
1515
Content-Type: application/json
16-
Content-Length: 43
16+
Content-Length: 27
1717

18-
{"id":2,"username":"user2","location":null}
18+
{"id":2,"username":"user2"}
1919
===
2020
GET /records/invisibles/e42c77c6-06a4-4502-816c-d112c7142e6d
2121
===
@@ -48,9 +48,9 @@ POST /logout
4848
===
4949
200
5050
Content-Type: application/json
51-
Content-Length: 43
51+
Content-Length: 27
5252

53-
{"id":2,"username":"user2","location":null}
53+
{"id":2,"username":"user2"}
5454
===
5555
GET /records/invisibles/e42c77c6-06a4-4502-816c-d112c7142e6d
5656
===

0 commit comments

Comments
 (0)