Created
September 13, 2010 16:24
-
-
Save jmikola/577549 to your computer and use it in GitHub Desktop.
Symfony2 security request listener using HTTP authentication and LDAP groups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
admin_action: | |
pattern: /secure/admin | |
defaults: { _security: [admin, moderator], _controller: AdminBundle:Secure:admin } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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 <[email protected]> | |
* @author Andy Stanberry <[email protected]> | |
*/ | |
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