App Engine Python SDK version 1.8.8
[gae.git] / python / php / sdk / google / appengine / api / app_identity / AppIdentityService.php
blob0bcdbcc4a9a37a2cde0cb81e4ef0679e812dbb96
1 <?php
2 /**
3 * Copyright 2007 Google Inc.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /**
20 namespace google\appengine\api\app_identity;
22 use google\appengine\AppIdentityServiceError\ErrorCode;
23 use google\appengine\GetAccessTokenRequest;
24 use google\appengine\GetAccessTokenResponse;
25 use google\appengine\GetPublicCertificateForAppRequest;
26 use google\appengine\GetPublicCertificateForAppResponse;
27 use google\appengine\GetServiceAccountNameRequest;
28 use google\appengine\GetServiceAccountNameResponse;
29 use google\appengine\SignForAppRequest;
30 use google\appengine\SignForAppResponse;
31 use google\appengine\runtime\ApiProxy;
32 use google\appengine\runtime\ApplicationError;
34 require_once 'google/appengine/api/app_identity/app_identity_service_pb.php';
35 require_once 'google/appengine/api/app_identity/AppIdentityException.php';
36 require_once 'google/appengine/api/app_identity/PublicCertificate.php';
37 require_once 'google/appengine/runtime/ApiProxy.php';
38 require_once 'google/appengine/runtime/ApplicationError.php';
40 /**
41 * The AppIdentityService allows you to sign arbitrary byte
42 * array using per app private key maintained by App Engine. You can also
43 * retrieve a list of public certificates which can be used to
44 * verify the signature.
46 * App Engine is responsible for maintaining per-application
47 * private key. App Engine will keep rotating private keys
48 * periodically. App Engine never releases these private keys externally.
50 * Since private keys are rotated periodically,
51 * getPublicCertificates() could return a list of public
52 * certificates. It's the caller's responsibility to try these
53 * certificates one by one when doing signature verification.
55 final class AppIdentityService {
57 const PACKAGE_NAME = 'app_identity_service';
58 const PARTITION_SEPARATOR = "~";
59 const DOMAIN_SEPARATOR = ":";
60 const MEMCACHE_KEY_PREFIX = '_ah_app_identity_';
62 /**
63 * Signs arbitrary byte array using per app private key.
65 * @param string $bytes_to_sign The bytes to generate the signature for.
67 * @throws \InvalidArgumentException If $bytes_to_sign is not a string.
68 * @throws AppIdentityException If there is an error using the AppIdentity
69 * service.
71 * @return array An array containing the elements
72 * 'key_name' - the name of the key used to sign the bytes
73 * 'signature' - the signature of the bytes.
76 public static function signForApp($bytes_to_sign) {
77 $req = new SignForAppRequest();
78 $resp = new SignForAppResponse();
80 if (!is_string($bytes_to_sign)) {
81 throw new \InvalidArgumentException('$bytes_to_sign must be a string.');
84 $req->setBytesToSign($bytes_to_sign);
86 try {
87 ApiProxy::makeSyncCall(self::PACKAGE_NAME, 'SignForApp', $req, $resp);
88 } catch (ApplicationError $e) {
89 throw self::applicationErrorToException($e);
92 return [
93 'key_name' => $resp->getKeyName(),
94 'signature' => $resp->getSignatureBytes(),
98 /**
99 * Get the service account name for the application.
101 * @throws AppIdentityException If there is an error using the AppIdentity
102 * service.
104 * @return string The service account name.
106 public static function getServiceAccountName() {
107 $req = new GetServiceAccountNameRequest();
108 $resp = new GetServiceAccountNameResponse();
110 try {
111 ApiProxy::makeSyncCall(self::PACKAGE_NAME, 'GetServiceAccountName', $req,
112 $resp);
113 } catch (ApplicationError $e) {
114 throw self::applicationErrorToException($e);
117 return $resp->getServiceAccountName();
121 * Get the list of public certifates for the application.
123 * @throws AppIdentityException If there is an error using the AppIdentity
124 * service.
126 * @return PublicCertificate[] An array of the applications public
127 * certificates.
129 public static function getPublicCertificates() {
130 $req = new GetPublicCertificateForAppRequest();
131 $resp = new GetPublicCertificateForAppResponse();
133 try {
134 ApiProxy::makeSyncCall(self::PACKAGE_NAME, 'GetPublicCertificatesForApp',
135 $req, $resp);
136 } catch (ApplicationError $e) {
137 throw self::applicationErrorToException($e);
140 $result = [];
142 foreach ($resp->getPublicCertificateListList() as $cert) {
143 $result[] = new PublicCertificate($cert->getKeyName(),
144 $cert->getX509CertificatePem());
147 return $result;
151 * Gets an OAuth2 access token for the application's service account from
152 * memcache or generates and caches one by calling
153 * getAccessTokenUncached($scopes)
155 * Each application has an associated Google account. This function returns
156 * OAuth2 access token corresponding to the running app. Access tokens are
157 * safe to cache and reuse until they expire.
159 * @param array $scopes The scopes to acquire the access token for.
160 * Can be either a single string or an array of strings.
162 * @throws \InvalidArgumentException If $scopes is not a string or an array of
163 * strings.
164 * @throws AppIdentityException If there is an error using the AppIdentity
165 * service.
167 * @return array An array with the following key/value pairs.
168 * 'access_token' - The access token for the application.
169 * 'expiration_time' - The expiration time for the access token.
171 public static function getAccessToken($scopes) {
172 $memcache_key = self::MEMCACHE_KEY_PREFIX . self::DOMAIN_SEPARATOR;
173 if (is_string($scopes)) {
174 $memcache_key .= $scopes;
175 } else if (is_array($scopes)) {
176 $memcache_key .= implode(self::DOMAIN_SEPARATOR, $scopes);
177 } else {
178 throw new \InvalidArgumentException('Invalid scope ' . $scopes);
181 $memcache = new \Memcache();
182 $result = $memcache->get($memcache_key);
184 if ($result === false) {
185 $result = self::getAccessTokenUncached($scopes);
187 // Cache in memcache allowing for 5 minute clock skew.
188 $memcache->set($memcache_key,
189 $result,
190 null,
191 $result['expiration_time'] - 300);
193 return $result;
197 * Get an OAuth2 access token for the applications service account without
198 * caching the result. Usually getAccessToken($scopes) should be used instead
199 * which calls this method and caches the result in memcache.
201 * @param array $scopes The scopes to acquire the access token for.
202 * Can be either a single string or an array of strings.
204 * @throws InvalidArgumentException If $scopes is not a string or an array of
205 * strings.
206 * @throws AppIdentityException If there is an error using the AppIdentity
207 * service.
209 * @return array An array with the following key/value pairs.
210 * 'access_token' - The access token for the application.
211 * 'expiration_time' - The expiration time for the access token.
213 private static function getAccessTokenUncached($scopes) {
214 $req = new GetAccessTokenRequest();
215 $resp = new GetAccessTokenResponse();
217 if (is_string($scopes)) {
218 $req->addScope($scopes);
219 } else if (is_array($scopes)) {
220 foreach($scopes as $scope) {
221 if (is_string($scope)) {
222 $req->addScope($scope);
223 } else {
224 throw new \InvalidArgumentException(
225 'Invalid scope ' . $scope);
228 } else {
229 throw new \InvalidArgumentException('Invalid scope ' . $scopes);
232 try {
233 ApiProxy::makeSyncCall(self::PACKAGE_NAME, 'GetAccessToken', $req, $resp);
234 } catch (ApplicationError $e) {
235 throw self::applicationErrorToException($e);
238 return [
239 'access_token' => $resp->getAccessToken(),
240 'expiration_time' => $resp->getExpirationTime(),
245 * Get the application id of an app.
247 * @return string The application id of the app.
249 public static function getApplicationId() {
250 $app_id = getenv("APPLICATION_ID");
251 $psep = strpos($app_id, self::PARTITION_SEPARATOR);
252 if ($psep > 0) {
253 $app_id = substr($app_id, $psep + 1);
255 return $app_id;
259 * Get the standard hostname of the default version of the app.
261 * @return string The standard hostname of the default version of the
262 * application, or FALSE if the call failed.
264 public static function getDefaultVersionHostname() {
265 return getenv("DEFAULT_VERSION_HOSTNAME");
269 * Converts an application error to the service specific exception.
271 * @param ApplicationError $application_error The application error
273 * @return mixed An exception that corresponds to the application error.
275 * @access private
277 private static function applicationErrorToException($application_error) {
278 switch ($application_error->getApplicationError()) {
279 case ErrorCode::UNKNOWN_SCOPE:
280 return new \InvalidArgumentException(
281 'An unknown scope was supplied.');
282 case ErrorCode::BLOB_TOO_LARGE:
283 return new \InvalidArgumentException(
284 'The supplied blob was too long.');
285 case ErrorCode::DEADLINE_EXCEEDED:
286 return new AppIdentityException(
287 'The deadline for the call was exceeded.');
288 case ErrorCode::NOT_A_VALID_APP:
289 return new AppIdentityException(
290 'The application is not valid.');
291 case ErrorCode::UNKNOWN_ERROR:
292 return new AppIdentityException(
293 'There was an unknown error using the AppIdentity service.');
294 case ErrorCode::GAIAMINT_NOT_INITIAILIZED:
295 return new AppIdentityException(
296 'There was a GAIA error using the AppIdentity service.');
297 case ErrorCode::NOT_ALLOWED:
298 return new AppIdentityException('The call is not allowed.');
299 default:
300 return new AppIdentityException(
301 'The AppIdentity service threw an unexpected error.');