Skip to content

Instantly share code, notes, and snippets.

@jmikola
Created September 13, 2010 16:24
Show Gist options
  • Save jmikola/577549 to your computer and use it in GitHub Desktop.
Save jmikola/577549 to your computer and use it in GitHub Desktop.
Symfony2 security request listener using HTTP authentication and LDAP groups
admin_action:
pattern: /secure/admin
defaults: { _security: [admin, moderator], _controller: AdminBundle:Secure:admin }
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="admin.security.require_user">true</parameter>
<parameter key="admin.security.ldap_server">null</parameter>
<parameter key="admin.security.base_dn">null</parameter>
</parameters>
<services>
<service id="admin.security" class="OpenSky\AdminBundle\Listener\Security">
<tag name="kernel.listener" />
<argument>%admin.security.require_user%</argument>
<argument>%admin.security.ldap_server%</argument>
<argument>%admin.security.base_dn%</argument>
<argument type="service" id="logger" on-invalid="ignore" />
</service>
</services>
</container>
<?php
namespace OpenSky\AdminBundle\Listener;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
/**
* Listens to the core.request event and requires HTTP authentication and
* LDAP group membership for requests with path parameter "_security" set
* as an array of group names.
*
* @author Jeremy Mikola <jmikola@theopenskyproject.com>
* @author Andy Stanberry <astanberry@theopenskyproject.com>
*/
class Security {
/**
* @var boolean
*/
protected $requireUser;
/**
* @var string
*/
protected $ldapServer;
/**
* @var string
*/
protected $baseDn;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* Constructor.
*
* If $requireUser is false, the request listener will not require a user to
* be authenticated but will log a message saying so.
*
* @param boolean $requireUser
* @param string $ldapServer
* @param string $baseDn
* @param LoggerInterface $logger
*/
public function __construct($requireUser, $ldapServer, $baseDn, LoggerInterface $logger = null) {
$this->requireUser = $requireUser;
$this->ldapServer = $ldapServer;
$this->baseDn = $baseDn;
$this->logger = $logger;
}
/**
* Registers a core.request listener.
*
* @param EventDispatcher $dispatcher
*/
public function register(EventDispatcher $dispatcher) {
$dispatcher->connect('core.request', array($this, 'checkAuthentication'));
}
/**
* Check that the current user is LDAP-authenticated unless the environment
* configuration does not require it.
*
* @param Event $event
* @throws UnauthorizedHttpException
*/
public function checkAuthentication(Event $event) {
$request = $event->getParameter('request');
$groups = $request->attributes->get('_security', array());
if (empty($groups)) {
return;
}
/* PHP_AUTH_USER will correspond to the LDAP-authenticated user
* accessing this action. HTTP_X_FORWARDED_FOR should be that user's
* IP address, as REMOTE_ADDR will likely be Varnish's IP. We'll log
* both just to be thorough.
*/
$authUser = $request->server->get('PHP_AUTH_USER');
$forwardedFor = $request->server->get('HTTP_X_FORWARDED_FOR');
$remoteAddr = $request->server->get('REMOTE_ADDR');
$logInfo = sprintf('PHP_AUTH_USER: %s, HTTP_X_FORWARDED_FOR: %s, REMOTE_ADDR: %s', $authUser, $forwardedFor, $remoteAddr);
if (! $this->requireUser) {
if (null !== $this->logger) {
$this->logger->info(sprintf('%s: Not requiring PHP_AUTH_USER for this request (%s)', __METHOD__, $logInfo));
}
} elseif (! $authUser) {
if (null !== $this->logger) {
$this->logger->info(sprintf('%s: Access denied for this request (%s)', __METHOD__, $logInfo));
}
throw new UnauthorizedHttpException();
} else {
// check to see if $authUser is a member of at least one of the groups
if (! $this->isMemberOfGroups($authUser, $groups)) {
$this->logger->info(sprintf('%s: Access denied, user not in groups: [%s] (%s)', __METHOD__, join(', ', $groups), $logInfo));
throw new UnauthorizedHttpException();
}
}
}
/**
* Returns true if $username is a member of any of these LDAP groups
*
* @param string $username
* @param array $groups
* @return boolean
*/
protected function isMemberOfGroups($username, array $groups) {
$inGroup = false;
$connection = ldap_connect($this->ldapServer);
if ($connection) {
ldap_bind($connection);
foreach ($groups as $group) {
$compare = @ldap_compare($connection,'cn=' . $group . ',' . $this->baseDn, 'memberuid', $username);
if ($compare === true) {
$inGroup = true;
break;
}
}
ldap_close($connection);
}
return $inGroup;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment