Symfony AD Integration


Introduction

Recently, I’ve been investigating Symfony as a solution for a student petitions web application that I’m working on at Taft College. Symfony is a PHP framework that greatly simplifies the creation of web applications. It’s essentially at Model View Controller (MVC) framework.
This blog outlines the enabling of Active Directory integration with Symfony. My reason for documenting it here, was because when I tried searching online for information/examples on how to achieve this, there were references on how to install various components but there were no real step-by-step instructions on how to verify everything it actually working. Hopefully this will help someone else out who is struggling to set this up properly.

Installing Symfony

This blog presumes you have some Linux OS and are using LAMP (Linux Apache MySql/MariaDB PHP) and each of those is configured correctly. You will need to install Symfony and create your Symfony project that you’ll be using to setup the Active Directory integeration. The Symfony Book Installing and Configuring Symfony gives instructions on how to install Symfony (binary) and Composer (you’ll need this too); I recommend to fully go over most of the Symfony Book, as it will help you get familiar with Symfony. Once you’ve created your Symfony project, you can proceed with the next steps.

Install FOSUserBundle

The Friends Of Symfony User Bundle (FOSUserBundle), is a bundle that persists and fetches users in a database. To install it, use composer with the following command within the root of your Symfony web application. For example, let’s presume your apache DocumentRoot is ‘/var/www/html’ and you’ve created your Symfony project application as ‘test’ in the DocumentRoot, then you would run this command under ‘/var/www/html/test’. Run the following command to install FOSUserBundle:

composer require friendsofsymfony/user-bundle “~2.0@dev”

Install PHP LDAP

In this example, I’m using Centos 6. So if you have a different Linux distro, you may have to install with different commands (i.e. rpm, apt-get). Run the following command to install ext-ldap:

yum install php-ldap

There is no post install configuration required.

Install FR3DLdapBundle

The FR3DLdapBundle provide LDAP authentication. To install it, run the following composer command from your Symfony project root:

composer require fr3d/ldap-bundle “3.0.*@dev”

Configuration

You will need to edit a number of files described here to setup the user, security and Active Directory configuration. First, open the file ‘app/AppKernel.php’ and add the FOSUserBundle and FR3DLdapBundle bundles to the registerBundles function:

<?php
 // app/AppKernel.php

public function registerBundles()
 {
   $bundles = [
      // ...
      new FOS\UserBundle\FOSUserBundle(),
      new FR3D\LdapBundle\FR3DLdapBundle(),
      // ...
   ];
 }

Create a file called ‘FOSUser.php’ under ‘src/AppBundle/Entity’. Note: create the Entity directory if it doesn’t exist. Add the following contents to FOSUser.php:

<?php

// src/AppBundle/Entity/FOSUser.php
 namespace AppBundle\Entity;

use FOS\UserBundle\Model\User as BaseUser;
 use FR3D\LdapBundle\Model\LdapUserInterface;
 use Doctrine\ORM\Mapping as ORM;

/**
   * @ORM\Entity
   * @ORM\Table(name="fos_user")
   */
 class FOSUser extends BaseUser implements LdapUserInterface
 {
 /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   */
 protected $id;

/**
   * @ORM\Column(type="string")
   */
 protected $dn;

public function __construct()
 {
   parent::__construct();
   if (empty($this->roles)) {
      $this->roles[] = 'ROLE_USER';
   }
 }

/**
   * {@inheritDoc}
   */
 public function setDn($dn)
 { $this->dn = $dn; }

/**
   * {@inheritDoc}
   */
 public function getDn()
 { return $this->dn; }
 }

Open the file ‘app/config/routing.yml’ and make the following change:

#    app/config/routing.yml
 ...
 fos_user:
   resource: "@FOSUserBundle/Resources/config/routing/all.xml"

The above setting enables Symfony routing based on configuration from the file vendor/friendsofsymfony/user-bundle/Resources/config/routing/all.xml which enables the routes such as “/login” and “/logout”, providing standard login/logout capability. The Bundle can be overriden but that is not discussed here.

Open the file ‘app/config/security.yml’ and make the following changes. NOTE: This is only my example; Read the Symfony Book > Security section for other examples on how you might configure security for your own scenario. Symfony is highly configurable, and this example is only used as a guideline on helping you to understand how security works in Symfony.

#    'app/config/security.yml'
 # To get started with security, check out the documentation:
 # http://symfony.com/doc/current/book/security.html
 security:
 # Preserve plain text password in token for refresh the user.
 # Analyze the security considerations before turn off this setting.
    erase_credentials: false

encoders:
  FOS\UserBundle\Model\UserInterface: bcrypt

