From 3db052771320763a3beb519dc92f1b7864704a2b Mon Sep 17 00:00:00 2001 From: nilov Date: Fri, 5 Feb 2016 08:27:08 +0600 Subject: [PATCH] Add user voters and api token authenticator --- Resources/config/services.yml | 18 +++ .../Authorization/ApiTokenAuthenticator.php | 124 ++++++++++++++++++ Security/Authorization/Voter/OwnerVoter.php | 113 ++++++++++++++++ .../Authorization/Voter/SuperAdminVoter.php | 71 ++++++++++ 4 files changed, 326 insertions(+) create mode 100644 Security/Authorization/ApiTokenAuthenticator.php create mode 100644 Security/Authorization/Voter/OwnerVoter.php create mode 100644 Security/Authorization/Voter/SuperAdminVoter.php diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 985aa10..506fb90 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -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 } + + diff --git a/Security/Authorization/ApiTokenAuthenticator.php b/Security/Authorization/ApiTokenAuthenticator.php new file mode 100644 index 0000000..93256d9 --- /dev/null +++ b/Security/Authorization/ApiTokenAuthenticator.php @@ -0,0 +1,124 @@ +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); + } + } +} diff --git a/Security/Authorization/Voter/OwnerVoter.php b/Security/Authorization/Voter/OwnerVoter.php new file mode 100644 index 0000000..8c6fde4 --- /dev/null +++ b/Security/Authorization/Voter/OwnerVoter.php @@ -0,0 +1,113 @@ +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; + } +} \ No newline at end of file diff --git a/Security/Authorization/Voter/SuperAdminVoter.php b/Security/Authorization/Voter/SuperAdminVoter.php new file mode 100644 index 0000000..6e49b12 --- /dev/null +++ b/Security/Authorization/Voter/SuperAdminVoter.php @@ -0,0 +1,71 @@ +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; + } +} \ No newline at end of file