Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Getting Started With Predix - A Builders Journey - Episode 1

19 Jan 2017 1  
In this first episode we'll tackle the first thing, security. We are at the start of a journey. We'll learn about many Predix services and components. We'll find the good, the bad and the ugly, and we'll complain the whole way. After security we'll hit Asset and Analytics.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Predix is GE’s application platform for the Industrial Internet. Built on Cloud Foundry, Predix is optimized for secure connectivity and analytics at scale - in the Cloud and on the Edge.

"The test of the machine is the satisfaction it gives you. There isn't any other test." wrote Pirsig. This episode is dedicated to increasing your satisfaction with Predix by helping you smoothly wire up services. Hi, I'm Rich Dost and I'm blogging while building an application on Predix.

In this first episode we'll tackle the first thing, security. We are at the start of a journey. We'll learn about many Predix services and components. We'll find the good, the bad and the ugly, and we'll complain the whole way. After security we'll hit Asset and Analytics. Then on we'll go from there.

Should you care? Should you read further? "A good workman is known by his tools". Our tool is Predix. We'll make Predix sing, and we'll do it together. Is that enough?

Ready to code the industrial internet? Sign up for a free trial here.

About Rich

THEY say "Bragging will convince you"... I've been a developer for thirty years now, and I've improved. I'm better now than a couple years ago. Then I was better than a few years earlier. And so on, back thirty years. The brain is plastic, and mine is optimized for development. It doesn't happen automatically. It only happens if you care about Quality.

At GE I've helped develop Predix Asset. For my first splash I rewrote the entire UI in the evenings. When revealed, the new UI garnered much applause, and some emnity too, and praise from THEM. Our reference customer found it "Awesome!". A peer declared me "GE's coolest new hire ever!" And I received a special GE award for "...ability to think outside the box, solve hard problems... ideas and delivery set you apart...". Nice stuff.

The next year I completed 25% of the stories, as one of 18 in the group, while leading the successful UI effort, meeting a schedule the principle UI architect had declaimed as impossibly demanding. That was recognized too. "You are an army!" stated one fellow developer.

Last year, things of Quality I produced included the Asset Scripting Engine, inspired by my love of Lambda, and patented by GE. It felt the best. Yet Spotlight (image below), a whiz-bang, quick and dirty D3 based tool for GEL query visualization in asset networks, done in a few evenings, gained the most notice. Eye candy works.

Now I'm somehow a Predix Builder. How did that happen? Is it a trick to get me to write this blog?

Most importantly for your entertainment, I'm iconoclastic, which is a fancy way of saying I'm a trouble maker. I get in hot water, because I care about Quality, and that is good too. But combining it with this blog will be the end of me. Won't that be fun to watch?

Setting Up Security

So let's start our journey to build a Predix application! Exciting is the prospect of creating value! All sorts of ideas spring to mind. But before creating any value we need to deal with security.

Setting up security is the subject of this blog post. Predix takes security seriously, which is hard to argue with these days. Security must be set up before anything else, and it is easy to get in trouble doing so. That's unfortunate and ironic because, once past security, Predix is easy to use.

There is a vignette in "Zen and the Art of Motorcycle Maintenance". Phaedrus has his detailed plan. His thinking is way down the road. But as he begins he can't even get the engine cover off! It upsets him. He makes mistakes and loses time. There's Zen there. I hope to save you from Phaedrus's experience.

At project start we want only the steps necessary to start. That is not what some expect, but it is how agile works. Setting up a UAA bound to one Predix microservice will suffice. First we'll go through the steps. Later we'll create an script framework extensible enough to add other microservices.

The Commands

After the commands below, code running locally on your laptop will be able to reach Predix Asset. Predix Asset is the microservice we'll use. In my experience it's a sensible place to start.

Cloud Foundry Login

Where should credentials be stored? Some people put them in scripts, and then accidently push them to github! That can result in a large bill. Personally I like to store them in a separate file. We will worry about that later when we script.

Let's also work in a clean directory. "Cleanliness is next to godliness", and this helps later when we push a dummy app.

1> mkdir ~/tempForBlog1; cd ~/tempForBlog1
2> cf login # with your credentials

Create UAA Service

Predix Asset, like all Predix services, requires a bound UAA. The Asset microservice authenticates your token by communicating with the UAA when you do a request. Let's create the UAA. Important: You need to remember the UAA secret, in this case "my-secret". Don't lose it or you'll have to redo the process. You'll also need the service name, which below is my-uaa. But if you forget that it can easily be recovered with the bash command > cf s.

