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
whoamiin *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.goprints the current session information as reported fromui.go. - Line 196 in
ui.goreferences aUserEmailmethod. - Line 175 of
ui.goshows thatconfigis actually from thecore_configpackage. - There are a few *.go files in the
core_configpackage, but searching this repository forUserEmailshows that the method is defined inconfig_repository.goon lines 199-204. UserEmailrequires a struct, calledConfigRepository.ConfigRepositoryis built from theNewRepositoryFromPersistormethod. (You can tell this both by thereturn &ConfigRepositoryon line 23 or by noting thatNewRepositoryFromPersistorreturns all the fields needed by the struct – i.e.data,mutex,initOnce,persistor, andonError.)NewRepositoryFromPersistoris 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
DefaultFilePathis defined using either$CF_HOME/.cfor$HOME/.cf($HOMEis 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 targetuses to report the org, space, and API information (there is other information in there as well). - Recall that
UserEmailis defined in lines 199-204 ofconfig_repository.go. You can see thatconfig_repository.goalso 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! 🙂