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
.apphosting
.api
.ApiProxy
;
17 import com
.google
.common
.collect
.Lists
;
18 import com
.google
.protobuf
.ByteString
;
19 import com
.google
.protobuf
.InvalidProtocolBufferException
;
21 import java
.util
.Date
;
22 import java
.util
.List
;
25 * Implementation of the AppIdentityService interface.
28 class AppIdentityServiceImpl
implements AppIdentityService
{
30 public static final String PACKAGE_NAME
= "app_identity_service";
32 public static final String SIGN_FOR_APP_METHOD_NAME
= "SignForApp";
34 public static final String GET_SERVICE_ACCOUNT_NAME_METHOD_NAME
= "GetServiceAccountName";
36 public static final String GET_CERTS_METHOD_NAME
= "GetPublicCertificatesForApp";
38 public static final String GET_ACCESS_TOKEN_METHOD_NAME
= "GetAccessToken";
40 public static final String MEMCACHE_NAMESPACE
= "_ah_";
42 public static final String MEMCACHE_KEY_PREFIX
= "_ah_app_identity_";
44 private static final char APP_PARTITION_SEPARATOR
= '~';
45 private static final char APP_DOMAIN_SEPARATOR
= ':';
47 private void handleApplicationError(ApiProxy
.ApplicationException e
) {
48 switch (AppIdentityServiceError
.ErrorCode
.valueOf(e
.getApplicationError())) {
50 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
52 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
53 case DEADLINE_EXCEEDED
:
54 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
56 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
58 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
60 throw new AppIdentityServiceFailureException(e
.getErrorDetail());
65 public List
<PublicCertificate
> getPublicCertificatesForApp() {
66 GetPublicCertificateForAppRequest
.Builder requestBuilder
=
67 GetPublicCertificateForAppRequest
.newBuilder();
68 GetPublicCertificateForAppResponse
.Builder responseBuilder
=
69 GetPublicCertificateForAppResponse
.newBuilder();
72 responseBuilder
.mergeFrom(
73 ApiProxy
.makeSyncCall(
74 PACKAGE_NAME
, GET_CERTS_METHOD_NAME
, requestBuilder
.build().toByteArray()));
75 } catch (ApiProxy
.ApplicationException e
) {
76 handleApplicationError(e
);
77 } catch (InvalidProtocolBufferException e
) {
78 throw new AppIdentityServiceFailureException(e
.getMessage());
80 GetPublicCertificateForAppResponse response
= responseBuilder
.build();
82 List
<PublicCertificate
> certs
= Lists
.newArrayList();
83 for (AppIdentityServicePb
.PublicCertificate cert
: response
.getPublicCertificateListList()) {
84 certs
.add(new PublicCertificate(cert
.getKeyName(), cert
.getX509CertificatePem()));
90 public SigningResult
signForApp(byte[] signBlob
) {
91 SignForAppRequest
.Builder requestBuilder
= SignForAppRequest
.newBuilder();
92 requestBuilder
.setBytesToSign(ByteString
.copyFrom(signBlob
));
93 SignForAppResponse
.Builder responseBuilder
= SignForAppResponse
.newBuilder();
95 responseBuilder
.mergeFrom(
96 ApiProxy
.makeSyncCall(
97 PACKAGE_NAME
, SIGN_FOR_APP_METHOD_NAME
, requestBuilder
.build().toByteArray()));
98 } catch (ApiProxy
.ApplicationException e
) {
99 handleApplicationError(e
);
100 } catch (InvalidProtocolBufferException e
) {
101 throw new AppIdentityServiceFailureException(e
.getMessage());
104 SignForAppResponse response
= responseBuilder
.build();
105 return new SigningResult(response
.getKeyName(), response
.getSignatureBytes().toByteArray());
109 public String
getServiceAccountName() {
110 GetServiceAccountNameRequest
.Builder requestBuilder
= GetServiceAccountNameRequest
.newBuilder();
111 GetServiceAccountNameResponse
.Builder responseBuilder
=
112 GetServiceAccountNameResponse
.newBuilder();
114 responseBuilder
.mergeFrom(
115 ApiProxy
.makeSyncCall(
116 PACKAGE_NAME
, GET_SERVICE_ACCOUNT_NAME_METHOD_NAME
,
117 requestBuilder
.build().toByteArray()));
118 } catch (ApiProxy
.ApplicationException e
) {
119 handleApplicationError(e
);
120 } catch (InvalidProtocolBufferException e
) {
121 throw new AppIdentityServiceFailureException(e
.getMessage());
124 GetServiceAccountNameResponse response
= responseBuilder
.build();
125 return response
.getServiceAccountName();
129 public GetAccessTokenResult
getAccessTokenUncached(Iterable
<String
> scopes
) {
130 GetAccessTokenRequest
.Builder requestBuilder
= GetAccessTokenRequest
.newBuilder();
131 for (String scope
: scopes
) {
132 requestBuilder
.addScope(scope
);
134 if (requestBuilder
.getScopeCount() == 0) {
135 throw new AppIdentityServiceFailureException("No scopes specified.");
137 GetAccessTokenResponse
.Builder responseBuilder
= GetAccessTokenResponse
.newBuilder();
139 responseBuilder
.mergeFrom(
140 ApiProxy
.makeSyncCall(
141 PACKAGE_NAME
, GET_ACCESS_TOKEN_METHOD_NAME
,
142 requestBuilder
.build().toByteArray()));
143 } catch (ApiProxy
.ApplicationException e
) {
144 handleApplicationError(e
);
145 } catch (InvalidProtocolBufferException e
) {
146 throw new AppIdentityServiceFailureException(e
.getMessage());
149 GetAccessTokenResponse response
= responseBuilder
.build();
150 return new GetAccessTokenResult(response
.getAccessToken(),
151 new Date(response
.getExpirationTime() * 1000));
154 private String
memcacheKeyForScopes(Iterable
<String
> scopes
) {
155 StringBuilder builder
= new StringBuilder();
156 builder
.append(MEMCACHE_KEY_PREFIX
);
158 boolean first
= true;
159 for (String scope
: scopes
) {
166 builder
.append(scope
);
170 return builder
.toString();
174 public GetAccessTokenResult
getAccessToken(Iterable
<String
> scopes
) {
175 MemcacheService memcache
= MemcacheServiceFactory
.getMemcacheService(MEMCACHE_NAMESPACE
);
176 String memcacheKey
= memcacheKeyForScopes(scopes
);
177 GetAccessTokenResult result
;
178 Object memcacheResult
= memcache
.get(memcacheKey
);
179 if (memcacheResult
!= null) {
180 result
= (GetAccessTokenResult
) memcacheResult
;
182 result
= getAccessTokenUncached(scopes
);
183 Date memcacheExpiration
= new Date(result
.getExpirationTime().getTime() - 300000);
184 memcache
.put(memcacheKey
, result
, Expiration
.onDate(memcacheExpiration
));
190 public ParsedAppId
parseFullAppId(String fullAppId
) {
191 int partitionIdx
= fullAppId
.indexOf(APP_PARTITION_SEPARATOR
);
193 if (partitionIdx
> 0) {
194 partition
= fullAppId
.substring(0, partitionIdx
);
195 fullAppId
= fullAppId
.substring(partitionIdx
+ 1);
200 int domainIdx
= fullAppId
.indexOf(APP_DOMAIN_SEPARATOR
);
203 domain
= fullAppId
.substring(0, domainIdx
);
204 fullAppId
= fullAppId
.substring(domainIdx
+ 1);
209 return new ParsedAppId(partition
, domain
, fullAppId
);