1

I'm trying to submit a new print job to Google Cloud Print from an App Engine app (Java).

In the Google Cloud Print settings for the printer, I set "can print" permissions to {{MY_APP_ID}}@appspot.gserviceaccount.com

Then, from the App Engine app I run the following code-

AppIdentityCredential credential = new AppIdentityCredential(
   Arrays.asList("https://www.googleapis.com/auth/cloudprint"));
HttpRequestFactory requestFactory =
   new UrlFetchTransport().createRequestFactory(credential);

Map<String, String> params = new LinkedHashMap<String, String>();
params.put("printerid", "{{PRINTER_ID}}");
params.put("title", "Title");
params.put("ticket", "{\"version\":\"1.0\", \"print\":{}}");
params.put("content", "<!DOCTYPE html>\n<html>\n<body>\nHello world!\n</body>\n</html>");
params.put("contentType", "text/html");

HttpRequest httpRequest = requestFactory.buildPostRequest(
   new GenericUrl("https://www.google.com/cloudprint/submit"),
   new UrlEncodedContent(params));

HttpResponse httpResponse = httpRequest.execute();
try {
    System.out.println(httpResponse.parseAsString());
} finally {
    httpResponse.ignore();
}

Google Cloud Print's API returns a response with success=false and message="User is not authorized."

However, it seems that it does recognize the correct user as the response's users field is indeed ["{{MY_APP_ID}}@appspot.gserviceaccount.com"].

What am I doing wrong? Is there any way to have an App Engine app print to Google Cloud Print with the app's own permissions?

dleshem
  • 43
  • 5
  • This API might not yet support Service Accounts. Try using a user token instead of app credentials and see if the submitting the job works (with the same parameters). – alex Apr 20 '14 at 11:45

3 Answers3

2

The above example code doesn't send the authentication string in the header which is throwing the unauthorized error message - the following modification and update to Oauth 2 works with a printer shared with a service account:

    private static final String APPLICATION_NAME = "my-cloud-print";
    private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
    private static final String KEY_FILE_LOCATION = "file.p12";
    private static final String SERVICE_ACCOUNT_EMAIL = "12345@developer.gserviceaccount.com";

    public static void main( String args[] ){

        try{

            HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();

            //service login
            GoogleCredential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(JSON_FACTORY)
                .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
                .setServiceAccountPrivateKeyFromP12File(new File(KEY_FILE_LOCATION))
                .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/cloudprint"))
                .build();

            //update to fetch token
            credential.refreshToken();
            String accessToken = credential.getAccessToken();

            Map<String, String> params = new LinkedHashMap<String, String>();
            params.put("printerid", "0000000-f960-1d6b-077f-750e5b45fbdd");
            params.put("title", "Title");
            params.put("ticket", "{\"version\":\"1.0\", \"print\":{}}");
            params.put("content", "<!DOCTYPE html>\n<html>\n<body>\nHello world!\n</body>\n</html>");
            params.put("contentType", "text/html");

            HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
            HttpRequest httpRequest = requestFactory.buildPostRequest(
               new GenericUrl("https://www.google.com/cloudprint/submit"),
               new UrlEncodedContent(params)
            );

            //important bit - must send the auth token to print service
            //after you process the invitation from the share 
            //see http://stackoverflow.com/questions/19767752
            httpRequest.getHeaders().set("Authorization", Arrays.asList( ("Bearer " + accessToken).split(",") ));

            HttpResponse httpResponse = httpRequest.execute();
            try {
                System.out.println(httpResponse.parseAsString());
            } finally {
                httpResponse.ignore();
            }
        } catch( Exception ex ){
            ex.printStackTrace();
        }

    }
Phil.Ng
  • 116
  • 8
0

TL;DR- https://stackoverflow.com/a/19951913/929444

Long version-

When you grant someone permissions to a Google Cloud Print printer, Google emails them for approval. Of course, Google's UI does not indicate it in any way. Since the AppEngine app user cannot press the email link, there really isn't a straight-forward way to do what I wanted.

A workaround, as suggested in the link above, is to create a Google Group that contains the AppEngine app and another regular user. Whenever someone adds the group, the regular user approves and the app will be granted permissions implicitly as part of the group.

Community
  • 1
  • 1
dleshem
  • 43
  • 5
0

Create a google group. Add members by direct invite:

  1. Your gmail account (gmail email address)

  2. Your google service account from which access token was furnished (service account email address)

Then follow the below steps:

  1. Go to google.com/cloudprint
  2. Select your printer and click Share tab. Add following members under invite people:

    -google service account email address

    -google group email address ( eg, xyz@googlegroups.com)

Then try calling GCP APIs. It will work perfectly.