waspinator
waspinator

Reputation: 6816

cakephp authenticate with ldap and match to local user (or create one)

I'm trying to configure ldap authentication on cakephp 3.8 using the new cakephp/authentication plugin, and I'm not sure how to match the authenticated ldap user with a local entity.

My config closely follows the documentation and is available in full here.

in my Application.php the Application class implements both the AuthenticationServiceProviderInterface and the AuthorizationServiceProviderInterface

public function getAuthenticationService(ServerRequestInterface $request, 
                                         ResponseInterface $response)
{
    $service = new AuthenticationService();
    $service->loadIdentifier('Authentication.Password', [...]),
    $service->loadIdentifier('Authentication.Ldap', [
        'fields' => [
           'username' => 'username',
           'password' => 'password'
        ],
        'host' => 'ldap.forumsys.com',
        'port' => '389',
        'bindDN' => function($username) {
                return 'uid='.$username.',DC=example,DC=com';
            },
        'options' => [LDAP_OPT_PROTOCOL_VERSION => 3]
    ]);

    $service->loadAuthenticator('Authentication.Session');
    $service->loadAuthenticator('Authentication.Form', [
            'fields' => [
                'username' => 'email', 
                'password' => 'password'
             ],
            'loginUrl' => '/users/login'
    ]);

    return $service;
}

In my middleware, I'm trying decorate the identity with authorization stuff. When authenticating using the regular local system the $identity is a App\Model\Entity\User, but when logging in with a ldap user it's a Authentication\Identity

So when I call setAuthorization

'identityDecorator' => function (AuthorizationServiceInterface $authorization, 
                                 ArrayAccess $identity) {
    $identity->setAuthorization($authorization);
}

it fails with a Call to undefined method Authentication\Identity::setAuthorization() since all I have in $identity is

object(Authentication\Identity) {

    'config' => [
        'fieldMap' => [
            'id' => 'id'
        ]
    ],
    'data' => object(ArrayObject) {
        username => 'einstein'
    }

}

How would I match an authenticated ldap user with their local counterpart, and transform from Authentication\Identity to App\Model\Entity\User?

The final goal is to also optionally generate local users from ldap data if they don't exist.


middleware attempt

Application.php

public function middleware($middlewareQueue)
{
    ...

    $middlewareQueue->add($authentication);
    $middlewareQueue->add($ldap_matcher);
    $middlewareQueue->add($authorization);

    return $middlewareQueue;
}

LdapMatcherMiddleware.php

class LdapMatcherMiddleware
{
    public function __invoke(ServerRequestInterface $request, 
                             ResponseInterface $response, $next)
    {
        $identity = $request->getAttribute('identity');

        if ($identity !== null) {
            $identity = $this->buildIdentity($identity);
            $request = $request->withAttribute('identity', $identity);
        }

        $response = $next($request, $response);

        return $response;
    }

    public function buildIdentity($identity)
    {
        $Users = TableRegistry::getTableLocator()->get('Users');

        $username = $identity->getOriginalData()['username'];
        $user = $Users->find()->where(['username' => $username])->first();

        if (is_null($identity)) {
            $user = $this->createLocalUserFromLdap($identity);
        }

        return $user;
    }

    public function createLocalUserFromLdap($identity)
    {
        $Users = TableRegistry::getTableLocator()->get('Users');

        $user = $Users->newEntity([
            'username' => $identity->getOriginalData()['username']
        ]);

        $Users->save($user);

        return $user;
    }
}

Upvotes: 2

Views: 584

Answers (1)

floriank
floriank

Reputation: 25698

How would I match an authenticated ldap user with their local counterpart, and transform from Authentication\Identity to App\Model\Entity\User?

I would add another middleware after the authentication middleware and do that step there.

$users = TableRegistry::getTableLocator()->get('Users');
$entity = $users->newEntity($identity->getOriginalData());

Then do whatever you need to do for authorization with this entity.

The final goal is to also optionally generate local users from ldap data if they don't exist.

Just implement your logic somewhere and get the entity the same way as shown above from the identity.

$users = TableRegistry::getTableLocator()->get('Users');
$entity = $users->newEntity($identity->getOriginalData());
// Check if it exists inside this method and if not just create it
$users->createLocalUserFromLDAP($entity);

Upvotes: 1

Related Questions