1 // Copyright 2011 Google Inc. All Rights Reserved.
2 package com
.google
.appengine
.api
.appidentity
;
4 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.AppIdentityServiceError
;
5 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetAccessTokenRequest
;
6 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetAccessTokenResponse
;
7 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetPublicCertificateForAppRequest
;
8 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetPublicCertificateForAppResponse
;
9 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetServiceAccountNameRequest
;
10 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.GetServiceAccountNameResponse
;
11 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.SignForAppRequest
;
12 import com
.google
.appengine
.api
.appidentity
.AppIdentityServicePb
.SignForAppResponse
;
13 import com
.google
.appengine
.api
.memcache
.Expiration
;
14 import com
.google
.appengine
.api
.memcache
.MemcacheService
;
15 import com
.google
.appengine
.api
.memcache
.MemcacheServiceFactory
;
16 import com
.google
.appengine
.api
.utils
.SystemProperty
;
17 import com
.google
.apphosting
.api
.ApiProxy
;
18 import com
.google
.common
.collect
.Lists
;
19 import com
.google
.protobuf
.ByteString
;
20 import com
.google
.protobuf
.InvalidProtocolBufferException
;
22 import java
.util
.Date
;
23 import java
.util
.List
;
26 * Implementation of the AppIdentityService interface.
29 class AppIdentityServiceImpl
implements AppIdentityService
{
31 public static final String PACKAGE_NAME
= "app_identity_service";
33 public static final String SIGN_FOR_APP_METHOD_NAME
= "SignForApp";
35 public static final String GET_SERVICE_ACCOUNT_NAME_METHOD_NAME
= "GetServiceAccountName";
37 public static final String GET_CERTS_METHOD_NAME
= "GetPublicCertificatesForApp";
39 public static final String GET_ACCESS_TOKEN_METHOD_NAME
= "GetAccessToken";
41 public static final String MEMCACHE_NAMESPACE
= "_ah_";
43 public static final String MEMCACHE_KEY_PREFIX
= "_ah_app_identity_";
45 private static final char APP_PARTITION_SEPARATOR
= '~';
46 private static final char APP_DOMAIN_SEPARATOR
= ':';
48 private void handleApplicationError(ApiProxy
.ApplicationException e
) {
49 switch (AppIdentityServiceError
.ErrorCode
.valueOf(e
.getApplicationError())) {
51 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
53 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
54 case DEADLINE_EXCEEDED
:
55 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
57 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
59 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
61 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
66 public List
<PublicCertificate
> getPublicCertificatesForApp() {
67 GetPublicCertificateForAppRequest
.Builder requestBuilder
=
68 GetPublicCertificateForAppRequest
.newBuilder();
69 GetPublicCertificateForAppResponse
.Builder responseBuilder
=
70 GetPublicCertificateForAppResponse
.newBuilder();
73 responseBuilder
.mergeFrom(
74 ApiProxy
.makeSyncCall(
75 PACKAGE_NAME
, GET_CERTS_METHOD_NAME
, requestBuilder
.build().toByteArray()));
76 } catch (ApiProxy
.ApplicationException e
) {
77 handleApplicationError(e
);
78 } catch (InvalidProtocolBufferException e
) {
79 throw new AppIdentityServiceFailureException(e
.getMessage());
81 GetPublicCertificateForAppResponse response
= responseBuilder
.build();
83 List
<PublicCertificate
> certs
= Lists
.newArrayList();
84 for (AppIdentityServicePb
.PublicCertificate cert
: response
.getPublicCertificateListList()) {
85 certs
.add(new PublicCertificate(cert
.getKeyName(), cert
.getX509CertificatePem()));
91 public SigningResult
signForApp(byte[] signBlob
) {
92 SignForAppRequest
.Builder requestBuilder
= SignForAppRequest
.newBuilder();
93 requestBuilder
.setBytesToSign(ByteString
.copyFrom(signBlob
));
94 SignForAppResponse
.Builder responseBuilder
= SignForAppResponse
.newBuilder();
96 responseBuilder
.mergeFrom(
97 ApiProxy
.makeSyncCall(
98 PACKAGE_NAME
, SIGN_FOR_APP_METHOD_NAME
, requestBuilder
.build().toByteArray()));
99 } catch (ApiProxy
.ApplicationException e
) {
100 handleApplicationError(e
);
101 } catch (InvalidProtocolBufferException e
) {
102 throw new AppIdentityServiceFailureException(e
.getMessage());
105 SignForAppResponse response
= responseBuilder
.build();
106 return new SigningResult(response
.getKeyName(), response
.getSignatureBytes().toByteArray());
110 public String
getServiceAccountName() {
111 GetServiceAccountNameRequest
.Builder requestBuilder
= GetServiceAccountNameRequest
.newBuilder();
112 GetServiceAccountNameResponse
.Builder responseBuilder
=
113 GetServiceAccountNameResponse
.newBuilder();
115 responseBuilder
.mergeFrom(
116 ApiProxy
.makeSyncCall(
117 getAccessTokenPackageName(), GET_SERVICE_ACCOUNT_NAME_METHOD_NAME
,
118 requestBuilder
.build().toByteArray()));
119 } catch (ApiProxy
.ApplicationException e
) {
120 handleApplicationError(e
);
121 } catch (InvalidProtocolBufferException e
) {
122 throw new AppIdentityServiceFailureException(e
.getMessage());
125 GetServiceAccountNameResponse response
= responseBuilder
.build();
126 return response
.getServiceAccountName();
130 public GetAccessTokenResult
getAccessTokenUncached(Iterable
<String
> scopes
) {
131 GetAccessTokenRequest
.Builder requestBuilder
= GetAccessTokenRequest
.newBuilder();
132 for (String scope
: scopes
) {
133 requestBuilder
.addScope(scope
);
135 if (requestBuilder
.getScopeCount() == 0) {
136 throw new AppIdentityServiceFailureException("No scopes specified.");
138 GetAccessTokenResponse
.Builder responseBuilder
= GetAccessTokenResponse
.newBuilder();
140 responseBuilder
.mergeFrom(
141 ApiProxy
.makeSyncCall(
142 getAccessTokenPackageName(), GET_ACCESS_TOKEN_METHOD_NAME
,
143 requestBuilder
.build().toByteArray()));
144 } catch (ApiProxy
.ApplicationException e
) {
145 handleApplicationError(e
);
146 } catch (InvalidProtocolBufferException e
) {
147 throw new AppIdentityServiceFailureException(e
.getMessage());
150 GetAccessTokenResponse response
= responseBuilder
.build();
151 return new GetAccessTokenResult(response
.getAccessToken(),
152 new Date(response
.getExpirationTime() * 1000));
155 private String
getAccessTokenPackageName() {
156 return Boolean
.getBoolean("appengine.app_identity.use_robot")
157 && SystemProperty
.environment
.value() != SystemProperty
.Environment
.Value
.Production
158 ?
"robot_enabled_app_identity_service"
159 : "app_identity_service";
162 private String
memcacheKeyForScopes(Iterable
<String
> scopes
) {
163 StringBuilder builder
= new StringBuilder();
164 builder
.append(MEMCACHE_KEY_PREFIX
);
166 boolean first
= true;
167 for (String scope
: scopes
) {
174 builder
.append(scope
);
178 return builder
.toString();
182 public GetAccessTokenResult
getAccessToken(Iterable
<String
> scopes
) {
183 MemcacheService memcache
= MemcacheServiceFactory
.getMemcacheService(MEMCACHE_NAMESPACE
);
184 String memcacheKey
= memcacheKeyForScopes(scopes
);
185 GetAccessTokenResult result
;
186 Object memcacheResult
= memcache
.get(memcacheKey
);
187 if (memcacheResult
!= null) {
188 result
= (GetAccessTokenResult
) memcacheResult
;
190 result
= getAccessTokenUncached(scopes
);
191 Date memcacheExpiration
= new Date(result
.getExpirationTime().getTime() - 300000);
192 memcache
.put(memcacheKey
, result
, Expiration
.onDate(memcacheExpiration
));
198 public ParsedAppId
parseFullAppId(String fullAppId
) {
199 int partitionIdx
= fullAppId
.indexOf(APP_PARTITION_SEPARATOR
);
201 if (partitionIdx
> 0) {
202 partition
= fullAppId
.substring(0, partitionIdx
);
203 fullAppId
= fullAppId
.substring(partitionIdx
+ 1);
208 int domainIdx
= fullAppId
.indexOf(APP_DOMAIN_SEPARATOR
);
211 domain
= fullAppId
.substring(0, domainIdx
);
212 fullAppId
= fullAppId
.substring(domainIdx
+ 1);
217 return new ParsedAppId(partition
, domain
, fullAppId
);