3> cf create-service predix-uaa Tiered my-uaa -c '{"adminClientSecret":"my-secret"}'

Get UAA Vitals

We need to get the environment variables of our new UAA. One way to accomplish that is to bind to a dummy application. Here we create a simple empty html file to serve as our dummy app. The environment variables are output from the cf env command below. The two we'll need are the URL and the issuerId. More on them below when we actually need them.

App names must be unique because the app URLs follow the pattern appname..., and URLs must be unique. Here we use the whoami command to generate a unique name. If whoami doesn't work for you, just put your initials in place of whoami.

4> appname=`whoami`.temp.html # need unique name
5> touch $appname
6> cf push --no-start $appname
7> cf bs $appname my-uaa 
8> cf env $appname # Output is environment variables
9> cf delete $appname --f

Create Asset Service

We are ready to create asset. The URL of the UAA was output above from the cf env command. You'll find it as the right hand side of the VCAP_SERVICES.predix-uaa.issuerId key. It should include the string "predix-uaa". Put the URL you find as the single member of the array on the right hand side of the "trustedIssuerIds" key below, replacing the existing URL. Then run the command.

10> cf create-service predix-asset Tiered my-asset -c '{"trustedIssuerIds":["https://2edfb28e-e02c-4ae9-a7cd-818c263128d8.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token"]}'

Get Asset Vitals

Again we need the environment variables, this time for Asset. The commands are similar to those for we used for the UAA above. We need the VCAP_SERVICES.predix-asset.instanceId value output from the cf env command below.

11> touch $appname
12> cf push --no-start $appname
13> cf bs $appname my-asset
14> cf env $appname

UAAC Targets UAA

If you haven't installed the UAAC, do so now.

The URL in the UAAC target command is the base URL of the UAA. It's the same URL we used above in command 10 (cf create-service predix-asset...), except we need to truncate the "/oauth/token" from the end. Then fetch a token into the UAA.

15> uaac target https://2edfb28e-e02c-4ae9-a7cd-818c263128d8.predix-uaa.run.aws-usw02-pr.ice.predix.io
16> uaac token client get admin --secret my-secret # gets the admin token, NOT usable for asset

Add UAAC Client

We need to use the instance id of Asset, sometimes called the Predix zone id. It's the VCAP_SERVICES.predix-asset.instanceId value from the output of command 14 (cf env...) above. Replace the UUIDs in command 17 (uaac client add asset-client...) between the "predix-asset.zones" and the ".user". There are two occurences to replace. It's easy to use the instanceId of the wrong service, which results in trauma.

17> uaac client add asset-client --authorities openid,uaa.none,uaa.resource,predix-asset.zones.6827f028-5b6b-439e-ae08-9587cedb6f56.user --scope uaa.none,openid,predix-asset.zones.6827f028-5b6b-439e-ae08-9587cedb6f56.user --autoapprove openid --authorized_grant_types authorization_code,client_credentials,refresh_token,password --secret my-secret --name asset-client

Get Then Display The Token

Run the command below and look for the "access_token" key. It's value is the token. We'll need to prepend the string "bearer " to it before using it with Asset. When the token expires repeat these two commands.

18> uaac token client get asset-client --secret my-secret # if token expires then do this again
19> uaac context

Validation By Talking To Asset

The output to the 13> cf env above, under "Get Asset Vitals", included the URL of asset. Look for an URI value for the VCAP_SERVICES.predix-asset[0].uri. Use that URL to hit Asset.

curl https://predix-asset.run.aws-usw02-pr.ice.predix.io

You should see a message with ..."PA_TENANT_NOT_PRESENT","message":"The predix-zone-id header was not provided... If not, you are not reaching Asset. Check the URL. The message means we have to add the predix-zone-id. That's the VCAP_SERVICES.predix-asset[0].instanceId value from above. Let's add it to the curl.

curl -H "predix-zone-id: 5aacd7de-f6da-42f6-8e3f-6c6c6dd595fd" https://predix-asset.run.aws-usw02-pr.ice.predix.io

Asset still won't talk, muttering something about "Authorization header is missing". Whenever you access the microservice you supply a token. Behind the scenes the microservice checks with the UAA to authenticate the token. The token goes in an "Authorization" header. Use 17> uaac context to get the token. Then prepend "bearer". It's big and ugly but you can do it.

