Dynamic database credentials based on user authentication

dsar
Backer | 53
+
0
-

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 | 1173
+
0
-

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
+
+1
-

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 | 1173
+
0
-

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.

dsar
Backer | 53
+
0
-

It works ;-) thank you