Skip to content

Commit 039279d

Browse files
authored
update to /admin/v3/integrations endpoint (#38)
1 parent 292c64e commit 039279d

3 files changed

Lines changed: 260 additions & 44 deletions

File tree

src/Admin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ public function groups($groupid = null)
188188
public function integrations($ikey = null)
189189
{
190190
$method = "GET";
191-
$endpoint = "/admin/v1/integrations";
191+
$endpoint = "/admin/v3/integrations";
192192
$params = [];
193193

194194
if ($ikey) {

src/Client.php

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function setRequesterOption($option, $value)
7272
return $this;
7373
}
7474

75-
private function signParameters($method, $host, $path, $params, $skey, $ikey, $now)
75+
private function signParameters($method, $host, $path, $params, $skey, $ikey, $now, $body, $additional_headers)
7676
{
7777
assert(is_string($method));
7878
assert(is_string($host));
@@ -82,8 +82,8 @@ private function signParameters($method, $host, $path, $params, $skey, $ikey, $n
8282
assert(is_string($ikey));
8383
assert(is_string($now));
8484

85-
$canon = self::canonicalize($method, $host, $path, $params, $now);
86-
85+
$canon = self::canonicalize($method, $host, $path, $params, $now, $body, $additional_headers);
86+
8787
$signature = self::sign($canon, $skey);
8888
$auth = sprintf("%s:%s", $ikey, $signature);
8989
$b64auth = base64_encode($auth);
@@ -96,25 +96,79 @@ private function sign($msg, $key)
9696
assert(is_string($msg));
9797
assert(is_string($key));
9898

99-
return hash_hmac("sha1", $msg, $key);
99+
$msg = mb_convert_encoding($msg ?? '', 'UTF-8', 'ISO-8859-1');
100+
$key = mb_convert_encoding($key ?? '', 'UTF-8', 'ISO-8859-1');
101+
102+
return hash_hmac("sha512", $msg, $key);
100103
}
101104

102-
private function canonicalize($method, $host, $path, $params, $now)
105+
private function canonicalize($method, $host, $path, $params, $now, $body = null, $additional_headers = [])
103106
{
104107
assert(is_string($method));
105108
assert(is_string($host));
106109
assert(is_string($path));
107110
assert(is_array($params));
108111
assert(is_string($now));
112+
assert(is_string($body) || $body === null);
113+
assert(is_array($additional_headers));
109114

110115
$args = self::urlEncodeParameters($params);
111-
$canon = array($now, strtoupper($method), strtolower($host), $path, $args);
116+
117+
$canon = array(
118+
$now,
119+
strtoupper($method),
120+
strtolower($host),
121+
$path,
122+
$args,
123+
hash('sha512', mb_convert_encoding($body ?? '', 'UTF-8', 'ISO-8859-1')),
124+
self::canonXDuoHeaders($additional_headers),
125+
);
112126

113127
$canon = implode("\n", $canon);
114128

115129
return $canon;
116130
}
117131

132+
private function canonXDuoHeaders($additional_headers = [])
133+
{
134+
assert(is_array($additional_headers));
135+
136+
$lowered_headers = array_change_key_case($additional_headers, CASE_LOWER);
137+
ksort($lowered_headers);
138+
139+
$canon_list = [];
140+
$added_headers = [];
141+
142+
foreach ($lowered_headers as $header_name => $value) {
143+
self::validateAdditionalHeader($header_name, $value, $added_headers);
144+
array_push($canon_list, $header_name, $value);
145+
array_push($added_headers, $header_name);
146+
}
147+
148+
$canon = implode("\x00", $canon_list);
149+
return hash('sha512', mb_convert_encoding($canon ?? '', 'UTF-8', 'ISO-8859-1'));
150+
}
151+
152+
private function validateAdditionalHeader($name, $value, $addedHeaders)
153+
{
154+
if ($name === null || $value === null)
155+
{
156+
throw new \InvalidArgumentException("Not allowed 'null' as a header name or value");
157+
} elseif (str_contains($name,"\x00"))
158+
{
159+
throw new \InvalidArgumentException("Not allowed 'Null' character in header name");
160+
} elseif (str_contains($value,"\x00"))
161+
{
162+
throw new \InvalidArgumentException("Not allowed 'Null' character in header value");
163+
} elseif (!str_starts_with(strtolower($name),"x-duo-"))
164+
{
165+
throw new \InvalidArgumentException("Additional headers must start with 'X-Duo-'");
166+
} elseif (in_array(strtolower($name), $addedHeaders, true))
167+
{
168+
throw new \InvalidArgumentException("Duplicate header passed, header=$name");
169+
}
170+
}
171+
118172
private function urlEncodeParameters($params)
119173
{
120174
assert(is_array($params));
@@ -149,17 +203,27 @@ private function makeRequest($method, $uri, $body, $headers)
149203
}
150204
}
151205

152-
public function apiCall($method, $path, $params)
206+
public function apiCall($method, $path, $params, $additional_headers = [])
153207
{
154208
assert(is_string($method));
155209
assert(is_string($path));
156210
assert(is_array($params));
211+
assert(is_array($additional_headers));
157212

158213
$now = date(DateTime::RFC2822);
159-
160214
$headers = [];
215+
if (in_array($method, ["POST", "PUT", "PATCH"], true)) {
216+
ksort($params);
217+
$body = json_encode($params);
218+
$params = [];
219+
$headers["Content-Type"] = "application/json";
220+
$uri = $path;
221+
} else {
222+
$body = "";
223+
$uri = $path . (!empty($params) ? "?" . self::urlEncodeParameters($params) : "");
224+
}
225+
161226
$headers["Date"] = $now;
162-
$headers["Host"] = $this->host;
163227
$headers["User-Agent"] = "duo_api_php/" . VERSION;
164228
$headers["Authorization"] = self::signParameters(
165229
$method,
@@ -168,19 +232,11 @@ public function apiCall($method, $path, $params)
168232
$params,
169233
$this->skey,
170234
$this->ikey,
171-
$now
235+
$now,
236+
$body,
237+
$additional_headers,
172238
);
173239

174-
if (in_array($method, ["POST", "PUT"], true)) {
175-
$body = http_build_query($params);
176-
$headers["Content-Type"] = "application/x-www-form-urlencoded";
177-
$headers["Content-Length"] = strval(strlen($body));
178-
$uri = $path;
179-
} else {
180-
$body = null;
181-
$uri = $path . (!empty($params) ? "?" . self::urlEncodeParameters($params) : "");
182-
}
183-
184240
return self::makeRequest($method, $uri, $body, $headers);
185241
}
186242

0 commit comments

Comments
 (0)