JsonResponseFormatter::process()   B
last analyzed

Complexity

Conditions 9
Paths 4

Size

Total Lines 50
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 34
c 1
b 0
f 0
dl 0
loc 50
rs 8.0555
cc 9
nc 4
nop 2
1
<?php declare(strict_types=1);
2
3
namespace ncryptf\middleware;
4
5
use Exception;
6
7
use Psr\Http\Message\MessageInterface;
8
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Message\StreamInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
14
use Psr\Http\Server\RequestHandlerInterface;
15
use Psr\SimpleCache\CacheInterface;
16
use Psr\SimpleCache\InvalidArgumentException;
17
use ncryptf\Request;
18
19
use ncryptf\middleware\EncryptionKeyInterface;
20
21
final class JsonResponseFormatter implements MiddlewareInterface
22
{
23
    const ENCODING_OPTIONS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION;
24
25
    /**
26
     * @var EncryptionKeyInterface $key
27
     */
28
    protected $key;
29
30
    /**
31
     * @var CacheInterface $cache
32
     */
33
    protected $cache;
34
35
    /**
36
     * @var array $contentType
37
     */
38
    protected $contentType = [
39
        'application/vnd.25519+json',
40
        'application/vnd.ncryptf+json'
41
    ];
42
43
    /**
44
     * Constructor
45
     * @param CacheInterface $cache
46
     * @param EncryptionKeyInterface $class
47
     */
48
    public function __construct(CacheInterface $cache, string $class)
49
    {
50
        $this->cache = $cache;
51
        $interface = new $class;
52
        if (!($interface instanceof EncryptionKeyInterface)) {
53
            throw new Exception('The class name provided is not an instance of EncryptionKeyInterface');
54
        }
55
56
        $this->key = $interface;
57
    }
58
59
    /**
60
     * Processes the request
61
     * @param ServerRequestInterface $request
62
     * @param RequestHandlerInterface $handler
63
     */
64
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
65
    {
66
        if ($this->checkRequest($request)) {
67
            $response = $handler->handle($request);
68
            $version = $request->getAttribute('ncryptf-version');
69
            $publicKey = $request->getAttribute('ncryptf-request-public-key');
70
            $token = $request->getAttribute('ncryptf-token');
71
            
72
            if ($version === null || $publicKey === null) {
73
                return $response->withStatus(400, 'Unable to encrypt request.');
74
            }
75
76
            $stream = $response->getBody();
77
            $class = $this->key;
78
            $key = $class::generate();
79
            
80
            $this->cache->set(
81
                $key->getHashIdentifier(),
82
                \function_exists('igbinary_serialize') ? \igbinary_serialize($key) : \serialize($key)
83
            );
84
85
            $r = new Request(
86
                $key->getBoxSecretKey(),
87
                $token === null ? $key->getSignSecretKey() : $token->signature
88
            );
89
90
            $content = $r->encrypt(
91
                (string)$stream,
92
                $publicKey,
93
                $version,
94
                $version === 2 ? null : \random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES)
95
            );
96
97
            if ($version === 1) {
98
                $response = $response->withHeader('x-sigpubkey', \base64_encode($token === null ? $key->getSignPublicKey() : $token->getSignaturePublicKey()))
99
                    ->withHeader('x-signature', \base64_encode($r->sign((string)$stream)))
100
                    ->withHeader('x-nonce', \base64_encode($r->getNonce()))
101
                    ->withHeader('x-pubkey', \base64_encode($key->getBoxPublicKey()));
102
            }
103
104
            $stream->rewind();
105
            $stream->write(\base64_encode($content));
106
            return $response->withBody($stream)
107
                ->withHeader('Content-Type', 'application/vnd.ncryptf+json')
108
                ->withHeader('x-public-key-expiration', $key->getPublicKeyExpiration())
109
                ->withHeader('x-hashid', $key->getHashIdentifier());
110
        }
111
112
        return $handler->handle($request)
113
            ->withHeader('Content-Type', 'application/vnd.ncryptf+json');
114
    }
115
116
    /**
117
     * Check whether the request payload need to be processed
118
     * @param ServerRequestInterface $request
119
     * @return bool
120
     */
121
    private function checkRequest(ServerRequestInterface $request): bool
122
    {
123
        $contentType = $request->getHeaderLine('Accept');
124
        foreach ($this->contentType as $allowedType) {
125
            if (\stripos($contentType, $allowedType) === 0) {
126
                return true;
127
            }
128
        }
129
        return false;
130
    }
131
}
132