<?php namespace Xentral\Components\Http\Session; use Xentral\Components\Http\Exception\CsrfTokenException; use Xentral\Components\Http\Exception\InvalidArgumentException; use Xentral\Components\Util\StringUtil; final class CsrfTokenManager { /** @var int CSRF_TOKEN_LENGTH */ const CSRF_TOKEN_LENGTH = 32; /** @var array $tokenData */ private $tokenData; /** * @param array $tokenData */ public function __construct($tokenData = []) { $this->tokenData = $tokenData; } /** * @param string $key * @param string $value * @param bool $remove * * @return bool */ public function isTokenValid($key, $value, $remove = true) { $this->ensureTokenKeyFormat($key); $valid = false; if (array_key_exists($key, $this->tokenData) && $this->tokenData[$key] === $value) { $valid = true; } if ($valid === true && $remove === true) { $this->removeToken($key); } return $valid; } /** * @param $key * * @throws CsrfTokenException * * @return string */ public function createToken($key) { $this->ensureTokenKeyFormat($key); $token = StringUtil::random(self::CSRF_TOKEN_LENGTH, true); $this->tokenData[$key] = $token; if (strlen($token) < self::CSRF_TOKEN_LENGTH) { throw new CsrfTokenException('Could not create CSRF token.'); } return $token; } /** * @param $key * * @return void */ public function removeToken($key) { $this->ensureTokenKeyFormat($key); unset($this->tokenData[$key]); } /** * @param string $key * * @throws InvalidArgumentException * * @return string */ public function refreshToken($key) { $this->ensureTokenKeyFormat($key); $this->removeToken($key); return $this->createToken($key); } /** * @param $target * * @return void */ public function dumpTokens(&$target) { $target = $this->tokenData; } /** * @param string $key * * @throws InvalidArgumentException * * @return void */ private function ensureTokenKeyFormat($key) { if (!preg_match('/^[a-z][a-z0-9_]*$/', $key)) { throw new InvalidArgumentException('Invalid token key format.'); } } }