RebelCode/rcmod-eddbk-rest-api

View on GitHub
src/Module/TransientNonceAuthHandler.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace RebelCode\EddBookings\RestApi\Module;

use Dhii\Exception\CreateInvalidArgumentExceptionCapableTrait;
use Dhii\Exception\CreateOutOfRangeExceptionCapableTrait;
use Dhii\I18n\StringTranslatingTrait;
use Dhii\Invocation\InvocableInterface;
use Dhii\Util\String\StringableInterface as Stringable;
use Psr\EventManager\EventInterface;
use WP_REST_Request;

/**
 * The handler that verifies a nonce in the header of requests and authorizes the client if valid.
 *
 * @since [*next-version*]
 */
class TransientNonceAuthHandler implements InvocableInterface
{
    /* @since [*next-version*] */
    use CreateInvalidArgumentExceptionCapableTrait;

    /* @since [*next-version*] */
    use CreateOutOfRangeExceptionCapableTrait;

    /* @since [*next-version*] */
    use StringTranslatingTrait;

    /**
     * The name of the header from where to get the nonce.
     *
     * @since [*next-version*]
     *
     * @var string|Stringable
     */
    protected $header;

    /**
     * The name of the nonce to verify.
     *
     * @since [*next-version*]
     *
     * @var string|Stringable
     */
    protected $nonce;

    /**
     * The key of the event param to filter.
     *
     * @since [*next-version*]
     *
     * @var string|Stringable
     */
    protected $paramKey;

    /**
     * The name of the transient.
     *
     * @since [*next-version*]
     *
     * @var string|Stringable
     */
    protected $transientName;

    /**
     * Constructor.
     *
     * @since [*next-version*]
     *
     * @param string|Stringable $header        The name of the header from where to get the nonce.
     * @param string|Stringable $nonce         The name of the nonce to verify.
     * @param string|Stringable $paramKey      The key of the event param to filter.
     * @param string|Stringable $transientName The name of the transient.
     */
    public function __construct($header, $nonce, $paramKey, $transientName)
    {
        $this->header        = $header;
        $this->nonce         = $nonce;
        $this->paramKey      = $paramKey;
        $this->transientName = $transientName;
    }

    /**
     * {@inheritdoc}
     *
     * @since [*next-version*]
     */
    public function __invoke()
    {
        $event = func_get_arg(0);

        if (!($event instanceof EventInterface)) {
            throw $this->_createInvalidArgumentException(
                $this->__('Argument is not an event instance'), null, null, $event
            );
        }

        $request = $event->getParam('request');
        if (!($request instanceof WP_REST_Request)) {
            throw $this->_createOutOfRangeException(
                $this->__('Request in event is not a valid request instance'), null, null, $request
            );
        }

        $nonce = $request->get_header($this->header);
        $valid = (bool) $this->_verifyNonce($nonce, $this->nonce);

        $event->setParams([$this->paramKey => $valid] + $event->getParams());
    }

    /**
     * Verifies that correct nonce was used and within its time limit.
     *
     * @since [*next-version*]
     *
     * @param string $nonce Nonce that was used in the request to verify.
     * @param string $name  Should give context to what is taking place and be the same when nonce was created.
     *
     * @return bool|int False if the nonce is invalid, 1 if the nonce is valid and generated between 0-12 hours ago,
     *                  2 if the nonce is valid and generated between 12-24 hours ago.
     */
    protected function _verifyNonce($nonce, $name)
    {
        $expected = (string) get_transient($this->transientName);
        $actual   = (string) $nonce;

        return $expected === $actual;
    }
}