1<?php
2/*
3 * Copyright 2019 Google LLC
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18namespace Google\Auth;
19
20use Google\Auth\HttpHandler\HttpClientCache;
21use Google\Auth\HttpHandler\HttpHandlerFactory;
22use GuzzleHttp\Psr7;
23
24/**
25 * Tools for using the IAM API.
26 *
27 * @see https://cloud.google.com/iam/docs IAM Documentation
28 */
29class Iam
30{
31 const IAM_API_ROOT = 'https://iamcredentials.googleapis.com/v1';
32 const SIGN_BLOB_PATH = '%s:signBlob?alt=json';
33 const SERVICE_ACCOUNT_NAME = 'projects/-/serviceAccounts/%s';
34
35 /**
36 * @var callable
37 */
38 private $httpHandler;
39
40 /**
41 * @param callable $httpHandler [optional] The HTTP Handler to send requests.
42 */
43 public function __construct(callable $httpHandler = null)
44 {
45 $this->httpHandler = $httpHandler
46 ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
47 }
48
49 /**
50 * Sign a string using the IAM signBlob API.
51 *
52 * Note that signing using IAM requires your service account to have the
53 * `iam.serviceAccounts.signBlob` permission, part of the "Service Account
54 * Token Creator" IAM role.
55 *
56 * @param string $email The service account email.
57 * @param string $accessToken An access token from the service account.
58 * @param string $stringToSign The string to be signed.
59 * @param array $delegates [optional] A list of service account emails to
60 * add to the delegate chain. If omitted, the value of `$email` will
61 * be used.
62 * @return string The signed string, base64-encoded.
63 */
64 public function signBlob($email, $accessToken, $stringToSign, array $delegates = [])
65 {
66 $httpHandler = $this->httpHandler;
67 $name = sprintf(self::SERVICE_ACCOUNT_NAME, $email);
68 $uri = self::IAM_API_ROOT . '/' . sprintf(self::SIGN_BLOB_PATH, $name);
69
70 if ($delegates) {
71 foreach ($delegates as &$delegate) {
72 $delegate = sprintf(self::SERVICE_ACCOUNT_NAME, $delegate);
73 }
74 } else {
75 $delegates = [$name];
76 }
77
78 $body = [
79 'delegates' => $delegates,
80 'payload' => base64_encode($stringToSign),
81 ];
82
83 $headers = [
84 'Authorization' => 'Bearer ' . $accessToken
85 ];
86
87 $request = new Psr7\Request(
88 'POST',
89 $uri,
90 $headers,
91 Psr7\stream_for(json_encode($body))
92 );
93
94 $res = $httpHandler($request);
95 $body = json_decode((string) $res->getBody(), true);
96
97 return $body['signedBlob'];
98 }
99}
100