Completed
Push — master ( 9473ea...cfd0e6 )
by Michał
05:47
created

Signer::formQueryString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 6
nc 2
nop 2
1
<?php namespace nyx\auth\id\protocols\oauth1;
2
3
// External dependencies
4
use Psr\Http\Message\RequestInterface as Request;
5
6
// Internal dependencies
7
use nyx\auth\id\credentials;
8
use nyx\auth;
9
10
/**
11
 * Base OAuth 1,0a Request Signer
12
 *
13
 * Note: Methods for building the base string for the signature are included in this base class despite
14
 *       the PLAINTEXT Signer not using them (as per spec). This is merely to avoid offloading them onto a trait and
15
 *       having to load an additional file (containing the trait's code) for the HMAC-SHA1 and RSA-SHA1 signers
16
 *       (which both utilize the base string), since use of PLAINTEXT signing is discouraged altogether,
17
 *       unless the connection itself is verified to be secure.
18
 *
19
 * @package     Nyx\Auth
20
 * @version     0.1.0
21
 * @author      Michal Chojnacki <[email protected]>
22
 * @copyright   2012-2017 Nyx Dev Team
23
 * @link        https://github.com/unyx/nyx
24
 */
25
abstract class Signer implements interfaces\Signer
26
{
27
    /**
28
     * Generates the signature's base string for the given Request.
29
     *
30
     * @param   Request     $request    The Request to generate the base string for.
31
     * @param   array       $params     Additional protocol parameters.
32
     * @return  string
33
     * ----
34
     * @see     https://tools.ietf.org/html/rfc5849#section-3.4.1
35
     * @see     https://tools.ietf.org/html/rfc5849#section-3.4.1.3
36
     */
37
    protected function buildBaseString(Request $request, array $params) : string
38
    {
39
        return $this->formMethod($request).'&'.$this->formUri($request).'&'.$this->formQueryString($request, $params);
40
    }
41
42
    /**
43
     * Forms the Request's method according to spec.
44
     *
45
     * @param   Request $request
46
     * @return  string
47
     */
48
    protected function formMethod(Request $request) : string
49
    {
50
        return strtoupper($request->getMethod());
51
    }
52
53
    /**
54
     * Forms the Request's URI according to spec.
55
     *
56
     * @param   Request $request
57
     * @return  string
58
     */
59
    protected function formUri(Request $request) : string
60
    {
61
        // Remove query params from the URL (Spec: #9.1.2.).
62
        return rawurlencode($request->getUri()->withQuery(''));
63
    }
64
65
    /**
66
     * Forms the Request's query string according to spec.
67
     *
68
     * @param   Request $request
69
     * @param   array   $params
70
     * @return  string
71
     */
72
    protected function formQueryString(Request $request, array $params) : string
73
    {
74
        // The 'realm', if present, is excluded from the protocol parameters when generating the base string.
75
        unset($params['realm']);
76
77
        // Form parameters, if present in the body, need to be included in query string portion of the base string.
78
        // Note: We are assuming the Request has a proper content-type set, not actually checking the body!
79
        if ($request->getHeaderLine('Content-Type') === 'application/x-www-form-urlencoded') {
80
            $params += \GuzzleHttp\Psr7\parse_query($request->getBody()->getContents());
81
        }
82
83
        // Initial query string parameters also need to be included in the base string and properly encoded.
84
        $params += \GuzzleHttp\Psr7\parse_query($request->getUri()->getQuery());
85
86
        return $this->buildQueryStringFromParams($this->normalizeParameters($params));
87
    }
88
89
    /**
90
     * Encodes and sorts the protocol parameters according to spec.
91
     *
92
     * @param   array   $params
93
     * @return  array
94
     */
95
    protected function normalizeParameters(array $params) : array
96
    {
97
        // Recursively percent encode each key/value pair in the params.
98
        array_walk_recursive($params, function (&$key, &$value) {
99
            $key   = rawurlencode(rawurldecode($key));
100
            $value = rawurlencode(rawurldecode($value));
101
        });
102
103
        // Sort the keys lexicographically (alphabetically in PHP's case).
104
        ksort($params);
105
106
        return $params;
107
    }
108
109
    /**
110
     * Creates a to-spec encoded query string out of each key/value pair in the initial array.
111
     * Handles multi-dimensional arrays recursively.
112
     *
113
     * @param   array   $params         Array of parameters to convert.
114
     * @param   array   $queryParams    The parameters to extend (used internally for nested data).
115
     * @param   string  $prevKey        Optional Array key to append
116
     * @return  string
117
     */
118
    protected function buildQueryStringFromParams(array $params, array $queryParams = null, string $prevKey = '') : string
119
    {
120
        if ($initial = (!isset($queryParams))) {
121
            $queryParams = [];
122
        }
123
124
        foreach ($params as $key => $value) {
125
            // When nested...
126
            if ($prevKey) {
127
                $key = $prevKey.'['.$key.']';
128
            }
129
130
            if (is_array($value)) {
131
                $queryParams = $this->buildQueryStringFromParams($value, $queryParams, $key);
132
            } else {
133
                $queryParams[] = rawurlencode($key.'='.$value);
134
            }
135
        }
136
137
        if ($initial) {
138
            return implode('%26', $queryParams); // Ampersand.
139
        }
140
141
        return $queryParams;
142
    }
143
144
    /**
145
     * Creates a to-spec encoded signing key based on the Client's Credentials (and Token Credentials, if provided).
146
     *
147
     * @param   credentials\Client          $client
148
     * @param   auth\interfaces\Credentials $token
149
     * @return  mixed
150
     */
151
    protected function createKey(credentials\Client $client, auth\interfaces\Credentials $token = null)
152
    {
153
        // The joining ampersand after the encoded Client's secret is correctly left in even if no Token is being
154
        // included in the key.
155
        return rawurlencode($client->getSecret()) . '&' . (isset($token) ? rawurlencode($token->getSecret()) : '');
156
    }
157
}
158