role_hierarchy:
  ROLE_ADMIN:       ROLE_USER
  ROLE_SUPER_ADMIN: ROLE_ADMIN

providers:
  chain_provider:
     chain:
        providers: [fos_userbundle, fr3d_ldapbundle]
  fr3d_ldapbundle:
     id: fr3d_ldap.security.user.provider
  fos_userbundle:
     id: fos_user.user_provider.username

firewalls:
 # disables authentication for assets and the profiler, adapt it according to your needs
 dev:
   pattern: ^/(_(profiler|wdt)|css|images|js)/
   fr3d_ldap:  ~
   security: false
   form_login:
     always_use_default_target_path: true
     default_target_path: /profile
   logout:       true
   anonymous:    true

 main:
   pattern: ^/
   fr3d_ldap:  ~
   form_login:
     always_use_default_target_path: true
     default_target_path: /profile
   logout:       true
   anonymous:    true

 access_control:
   - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
   - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
   - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
   - { path: ^/admin/, role: ROLE_ADMIN }

 

Open the file ‘app/config/config.yml’ and add the following lines at the bottom of the file:

#    app/config/config.yml
 ...
 # Friends of Symfony user
 fos_user:
   db_driver: orm
   firewall_name: main
   user_class: AppBundle\Entity\FOSUser

fr3d_ldap:
   driver:
      host: ldap.forumsys.com
   user:
      baseDn: dc=example, dc=com
      attributes:
         - { ldap_attr: uid,  user_method: setUsername }
         - { ldap_attr: mail,  user_method: setEmail }
      filter: (&(ObjectClass=person))

In the above configuration, I’m using the online test LDAP server ‘ldap.forumsys.com’, which is available for anyone to use to test LDAP authentication. Information on this LDAP test server is found at the following web site: http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/

Update Schema

Run the following command from the project root directory:

php bin/console doctrine:schema:update –force

This updates the database schema.

Testing Operation

This section describes the steps needed to verify that the above configuration is actually working and doing what it is supposed to. The key to troubleshooting is checking the ‘var/logs/dev.log’ entries. Since the login page configured is ‘^/login$’, you need to point your browser to your apache Directory path. I’m presuming here that you’ve configured this as your Symfony root/web path (the web folder under your Symfony project root directory). Then in your browser enter the following URL:

http://host.name/app_dev.php/login

Where ‘host.name’ is either an IP address or hostname where your apache web server is running. Notice the path contains ‘app_dev.php’. This is a file located in the web folder. If you are not running your browser on the same host that the apache web server is running on, then you’ll need to open this file and add the IP address of your host machine to the ‘REMOTE_ADDR’ array. NOTE: Only add trusted hosts to the ‘REMOTE_ADDR’ array. The following line shows an example where my host machine has an IP address of ‘192.168.40.208’:

//    web/app_dev.php
 ...
 if (isset($_SERVER['HTTP_CLIENT_IP'])
   || isset($_SERVER['HTTP_X_FORWARDED_FOR'])
   || !(in_array(@$_SERVER['REMOTE_ADDR'], ['192.168.40.208', '127.0.0.1',
   'fe80::1', '::1']) || php_sapi_name() === 'cli-server')
 )

After you browse to the login page, you should should see labels and text fields for entering username and password and a submit button. Enter a username of “riemann” and password of “password”, then click “security.login.submit”, then you should the profile information for the user. Click the “layout.logout” link to logout.

If the login/logout works correctly, then the normal login URL to use is:

http://host.name/login

And to logout, simply use:

http://host.name/logout

 

Getting AD Authentication to Work

Once you have verified the above (verifies LDAP authentication works), then you need to change your “config.yml” to use your Active Directory server. Edit your “config.yml” as follows (the following data is obsfucated):

# FILE: app/config/config.yml
 ...
 fr3d_ldap:
   driver:
      host: my_ad_server.hostname.com
      username: ad_admin_user_account
      password: admin_password
      accountDomainName: DOM.hostname.com
      accountDomainNameShort: DOM # i.e. prefix you use to login.
   user:
      usernameAttribute: sAMAccountName
      baseDn: cn=Users,dc=hostname,dc=com # Use a LDAP browser to verify this.
      filter: (&(ObjectClass=Person))
      attributes:
         - { ldap_attr: samaccountname, user_method: setUsername }
         - { ldap_attr: mail, user_method: setEmail }

You should now be able to login to your Symfony system using your Active Directory accounts. Always start in the dev environment (append ‘app_dev.php’ to URL) to check first that things are working normally, and check the var/logs/dev.log for error messages.

 

Advertisements

