PHP Classes

File: src/Discretion/Discretion.php

Recommend this page to a friend!
  Classes of Scott Arciszewski  >  Discretion  >  src/Discretion/Discretion.php  >  Download  
File: src/Discretion/Discretion.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Discretion
Show contact forms and deliver encrypted mail
Author: By
Last change:
Date: 4 months ago
Size: 10,990 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace ParagonIE\Discretion;

use Monolog\Logger;
use ParagonIE\AntiCSRF\AntiCSRF;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\CSPBuilder\CSPBuilder;
use ParagonIE\Discretion\Data\HiddenString;
use ParagonIE\Discretion\Exception\{
    FilesystemException,
    RecordNotFound
};
use ParagonIE\EasyDB\EasyDB;
use ParagonIE\Sapient\Adapter\Slim;
use ParagonIE\Sapient\CryptographyKeys\SigningSecretKey;
use ParagonIE\Sapient\Sapient;
use Slim\Http\{
    Headers,
    Response
};

/**
 * Class Discretion
 * @package ParagonIE\Discretion
 */
class Discretion
{
    /** @var AntiCSRF $antiCSRF */
    protected static $antiCSRF;

    /** @var CSPBuilder $cspBuilder */
    protected static $cspBuilder;

    /** @var EasyDB $easyDb */
    protected static $easyDb;

    /** @var HiddenString $localEncryptionKey */
    protected static $localEncryptionKey;

    /** @var Logger $logger */
    protected static $logger;

    /** @var array $settings */
    protected static $settings;

    /** @var SigningSecretKey $signingKey */
    protected static $signingKey;

    /** @var \Twig_Environment $twig */
    protected static $twig;

    /** @var array $twigVars */
    protected static $twigVars = [];

    /**
     * Create a generic HTTP response, unsigned.
     *
     * @param string $body
     * @param array $headers
     * @param int $status
     * @return Response
     * @throws \Error
     */
    public static function createNormalResponse(
        string $body = '',
        array $headers = [],
        int $status = 200
    ): Response {
        return new Response(
            $status,
            new Headers($headers),
            (new Slim())->stringToStream($body)
        );
    }

    /**
     * @param string $class
     * @return string
     */
    public static function decorateClassName($class = '')
    {
        return 'Object (' . \trim($class, '\\') . ')';
    }

    /**
     * Generic error message responder.
     *
     * @param string $errorMessage
     * @param int $statusCode
     * @param array $headers
     * @return Response
     *
     * @throws \Error
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     */
    public static function errorResponse(
        string $errorMessage = '',
        int $statusCode = 500,
        array $headers = []
    ): Response {
        return Discretion::view(
            'error.twig',
            ['error' => $errorMessage],
            $headers + static::getDefaultHeaders(),
            $statusCode
        );
    }

    /**
     * @return AntiCSRF
     */
    public static function getAntiCSRF(): AntiCSRF
    {
        return self::$antiCSRF;
    }

    /**
     * @return CSPBuilder
     */
    public static function getCSPBuilder(): CSPBuilder
    {
        return self::$cspBuilder;
    }

    /**
     * Get the EasyDB object (used for database queries)
     *
     * @return EasyDB
     */
    public static function getDatabase(): EasyDB
    {
        return self::$easyDb;
    }

    /**
     * If we're using SQLite, we need a 1 or a 0.
     * Otherwise, TRUE/FALSE is fine.
     *
     * @param bool $value
     * @return bool|int|string
     */
    public static function getDatabaseBoolean(bool $value)
    {
        if (self::$easyDb->getDriver() === 'sqlite') {
            return $value ? 1 : 0;
        }
        if (self::$easyDb->getDriver() === 'pgsql') {
            return $value ? 't' : 'f';
        }
        return !empty($value);
    }

    /**
     * Default HTTP headers. Doesn't include the Content-Security-Policy header.
     *
     * @return array
     */
    public static function getDefaultHeaders(): array
    {
        return [
            'Content-Type' => 'text/html; charset=UTF-8',
            'X-Content-Type-Options' => 'nosniff',
            'X-Frame-Options' => 'SAMEORIGIN',
            'X-XSS-Protection' => '1; mode=block'
        ];
    }

    /**
     * Get a variable's type. If it's an object, also get the class name.
     *
     * @param mixed $obj
     * @return string
     */
    public static function getGenericType($obj = null)
    {
        if (\func_num_args() === 0) {
            return 'void';
        }
        if ($obj === null) {
            return 'null';
        }
        if (\is_object($obj)) {
            return static::decorateClassName(\get_class($obj));
        }
        $type = \gettype($obj);
        switch ($type) {
            case 'boolean':
                return 'bool';
            case 'double':
                return 'float';
            case 'integer':
                return 'int';
            default:
                return $type;
        }
    }

    /**
     * Get the local encryption key.
     *
     * @return HiddenString
     * @throws FilesystemException
     */
    public static function getLocalEncryptionKey(): HiddenString
    {
        if (self::$localEncryptionKey) {
            return self::$localEncryptionKey;
        }

        // Load the signing key:
        $keyFile = \file_get_contents(DISCRETION_APP_ROOT . '/local/encryption.key');
        if (!\is_string($keyFile)) {
            throw new FilesystemException('Could not load key file');
        }
        self::$localEncryptionKey = new HiddenString(
            Base64UrlSafe::decode($keyFile)
        );
        return self::$localEncryptionKey;
    }

