From 94f2f9306bd859880bc70461d1a97cd5dd3265e4 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 23 May 2017 17:31:31 -0700 Subject: [PATCH 1/3] Changes script to use Java for IAM. --- iot/api-client/README.md | 2 +- .../cloud/iot/examples/MqttExample.java | 53 +++++--- iot/api-client/scripts/README.md | 33 +++++ iot/api-client/scripts/README.rst | 115 ------------------ iot/api-client/scripts/README.rst.in | 22 ---- iot/api-client/scripts/iam.py | 57 --------- iot/api-client/scripts/pom.xml | 76 ++++++++++++ iot/api-client/scripts/requirements.txt | 1 - .../example/pubsub/AddCloudIotService.java | 56 +++++++++ 9 files changed, 199 insertions(+), 216 deletions(-) create mode 100644 iot/api-client/scripts/README.md delete mode 100644 iot/api-client/scripts/README.rst delete mode 100644 iot/api-client/scripts/README.rst.in delete mode 100644 iot/api-client/scripts/iam.py create mode 100644 iot/api-client/scripts/pom.xml delete mode 100644 iot/api-client/scripts/requirements.txt create mode 100644 iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java diff --git a/iot/api-client/README.md b/iot/api-client/README.md index 2c61fe001a9..c97d6bbe5e3 100644 --- a/iot/api-client/README.md +++ b/iot/api-client/README.md @@ -4,7 +4,7 @@ Google Cloud IoT Core platform. ## Quickstart -1. Install the gCloud CLI as described in [the device manager guide](https://cloud-dot-devsite.googleplex.com/iot/docs/device_manager_guide). +1. Install the gCloud CLI as described in [the device manager guide](https://cloud.google.com/iot/docs/device_manager_guide). 2. Create a PubSub topic: gcloud beta pubsub topics create projects/my-iot-project/topics/device-events diff --git a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java index 09528953587..8eb34750586 100644 --- a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java +++ b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java @@ -39,17 +39,26 @@ * */ public class MqttExample { - /** Load a PKCS8 encoded keyfile from the given path. */ - private static PrivateKey loadKeyFile(String filename, String algorithm) throws Exception { - byte[] keyBytes = Files.readAllBytes(Paths.get(filename)); + /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key. */ + private static String createJwtRsa(String projectId, String privateKeyFile) throws Exception { + DateTime now = new DateTime(); + // Create a JWT to authenticate this device. The device will be disconnected after the token + // expires, and will have to reconnect with a new token. The audience field should always be set + // to the GCP project id. + JwtBuilder jwtBuilder = + Jwts.builder() + .setIssuedAt(now.toDate()) + .setExpiration(now.plusMinutes(20).toDate()) + .setAudience(projectId); + + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory kf = KeyFactory.getInstance(algorithm); - return kf.generatePrivate(spec); + KeyFactory kf = KeyFactory.getInstance("RSA256"); + + return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact(); } - /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key. */ - private static String createJwt(String projectId, String privateKeyFile, String algorithm) - throws Exception { + private static String createJwtEs(String projectId, String privateKeyFile) throws Exception { DateTime now = new DateTime(); // Create a JWT to authenticate this device. The device will be disconnected after the token // expires, and will have to reconnect with a new token. The audience field should always be set @@ -60,16 +69,11 @@ private static String createJwt(String projectId, String privateKeyFile, String .setExpiration(now.plusMinutes(20).toDate()) .setAudience(projectId); - if (algorithm.equals("RS256")) { - PrivateKey privateKey = loadKeyFile(privateKeyFile, "RSA"); - return jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey).compact(); - } else if (algorithm.equals("ES256")) { - PrivateKey privateKey = loadKeyFile(privateKeyFile, "EC"); - return jwtBuilder.signWith(SignatureAlgorithm.ES256, privateKey).compact(); - } else { - throw new IllegalArgumentException( - "Invalid algorithm " + algorithm + ". Should be one of 'RS256' or 'ES256'."); - } + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("ES256"); + + return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact(); } public static void main(String[] args) throws Exception { @@ -102,8 +106,17 @@ public static void main(String[] args) throws Exception { // Paho client library to send the password field. The password field is used to transmit a JWT // to authorize the device. connectOptions.setUserName("unused"); - connectOptions.setPassword( - createJwt(options.projectId, options.privateKeyFile, options.algorithm).toCharArray()); + + if (options.algorithm == "RSA256") { + connectOptions.setPassword( + createJwtRsa(options.projectId, options.privateKeyFile).toCharArray()); + } else if (options.algorithm == "ES256") { + connectOptions.setPassword( + createJwtEs(options.projectId, options.privateKeyFile).toCharArray()); + } else { + throw new IllegalArgumentException( + "Invalid algorithm " + options.algorithm + ". Should be one of 'RS256' or 'ES256'."); + } // Create a client, and connect to the Google MQTT bridge. MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence()); diff --git a/iot/api-client/scripts/README.md b/iot/api-client/scripts/README.md new file mode 100644 index 00000000000..4b7f53f377f --- /dev/null +++ b/iot/api-client/scripts/README.md @@ -0,0 +1,33 @@ +# Getting Started with Cloud Pub/Sub and the Google Cloud Client libraries + +[Google Cloud IoT Core](https://cloud.google.com/iot-core/) +is a fully-managed, globally distributed solution for managing devices and +sending / receiving messages from devices. + +This script manages the [Google Cloud Pub/Sub][pubsub] project associated with +your Google Cloud IoT Core project to grant permissions to the protocol bridge. + +Create your PubSub topic noting the project ID and topic ID, then build and run +the sample to configure your topic. + +[pubsub]: https://cloud.google.com/pubsub/ + +#### Setup + +* Install [Maven](http://maven.apache.org/) +* Build your project with: + + mvn clean compile assembly:single + +#### Running the script + +The following code will run the helper script: + + java -cp target/pubsub-google-cloud-samples-1.0.0-jar-with-dependencies.jar \ + com.example.pubsub.AddCloudIotService + +For example, the following example will configure the `device-events` topic +for the `my-iot-project` project. + + java -cp target/pubsub-google-cloud-samples-1.0.0-jar-with-dependencies.jar \ + com.example.pubsub.AddCloudIotService device-events my-iot-project diff --git a/iot/api-client/scripts/README.rst b/iot/api-client/scripts/README.rst deleted file mode 100644 index 2d48c4a9aba..00000000000 --- a/iot/api-client/scripts/README.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. This file is automatically generated. Do not edit this file directly. - -Google Cloud IoT Core Python Samples -=============================================================================== - -This directory contains samples for Google Cloud IoT Core. `Google Cloud IoT Core`_ is a fully-managed, globally distributed solution for managing devices and sending / receiving messages from devices. Set the `GOOGLE_CLOUD_PROJECT` environment variable and call the script with your topic ID. - - - - -.. _Google Cloud IoT Core: https://cloud.google.com/iot/ - -Setup -------------------------------------------------------------------------------- - - -Authentication -++++++++++++++ - -Authentication is typically done through `Application Default Credentials`_, -which means you do not have to change the code to authenticate as long as -your environment has credentials. You have a few options for setting up -authentication: - -#. When running locally, use the `Google Cloud SDK`_ - - .. code-block:: bash - - gcloud auth application-default login - - -#. When running on App Engine or Compute Engine, credentials are already - set-up. However, you may need to configure your Compute Engine instance - with `additional scopes`_. - -#. You can create a `Service Account key file`_. This file can be used to - authenticate to Google Cloud Platform services from any environment. To use - the file, set the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable to - the path to the key file, for example: - - .. code-block:: bash - - export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json - -.. _Application Default Credentials: https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow -.. _additional scopes: https://cloud.google.com/compute/docs/authentication#using -.. _Service Account key file: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount - -Install Dependencies -++++++++++++++++++++ - -#. Install `pip`_ and `virtualenv`_ if you do not already have them. - -#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. - - .. code-block:: bash - - $ virtualenv env - $ source env/bin/activate - -#. Install the dependencies needed to run the samples. - - .. code-block:: bash - - $ pip install -r requirements.txt - -.. _pip: https://pip.pypa.io/ -.. _virtualenv: https://virtualenv.pypa.io/ - -Samples -------------------------------------------------------------------------------- - -PubSub helper -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - - -To run this sample: - -.. code-block:: bash - - $ python iam.py - - usage: iam.py [-h] topic_name - - This application demonstrates how programatically grant access to the - Cloud IoT Core service account on a given PubSub topic. - - For more information, see https://cloud.google.com/iot. - - positional arguments: - topic_name The PubSub topic to grant Cloud IoT Core access to - - optional arguments: - -h, --help show this help message and exit - - - - -The client library -------------------------------------------------------------------------------- - -This sample uses the `Google Cloud Client Library for Python`_. -You can read the documentation for more details on API usage and use GitHub -to `browse the source`_ and `report issues`_. - -.. Google Cloud Client Library for Python: - https://googlecloudplatform.github.io/google-cloud-python/ -.. browse the source: - https://github.com/GoogleCloudPlatform/google-cloud-python -.. report issues: - https://github.com/GoogleCloudPlatform/google-cloud-python/issues - - -.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/iot/api-client/scripts/README.rst.in b/iot/api-client/scripts/README.rst.in deleted file mode 100644 index dfd077ff41b..00000000000 --- a/iot/api-client/scripts/README.rst.in +++ /dev/null @@ -1,22 +0,0 @@ -# This file is used to generate README.rst - -product: - name: Google Cloud IoT Core - short_name: Cloud IoT Core - url: https://cloud.google.com/iot/ - description: > - `Google Cloud IoT Core`_ is a fully-managed, globally distributed solution - for managing devices and sending / receiving messages from devices. Set the - `GOOGLE_CLOUD_PROJECT` environment variable and call the script with your - topic ID. - -setup: -- auth -- install_deps - -samples: -- name: PubSub helper - file: iam.py - show_help: true - -cloud_client_library: true diff --git a/iot/api-client/scripts/iam.py b/iot/api-client/scripts/iam.py deleted file mode 100644 index 43cd838b78c..00000000000 --- a/iot/api-client/scripts/iam.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2017 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""This application demonstrates how programatically grant access to the -Cloud IoT Core service account on a given PubSub topic. - -For more information, see https://cloud.google.com/iot. -""" - -import argparse - -from google.cloud import pubsub - - -def set_topic_policy(topic_name): - """Sets the IAM policy for a topic for Cloud IoT Core.""" - pubsub_client = pubsub.Client() - topic = pubsub_client.topic(topic_name) - policy = topic.get_iam_policy() - - # Add a group as publishers. - publishers = policy.get('roles/pubsub.publisher', []) - publishers.append(policy.service_account( - 'cloud-iot@system.gserviceaccount.com')) - policy['roles/pubsub.publisher'] = publishers - - # Set the policy - topic.set_iam_policy(policy) - - print('IAM policy for topic {} set.'.format(topic.name)) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter - ) - - subparsers = parser.add_argument(dest='topic_name', - help='The PubSub topic to grant Cloud IoT Core access to') - - args = parser.parse_args() - - set_topic_policy(args.topic_name) diff --git a/iot/api-client/scripts/pom.xml b/iot/api-client/scripts/pom.xml new file mode 100644 index 00000000000..d91f558eb9e --- /dev/null +++ b/iot/api-client/scripts/pom.xml @@ -0,0 +1,76 @@ + + + 4.0.0 + com.example.pubsub + pubsub-google-cloud-samples + jar + + + + doc-samples + com.google.cloud + 1.0.0 + ../../.. + + + + 1.8 + 1.8 + UTF-8 + 0.17.2-alpha + + + + + + maven-assembly-plugin + + + + com.example.pubsub.AddCloudIotService + + + + jar-with-dependencies + + + + + + + + + com.google.cloud + google-cloud-pubsub + ${pubsub.version} + + + + + junit + junit + 4.12 + test + + + com.google.truth + truth + 0.32 + test + + + diff --git a/iot/api-client/scripts/requirements.txt b/iot/api-client/scripts/requirements.txt deleted file mode 100644 index 1412eed3dca..00000000000 --- a/iot/api-client/scripts/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -google-cloud-pubsub==0.25.0 diff --git a/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java b/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java new file mode 100644 index 00000000000..5389536932f --- /dev/null +++ b/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java @@ -0,0 +1,56 @@ +/* + Copyright 2016, Google, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.example.pubsub; + +// [START cloudiotcore_add_service_account] +// Imports the Google Cloud client library +import com.google.cloud.Identity; +import com.google.cloud.Role; +import com.google.cloud.pubsub.spi.v1.TopicAdminClient; +import com.google.iam.v1.Binding; +import com.google.iam.v1.Policy; +import com.google.pubsub.v1.TopicName; + +public class AddCloudIotService { + + public static void main(String... args) throws Exception { + if (args.length < 2) { + System.out.printf("Usage:\n java %s \n\n", + AddCloudIotService.class.getCanonicalName()); + return; + } + + String topicId = args[0]; + String projectId = args[1]; // Could make optional, use: ServiceOptions.getDefaultProjectId(); + + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + String topicName = TopicName.create(projectId, topicId).toString(); + Policy policy = topicAdminClient.getIamPolicy(topicName); + // add role -> members binding + Binding binding = + Binding.newBuilder() + .setRole(Role.editor().toString()) + .addMembers(Identity.serviceAccount("cloud-iot@system.gserviceaccount.com") + .toString()) + .build(); + // Add Cloud IoT Core service account. + Policy updatedPolicy = Policy.newBuilder(policy).addBindings(binding).build(); + topicAdminClient.setIamPolicy(topicName, updatedPolicy); + } + } +} +// [END cloudiotcore_add_service_account] From 99b989edbb18245fabc10f8e65032b733ba2f670 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Wed, 24 May 2017 09:25:53 -0700 Subject: [PATCH 2/3] Fixes date. --- .../src/main/java/com/example/pubsub/AddCloudIotService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java b/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java index 5389536932f..7e1babf65b3 100644 --- a/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java +++ b/iot/api-client/scripts/src/main/java/com/example/pubsub/AddCloudIotService.java @@ -1,5 +1,5 @@ /* - Copyright 2016, Google, Inc. + Copyright 2017, Google, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 3fc3abd227eb146d78da0677b65ac0eb8333a7a3 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Wed, 24 May 2017 13:19:16 -0700 Subject: [PATCH 3/3] Feedback from review --- iot/api-client/scripts/README.md | 4 ++-- iot/api-client/scripts/pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iot/api-client/scripts/README.md b/iot/api-client/scripts/README.md index 4b7f53f377f..d15c79466f3 100644 --- a/iot/api-client/scripts/README.md +++ b/iot/api-client/scripts/README.md @@ -23,11 +23,11 @@ the sample to configure your topic. The following code will run the helper script: - java -cp target/pubsub-google-cloud-samples-1.0.0-jar-with-dependencies.jar \ + java -cp target/pubsub-policy-helper-1.0.0-jar-with-dependencies.jar \ com.example.pubsub.AddCloudIotService For example, the following example will configure the `device-events` topic for the `my-iot-project` project. - java -cp target/pubsub-google-cloud-samples-1.0.0-jar-with-dependencies.jar \ + java -cp target/pubsub-policy-helper-1.0.0-jar-with-dependencies.jar \ com.example.pubsub.AddCloudIotService device-events my-iot-project diff --git a/iot/api-client/scripts/pom.xml b/iot/api-client/scripts/pom.xml index d91f558eb9e..5c88c2afa46 100644 --- a/iot/api-client/scripts/pom.xml +++ b/iot/api-client/scripts/pom.xml @@ -16,7 +16,7 @@ 4.0.0 com.example.pubsub - pubsub-google-cloud-samples + pubsub-policy-helper jar