Version 1.7.4
[gae.git] / java / src / main / com / google / appengine / api / appidentity / AppIdentityServiceImpl.java
blob75636a500a6ce58b75f60d74896cfad14e6ee83f
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;
25 /**
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())) {
50 case BLOB_TOO_LARGE:
51 throw new AppIdentityServiceFailureException(e.getErrorDetail());
52 case NOT_A_VALID_APP:
53 throw new AppIdentityServiceFailureException(e.getErrorDetail());
54 case DEADLINE_EXCEEDED:
55 throw new AppIdentityServiceFailureException(e.getErrorDetail());
56 case UNKNOWN_ERROR:
57 throw new AppIdentityServiceFailureException(e.getErrorDetail());
58 case UNKNOWN_SCOPE:
59 throw new AppIdentityServiceFailureException(e.getErrorDetail());
60 default:
61 throw new AppIdentityServiceFailureException(e.getErrorDetail());
65 @Override
66 public List<PublicCertificate> getPublicCertificatesForApp() {
67 GetPublicCertificateForAppRequest.Builder requestBuilder =
68 GetPublicCertificateForAppRequest.newBuilder();
69 GetPublicCertificateForAppResponse.Builder responseBuilder =
70 GetPublicCertificateForAppResponse.newBuilder();
72 try {
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()));
87 return certs;
90 @Override
91 public SigningResult signForApp(byte[] signBlob) {
92 SignForAppRequest.Builder requestBuilder = SignForAppRequest.newBuilder();
93 requestBuilder.setBytesToSign(ByteString.copyFrom(signBlob));
94 SignForAppResponse.Builder responseBuilder = SignForAppResponse.newBuilder();
95 try {
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());
109 @Override
110 public String getServiceAccountName() {
111 GetServiceAccountNameRequest.Builder requestBuilder = GetServiceAccountNameRequest.newBuilder();
112 GetServiceAccountNameResponse.Builder responseBuilder =
113 GetServiceAccountNameResponse.newBuilder();
114 try {
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();
129 @Override
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();
139 try {
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);
165 builder.append("[");
166 boolean first = true;
167 for (String scope : scopes) {
168 if (first) {
169 first = false;
170 } else {
171 builder.append(",");
173 builder.append("'");
174 builder.append(scope);
175 builder.append("'");
177 builder.append("]");
178 return builder.toString();
181 @Override
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;
189 } else {
190 result = getAccessTokenUncached(scopes);
191 Date memcacheExpiration = new Date(result.getExpirationTime().getTime() - 300000);
192 memcache.put(memcacheKey, result, Expiration.onDate(memcacheExpiration));
194 return result;
197 @Override
198 public ParsedAppId parseFullAppId(String fullAppId) {
199 int partitionIdx = fullAppId.indexOf(APP_PARTITION_SEPARATOR);
200 String partition;
201 if (partitionIdx > 0) {
202 partition = fullAppId.substring(0, partitionIdx);
203 fullAppId = fullAppId.substring(partitionIdx + 1);
204 } else {
205 partition = "";
208 int domainIdx = fullAppId.indexOf(APP_DOMAIN_SEPARATOR);
209 String domain;
210 if (domainIdx > 0) {
211 domain = fullAppId.substring(0, domainIdx);
212 fullAppId = fullAppId.substring(domainIdx + 1);
213 } else {
214 domain = "";
217 return new ParsedAppId(partition, domain, fullAppId);