aboutsummaryrefslogtreecommitdiffhomepage
path: root/misc/send_request_authenticated.php
blob: 842334c7e121fba694bae518fb32f743a2d2688d (plain)
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
<?php

use Digitigrade\GlobalConfig;
use Digitigrade\Model\Actor;
use Digitigrade\Model\Instance;

/**
 * Makes an HTTPS request to a remote instance, using the corresponding outbound auth token (if any)
 * @param string $uri
 * @param bool $dontLookUpInstance if true, will skip looking up the Instance, and consequently the auth token
 * @param string $method HTTP verb
 * @param ?Actor $actingAs if specified, the local actor to send the request on behalf of
 * @return stdClass object with fields `body` and `headers`
 */
function send_request_authenticated(
    string $uri,
    bool $dontLookUpInstance = false,
    string $method = 'GET',
    ?Actor $actingAs = null,
    ?string $body = null,
    ?string $contentType = null
) {
    if (!str_starts_with($uri, 'https://')) {
        throw new RuntimeException('refusing to fetch a non-https uri');
    }

    $remoteHost = hostname_from_uri($uri);
    if ($remoteHost == GlobalConfig::getInstance()->getCanonicalHost()) {
        throw new RuntimeException('refusing to fetch content from myself');
    }

    $instance = null;
    if (!$dontLookUpInstance) {
        $instance = Instance::findByHostname($remoteHost);
    }

    if ($body != null && $method != 'GET') {
        $contentType ??= 'application/x-www-form-urlencoded';
    }

    if (!isset($instance->auth->outboundToken) || $instance->auth->outboundToken == null) {
        // hopefully we can make the request anyway?
        $context = stream_context_create(['http' => [
            'method' => $method,
            'content' => $body,
            'header' => isset($contentType) ? "Content-Type: $contentType" : null
        ]]);
        $resp = file_get_contents($uri, context: $context);
        // $http_response_header just poofs into existence .
        // i don't like this api. i should probably use curl instead. hmm
        // TODO: use curl instead of file_get_contents
        if (str_contains($http_response_header[0], '401') || str_contains($http_response_header[0], '403')) {
            // we can't authenticate right now because it's asynchronous
            // so i'm choosing to begin the auth process now and return false in the hopes that
            // this request will be retried later
            $instance?->beginOutboundAuth();
        }
        $result = new stdClass();
        $result->body = $resp;
        $result->headers = $http_response_header;
        return $result;
    }

    $header = 'Authorization: Bearer ' . $instance->auth->outboundToken;
    if ($actingAs != null) {
        $header .= "\nX-PawPub-Actor: $actingAs->uri";
    }
    if ($contentType != null) {
        $header .= "\nContent-Type: $contentType";
    }
    $context = stream_context_create(['http' => [
        'method' => $method,
        'header' => $header,
        'content' => $body
    ]]);
    $result = new stdClass();
    $result->body = file_get_contents($uri, context: $context);
    $result->headers = $http_response_header;
    return $result;
}