This is a quick "how to" for how Long found the information he needed to build the cf info plugin.
Following a trail of breadcrumbs
Determine the requirements:
- Print the currently targeted org and space
- Print the API version and endpoint
- Print the user id of the current user (similar to
whoami
in *nix OSes).
Fulfilling the requirements:
This information comes from cf target
, so first we’ll take a look in target.go
.
- Line 83 in
target.go
prints the current session information as reported fromui.go
. - Line 196 in
ui.go
references aUserEmail
method. - Line 175 of
ui.go
shows thatconfig
is actually from thecore_config
package. - There are a few *.go files in the
core_config
package, but searching this repository forUserEmail
shows that the method is defined inconfig_repository.go
on lines 199-204. UserEmail
requires a struct, calledConfigRepository
.ConfigRepository
is built from theNewRepositoryFromPersistor
method. (You can tell this both by thereturn &ConfigRepository
on line 23 or by noting thatNewRepositoryFromPersistor
returns all the fields needed by the struct – i.e.data
,mutex
,initOnce
,persistor
, andonError
.)NewRepositoryFromPersistor
is returned from the method above it,NewRepositoryFromFilepath
.
How to get the file path? Searching for CF_HOME
(environmental variable storing the home directory) shows that it is defined in config_helpers.go
. Now what to do with that information:
- The
DefaultFilePath
is defined using either$CF_HOME/.cf
or$HOME/.cf
($HOME
is the user’s home directory in Unix environments). - As a sanity check, if you are using a Unix environment run
echo $CF_HOME
. If it is non-empty, then runless $CF_HOME/.cf/config.json
. If it is empty, runless $HOME/.cf/config.json
. You should see the configuration file thatcf target
uses to report the org, space, and API information (there is other information in there as well). - Recall that
UserEmail
is defined in lines 199-204 ofconfig_repository.go
. You can see thatconfig_repository.go
also has the other information we wish to pull – the org (OrganizationFields
), space (SpaceFields
), API endpoint (ApiEndpoint
), and API version (ApiVersion
).
What to do with all of that
Basically everything in reverse. You supply the default file path, which is config.json
, to NewRepositoryFromFilepath
. config.json
has the org, space, API endpoint, API version, and Access Token. The existing cf
code decodes the Access Token and the UserEmail
method extracts the email address from that information. Everything is just pulled directly from the repository you just defined.
Addendum: What is going on to get that email anyway?
Let’s take a quick look in config.json
. The 7th line should be the Access Token, which should look similar to this:
"AccessToken": "bearer eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI3NzI2ZDE2MS1hNWUzLTQwZjMtYTEzYy00OTlmMDNjOTBhZGIiLCJzdWIiOiI2Y2I5MTA0Yy1hZTMxLTQxYTMtOGQ4MS1jYjUxZjg0MTk5ZTMiLCJzY29wZSI6WyJjbG91ZF9jb250cm9sbGVyLmFkbWluIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwiY2xvdWRfY29udHJvbGxlci53cml0ZSIsIm9wZW5pZCIsInBhc3N3b3JkLndyaXRlIiwic2NpbS5yZWFkIiwic2NpbS53cml0ZSJdLCJjbGllbnRfaWQiOiJjZiIsImNpZCI6ImNmIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjZjYjkxMDRjLWFlMzEtNDFhMy04ZDgxLWNiNTFmODQxOTllMyIsInVzZXJfbmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbiIsImlhdCI6MTQxNzgxMTU3NywiZXhwIjoxNDE3ODEyMTc3LCJpc3MiOiJodHRwczovL3VhYS41NC4xNzQuMjI5LjExOS54aXAuaW8vb2F1dGgvdG9rZW4iLCJhdWQiOlsic2NpbSIsIm9wZW5pZCIsImNsb3VkX2NvbnRyb2xsZXIiLCJwYXNzd29yZCJdfQ.XNaYq8rxpvwWx9kySIDqbKs0BuyeOMMwAPb5YQaT-9MIyr3YalCE_2gTg-fl0xulj4u-VoNme3OGZ2T3tFFUfBKgo3U7R_pl5OpcaetKslbvKtYpne7N30KMQySMqVVVooGqlReoI_n5m5O7ZIASiG8P1QtwuVrZPkPhbjsGfBE",
Source: The above is the access token from a now-destroyed TryCF instance.
This is a JSON Web Token (JWT). You can read about JWT here. For now, all I really care about is that its structure is <header>.<claims>.<signature>
. The <claims>
section of the above token is between the two periods:
eyJqdGkiOiI3NzI2ZDE2MS1hNWUzLTQwZjMtYTEzYy00OTlmMDNjOTBhZGIiLCJzdWIiOiI2Y2I5MTA0Yy1hZTMxLTQxYTMtOGQ4MS1jYjUxZjg0MTk5ZTMiLCJzY29wZSI6WyJjbG91ZF9jb250cm9sbGVyLmFkbWluIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwiY2xvdWRfY29udHJvbGxlci53cml0ZSIsIm9wZW5pZCIsInBhc3N3b3JkLndyaXRlIiwic2NpbS5yZWFkIiwic2NpbS53cml0ZSJdLCJjbGllbnRfaWQiOiJjZiIsImNpZCI6ImNmIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjZjYjkxMDRjLWFlMzEtNDFhMy04ZDgxLWNiNTFmODQxOTllMyIsInVzZXJfbmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbiIsImlhdCI6MTQxNzgxMTU3NywiZXhwIjoxNDE3ODEyMTc3LCJpc3MiOiJodHRwczovL3VhYS41NC4xNzQuMjI5LjExOS54aXAuaW8vb2F1dGgvdG9rZW4iLCJhdWQiOlsic2NpbSIsIm9wZW5pZCIsImNsb3VkX2NvbnRyb2xsZXIiLCJwYXNzd29yZCJdfQ
If you paste that information into Base 64 Decode, you can see the user’s credentials – including the email field. (For TryCF the user_name
and email
are both admin
.) If you paste in the above and decode it, you will see:
{"jti":"7726d161-a5e3-40f3-a13c-499f03c90adb","sub":"6cb9104c-ae31-41a3-8d81-cb51f84199e3","scope":["cloud_controller.admin","cloud_controller.read","cloud_controller.write","openid","password.write","scim.read","scim.write"],"client_id":"cf","cid":"cf","grant_type":"password","user_id":"6cb9104c-ae31-41a3-8d81-cb51f84199e3","user_name":"admin","email":"admin","iat":1417811577,"exp":1417812177,"iss":"https://uaa.54.174.229.119.xip.io/oauth/token","aud":["scim","openid","cloud_controller","password"]}
Feel free to try it with your own Access Token! 🙂