Dynamic database credentials based on user authentication
- dsar
- Backer | 53
Hi,
this is my use-case: there is a desktop application that I want to bring online for conveniency. Fortunately much of the logic is on the database side via stored procedures, even user access-crontrol per row (and column) is done on the database side. There is almost nothing to code.
So the Authenticator must authenticate the user against database credentials, then saving these to a special Identity class. However access to the database must also be done against these, i.e. database credentials must be read from Identity of the logged user.
Has anyone tried doing this? How to do the last step?
Thank you
- Marek Bartoš
- Nette Blogger | 1260
If I understand it correctly:
- you have one database
- if user is not logged in, use shared credentials (to be able to log in the user)
- once the user is logged in, get the credentials stored in Identity and connect with user credentials instead
Simplest would be to extend Connection (no interface exists for Nette\Database\Connection), request User in constructor and call parent construct with either shared or user credentials depending whether User is logged in or not.
- dsar
- Backer | 53
I thought a more elegant solution would be using DI? But things got really complicated.
I don't like to extend classes, furthermore David is changing (correctly) many classes to final.
Based on your suggestion, something like this may work?
namespace App\Database;
use Nette\Database\Connection;
use Nette\Security\User;
final class ConnectionFactory
{
protected User $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function create(string $dsn, string $user, string $pass, array $options): Connection
{
if ($this->user->getIdentity()) {
$user = $this->user->getIdentity()->data['dbuser'];
$pass = $this->user->getIdentity()->data['dbpass'];
}
return new Connection($dsn, $user, $pass, $options);
}
}
parameters:
database:
dsn: ...
user: ...
password: ...
options:
...
database:
dsn: %database.dsn%
services:
- App\Database\ConnectionFactory
database.default:
create: @App\Database\ConnectionFactory::create(%database.dsn%, %database.user%, %database.password%, %database.options%)
I didn't test it because I'm not at home
Last edited by dsar (2023-06-16 10:10)
- Marek Bartoš
- Nette Blogger | 1260
Problem with final classes is that sometimes it makes sense to decorate them. And you can't do it without inheritance or an interface (which is currently missing)
Your solution could work and likely will work, yet I am not sure.