Love & Loss
A Symfony Security Play
brewcycleportland.com
@kriswallsmith
assetic
Buzz
Spork
“…the current implementation of the Security
Component is … not easily accessible”
http://www.testically.org/2011/03/14/why-i-gave-up-on-the-symfony2-security-component/
“I would rather see Symfony2 postponed again
or the Security Component removed …
I don’t think it is even near of being usable to
the community outside the core.”
http://www.testically.org/2011/03/14/why-i-gave-up-on-the-symfony2-security-component/
“The past few days I have really be struggling
with the Symfony2 security component.
It is the most complex component of
Symfony2 if you ask me!”
http://blog.vandenbrand.org/2012/06/19/symfony2-authentication-provider-authenticate-against-webservice/
“(I’m) wondering if I should just work around
rather than work with the framework”
https://groups.google.com/forum/#!msg/symfony2/AZpgbEk4Src/73P99zOmq2YJ
Enhance your
PHPfun!
http://curiouscomedy.org
HttpKernel
kernel.exception
kernel.request kernel.terminatekernel.controller kernel.view kernel.response
kernel.request kernel.controller kernel.view kernel.response kernel.terminate
kernel.exception
HttpKernel
kernel.request kernel.controller kernel.view kernel.response kernel.terminate
kernel.exception
HttpKernel
HttpKernel
Get the response and get out
kernel.request
Routeretc…
Firewall
Firewall
Just another listener
class YesFirewall
{
public function handle($event)
{
// always say yes
}
}
use SymfonyComponentHttpFoundationResponse;
class NoFirewall
{
public function handle($event)
{
// always say no
$event->setResponse(
new Response('go away', 401)
);
}
}
use SymfonyComponentHttpFoundationResponse;
class PickyFirewall
{
public function handle($event)
{
$request = $event->getRequest();
$user = $request->headers->get('PHP_AUTH_USER');
// only names that start with "Q"
if ('Q' == $user[0]) return;
$event->setResponse(new Response('go away', 401));
}
}
Security Listeners
The firewall’s henchmen
Firewall
Listeners
kernel.request
class Firewall
{
public $listeners = array();
public function handle($event)
{
foreach ($this->listeners as $listener) {
$listener->handle($event);
if ($event->hasResponse()) return;
}
}
}
class YesListener
{
public function handle($event)
{
// always say yes
}
}
use SymfonyComponentHttpFoundationResponse;
class NoListener
{
public function handle($event)
{
// always say no
$event->setResponse(
new Response('go away', 401)
);
}
}
use SymfonyComponentHttpFoundationResponse;
class PickyListener
{
public function handle($event)
{
$request = $event->getRequest();
$user = $request->headers->get('PHP_AUTH_USER');
// only names that start with "Q"
if ('Q' == $user[0]) return;
$event->setResponse(new Response('go away', 401));
}
}
Authentication
Are you who you say you are?
Authorization
Are you allowed to ____?
Tokens
The Language of Security
Authentication Listeners
Map from request to token
Request
Response (?) Token
CoreHTTP
Authentication
Listener A
Authentication
Listener B
Authentication
Manager
Firewall
class AuthenticationListener
{
public $authMan, $context;
public function handle($e)
{
$r = $e->getRequest();
$u = $r->headers->get('PHP_AUTH_USER');
$t = new AnonToken($u);
$t = $this->authMan->authenticate($t);
$this->context->setToken($t);
}
}
class AuthenticationManager
{
public function authenticate($t)
{
// always say no
}
}
class AuthenticationManager
{
public function authenticate($t)
{
// always say yes
return new AuthToken($t->getUser());
}
}
class AuthenticationManager
{
public function authenticate($t)
{
$u = $t->getUser();
// only names that start with "Q"
if ('Q' == $u[0]) {
return new AuthToken($u);
}
}
}
Authentication Manager
Responsible for authenticating
the token
Authentication Providers
Do the actual authentication work
User
Providers
Authentication
Providers
Authentication
Listener A
Authentication
Listener B
Authentication
Manager
User Providers
Access the repository of users
class AuthenticationManager
{
public $providers = array();
public function authenticate($t)
{
foreach ($this->providers as $p) {
if ($p->supports($t)) {
return $p->authenticate($t);
}
}
}
}
class AuthenticationProvider
{
public $up;
public function authenticate($t)
{
$u = $t->getUser();
$u = $this->up->loadUserByUsername($u);
if ($u) return new AuthToken($u);
}
}
class UserProvider
{
public $repo;
public function loadUserByUsername($u)
{
return ($this->repo->find(array(
'username' => $u,
)));
}
}
Authentication
Authentication Listeners
• Map client data from request to
token
• Pass token to authentication
manager
• Update state of security context
Authentication Manager
• Responsible for authenticating the
token
• Calls the appropriate
authentication provider
• Handles exceptions
Authentication Providers
• Performs authentication using client
data in the token
• Marks the token as authenticated
• Attaches the user object to the
token
User Providers
• Retrieves the user from the database
Authorization
class AuthorizationListener
{
public function handle($e)
{
// always say yes
}
}
use SymfonyComponentHttpFoundationResponse;
class AuthorizationListener
{
public function handle($e)
{
// always say no
$e->setResponse(
new Response('go away', 403)
);
}
}
Access Map
Looks at a request and determines
token requirements
Access Decision Manager
The gatekeeper
Voters
Decision
Manager
Listener Map
use SymfonyComponentHttpFoundationResponse;
class AccessListener
{
public $context, $map, $decider;
public function handle($e)
{
$r = $e->getRequest();
$t = $this->context->getToken();
$reqs = $this->map->getRequirements($r);
if (!$this->decider->decide($t, $reqs)) {
$e->setResponse(
new Response('go away', 403)
);
}
}
}
class AccessMap
{
public function getRequirements($r)
{
$path = $r->getPathInfo();
if (0 === strpos($path, '/admin')) {
return array('ADMIN');
}
}
}
class AccessDecisionManager
{
public $voters;
public function decide($t, $reqs)
{
foreach ($this->voters as $v) {
if ($v->vote($t, null, $reqs)) {
return true;
}
}
return false;
}
}
class AccessVoter
{
public function vote($t, $obj, $reqs)
{
foreach ($reqs as $req) {
if (!$t->hasAttribute($req)) {
return false;
}
}
return true;
}
}
Authorization
Extension Points
The firewall has many listeners
The authentication manager has
many authentication providers
Which MAY rely on
user providers
The access decision manager
has many voters
Authenticated
Roles
ACL
Questions?
is hiring
“Horrible”
“Worst talk ever”
“Go back to high school”
https://joind.in/8665

Love and Loss: A Symfony Security Play