curl -H "predix-zone-id: 5aacd7de-f6da-42f6-8e3f-6c6c6dd595fd" -H "Authorization: bearer eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI1MzI5NjhlZS0yODgzLTQ2MTUtOGQ4Ny05OTI1NzhiZDcyNjAiLCJzdWIiOiJhZG1pbiIsInNjb3BlIjpbImNsaWVudHMucmVhZCIsInpvbmVzLjFmNmNkZGJkLTMzZTMtNDYwMy1iYzhmLTllYzVmNTMyM2FhNS5hZG1pbiIsImNsaWVudHMuc2VjcmV0IiwiaWRwcy53cml0ZSIsInVhYS5yZXNvdXJjZSIsImNsaWVudHMud3JpdGUiLCJjbGllbnRzLmFkbWluIiwiaWRwcy5yZWFkIiwic2NpbS53cml0ZSIsInNjaW0ucmVhZCJdLCJjbGllbnRfaWQiOiJhZG1pbiIsImNpZCI6ImFkbWluIiwiYXpwIjoiYWRtaW4iLCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwicmV2X3NpZyI6IjFjODQ5ZTE1IiwiaWF0IjoxNDcyMTU2Mzc5LCJleHAiOjE0NzIxOTk1NzksImlzcyI6Imh0dHBzOi8vMWY2Y2RkYmQtMzNlMy00NjAzLWJjOGYtOWVjNWY1MzIzYWE1LnByZWRpeC11YWEucnVuLmF3cy11c3cwMi1wci5pY2UucHJlZGl4LmlvL29hdXRoL3Rva2VuIiwiemlkIjoiMWY2Y2RkYmQtMzNlMy00NjAzLWJjOGYtOWVjNWY1MzIzYWE1IiwiYXVkIjpbImFkbWluIiwiY2xpZW50cyIsInpvbmVzLjFmNmNkZGJkLTMzZTMtNDYwMy1iYzhmLTllYzVmNTMyM2FhNSIsImlkcHMiLCJ1YWEiLCJzY2ltIl19.PefjwSI-x43lZebZRBxJ93IrSxz9Ha9rgpILCUI41th-i4G3PGqatjckAgdgdtpcVsT4sLs_nYZQxqHstiH5_B2Bs6crmhTkr1-owAGhL3U_hB4T5Jia3ahHHnsTjE768Nq6pw-pL6KvgZkcf5RiwoUtaKmSrtpeziUw8RLE-JJFNq-rTWd_yNh7YqzjzsxNuZ56vufRWq2KLhZEddwGAr_9EFQ4g2bDypmC5xrpFzB2CxGIx-5WDklMdYH-9TJ5BYt06gCh0ctd1ryWhV3gSs4-QM2D1w7Nvub4m9yFYFaJ5_6EizAMkTASOPLaF8JaWxo_pADf66_DgU-eH2if7Q" https://predix-asset.run.aws-usw02-pr.ice.predix.io

If you did everything right, you will be rewarded with a return value of []. I told you this blog was worthy of your time. You got an empty array for your troubles!

Seriously, that is Asset's way of telling you there is nothing in the database. Let's post an object. We'll add a "Content-Type: Application/json" header, and a JSON object surrounded by an array. For posts, Asset requires the URL end with the collection which must match the URI property of the object. The collection gets created by the post so it's a nice, one step process. We don't need a schema etc. For this example we'll create resource /tests/my-first-test containing some random data.

For brevity we'll use "" instead of the big ugly below. You'll still need to use your real token, of course, or you'll get a permission error.

curl -X POST -d '[{ "uri": "/tests/my-first-test", "foo": true, "bar": [34,56] }]' -H "Content-Type: application/json" -H "predix-zone-id: 87af97a0-c4a9-49fd-a002-240aacb3c540" -H "Authorization: <BEARER-TOKEN>" https://predix-asset.run.aws-usw02-pr.ice.predix.io/tests

Asset won't reply with much except a status. So let's get our resource back. You should see the javascript object that was posted.

curl -H "predix-zone-id: 87af97a0-c4a9-49fd-a002-240aacb3c540" -H "Authorization: <BEARER-TOKEN>" https://predix-asset.run.aws-usw02-pr.ice.predix.io/tests/my-first-test

And completing the circle let's get the base page again. Last time an empty array was returned. Now there should be more.

curl -H "predix-zone-id: 5aacd7de-f6da-42f6-8e3f-6c6c6dd595fd" -H "Authorization: <BEARER-TOKEN>" https://predix-asset.run.aws-usw02-pr.ice.predix.io

You should see [{"collection":"tests","count":1}]. That means there is one collection named tests with one resource in it.

