Categories
Tutorial

CakePHP and Active Directory

If your company uses Active Directory (AD) to store information for their employees, then why have that information duplicated on your database? In this tutorial I’ll show you how to use AD so that you’ll be able to store all data in one centralized location!

Setting Up The Plugin

Assuming you’ve got you’re project up and running let’s install the adldap2 plugin. Be sure to have your php-ldap activated!

composer require "adldap2/adldap2"

The adldap2 plugin allows you to easily manage multiple LDAP connections at once, perform authorization, CRUD your LDAP entities.

Next we’ll add our AD credentials to the app.php. You can use your company’s AD credentials to set your configuration.

app.php

'AdLdapAuth' => true,

'AdLdap' => [
    'base_dn' => 'DC=example,DC=com',
    'hosts' => [
      '192.168.0.1'
      '192.168.0.2'
    ],
    'username' => 'username',
    'password' => 'secret'
],

These are the required options with adldap2. You can find the full list of options here.

Adding Authorization

Now that our application can connect to the AD server we’ll add in authorization. Add the following to your initialize function in your AppController.

src/Controller/AppController.php

//...
    $this->loadComponent('Auth');

    $this->Auth->setConfig([
      'authenticate' => [ 
        'AdLdap' 
      ],
      'loginAction' => [
        'controller' => 'Users',
        'action' => 'login'
      ],
      'loginRedirect' => [
        'controller' => 'Users',
        'action' => 'index'
      **],
      'logoutRedirect' => [
        'controller' => 'Users',
        'action' => 'login'
      ],
      'storage' => [
        'className' => 'Session',
        'key' => 'Auth.User'
      ],
      'unauthorizedRedirect' => false,
      'authorize' => ['Controller']
    ]);
//..

As you can see we’ve add a login action, redirect, and storage. You can substitute where you’d like a user to be redirected when they try to login, or if they’re not authorized to access a location. The session we’ll establish next.

To establish our session array, and check if the user exists in our AD records you will need to create a new file:

src/Auth/AdLdapAuthenticate.php

<?php
namespace App\\Auth;

use Cake\\Auth\\FormAuthenticate;
use Cake\\Http\\ServerRequest;
use Cake\\Http\\Response;
use Cake\\Core\\Configure;
use \\Adldap\\Adldap;

class AdLdapAuthenticate extends FormAuthenticate {
  public function authenticate(ServerRequest $request, Response $response) {
    $fields = $this->_config['fields'];

    if (!$this->_checkFields($request, $fields)) {
      return false;
    }

    $username = $request->getData($fields['username']);
    $password = $request->getData($fields['password']);

    $config   = Configure::read('AdLdap');
    $ad       = new Adldap();
		$ad->addProvider($config);
		$provider = $ad->connect();

    if ($provider->auth()->attempt($username, $password)) {
      $user = $this->findUser($username);

      if ($user) {
        return ['username' => $username,
								'is_admin' => 0,
								//...];
      }
    }

    return false;
  }
}
?>

Again, you can customize the session array I use in the return to represent different user aspects while they are logged in.

Finally we’ll add a login page!

Adding a Login Page

To add a login page for the configuration I’ve provided we’ll need to have a Users controller, and login view. Add the following to your Users controller, or wherever you want your login function to be stored. You configured where your login action is under AppController.php: loginAction.

src/Controller/UsersController.php

public function login($id = null) {
    if ($this->request->is('post')) {
      try {
        $user = $this->Auth->identify();

        if ($user) {
          $this->Auth->setUser($user);
          return $this->redirect($this->Auth->redirectUrl());
        }

        $this->Flash->error(__('Access Denied: Please double check your credentials and verify you have permission to use this application.'));
      }
      catch (BindException $e) {
        $this->Flash->error(__('There was an issue connecting to active directory for authentication.'));
      }
      catch (UsernameRequiredException $e) {
        $this->Flash->error(__('Username is required for authentication.'));
      }
      catch (PasswordRequiredException $e) {
        $this->Flash->error(__('Password is required for authentication.'));
      }
      catch (Exception $e) {
        $this->Flash->error(__($e->getMessage()));
      }
    }
  }

As you can see this is a fairly simple login script that catches some common errors and flashes them to the user.

Now we’ll add our login form.

src/Template/Users/login.ctp

<form class="form-signin" method="post" action="/users/login">
  <div class="text-center mb-4">
    <div class="row">
      <div class="col-12 text-center">
        <h1 class="h3 mb-3 font-weight-normal">Title</h1>
      </div>
    </div>
    <div class="row">
      <div class="col-12 text-center">
        <h6 class="text-dark">Subtitle</h6>
      </div>
    </div>
  </div>

  <input type="hidden" name="_csrfToken" value="<?= $this->request->getParam('_csrfToken'); ?>">
  <div class="form-group">
    <input id="username" name="username" class="form-control" required="" autofocus="" type="text">
    <label for="username">Username</label>
  </div>

  <div class="form-group">
    <input id="password" name="password" class="form-control" required="" type="password">
    <label for="password">Password</label>
  </div>

  <button class="btn btn-lg btn-block text-white" style="background-color: #FB6055" type="submit">Sign in</button>
</form>

Once you’ve added you’re new files you should be ready to use your AD connection to log users in!

Conclusion

If I missed anything, or you’d like help with a problem please comment down below!

Don’t forget to subscribe to get great new CakePHP content every Tuesday and Thursday!