About Alvin Bunk
Hi, I'm a software developer at Taft College.

16 Responses to Symfony AD Integration

  1. Alvin Bunk says:

    Change this: “fr3d/ldap-bundle”: “3.0.*@dev” to “fr3d/ldap-bundle”: “2.0.*@dev”. If you are still having problems, please post a question on Stackoverflow here (http://stackoverflow.com/questions/tagged/fr3dldapbundle). Hopefully you still don’t have that namespace problem…

  2. Stuart Bradley says:

    Small error in the article: In the config.yml the FOS section should be:

    # Friends of Symfony user
    fos_user:
    db_driver: orm
    firewall_name: main
    user_class: AppBundle\Entity\FOSUser

    not:

    # Friends of Symfony user
    fos_user:
    db_driver: orm
    firewall_name: main
    user_class: AppBundle\Entity\User

    Since that’s the name of your entity class. Once I’d changed that, everything worked fine 🙂

  3. Sic says:

    Hello and thanks for this article.

    Have you try to connect with an user, move this user in another OU in your AD (this will change DN) and retry to connect ?

    It seems ldap_bind reject connection because the DN register in database don’t match with the DN of the user in AD.

    So I don’t know how to patch it.

    • Alvin Bunk says:

      You might have to change the basedn value temporarily and get the user to login, and then change the basedn back to the normal value. Once they are able to login, the user record is written to the fos_user table and they are able to login afterwards.

  4. lunik971 says:

    Hello, first of all I would like to thank you for your help and your sharing. Rare are the tutorials regarding the LDAP on symfony 3, really thank you. After following the tutorial, I encounter an error when I enter the identifiers. I am returned “The authentication request could not be executed due to a system problem.” My files security.yml, config.yml and my entity User are the same as you except that my entity is called User and not FOSUser and my Bundle is called UserBundle. Did you know where the problem came from? If you want I can show you my files and my “var log”.

    • Alvin Bunk says:

      Glad I could help. This is not the place to post a lot of code and logs. Can you go to stackoverflow and the FR3DLdapBundle tags and post there? I will look when I can. Here is the link: http://stackoverflow.com/questions/tagged/fr3dldapbundle

      • lunik971 says:

        Thank you very much !!!! 😀 you’re my hero ! 😀

      • Alvin Bunk says:

        Also, if I don’t respond right away, let me know here that you’ve posted and maybe your Stackoverflow account name. I don’t check the FR3DLdapBundle tags very often.

      • Jérôme CHRISTOPHE says:

        Thanks to you I was able to get out and contact the server. However when I wanted to adapt the code to my LDAP server I encountered some difficulties. At first I could not contact him and although it is settling now, “I believe”, the problem is that he tells me that my IDs are invalid when I’m sure. I posted as you said a question on the site stackoverflow whose title is: “Why can not I log in with my LDAP credentials on Symfony3?” And my account name: Jérôme CHRISTOPHE.

      • Alvin Bunk says:

        I’ve responded to your question on Stackoverflow Jérôme.

  5. Fran says:

    Thanks for this article, it helped me a lot and it works for my Symfony3 project: when a user acces to my intranet, a login screen is shown, where the user enter an username and a password and the authentication is done as expected.

    So, I have a question:

    Is it possible to prevent the login screen (user and password) from being displayed and automatically authenticated with windows credentials? (The current user)
    In case the authentication was not satisfactory, then display the Login screen.

    (Note: the server is a Windows 2012 R2 server with wampserver)

    • Alvin Bunk says:

      Hi there Fran. Thanks for reviewing my article. Can you clarify what you mean by “automatically authenticated” ? How would the user enter their credentials? The way it works is if you enter your username/password, and as long as you don’t close the browser, your authentication is maintained when you’ve clicked the “Remember me” checkbox. If they close the browser, then they will need to re-authenticate. You can use the browser to “store” passwords too.

      • Fran says:

        Hi, Alvin. Thanks for your answer.
        What I need is that the browser does not ask for user credentials and password, but rather uses Windows authentication (the user who has been identified in Windows).
        Following your article, the LDAP ID works perfectly, inserting in the browser the same user and password that is entered to access Windows, and I would like to know if it is possible to avoid this “double” identification.
        Thank you.

      • Alvin Bunk says:

        Hi there Fran. You are talking about Windows Integrated Authentication. Try looking that up on Google. Apache does support it using the mod_authnz_ldap module. This is beyond the scope of this article. I found this link which should help you: http://www.held-im-ruhestand.de/software/apache-ldap-active-directory-authentication.html I have not tried this at all, but I think it might work for you.

      • Fran says:

        Thanks Alvin!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: