Skip to content
This repository was archived by the owner on Jan 17, 2025. It is now read-only.

Commit 48cc891

Browse files
authored
Add IAM token expiration check (#72)
* Add IAM token expiration check * Fix linter issues * Use ibmcloud config path from env; mock test configs instead * Compare timestamp with UTC time in tests * Mock env config path and default locations
1 parent fcbf554 commit 48cc891

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ env:
2525
- __OW_IGNORE_CERTS=true
2626
- REDIS=redis://172.17.0.1:6379
2727
- IC_FN_CONFIG_FILE=./test/cf-plugin-config.json
28+
- IC_CONFIG_FILE=./test/cf-config.json
2829
before_install:
2930
- ./travis/scancode.sh
3031
before_script:

client.js

+10
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ module.exports = function (options, basic, bearer) {
7676
//
7777
// check for IAM-based namespaces, first
7878
if (namespaceType === NS_TYPE_IAM) {
79+
const tokenTimestamp = ibmcloudUtils.getIamTokenTimestamp()
80+
81+
if (ibmcloudUtils.iamTokenExpired(tokenTimestamp)) {
82+
console.log(
83+
'Error: Your IAM token seems to be expired. Plase perform an `ibmcloud login` ' +
84+
'to make sure your token is up to date.'
85+
)
86+
throw new Error('IAM token expired')
87+
}
88+
7989
// for authentication, we'll use the user IAM access token
8090
const iamToken = ibmcloudUtils.getIamAuthHeader()
8191

ibmcloud-utils.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,24 @@ const getIamAuthHeader = () => {
6767
return iamToken
6868
}
6969

70+
const getIamTokenTimestamp = () => {
71+
const timestamp = getCloudFunctionsConfig().IamTimeTokenRefreshed
72+
return new Date(timestamp)
73+
}
74+
75+
const iamTokenExpired = (timeRefreshed, timeReference) => {
76+
if (typeof (timeReference) === 'undefined') {
77+
timeReference = new Date()
78+
}
79+
80+
// time difference in hours exceeds 1 hour
81+
return (timeReference - timeRefreshed) / 1000 / 3600 > 1
82+
}
83+
7084
module.exports = {
85+
iamTokenExpired,
7186
getIamAuthHeader,
72-
getNamespaceType,
73-
getNamespaceId
87+
getIamTokenTimestamp,
88+
getNamespaceId,
89+
getNamespaceType
7490
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
},
3939
"devDependencies": {
4040
"mocha": "^5.2.0",
41+
"mock-fs": "^4.13.0",
4142
"pre-commit": "^1.2.2",
4243
"standard": "^12.0.1"
4344
},

test/ibmcloud-utils.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/* eslint-env mocha */
19+
20+
'use strict'
21+
22+
const assert = require('assert')
23+
const mock = require('mock-fs')
24+
const path = require('path')
25+
const os = require('os')
26+
27+
const ibmcloudUtils = require('../ibmcloud-utils')
28+
const client = require('../client')
29+
30+
describe('ibmcloud-utils', function () {
31+
describe('ibmcloud-utils.token-expiration', function () {
32+
const ibmCloudFunctionsPropsPath =
33+
process.env.IC_FN_CONFIG_FILE || path.join(os.homedir(), '.bluemix/plugins/cloud-functions/config.json')
34+
const ibmCloudPropsPath = process.env.IC_CONFIG_FILE || path.join(os.homedir(), '.bluemix/config.json')
35+
36+
it('read timestamp', function () {
37+
mock({
38+
[ibmCloudFunctionsPropsPath]: '{ "IamTimeTokenRefreshed": "2021-03-15T13:24:14+01:00" }'
39+
})
40+
const timestamp = ibmcloudUtils.getIamTokenTimestamp()
41+
assert.strictEqual(timestamp.getTime(), Date.UTC(2021, 2, 15, 12, 24, 14))
42+
})
43+
44+
it('token not expired', function () {
45+
const timeRefreshed = new Date(2021, 2, 13, 13, 24, 14)
46+
const timeReference = new Date(2021, 2, 13, 13, 30, 0)
47+
const tokenExpired = ibmcloudUtils.iamTokenExpired(timeRefreshed, timeReference)
48+
assert.strictEqual(tokenExpired, false)
49+
})
50+
51+
it('token expired', function () {
52+
const timeRefreshed = new Date(2021, 2, 13, 13, 24, 14)
53+
const timeReference = new Date(2021, 2, 13, 14, 25, 0)
54+
const tokenExpired = ibmcloudUtils.iamTokenExpired(timeRefreshed, timeReference)
55+
assert.strictEqual(tokenExpired, true)
56+
})
57+
58+
it('client fails when token expired', function () {
59+
mock({
60+
[ibmCloudFunctionsPropsPath]: '{ "IamTimeTokenRefreshed": "2021-03-14T12:00:00+01:00", ' +
61+
'"WskCliNamespaceId": "some-namespace-id", ' +
62+
'"WskCliNamespaceMode": "IAM" }',
63+
[ibmCloudPropsPath]: '{ "IAMToken": "some-token" }'
64+
})
65+
66+
assert.throws(() => client(), /IAM token expired/)
67+
})
68+
})
69+
})

0 commit comments

Comments
 (0)