-
Notifications
You must be signed in to change notification settings - Fork 0
/
RequestGenerator.php
156 lines (136 loc) · 5.69 KB
/
RequestGenerator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<?php
namespace CashID\Services;
use CashID\API;
use CashID\Cache\RequestCacheInterface;
use CashID\Cache\APCuCache;
use CashID\Exceptions\InternalException;
/**
* Request Generator
*
* The RequestGenerator creates CashID requests following the CashID standard.
* The constructor is given the domain name and path that will be handling the
* response, while the createRequest function creates a request string from
* the required data and metadata.
*/
class RequestGenerator extends CashIDService
{
protected $defaultDependencies = [
'CashID\Cache\RequestCacheInterface' => ['name' => 'cache', 'class' => '\CashID\Cache\APCuCache'],
];
/**
* Create a request
*
* Given an action, data, and a set of metadata, construct a valid CashID
* request. A unique random nonce is generated for each request and saved
* in the user cache.
*
* @param string $action
* Name of the action the user authenticates to perform
* @param string $data
* Data relevant to the requested action
* @param array $metadata
* Array with requested and optional metadata
* @return mixed
* returns the request URI or false if error
*/
public function createRequest(?string $action = "", ?string $data = "", ?array $metadata = [])
{
try {
$user_generated = false;
$nonce = null;
// Check is the action is a user-initiated action.
// If so, we will provide a nonce-less request as
// a favor.
if (in_array($action, API::USER_ACTIONS)) {
$user_generated = true;
} else {
// generate a random nonce.
$nonce = rand(100000000, 999999999);
// Check if the nonce is already used, and regenerate until it does not exist.
while ($this->cache->exists("cashid_request_{$nonce}")) {
// generate a random nonce.
$nonce = rand(100000000, 999999999);
}
}
// Initialize an empty parameter list.
$parameters = [];
// If a specific action was requested, add it to the parameter list.
if ($action) {
$parameters['a'] = "a={$action}";
}
// If specific data was requested, add it to the parameter list.
if ($data) {
$parameters['d'] = "d={$data}";
}
// If required metadata was requested, add them to the parameter list.
if (isset($metadata['required'])) {
$parameters['r'] = "r=" . $this->encodeRequestMetadata($metadata['required']);
}
// If optional metadata was requested, add them to the parameter list.
if (isset($metadata['optional'])) {
$parameters['o'] = "o=" . $this->encodeRequestMetadata($metadata['optional']);
}
if (!$user_generated) {
// Append the nonce to the parameter list.
$parameters['x'] = "x={$nonce}";
}
// Form the request URI from the configured values.
$request_uri = "cashid:" . $this->service_domain . $this->service_path . "?" . implode($parameters, '&');
// Store the request and nonce in local cache if not user generated.
if (!$user_generated && !$this->cache->store("cashid_request_{$nonce}", [ 'available' => true, 'request' => $request_uri, 'expires' => time() + (60 * 15) ])) {
throw new InternalException("Failed to store request metadata in APCu.");
}
// Return the request URI to indicate success.
return $request_uri;
} catch (InternalException $exception) {
// Return false to indicate error.
return false;
}
}
/**
* Creates a metadata request string part from a metadata array
*
* The metadata array is in the form:
*
* $metadata = [
* 'identification' => [list, of, fields],
* 'position' => [list, of, fields],
* 'contact' => [list, of, fields],
* ]
*
* @param array $metadata
* Specification for which metadata is requested from the client.
* @return string
* The request metadata part
*/
private function encodeRequestMetadata(array $metadata): string
{
// Initialize an empty metadata string.
$metadata_string = "";
// Iterate over the available metadata names.
foreach (API::METADATA_NAMES as $metadata_type => $metadata_fields) {
// Store the first letter of the metadata type.
$metadata_letter = substr($metadata_type, 0, 1);
// Initialize an empty metadata part string.
$metadata_part = "";
//
if (isset($metadata[$metadata_type])) {
// Iterate over each field of this metadata type.
foreach ($metadata_fields as $field_name => $field_code) {
// If this field was requested..
if (in_array($field_name, $metadata[$metadata_type])) {
// .. add it to the metadata part.
$metadata_part .= $field_code;
}
}
// If, after checking for requested metadata of this type, some matches were found..
if ($metadata_part !== "") {
// Add the letter and numbers matching the requested metadata to the metadata string.
$metadata_string .= "{$metadata_letter}{$metadata_part}";
}
}
}
// Return the filled in metadata string.
return $metadata_string;
}
}