    /**
     * @return Logger
     */
    public static function getLogger(): Logger
    {
        return self::$logger;
    }

    /**
     * @return Sapient
     */
    public static function getSapient(): Sapient
    {
        return new Sapient(new Slim());
    }

    /**
     * This gets the server's signing key.
     *
     * We should audit all calls to this method.
     *
     * @return SigningSecretKey
     * @throws \Exception
     */
    public static function getSecretKey(): SigningSecretKey
    {
        if (self::$signingKey) {
            return self::$signingKey;
        }

        // Load the signing key:
        $keyFile = \file_get_contents(DISCRETION_APP_ROOT . '/local/signing-secret.key');
        if (!\is_string($keyFile)) {
            throw new FilesystemException('Could not load key file');
        }
        self::$signingKey = new SigningSecretKey(
            Base64UrlSafe::decode($keyFile)
        );
        return self::$signingKey;
    }

    /**
     * @return array
     */
    public static function getSettings(): array
    {
        return self::$settings;
    }

    /**
     * @return \Twig_Environment
     */
    public static function getTwig(): \Twig_Environment
    {
        return self::$twig;
    }

    /**
     * Returns a value stored in the twig variables cache, if it exists.
     *
     * @param string $key
     * @return mixed
     * @throws RecordNotFound
     */
    public static function getTwigVar(string $key)
    {
        if (\array_key_exists($key, self::$twigVars)) {
            return self::$twigVars[$key];
        }
        throw new RecordNotFound('Could not find twig variable');
    }

    /**
     * Create a 301 redirect to a new destination.
     *
     * @param string $path
     * @param bool $allowRemote
     * @return Response
     */
    public static function redirect(
        string $path,
        bool $allowRemote = false
    ): Response {
        $headers = static::getDefaultHeaders();

        if (!$allowRemote && \strpos($path, '://') !== false) {
            // Fail closed:
            $path = '/';
        }

        $headers['Location'] = $path;
        /** @var Response $response */
        $response = self::$cspBuilder->injectCSPHeader(
            new Response(
                301,
                new Headers($headers)
            )
        );
        return $response;
    }

    /**
     * @param string $message
     * @param array $context
     * @param int $level
     * @return void
     */
    public static function securityLog(string $message, array $context = [], int $level = Logger::INFO)
    {
        if (self::$logger instanceof Logger) {
            /** @psalm-suppress UndefinedClass of Boolean, defined in Monolog\Logger */
            self::$logger->log($level, $message, $context);
        }
    }

    /**
     * @param AntiCSRF $antiCSRF
     * @return AntiCSRF
     */
    public static function setAntiCSRF(AntiCSRF $antiCSRF): AntiCSRF
    {
        self::$antiCSRF = $antiCSRF;
        return self::$antiCSRF;
    }

    /**
     * @param CSPBuilder $CSPBuilder
     * @return CSPBuilder
     */
    public static function setCSPBuilder(CSPBuilder $CSPBuilder): CSPBuilder
    {
        self::$cspBuilder = $CSPBuilder;
        return self::$cspBuilder;
    }

    /**
     * Store the database object in the Chronicle class.
     *
     * @param EasyDB $db
     * @return EasyDB
     */
    public static function setDatabase(EasyDB $db): EasyDB
    {
        self::$easyDb = $db;
        return self::$easyDb;
    }

    /**
     * @param Logger $logger
     * @return Logger
     */
    public static function setMonolog(Logger $logger): Logger
    {
        self::$logger = $logger;
        return self::$logger;
    }

    /**
     * @param array $settings
     * @return array
     */
    public static function setSettings(array $settings = []): array
    {
        self::$settings = $settings;
        return self::$settings;
    }

    /**
     * @param \Twig_Environment $twig
     * @return \Twig_Environment
     */
    public static function setTwig(\Twig_Environment $twig): \Twig_Environment
    {
        self::$twig = $twig;
        return self::$twig;
    }

    /**
     * Set a twig variable.
     *
     * @param string $key
     * @param mixed $value
     * @return void
     */
    public static function setTwigVar(string $key, $value)
    {
        /** @psalm-suppress MixedAssignment */
        self::$twigVars[$key] = $value;
    }

    /**
     * Quick shortcut method for generating an HTML response from a template.
     *
     * @param string $template
     * @param array $args
     * @param array $headers
     * @param int $status
     * @return Response
     *
     * @throws \Error
     * @throws \Twig_Error_Loader
     * @throws \Twig_Error_Runtime
     * @throws \Twig_Error_Syntax
     */
    public static function view(
        string $template,
        array $args = [],
        array $headers = [],
        int $status = 200
    ): Response {
        if (empty($headers)) {
            $headers = static::getDefaultHeaders();
        }
        /** @var Response $response */
        $response = self::$cspBuilder->injectCSPHeader(
            new Response(
                $status,
                new Headers($headers),
                (new Slim())->stringToStream(
                    static::getTwig()->render(
                        $template,
                        $args + self::$twigVars
                    )
                )
            )
        );
        return $response;
    }
}
For more information send a message to info at phpclasses dot org.