silverstripe-framework/src/Core/Startup/ConfirmationTokenChain.php

182 lines
4.5 KiB
PHP

<?php
namespace SilverStripe\Core\Startup;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Convert;
/**
* A chain of confirmation tokens to be validated on each request. This allows the application to
* check multiple tokens at once without having to potentially redirect the user for each of them
*
* @internal This class is designed specifically for use pre-startup and may change without warning
*
* @deprecated 5.0 To be removed in SilverStripe 5.0
*/
class ConfirmationTokenChain
{
/**
* @var array
*/
protected $tokens = [];
/**
* @param AbstractConfirmationToken $token
*/
public function pushToken(AbstractConfirmationToken $token)
{
$this->tokens[] = $token;
}
/**
* Collect all tokens that require a redirect
*
* @return \Generator
*/
protected function filteredTokens()
{
foreach ($this->tokens as $token) {
if ($token->reloadRequired() || $token->reloadRequiredIfError()) {
yield $token;
}
}
}
/**
* @return bool
*/
public function suppressionRequired()
{
foreach ($this->tokens as $token) {
if ($token->reloadRequired()) {
return true;
}
}
return false;
}
/**
* Suppress URLs & GET vars from tokens that require a redirect
*/
public function suppressTokens()
{
foreach ($this->filteredTokens() as $token) {
$token->suppress();
}
}
/**
* @return bool
*/
public function reloadRequired()
{
foreach ($this->tokens as $token) {
if ($token->reloadRequired()) {
return true;
}
}
return false;
}
/**
* @return bool
*/
public function reloadRequiredIfError()
{
foreach ($this->tokens as $token) {
if ($token->reloadRequiredIfError()) {
return true;
}
}
return false;
}
/**
* @param bool $includeToken
* @return array
*/
public function params($includeToken = true)
{
$params = [];
foreach ($this->tokens as $token) {
$params = array_merge($params, $token->params($includeToken));
}
return $params;
}
/**
* Fetch the URL we want to redirect to, excluding query string parameters. This may
* be the same URL (with a token to be added outside this method), or to a different
* URL if the current one has been suppressed
*
* @return string
*/
public function getRedirectUrlBase()
{
// URLConfirmationTokens may alter the URL to suppress the URL they're protecting,
// so we need to ensure they're inspected last and therefore take priority
$tokens = iterator_to_array($this->filteredTokens(), false);
usort($tokens, function ($a, $b) {
return ($a instanceof URLConfirmationToken) ? 1 : 0;
});
$urlBase = Director::baseURL();
foreach ($tokens as $token) {
$urlBase = $token->getRedirectUrlBase();
}
return $urlBase;
}
/**
* Collate GET vars from all token providers that need to apply a token
*
* @return array
*/
public function getRedirectUrlParams()
{
$params = $_GET;
unset($params['url']); // CLIRequestBuilder may add this
foreach ($this->filteredTokens() as $token) {
$params = array_merge($params, $token->params());
}
return $params;
}
/**
* @return string
*/
protected function redirectURL()
{
$params = http_build_query($this->getRedirectUrlParams());
return Controller::join_links($this->getRedirectUrlBase(), '?' . $params);
}
/**
* @return HTTPResponse
*/
public function reloadWithTokens()
{
$location = $this->redirectURL();
$locationJS = Convert::raw2js($location);
$locationATT = Convert::raw2att($location);
$body = <<<HTML
<script>location.href='$locationJS';</script>
<noscript><meta http-equiv="refresh" content="0; url=$locationATT"></noscript>
You are being redirected. If you are not redirected soon, <a href="$locationATT">click here to continue</a>
HTML;
// Build response
$result = new HTTPResponse($body);
$result->redirect($location);
return $result;
}
}