diff --git a/.secrets.baseline b/.secrets.baseline index 60e62cf3..cfc57f20 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2021-12-02T17:22:21Z", + "generated_at": "2021-12-06T21:18:44Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -188,10 +188,18 @@ ], "bluemix/configuration/core_config/bx_config_test.go": [ { - "hashed_secret": "9507a758af9127f99a700b500657fd558b705dc9", + "hashed_secret": "90919f1360ad1f9525de7a64061c105d061df1c8", "is_secret": false, "is_verified": false, - "line_number": 303, + "line_number": 377, + "type": "JSON Web Token", + "verified_result": null + }, + { + "hashed_secret": "40e89785a3d6a88c5ef431f64ec054b1d39e186e", + "is_secret": false, + "is_verified": false, + "line_number": 393, "type": "JSON Web Token", "verified_result": null } @@ -201,7 +209,7 @@ "hashed_secret": "e85f6eac7402c010fcea6b6d024a1875ac213f99", "is_secret": false, "is_verified": false, - "line_number": 221, + "line_number": 241, "type": "Secret Keyword", "verified_result": null } @@ -211,7 +219,7 @@ "hashed_secret": "8d8e2cec529a6f642c25b4473b2adc8cde563c36", "is_secret": false, "is_verified": false, - "line_number": 12, + "line_number": 18, "type": "JSON Web Token", "verified_result": null }, @@ -219,7 +227,7 @@ "hashed_secret": "de89e230a57dd89a612cc8439c00d0001ffa6389", "is_secret": false, "is_verified": false, - "line_number": 15, + "line_number": 21, "type": "JSON Web Token", "verified_result": null }, @@ -227,7 +235,7 @@ "hashed_secret": "6a4b65a72c0da02a03007867d9838f4a256a384b", "is_secret": false, "is_verified": false, - "line_number": 21, + "line_number": 27, "type": "JSON Web Token", "verified_result": null }, @@ -235,7 +243,7 @@ "hashed_secret": "42be9b0e85dc9f0fcb42c69058b133fd23dfde2b", "is_secret": false, "is_verified": false, - "line_number": 27, + "line_number": 33, "type": "JSON Web Token", "verified_result": null }, @@ -243,19 +251,53 @@ "hashed_secret": "73f596843cdc77ecc6a0a4cdc5b5d89071ad1b79", "is_secret": false, "is_verified": false, - "line_number": 31, + "line_number": 37, "type": "JSON Web Token", "verified_result": null }, { - "hashed_secret": "63a47776714d85556701c61dd731a302ed132385", + "hashed_secret": "c017c618a0ef5209f2cbbb367a7c68eb0404e043", "is_secret": false, "is_verified": false, "line_number": 47, + "type": "JSON Web Token", + "verified_result": null + }, + { + "hashed_secret": "6160045cbf6bfe6c1669311f5720fd73cbe56243", + "is_secret": false, + "is_verified": false, + "line_number": 57, + "type": "JSON Web Token", + "verified_result": null + }, + { + "hashed_secret": "63a47776714d85556701c61dd731a302ed132385", + "is_secret": false, + "is_verified": false, + "line_number": 76, "type": "Hex High Entropy String", "verified_result": null } ], + "bluemix/configuration/core_config/uaa_token_test.go": [ + { + "hashed_secret": "c017c618a0ef5209f2cbbb367a7c68eb0404e043", + "is_secret": false, + "is_verified": false, + "line_number": 22, + "type": "JSON Web Token", + "verified_result": null + }, + { + "hashed_secret": "3c1dbf49c59ee96e6a9ac90bea40471c0cb8b4f1", + "is_secret": false, + "is_verified": false, + "line_number": 32, + "type": "JSON Web Token", + "verified_result": null + } + ], "bluemix/env.go": [ { "hashed_secret": "33e5e588d24c979db687c950cf004784e95ddb1b", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7a1cb05..d23477ff 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,7 +17,7 @@ We follow the offical [CodeReviewComments](https://github.com/golang/go/wiki/Cod Make sure you have good unit test. Run `go test -cover $(go list ./...)`, and ensure coverage is above 80% for major packages (aka packages other than i18n, fakes, docs...). #### Secret Detection -This project uses the IBM Detect Secrets Module. Install the module, by following these [instructions](https://github.com/ibm/detect-secrets#installupgrade-module). Once installed, enable the pre-commit secret detection hook by following these [instructions](https://github.com/ibm/detect-secrets#installupgrade-module) to ensure no secrets are committed to this repo. +This project uses the IBM Detect Secrets Module. Install the module, by following these [instructions](https://github.com/ibm/detect-secrets#installupgrade-module). Once installed, enable the pre-commit secret detection hook by following these [instructions](https://github.com/ibm/detect-secrets#prevention-pre-commit-hook) to ensure no secrets are committed to this repo. #### Commit Message diff --git a/bluemix/configuration/core_config/bx_config.go b/bluemix/configuration/core_config/bx_config.go index 7a33964b..a9774304 100644 --- a/bluemix/configuration/core_config/bx_config.go +++ b/bluemix/configuration/core_config/bx_config.go @@ -296,11 +296,34 @@ func (c *bxConfig) IAMID() (guid string) { return } -func (c *bxConfig) IsLoggedIn() (loggedIn bool) { - c.read(func() { - loggedIn = c.data.IAMToken != "" - }) - return +// IsLoggedIn will check if the user is logged in. To determine if the user is logged in both the +// token and the refresh token will be checked +// If token is near expiration or expired, and a refresh token is present attempt to refresh the token. +// If token refresh was successful, check if the new IAM token is valid. If valid, user is logged in, +// otherwise user can be considered logged out. If refresh failed, then user is considered logged out. +// If no refresh token is present, and token is expired, then user is considered logged out. +func (c *bxConfig) IsLoggedIn() bool { + if token, refresh := c.IAMToken(), c.IAMRefreshToken(); token != "" || refresh != "" { + iamTokenInfo := NewIAMTokenInfo(token) + if iamTokenInfo.hasExpired() && refresh != "" { + repo := newRepository(c, nil) + if _, err := repo.RefreshIAMToken(); err != nil { + return false + } + // Check again to make sure that the new token has not expired + if iamTokenInfo = NewIAMTokenInfo(c.IAMToken()); iamTokenInfo.hasExpired() { + return false + } + + return true + } else if iamTokenInfo.hasExpired() && refresh == "" { + return false + } else { + return true + } + } + + return false } func (c *bxConfig) CurrentAccount() (account models.Account) { diff --git a/bluemix/configuration/core_config/bx_config_test.go b/bluemix/configuration/core_config/bx_config_test.go index 866eca10..ed360da8 100644 --- a/bluemix/configuration/core_config/bx_config_test.go +++ b/bluemix/configuration/core_config/bx_config_test.go @@ -1,7 +1,10 @@ package core_config_test import ( + "fmt" "io/ioutil" + "net/http" + "net/http/httptest" "os" "testing" @@ -298,9 +301,80 @@ func TestVPCCRITokenURL(t *testing.T) { t.Cleanup(cleanupConfigFiles) } +func TestIsLoggedIn(t *testing.T) { + config := prepareConfigForCLI(`{"UsageStatsEnabledLastUpdate": "2021-11-29T12:23:43.519017+08:00","UsageStatsEnabled": true}`, t) + refresh := "eyJhbGciOiJydCJ9.eyJzZXNzaW9uX2lkIjoiQy02YzUzN2U3My0wMjAxLTQ0YmUtODZlZC0zMDY0YTUwMTMwNGYiLCJpYW1faWQiOiJJQk1pZC02NjYwMDE1UktKIiwiYWNjb3VudF9pZCI6IjYwOWMxNGI4NjhmNTQ1OWM5YmZkOGJhOGI4OTZiYmE5In0._WLlp2iYEpA9PTStswtoXPj5GhiXwrEB0EhEVIF-SKu34qL3-gmfjIeN7RJGT--nwvmuuDfcVIdhA2j7MCmjPqk8YUMVmvR38YY7pA81sk7ynBOeJSg_D2_QCDeH-p1waMZlmedvzhVhSJAqhsorQYOR5GMDmz-kiwOwbQ0ewBiX7Bkc5S_49spJG7T5qsBwLjd8EXjCFGGWg4MS-QXn1SnVKBJZ82VyRL8lSTrMCC2DGseA29ptLOhJJldKVjDrfgLAMDILge08Rbz5NZplwkBRLYT7bvMEaO1cj_we6Ya1DPEO70rYkkIJQ-UtYyOUMEVw4th1LKHkKFNYK4oR_it5DpX-w4jTi49yNbV8ragDtmfUQy1dQ7Vxv5Xc7IsF8htFboxtqYwqRi32M0821ftoGYbxZRX2W6BsijmaBUpE_iaBV39ulFJrvW4Uf9fp-GvjIkZo3iJZN8syrr6LQ_RicB73s3rhZ1tIA7i4w7lapSMAgGH2xqufh6Ca31YBN7Karj0Cy6CxX_2P4aLPlAgZL4qJK3gOz56h4hWqmLhurjf5bn0uPXznPAMAoWpim2MwvSSH3EPRbxUyqGusUe-AXhcY_bTjfWibQJ59wL0q3s6gNwkn1y0RKEdlGoc1ofkJU9XfJWc_HCrZ0Lkq5PjQDqk3WkhkxxVGuD92Ha8LNlff-u_hqpBT86nV7D9r9das0vl4etJA6QK8FJMJ5prRNUpKmrVWpMJrSCQr5o8FxvB94yC0H6FcvDTdIl2slVtOO5wtOkaqtcHLm2trpQO6T3nyJQMWd9rUalJjWP32Gpv5yBSEHKC_toJk050oVemk8fis2B5_Qlb86b5vJsnAMqhBC4SttBztlPf0vIDlReMle_HHRnNXUl58Eaady9jgaGqD2b-dU20wjX8p6SLtfMuYbN0hWhYl2_hP2jfzY4zCtQtO3nGNYKeHy_H678NK_WTjvvqvLighORbbi0KINDEVWqepxLxBnzXywWrSMki6CF6016nUh49DMpEpSeziriuxYllg_j2Uwcl396ecuGxkuN9iziHt9qKhnjS4oyjRo8pE3lGw6oc5DMBgxZcpgPJ8QHGlti1rs8vxdCxl2XxFu-Rsckj9jKjTeULKRV4voQKmiFoFELbgodp91fanimBVCLdBygLhm0v04o5tw6J6NkLnu4GPlFvASTLTG9PydIdSnWvP8sXR0T8PS_4l4ot96N2jqd-UKcaQgk5A-fo3NVfTELMz9CvdFYH1r33GtQvO2BtWmlJxU24wge5KtHVwmXe1BFZOxwE_nfV2AMxY-SplWh8lkjM34vJ7YUw8kmvjdSB7nFjCAyxmhPA" // pragma: allowlist secret + expiredToken := "eyJpYW1faWQiOiJJQk1pZC02NjYwMDE1UktKIiwiaWQiOiJJQk1pZC02NjYwMDE1UktKIiwicmVhbG1pZCI6IklCTWlkIiwic2Vzc2lvbl9pZCI6IkMtMDBkNDIyYjAtYzcyZC00MzNmLWE0YmUtMzc2ZjkyZDEyNDliIiwianRpIjoiNzNmMzVmNGQtZmI2Ny00NTc3LThlNGMtNDE3YzA5MDYwNDU3IiwiaWRlbnRpZmllciI6IjY2NjAwMTVSS0oiLCJnaXZlbl9uYW1lIjoiTkFOQSIsImZhbWlseV9uYW1lIjoiQU1GTyIsIm5hbWUiOiJOQU5BIEFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIiwic3ViIjoibm9hbWZvQGlibS5jb20iLCJhdXRobiI6eyJzdWIiOiJub2FtZm9AaWJtLmNvbSIsImlhbV9pZCI6IklCTWlkLTY2NjAwMTVSS0oiLCJuYW1lIjoiTkFOQSBBTUZPIiwiZ2l2ZW5fbmFtZSI6Ik5BTkEiLCJmYW1pbHlfbmFtZSI6IkFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiMDY3OGUzOWY3ZWYxNDkyODk1OWM0YzFhOGY2YTdiYmYifSwiaWF0IjoxNjM1NDQyMDI3LCJleHAiOjE2MzU0NDI5MjcsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpwYXNzY29kZSIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4IiwiYWNyIjozLCJhbXIiOlsidG90cCIsIm1mYSIsIm90cCIsInB3ZCJdfQ.RsBd371ACEKOlhkTJngqBVDCY90Z-MT-iYb1OiLA5OpLYPZunR0saHUzBLh2LxnV-Jo0oeitPBmIK38jDk8MCb-rZa3qYNB2qe0WgO50bCMLKgwhKqJwVM6jMMpg4vg6up8kH8Ftc61kivaa1GrJKmQkonnHrjgrLo5IB2yfkMEAbUAMPb_jcRfjEsSP44I-Vx3dYIVSZs8bIufkgmDbJjlMmdhRenh57iwtQ7uImFgK2d-qQ-7sWLvhfzj2VdBLRHPa-dWYlrVgOAMpk6SCMz8wh6LcDUx9LdNKHpxMGCXpGT_UUWvwYqBuLTI3nmkIWIb_Cqa6al7-gQKPTC00Fw" + newToken := "eyJraWQiOiIyMDE3MTAzMC0wMDowMDowMCIsImFsZyI6IkhTMjU2In0.eyJpYW1faWQiOiJpYW0tUHJvZmlsZS05NDQ5N2QwZC0yYWMzLTQxYmYtYTk5My1hNDlkMWIxNDYyN2MiLCJpZCI6IklCTWlkLXRlc3QiLCJyZWFsbWlkIjoiaWFtIiwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4IiwiaWRlbnRpZmllciI6IlByb2ZpbGUtOTQ0OTdkMGQtMmFjMy00MWJmLWE5OTMtYTQ5ZDFiMTQ2MjdjIiwibmFtZSI6Ik15IFByb2ZpbGUiLCJzdWIiOiJQcm9maWxlLTk0NDk3ZDBkLTJhYzMtNDFiZi1hOTkzLWE0OWQxYjE0NjI3YyIsInN1Yl90eXBlIjoiUHJvZmlsZSIsImF1dGhuIjp7InN1YiI6ImNybjp2MTpzdGFnaW5nOnB1YmxpYzppYW0taWRlbnRpdHk6OmEvMThlMzAyMDc0OWNlNDc0NGIwYjQ3MjQ2NmQ2MWZkYjQ6OmNvbXB1dGVyZXNvdXJjZTpGYWtlLUNvbXB1dGUtUmVzb3VyY2UiLCJpYW1faWQiOiJjcm4tY3JuOnYxOnN0YWdpbmc6cHVibGljOmlhbS1pZGVudGl0eTo6YS8xOGUzMDIwNzQ5Y2U0NzQ0YjBiNDcyNDY2ZDYxZmRiNDo6Y29tcHV0ZXJlc291cmNlOkZha2UtQ29tcHV0ZS1SZXNvdXJjZSIsIm5hbWUiOiJteV9jb21wdXRlX3Jlc291cmNlIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiZmFrZV9ic3MifSwiaWF0IjoxNjI5OTI5NDYzLCJleHAiOjgwMjk5MzMwNjMsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpjci10b2tlbiIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4In0.ACeIK_8Wi0QmgQ19w4J2OA0OKgC4zb6M6PuGuPTEY_E" + tests := []struct { + name string + token string + newToken string + refresh string + newRefresh string + isLoggedIn bool + }{ + { + name: "token is expired and refresh token is present", + token: expiredToken, + newToken: "bearer " + newToken, // on refresh the bearer header is append to the token + refresh: refresh, + newRefresh: refresh, + isLoggedIn: true, + }, + { + name: "token is expired and refresh token is NOT present", + token: expiredToken, + newToken: expiredToken, + refresh: "", + newRefresh: "", + isLoggedIn: false, + }, + { + name: "token is not expired", + token: newToken, + newToken: newToken, + refresh: refresh, + newRefresh: refresh, + isLoggedIn: true, + }, + { + name: "token is not expired and refresh is NOT present", + token: newToken, + newToken: newToken, + refresh: "", + newRefresh: "", + isLoggedIn: true, + }, + } + + IAMEndpoints := models.Endpoints{} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + fmt.Fprintf(w, "{\"access_token\": \"%s\", \"refresh_token\": \"%s\", \"token_type\": \"bearer\"}", newToken, refresh) + })) + defer ts.Close() + + IAMEndpoints.PublicEndpoint = ts.URL + IAMEndpoints.PrivateEndpoint = ts.URL + IAMEndpoints.PrivateVPCEndpoint = ts.URL + + config.SetIAMToken(test.token) + config.SetIAMRefreshToken(test.refresh) + config.SetIAMEndpoints(IAMEndpoints) + assert.Equal(t, test.isLoggedIn, config.IsLoggedIn()) + assert.Equal(t, test.newToken, config.IAMToken()) + assert.Equal(t, test.newRefresh, config.IAMRefreshToken()) + t.Cleanup(cleanupConfigFiles) + }) + } +} + func TestIsLoggedInAsProfile(t *testing.T) { config := prepareConfigForCLI(`{"UsageStatsEnabledLastUpdate": "2020-03-29T12:23:43.519017+08:00","UsageStatsEnabled": true}`, t) - testIAMCRTokenData := "Bearer eyJraWQiOiIyMDE3MTAzMC0wMDowMDowMCIsImFsZyI6IlJTMjU2In0.ewoJImlhbV9pZCI6ICJpYW0tUHJvZmlsZS05NDQ5N2QwZC0yYWMzLTQxYmYtYTk5My1hNDlkMWIxNDYyN2MiLAoJImlkIjogIklCTWlkLXRlc3QiLAoJInJlYWxtaWQiOiAiaWFtIiwKCSJqdGkiOiAiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4IiwKCSJpZGVudGlmaWVyIjogIlByb2ZpbGUtOTQ0OTdkMGQtMmFjMy00MWJmLWE5OTMtYTQ5ZDFiMTQ2MjdjIiwKCSJuYW1lIjogIk15IFByb2ZpbGUiLAoJInN1YiI6ICJQcm9maWxlLTk0NDk3ZDBkLTJhYzMtNDFiZi1hOTkzLWE0OWQxYjE0NjI3YyIsCgkic3ViX3R5cGUiOiAiUHJvZmlsZSIsCgkiYXV0aG4iOiB7CgkgICJzdWIiOiAiY3JuOnYxOnN0YWdpbmc6cHVibGljOmlhbS1pZGVudGl0eTo6YS8xOGUzMDIwNzQ5Y2U0NzQ0YjBiNDcyNDY2ZDYxZmRiNDo6Y29tcHV0ZXJlc291cmNlOkZha2UtQ29tcHV0ZS1SZXNvdXJjZSIsCgkgICJpYW1faWQiOiAiY3JuLWNybjp2MTpzdGFnaW5nOnB1YmxpYzppYW0taWRlbnRpdHk6OmEvMThlMzAyMDc0OWNlNDc0NGIwYjQ3MjQ2NmQ2MWZkYjQ6OmNvbXB1dGVyZXNvdXJjZTpGYWtlLUNvbXB1dGUtUmVzb3VyY2UiLAoJICAibmFtZSI6ICJteV9jb21wdXRlX3Jlc291cmNlIgoJfSwKCSJhY2NvdW50IjogewoJICAiYm91bmRhcnkiOiAiZ2xvYmFsIiwKCSAgInZhbGlkIjogdHJ1ZSwKCSAgImJzcyI6ICJmYWtlX2JzcyIKCX0sCgkiaWF0IjogMTYyOTkyOTQ2MywKCSJleHAiOiAxNjI5OTMzMDYzLAoJImlzcyI6ICJodHRwczovL2lhbS5jbG91ZC5pYm0uY29tL2lkZW50aXR5IiwKCSJncmFudF90eXBlIjogInVybjppYm06cGFyYW1zOm9hdXRoOmdyYW50LXR5cGU6Y3ItdG9rZW4iLAoJInNjb3BlIjogImlibSBvcGVuaWQiLAoJImNsaWVudF9pZCI6ICJieCIKICB9.CuSOKifh4DvE__bjwDsn5BKmAHF2NaXznoiA1KG-2s2njbJs9nQdOJ3lkOnM77BqvLEpu2cwsmhi4Gsdy-MiJ6ACub0A5zyB-D95IXsGYa5tbFQBLbPpmFDAgAhLG5gXlVnU7nyIJN17Slm3pcWSNXEdWcsA1tgDkC9gQc_rpDhUfhnFeGA2LpvVMtRDolcOrbRuWN4NEbBOwdTbG5-6ijZ5Ag2z3lVmlQZ_6BLBCSVM8WlI8eIGICqCx0HYsmCiMlSqZ-4fkpg2DBYYYX_XsMQlamGynuPeoiBckJIyGEgsJD2egYN2bOUNLcn5htSCGxoJ4HJfXJ70_iCzmovb0w" + testIAMCRTokenData := "Bearer eyJraWQiOiIyMDE3MTAzMC0wMDowMDowMCIsImFsZyI6IkhTMjU2In0.eyJpYW1faWQiOiJpYW0tUHJvZmlsZS05NDQ5N2QwZC0yYWMzLTQxYmYtYTk5My1hNDlkMWIxNDYyN2MiLCJpZCI6IklCTWlkLXRlc3QiLCJyZWFsbWlkIjoiaWFtIiwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4IiwiaWRlbnRpZmllciI6IlByb2ZpbGUtOTQ0OTdkMGQtMmFjMy00MWJmLWE5OTMtYTQ5ZDFiMTQ2MjdjIiwibmFtZSI6Ik15IFByb2ZpbGUiLCJzdWIiOiJQcm9maWxlLTk0NDk3ZDBkLTJhYzMtNDFiZi1hOTkzLWE0OWQxYjE0NjI3YyIsInN1Yl90eXBlIjoiUHJvZmlsZSIsImF1dGhuIjp7InN1YiI6ImNybjp2MTpzdGFnaW5nOnB1YmxpYzppYW0taWRlbnRpdHk6OmEvMThlMzAyMDc0OWNlNDc0NGIwYjQ3MjQ2NmQ2MWZkYjQ6OmNvbXB1dGVyZXNvdXJjZTpGYWtlLUNvbXB1dGUtUmVzb3VyY2UiLCJpYW1faWQiOiJjcm4tY3JuOnYxOnN0YWdpbmc6cHVibGljOmlhbS1pZGVudGl0eTo6YS8xOGUzMDIwNzQ5Y2U0NzQ0YjBiNDcyNDY2ZDYxZmRiNDo6Y29tcHV0ZXJlc291cmNlOkZha2UtQ29tcHV0ZS1SZXNvdXJjZSIsIm5hbWUiOiJteV9jb21wdXRlX3Jlc291cmNlIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiZmFrZV9ic3MifSwiaWF0IjoxNjI5OTI5NDYzLCJleHAiOjgwMjk5MzMwNjMsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpjci10b2tlbiIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4In0.ACeIK_8Wi0QmgQ19w4J2OA0OKgC4zb6M6PuGuPTEY_E" // check checkUsageStats(true, true, config, t) @@ -313,6 +387,16 @@ func TestIsLoggedInAsProfile(t *testing.T) { t.Cleanup(cleanupConfigFiles) } +func TestIsLoggedInWithServiceID(t *testing.T) { + // Setup + config := prepareConfigForCLI(`{"UsageStatsEnabledLastUpdate": "2020-03-29T12:23:43.519017+08:00","UsageStatsEnabled": true}`, t) + testIAMCRTokenData := "eyJraWQiOiIyMDE3MTAzMC0wMDowMDowMCIsImFsZyI6IkhTMjU2In0.eyJpYW1faWQiOiJpYW0tUHJvZmlsZS05NDQ5N2QwZC0yYWMzLTQxYmYtYTk5My1hNDlkMWIxNDYyN2MiLCJpZCI6IklCTWlkLXRlc3QiLCJyZWFsbWlkIjoiaWFtIiwianRpIjoiMDRkMjBiMjUtZWUyZC00MDBmLTg2MjMtOGNkODA3MGI1NDY4IiwiaWRlbnRpZmllciI6IlByb2ZpbGUtOTQ0OTdkMGQtMmFjMy00MWJmLWE5OTMtYTQ5ZDFiMTQ2MjdjIiwibmFtZSI6Ik15IFNlcnZpY2UiLCJzdWIiOiJQcm9maWxlLTk0NDk3ZDBkLTJhYzMtNDFiZi1hOTkzLWE0OWQxYjE0NjI3YyIsInN1Yl90eXBlIjoiU2VydmljZUlkIiwiYXV0aG4iOnsic3ViIjoiY3JuOnYxOnN0YWdpbmc6cHVibGljOmlhbS1pZGVudGl0eTo6YS8xOGUzMDIwNzQ5Y2U0NzQ0YjBiNDcyNDY2ZDYxZmRiNDo6Y29tcHV0ZXJlc291cmNlOkZha2UtQ29tcHV0ZS1SZXNvdXJjZSIsImlhbV9pZCI6ImNybi1jcm46djE6c3RhZ2luZzpwdWJsaWM6aWFtLWlkZW50aXR5OjphLzE4ZTMwMjA3NDljZTQ3NDRiMGI0NzI0NjZkNjFmZGI0Ojpjb21wdXRlcmVzb3VyY2U6RmFrZS1Db21wdXRlLVJlc291cmNlIiwibmFtZSI6Im15X2NvbXB1dGVfcmVzb3VyY2UifSwiYWNjb3VudCI6eyJib3VuZGFyeSI6Imdsb2JhbCIsInZhbGlkIjp0cnVlLCJic3MiOiJmYWtlX2JzcyJ9LCJpYXQiOjE2Mjk5Mjk0NjMsImV4cCI6MjYyOTkzMzA2MywiaXNzIjoiaHR0cHM6Ly9pYW0uY2xvdWQuaWJtLmNvbS9pZGVudGl0eSIsImdyYW50X3R5cGUiOiJ1cm46aWJtOnBhcmFtczpvYXV0aDpncmFudC10eXBlOmNyLXRva2VuIiwic2NvcGUiOiJpYm0gb3BlbmlkIiwiY2xpZW50X2lkIjoiYngifQ.n3WF2O2KMmhW0nBDN2CooOwgcSDGI2qK858BzaHB6YI" + config.SetIAMToken(testIAMCRTokenData) + + // Assertions + assert.True(t, config.IsLoggedInWithServiceID()) +} + func TestIsLoggedInAsCRI(t *testing.T) { config := prepareConfigForCLI(`{"UsageStatsEnabledLastUpdate": "2020-03-29T12:23:43.519017+08:00","UsageStatsEnabled": true}`, t) diff --git a/bluemix/configuration/core_config/cf_config.go b/bluemix/configuration/core_config/cf_config.go index f759d527..93e88185 100644 --- a/bluemix/configuration/core_config/cf_config.go +++ b/bluemix/configuration/core_config/cf_config.go @@ -2,11 +2,15 @@ package core_config import ( "encoding/json" + "errors" + "fmt" "sync" "time" + "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/authentication/uaa" "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/configuration" "github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/models" + "github.com/IBM-Cloud/ibm-cloud-cli-sdk/common/rest" "github.com/fatih/structs" ) @@ -153,6 +157,22 @@ func (c *cfConfig) writeRaw(cb func()) { } } +func (c *cfConfig) refreshToken(refreshToken string) (uaa.Token, error) { + auth := uaa.NewClient(uaa.DefaultConfig(c.data.AuthorizationEndpoint), rest.NewClient()) + refreshedToken, err := auth.GetToken(uaa.RefreshTokenRequest(refreshToken)) + if err != nil { + return uaa.Token{}, err + } + + // return an error if refreshed token is invalid + refreshedTokenInfo := NewUAATokenInfo(refreshedToken.AccessToken) + if !refreshedTokenInfo.exists() { + return uaa.Token{}, errors.New("could not refresh token") + } + + return *refreshedToken, nil +} + func (c *cfConfig) APIVersion() (version string) { c.read(func() { version = c.data.APIVersion @@ -279,11 +299,39 @@ func (c *cfConfig) Username() (name string) { return } +// IsLoggedIn will check if the user is logged in. To determine if the user is logged in both the +// token and the refresh token will be checked +// If token is near expiration or expired, and a refresh token is present attempt to refresh the token. +// If token refresh was successful, check if the new UAA token is valid. If valid, user is logged in, +// otherwise user can be considered logged out. If refresh failed, then user is considered logged out. +// If no refresh token is present, and token is expired, then user is considered logged out. func (c *cfConfig) IsLoggedIn() (loggedIn bool) { - c.read(func() { - loggedIn = c.data.AccessToken != "" - }) - return + if token, refresh := c.UAAToken(), c.UAARefreshToken(); token != "" || refresh != "" { + uaaTokenInfo := NewUAATokenInfo(token) + if uaaTokenInfo.hasExpired() && refresh != "" { + refreshedToken, err := c.refreshToken(token) + if err != nil { + return false + } + + // Check again to make sure that the new token has not expired + if uaaTokenInfo = NewUAATokenInfo(c.UAAToken()); uaaTokenInfo.hasExpired() { + return false + } + + uaaToken := fmt.Sprintf("%s %s", refreshedToken.TokenType, refreshedToken.AccessToken) + c.SetUAAToken(uaaToken) + c.SetUAARefreshToken(refreshedToken.RefreshToken) + + return true + } else if uaaTokenInfo.hasExpired() && refresh == "" { + return false + } else { + return true + } + } + + return false } func (c *cfConfig) CurrentOrganization() (org models.OrganizationFields) { diff --git a/bluemix/configuration/core_config/iam_token.go b/bluemix/configuration/core_config/iam_token.go index 72f98128..b458debf 100644 --- a/bluemix/configuration/core_config/iam_token.go +++ b/bluemix/configuration/core_config/iam_token.go @@ -14,6 +14,8 @@ const ( SubjectTypeTrustedProfile = "Profile" ) +const expiryDelta = 10 * time.Second + type IAMTokenInfo struct { IAMID string `json:"iam_id"` ID string `json:"id"` @@ -51,7 +53,7 @@ type Authn struct { } func NewIAMTokenInfo(token string) IAMTokenInfo { - tokenJSON, err := decodeAccessToken(token) + tokenJSON, err := DecodeAccessToken(token) if err != nil { return IAMTokenInfo{} } @@ -72,7 +74,10 @@ func NewIAMTokenInfo(token string) IAMTokenInfo { return ret } -func decodeAccessToken(token string) (tokenJSON []byte, err error) { +// DecodeAccessToken will decode an access token string into a raw JSON. +// The encoded string is expected to be in three parts separated by a period. +// This method does not validate the contents of the parts +func DecodeAccessToken(token string) (tokenJSON []byte, err error) { encodedParts := strings.Split(token, ".") if len(encodedParts) < 3 { @@ -82,3 +87,18 @@ func decodeAccessToken(token string) (tokenJSON []byte, err error) { encodedTokenJSON := encodedParts[1] return base64.RawURLEncoding.DecodeString(encodedTokenJSON) } + +func (t IAMTokenInfo) exists() bool { + // token without an ID is invalid + return t.ID != "" +} + +func (t IAMTokenInfo) hasExpired() bool { + if !t.exists() { + return true + } + if t.Expiry.IsZero() { + return false + } + return t.Expiry.Before(time.Now().Add(expiryDelta)) +} diff --git a/bluemix/configuration/core_config/iam_token_test.go b/bluemix/configuration/core_config/iam_token_test.go index 971cf82c..aa71aa0d 100644 --- a/bluemix/configuration/core_config/iam_token_test.go +++ b/bluemix/configuration/core_config/iam_token_test.go @@ -6,6 +6,12 @@ import ( "github.com/stretchr/testify/assert" ) +type iamTokenTestCases struct { + token string + name string + isExpired bool +} + var TestIAMTokenData = []string{ //token from password "eyJraWQiOiIyMDE3MTAzMC0wMDowMDowMCIsImFsZyI6IlJTMjU2In0.eyJpYW1faWQiOiJJQk1pZC0yNzAwMDZWOEhNIiwiaWQiOiJJQk1pZC0yNzAwMDZWOEhNIiwicmVhbG1pZCI6IklCTWlkIiwiaWRlbnRpZmllciI6IjI3MDAwNlY4SE0iLCJnaXZlbl9uYW1lIjoiT0UgUnVudGltZXMiLCJmYW1pbHlfbmFtZSI6IlN5c3RlbSBVc2VyIiwibmFtZSI6Ik9FIFJ1bnRpbWVzIFN5c3RlbSBVc2VyIiwiZW1haWwiOiJydHN5c3VzckBjbi5pYm0uY29tIiwic3ViIjoicnRzeXN1c3JAY24uaWJtLmNvbSIsImFjY291bnQiOnsiYnNzIjoiOGQ2M2ZiMWNjNWU5OWU4NmRkNzIyOWRkZGZmYzA1YTUifSwiaWF0IjoxNTE2MTc0NjAzLCJleHAiOjE1MTYxNzgyMDMsImlzcyI6Imh0dHBzOi8vaWFtLmJsdWVtaXgubmV0L2lkZW50aXR5IiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwic2NvcGUiOiJvcGVuaWQiLCJjbGllbnRfaWQiOiJieCJ9.gx-HQ1CSEwz5d4O1HXx4pusaYeEsqkQZgoBZ6esMBZG6wK6wQFPvC4D0Yvdi6CvKrVU-zV9PM_o3n5c-DFKjjTyTnRbQgrG0EPCRPmFW3bpepSb7eSw01S2YOLy5UTbz0cdM9hq-jafOu1S8pe9xeSMIMiA3-EFzCap5Z5CuoK9oIYJIFWseb1KsOyoiNOellbw1MaOmMzb4fsFz5Dr1Y8c1pNhoqp8M62E3y1yHe2jc6YepDab7Dqn2benK_e-MI3BlyWuBu4yo5mY2oCinJthr2E1YgbzWvcMy5a-ximnQIb4K6kscuUW_Yj_1GhDGJs4MP9u7M3-XdY1CNBGYeQ", @@ -31,6 +37,29 @@ var TestUAATokenData = []string{ "Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleS0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiJhZGFkNWYwYzQ0ZDI0ZDlkYmVhN2YyNGIzMDNmOWNhNyIsInN1YiI6IjY3ODdiMzM2LTAwNzUtNGYwYy1hZmZiLWUyOWZjMmVhZWI4OCIsInNjb3BlIjpbIm9wZW5pZCIsInVhYS51c2VyIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwicGFzc3dvcmQud3JpdGUiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIl0sImNsaWVudF9pZCI6ImNmIiwiY2lkIjoiY2YiLCJhenAiOiJjZiIsImdyYW50X3R5cGUiOiJwYXNzd29yZCIsInVzZXJfaWQiOiI2Nzg3YjMzNi0wMDc1LTRmMGMtYWZmYi1lMjlmYzJlYWViODgiLCJvcmlnaW4iOiJ1YWEiLCJ1c2VyX25hbWUiOiJ3YW5nanVubEBjbi5pYm0uY29tIiwiZW1haWwiOiJ3YW5nanVubEBjbi5pYm0uY29tIiwiYXV0aF90aW1lIjoxNTE2MTczMjgxLCJpYXQiOjE1MTYxNzMyODEsImV4cCI6MTUxNjE3Njg4MCwiaXNzIjoiaHR0cHM6Ly91YWEubmcuYmx1ZW1peC5uZXQvb2F1dGgvdG9rZW4iLCJ6aWQiOiJ1YWEiLCJhdWQiOlsiY2xvdWRfY29udHJvbGxlciIsInBhc3N3b3JkIiwiY2YiLCJ1YWEiLCJvcGVuaWQiXX0.K-9HdMCrNdln81ewX_TQLR63F4wChz035G5KtMq9wkk", } +var TestIAMTokenHasExpiredTestCases = []iamTokenTestCases{ + { + token: "", + name: "empty token", + isExpired: true, + }, + { + token: "eyJraWQiOiIyMDIxMTAxODA4MTkiLCJhbGciOiJSUzI1NiJ9.eyJpYW1faWQiOiJJQk1pZC02NjYwMDE1UktKIiwiaWQiOiJJQk1pZC02NjYwMDE1UktKIiwicmVhbG1pZCI6IklCTWlkIiwic2Vzc2lvbl9pZCI6IkMtMDBkNDIyYjAtYzcyZC00MzNmLWE0YmUtMzc2ZjkyZDEyNDliIiwianRpIjoiNzNmMzVmNGQtZmI2Ny00NTc3LThlNGMtNDE3YzA5MDYwNDU3IiwiaWRlbnRpZmllciI6IjY2NjAwMTVSS0oiLCJnaXZlbl9uYW1lIjoiTkFOQSIsImZhbWlseV9uYW1lIjoiQU1GTyIsIm5hbWUiOiJOQU5BIEFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIiwic3ViIjoibm9hbWZvQGlibS5jb20iLCJhdXRobiI6eyJzdWIiOiJub2FtZm9AaWJtLmNvbSIsImlhbV9pZCI6IklCTWlkLTY2NjAwMTVSS0oiLCJuYW1lIjoiTkFOQSBBTUZPIiwiZ2l2ZW5fbmFtZSI6Ik5BTkEiLCJmYW1pbHlfbmFtZSI6IkFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiMDY3OGUzOWY3ZWYxNDkyODk1OWM0YzFhOGY2YTdiYmYifSwiaWF0IjoxNjM1NDQyMDI3LCJleHAiOjE2MzU0NDI5MjcsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpwYXNzY29kZSIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4IiwiYWNyIjozLCJhbXIiOlsidG90cCIsIm1mYSIsIm90cCIsInB3ZCJdfQ.RsBd371ACEKOlhkTJngqBVDCY90Z-MT-iYb1OiLA5OpLYPZunR0saHUzBLh2LxnV-Jo0oeitPBmIK38jDk8MCb-rZa3qYNB2qe0WgO50bCMLKgwhKqJwVM6jMMpg4vg6up8kH8Ftc61kivaa1GrJKmQkonnHrjgrLo5IB2yfkMEAbUAMPb_jcRfjEsSP44I-Vx3dYIVSZs8bIufkgmDbJjlMmdhRenh57iwtQ7uImFgK2d-qQ-7sWLvhfzj2VdBLRHPa-dWYlrVgOAMpk6SCMz8wh6LcDUx9LdNKHpxMGCXpGT_UUWvwYqBuLTI3nmkIWIb_Cqa6al7-gQKPTC00Fw", + name: "expired token", + isExpired: true, + }, + { + token: "ABCD.DEFG.HIGK", + name: "invalid token", + isExpired: true, + }, + { + token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYW1faWQiOiJJQk1pZC02NjYwMDE1UktKIiwiaWQiOiJJQk1pZC02NjYwMDE1UktKIiwicmVhbG1pZCI6IklCTWlkIiwic2Vzc2lvbl9pZCI6IkMtMDBkNDIyYjAtYzcyZC00MzNmLWE0YmUtMzc2ZjkyZDEyNDliIiwianRpIjoiNzNmMzVmNGQtZmI2Ny00NTc3LThlNGMtNDE3YzA5MDYwNDU3IiwiaWRlbnRpZmllciI6IjY2NjAwMTVSS0oiLCJnaXZlbl9uYW1lIjoiTkFOQSIsImZhbWlseV9uYW1lIjoiQU1GTyIsIm5hbWUiOiJOQU5BIEFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIiwic3ViIjoibm9hbWZvQGlibS5jb20iLCJhdXRobiI6eyJzdWIiOiJub2FtZm9AaWJtLmNvbSIsImlhbV9pZCI6IklCTWlkLTY2NjAwMTVSS0oiLCJuYW1lIjoiTkFOQSBBTUZPIiwiZ2l2ZW5fbmFtZSI6Ik5BTkEiLCJmYW1pbHlfbmFtZSI6IkFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiMDY3OGUzOWY3ZWYxNDkyODk1OWM0YzFhOGY2YTdiYmYifSwiaWF0IjoxNzM1NDQyMDI3LCJleHAiOjE3ODYxNzgyMDMsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpwYXNzY29kZSIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4IiwiYWNyIjozLCJhbXIiOlsidG90cCIsIm1mYSIsIm90cCIsInB3ZCJdfQ.3ZOgRfe6JcQv303O3DYdCI9OCDkxdlMEBHp8f7iy1fI", + name: "token expired in 2029", + isExpired: false, + }, +} + func TestNewIAMTokenInfo(t *testing.T) { for _, token := range TestIAMTokenData { tokenInfo := NewIAMTokenInfo(token) @@ -72,3 +101,12 @@ func TestUAATokenInfo(t *testing.T) { assert.Equal(t, tokenInfo.UserGUID, "6787b336-0075-4f0c-affb-e29fc2eaeb88") } } + +func TestIAMTokenHasExpired(t *testing.T) { + for _, testCase := range TestIAMTokenHasExpiredTestCases { + t.Run(testCase.name, func(t *testing.T) { + tokenInfo := NewIAMTokenInfo(testCase.token) + assert.Equal(t, testCase.isExpired, tokenInfo.hasExpired()) + }) + } +} diff --git a/bluemix/configuration/core_config/uaa_token.go b/bluemix/configuration/core_config/uaa_token.go index 0c3e1e23..46dba9e8 100644 --- a/bluemix/configuration/core_config/uaa_token.go +++ b/bluemix/configuration/core_config/uaa_token.go @@ -16,7 +16,7 @@ type UAATokenInfo struct { } func NewUAATokenInfo(token string) UAATokenInfo { - tokenJSON, err := decodeAccessToken(token) + tokenJSON, err := DecodeAccessToken(token) if err != nil { return UAATokenInfo{} } @@ -36,3 +36,18 @@ func NewUAATokenInfo(token string) UAATokenInfo { ret.IssueAt = t.IssueAt.Time() return ret } + +func (t UAATokenInfo) exists() bool { + // UAA token without an UserGUID is invalid + return t.UserGUID != "" +} + +func (t UAATokenInfo) hasExpired() bool { + if !t.exists() { + return true + } + if t.Expiry.IsZero() { + return false + } + return t.Expiry.Before(time.Now().Add(expiryDelta)) +} diff --git a/bluemix/configuration/core_config/uaa_token_test.go b/bluemix/configuration/core_config/uaa_token_test.go new file mode 100644 index 00000000..547a1372 --- /dev/null +++ b/bluemix/configuration/core_config/uaa_token_test.go @@ -0,0 +1,45 @@ +package core_config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type uaaTokenTestCases struct { + token string + name string + isExpired bool +} + +var TestUAATokenHasExpiredTestCases = []uaaTokenTestCases{ + { + token: "", + name: "empty token", + isExpired: true, + }, + { + token: "eyJraWQiOiIyMDIxMTAxODA4MTkiLCJhbGciOiJSUzI1NiJ9.eyJpYW1faWQiOiJJQk1pZC02NjYwMDE1UktKIiwiaWQiOiJJQk1pZC02NjYwMDE1UktKIiwicmVhbG1pZCI6IklCTWlkIiwic2Vzc2lvbl9pZCI6IkMtMDBkNDIyYjAtYzcyZC00MzNmLWE0YmUtMzc2ZjkyZDEyNDliIiwianRpIjoiNzNmMzVmNGQtZmI2Ny00NTc3LThlNGMtNDE3YzA5MDYwNDU3IiwiaWRlbnRpZmllciI6IjY2NjAwMTVSS0oiLCJnaXZlbl9uYW1lIjoiTkFOQSIsImZhbWlseV9uYW1lIjoiQU1GTyIsIm5hbWUiOiJOQU5BIEFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIiwic3ViIjoibm9hbWZvQGlibS5jb20iLCJhdXRobiI6eyJzdWIiOiJub2FtZm9AaWJtLmNvbSIsImlhbV9pZCI6IklCTWlkLTY2NjAwMTVSS0oiLCJuYW1lIjoiTkFOQSBBTUZPIiwiZ2l2ZW5fbmFtZSI6Ik5BTkEiLCJmYW1pbHlfbmFtZSI6IkFNRk8iLCJlbWFpbCI6Im5vYW1mb0BpYm0uY29tIn0sImFjY291bnQiOnsiYm91bmRhcnkiOiJnbG9iYWwiLCJ2YWxpZCI6dHJ1ZSwiYnNzIjoiMDY3OGUzOWY3ZWYxNDkyODk1OWM0YzFhOGY2YTdiYmYifSwiaWF0IjoxNjM1NDQyMDI3LCJleHAiOjE2MzU0NDI5MjcsImlzcyI6Imh0dHBzOi8vaWFtLmNsb3VkLmlibS5jb20vaWRlbnRpdHkiLCJncmFudF90eXBlIjoidXJuOmlibTpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTpwYXNzY29kZSIsInNjb3BlIjoiaWJtIG9wZW5pZCIsImNsaWVudF9pZCI6ImJ4IiwiYWNyIjozLCJhbXIiOlsidG90cCIsIm1mYSIsIm90cCIsInB3ZCJdfQ.RsBd371ACEKOlhkTJngqBVDCY90Z-MT-iYb1OiLA5OpLYPZunR0saHUzBLh2LxnV-Jo0oeitPBmIK38jDk8MCb-rZa3qYNB2qe0WgO50bCMLKgwhKqJwVM6jMMpg4vg6up8kH8Ftc61kivaa1GrJKmQkonnHrjgrLo5IB2yfkMEAbUAMPb_jcRfjEsSP44I-Vx3dYIVSZs8bIufkgmDbJjlMmdhRenh57iwtQ7uImFgK2d-qQ-7sWLvhfzj2VdBLRHPa-dWYlrVgOAMpk6SCMz8wh6LcDUx9LdNKHpxMGCXpGT_UUWvwYqBuLTI3nmkIWIb_Cqa6al7-gQKPTC00Fw", + name: "expired token", + isExpired: true, + }, + { + token: "ABCD.DEFG.HIGK", + name: "invalid token", + isExpired: true, + }, + { + token: "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleS0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiJhZGFkNWYwYzQ0ZDI0ZDlkYmVhN2YyNGIzMDNmOWNhNyIsInN1YiI6IjY3ODdiMzM2LTAwNzUtNGYwYy1hZmZiLWUyOWZjMmVhZWI4OCIsInNjb3BlIjpbIm9wZW5pZCIsInVhYS51c2VyIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwicGFzc3dvcmQud3JpdGUiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIl0sImNsaWVudF9pZCI6ImNmIiwiY2lkIjoiY2YiLCJhenAiOiJjZiIsImdyYW50X3R5cGUiOiJwYXNzd29yZCIsInVzZXJfaWQiOiI2Nzg3YjMzNi0wMDc1LTRmMGMtYWZmYi1lMjlmYzJlYWViODgiLCJvcmlnaW4iOiJ1YWEiLCJ1c2VyX25hbWUiOiJ3YW5nanVubEBjbi5pYm0uY29tIiwiZW1haWwiOiJ3YW5nanVubEBjbi5pYm0uY29tIiwiYXV0aF90aW1lIjoxNTE2MTczMjgxLCJpYXQiOjE4MTYxNzMyODEsImV4cCI6MjkyNjE3Njg4MCwiaXNzIjoiaHR0cHM6Ly91YWEubmcuYmx1ZW1peC5uZXQvb2F1dGgvdG9rZW4iLCJ6aWQiOiJ1YWEiLCJhdWQiOlsiY2xvdWRfY29udHJvbGxlciIsInBhc3N3b3JkIiwiY2YiLCJ1YWEiLCJvcGVuaWQiXX0.CPUPLMtDpGScEbK9pyD5pzOCWXUir7TX-gZSmFFSRLM", + name: "token expired in 2062", + isExpired: false, + }, +} + +func TestUAATokenHasExpired(t *testing.T) { + for _, testCase := range TestUAATokenHasExpiredTestCases { + t.Run(testCase.name, func(t *testing.T) { + tokenInfo := NewUAATokenInfo(testCase.token) + assert.Equal(t, testCase.isExpired, tokenInfo.hasExpired()) + }) + } +} diff --git a/bluemix/version.go b/bluemix/version.go index d867f581..8c9bebd3 100644 --- a/bluemix/version.go +++ b/bluemix/version.go @@ -3,7 +3,7 @@ package bluemix import "fmt" // Version is the SDK version -var Version = VersionType{Major: 0, Minor: 8, Build: 0} +var Version = VersionType{Major: 0, Minor: 8, Build: 1} // VersionType describe version info type VersionType struct {