Skip to content

Commit

Permalink
Add user voters and api token authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
nilov committed Feb 5, 2016
1 parent 477914f commit 3db0527
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,21 @@ services:
class: Glavweb\CoreBundle\Form\Type\FormStaticControlRawType
tags:
- { name: form.type, alias: bs_static_raw }

glavweb_core.api_token_authenticator:
class: Glavweb\CoreBundle\Security\Authorization\ApiTokenAuthenticator
arguments: [@doctrine]

glavweb_core.security.access.owner_voter:
class: Glavweb\CoreBundle\Security\Authorization\Voter\OwnerVoter
public: false
tags:
- { name: security.voter }

glavweb_core.security.access.super_admin_voter:
class: Glavweb\CoreBundle\Security\Authorization\Voter\SuperAdminVoter
public: false
tags:
- { name: security.voter }


124 changes: 124 additions & 0 deletions Security/Authorization/ApiTokenAuthenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

namespace Glavweb\CoreBundle\Security\Authorization;

use Doctrine\Bundle\DoctrineBundle\Registry;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use UserBundle\Entity\User;

/**
* Class ApiTokenAuthenticator
* @package Glavweb\CoreBundle\Security\Authorization
*/
class ApiTokenAuthenticator implements SimplePreAuthenticatorInterface
{
/**
* @var Registry
*/
private $doctrine;

/**
* @param Registry $doctrine
*/
public function __construct(Registry $doctrine)
{
$this->doctrine = $doctrine;
}

/**
* @param Request $request
* @param string $providerKey
* @return PreAuthenticatedToken
*/
public function createToken(Request $request, $providerKey)
{
$credentials = array(
'token' => $this->getValueByHeaderOrCookie($request, 'Token'),
'username' => $this->getValueByHeaderOrCookie($request, 'Username'),
'expireAt' => $this->getValueByHeaderOrCookie($request, 'ExpireAt'),
);

return new PreAuthenticatedToken(
'anon.',
$credentials,
$providerKey
);
}

/**
* @param TokenInterface $token
* @param UserProviderInterface $userProvider
* @param string $providerKey
* @return PreAuthenticatedToken
*/
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$em = $this->doctrine->getManager();
$userRepository = $em->getRepository('UserBundle:User');

$credentials = $token->getCredentials();

/** @var User $user */

if ($credentials['token']) {
$user = $userRepository->findOneByApiToken($credentials['token']);

if ($user) {
return new PreAuthenticatedToken(
$user,
$credentials,
$providerKey,
$user->getRoles()
);
}
}

return new PreAuthenticatedToken(
'anon.',
$credentials,
$providerKey
);
}

/**
* @param Request $request
* @param AuthenticationException $exception
* @return Response
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new Response("Authentication Failed.", 403);
}

/**
* @param TokenInterface $token
* @param string $providerKey
* @return bool
*/
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}

/**
* @param Request $request
* @param string $name
* @return array|mixed|string
*/
private function getValueByHeaderOrCookie(Request $request, $name)
{
if ($request->headers->has($name)) {
return $request->headers->get($name);
}

if ($request->cookies->has($name)) {
return $request->cookies->get($name);
}
}
}
113 changes: 113 additions & 0 deletions Security/Authorization/Voter/OwnerVoter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace Glavweb\CoreBundle\Security\Authorization\Voter;

use Sonata\UserBundle\Model\HasOwnerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* Class OwnerVoter
* @package Glavweb\CoreBundle\Security\Authorization\Voter
*/
class OwnerVoter implements VoterInterface
{
/**
* Name of owner role
*/
const ROLE_NAME_OWNER = 'OWNER';

/**
* Checks if the voter supports the given attribute.
*
* @param string $attribute An attribute
*
* @return bool true if this Voter supports the attribute, false otherwise
*/
public function supportsAttribute($attribute)
{
return true;
}

/**
* Checks if the voter supports the given class.
*
* @param string $class A class name
*
* @return bool true if this Voter can process the class
*/
public function supportsClass($class)
{
$reflectionClass = new \ReflectionClass($class);
return $reflectionClass->implementsInterface('Sonata\UserBundle\Model\HasOwnerInterface');
}

/**
* Returns the vote for the given parameters.
*
* This method must return one of the following constants:
* ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN.
*
* @param TokenInterface $token A TokenInterface instance
* @param object|null $object The object to secure
* @param array $attributes An array of attributes associated with the method being invoked
*
* @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
*/
public function vote(TokenInterface $token, $object, array $attributes)
{
if (!$this->supportsClass(get_class($object))) {
return VoterInterface::ACCESS_ABSTAIN;
}

/** @var UserInterface $user */
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_ABSTAIN;
}
$userRoles = $user->getRoles();

foreach ($attributes as $attribute) {
$attribute = $attribute . '_' . self::ROLE_NAME_OWNER;
if (in_array($attribute, $userRoles)) {
$owners = $this->getOwners($object);

foreach ($owners as $owner) {
if ($owner && $owner->getId() == $user->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
}
}
}

return VoterInterface::ACCESS_ABSTAIN;
}

/**
* @param $object
* @return array
*/
public function getOwners($object)
{
/** @var HasOwnerInterface $class */
$class = get_class($object);
$ownerFields = $class::getOwnerFields();

$owners = array();
foreach ($ownerFields as $ownerField) {
if ($ownerField == 'id') {
$owner = $object;
} else {
$method = 'get' . ucfirst($ownerField);
$owner = $object->$method();
}

if ($owner instanceof UserInterface) {
$owners[] = $owner;
}
}

return $owners;
}
}
71 changes: 71 additions & 0 deletions Security/Authorization/Voter/SuperAdminVoter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

namespace Glavweb\CoreBundle\Security\Authorization\Voter;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
* Class SuperAdminVoter
* @package Glavweb\CoreBundle\Security\Authorization\Voter
*/
class SuperAdminVoter implements VoterInterface
{
/**
* Name of super admin role
*/
const SUPER_ADMIN_ROLE = 'ROLE_SUPER_ADMIN';

/**
* Checks if the voter supports the given attribute.
*
* @param string $attribute An attribute
*
* @return bool true if this Voter supports the attribute, false otherwise
*/
public function supportsAttribute($attribute)
{
return true;
}

/**
* Checks if the voter supports the given class.
*
* @param string $class A class name
*
* @return bool true if this Voter can process the class
*/
public function supportsClass($class)
{
return true;
}

/**
* Returns the vote for the given parameters.
*
* This method must return one of the following constants:
* ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN.
*
* @param TokenInterface $token A TokenInterface instance
* @param object|null $object The object to secure
* @param array $attributes An array of attributes associated with the method being invoked
*
* @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
*/
public function vote(TokenInterface $token, $object, array $attributes)
{
/** @var UserInterface $user */
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_ABSTAIN;
}

$roles = $user->getRoles();
if (in_array(self::SUPER_ADMIN_ROLE, $roles)) {
return VoterInterface::ACCESS_GRANTED;
}

return VoterInterface::ACCESS_ABSTAIN;
}
}

0 comments on commit 3db0527

Please sign in to comment.