Troubleshooting

Decoding The Token

The most common mistake is to use the wrong token. The token contains encoded information. You can decode the token using the UAAC token decode command. Some prefer to use the jwt.io website. Verify the aud and sub in particular.

>uaac token decode

Note: no key given to validate token signature

  jti: 50bff558-ff1f-4022-a365-2910d43e843f
  sub: my-asset-client
  scope: uaa.resource openid uaa.none predix-asset.zones.87af97a0-c4a9-49fd-a002-240aacb3c540.user
  client_id: my-asset-client
  cid: my-asset-client
  azp: my-asset-client
  grant_type: client_credentials
  rev_sig: b35a26d7
  iat: 1472492881
  exp: 1472536081
  iss: https://15c87001-2030-424d-99d8-5778cd314f46.predix-uaa.run.aws-usw02-pr.ice.predix.io/oauth/token
  zid: 15c87001-2030-424d-99d8-5778cd314f46
  aud: my-asset-client uaa openid predix-asset.zones.87af97a0-c4a9-49fd-a002-240aacb3c540

Retrieving InstanceIds

You can retrieve your instanceIds using the cf service command.

>cf s
Getting services in org dost@ge.com / space dev as dost@ge.com...
OK

name       service        plan     bound apps   last operation   
my-asset   predix-asset   Tiered                create succeeded   
my-uaa     predix-uaa     Tiered                create succeeded   
>cf service my-uaa --guid
15c87001-2030-424d-99d8-5778cd314f46
>cf service my-asset --guid
87af97a0-c4a9-49fd-a002-240aacb3c540
>uaac token get client my-asset-client--secret my-secret
>uaac token get client my-asset-client --secret my-secret

Tracing CF Commands

You can learn what cf commands do under the hood using CF_TRACE. Try a command without then with tracing.

>cf service my-asset
>CF_TRACE=true cf service my-asset

Tracing UAAC Commands

The UAAC hits UAA REST endpoints to do its work. Some people prefer to work directly that way. To see the REST calls that the UAAC makes to the UAA, simply add the --trace command option. Below we do uaac groups first without then with --trace.

>uaac groups

error response:
{
  "error": "insufficient_scope",
  "error_description": "Insufficient scope for this resource",
  "scope": "scim.read zones.15c87001-2030-424d-99d8-5778cd314f46.admin"
}
>uaac --trace groups
--->
request: get https://15c87001-2030-424d-99d8-5778cd314f46.predix-uaa.run.aws-usw02-pr.ice.predix.io/Groups?startIndex=1
headers: {"authorization"=>"bearer eyJhbGc... ...OVejRJfw", "accept"=>"application/json;charset=utf-8"}
<---
response: 403
headers: {"cache-control"=>"no-cache, no-store, max-age=0, must-revalidate, no-store", "content-type"=>"application/json;charset=utf-8", "correlation-id"=>"f8924b82-e0d6-404c-a89a-c36b1f6e6616", "date"=>"Mon, 29 Aug 2016 21:04:03 GMT", "expires"=>"0", "pragma"=>"no-cache, no-cache", "server"=>"Apache-Coyote/1.1", "strict-transport-security"=>"max-age=31536000 ; includeSubDomains", "www-authenticate"=>"Bearer error=\"insufficient_scope\", error_description=\"Insufficient scope for this resource\", scope=\"scim.read zones.15c87001-2030-424d-99d8-5778cd314f46.admin\"", "x-content-type-options"=>"nosniff", "x-frame-options"=>"DENY", "x-vcap-request-id"=>"eb1fb8b7-1188-4417-537d-511361d56830", "x-xss-protection"=>"1; mode=block", "content-length"=>"158", "connection"=>"Close"}
body: {"error":"insufficient_scope","error_description":"Insufficient scope for this resource","scope":"scim.read zones.15c87001-2030-424d-99d8-5778cd314f46.admin"}
error response:
{
  "error": "insufficient_scope",
  "error_description": "Insufficient scope for this resource",
  "scope": "scim.read zones.15c87001-2030-424d-99d8-5778cd314f46.admin"
}

Related Resources

Here are resources that I've found valuable for understanding the security system.

Stay Tuned

In the near future we'll have a formation of many Predix services, all working together. Setting it up using manual cf commands would be far too error prone. Serious development requires repeatability and automation. In a future episode we'll create an extensible script framework to meet this need.

You can register for a Predix account here, and begin your industrial internet journey at Predix.io.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here