diff --git a/config/setupTests.js b/config/setupTests.js index 17afc4781..f592089a2 100644 --- a/config/setupTests.js +++ b/config/setupTests.js @@ -30,6 +30,7 @@ global.window.insights = { type: 'User' } })) - } + }, + getUserPermissions: () => Promise.resolve([]) } }; diff --git a/package-lock.json b/package-lock.json index 3b12141ac..707303929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3367,8 +3367,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -3389,14 +3388,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3411,20 +3408,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3541,8 +3535,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3554,7 +3547,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3569,7 +3561,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3577,14 +3568,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3603,7 +3592,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -3684,8 +3672,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3697,7 +3684,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3783,8 +3769,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -3820,7 +3805,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3840,7 +3824,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3884,14 +3867,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -4383,6 +4364,14 @@ "js-sha256": "0.9.0" } }, + "@redhat-cloud-services/rbac-client": { + "version": "1.0.49", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/rbac-client/-/rbac-client-1.0.49.tgz", + "integrity": "sha512-9E1gcKaLLWFyfkszv6q28d5NArSlQFZcWZAQnceVm3ZZEBvimelnLlp6NTuSxkpFaidTp9YcBH/+4e3xxB+C0Q==", + "requires": { + "axios": "^0.19.0" + } + }, "@sentry/browser": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.4.3.tgz", @@ -5332,6 +5321,14 @@ } } }, + "axios-mock-adapter": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.17.0.tgz", + "integrity": "sha512-q3efmwJUOO4g+wsLNSk9Ps1UlJoF3fQ3FSEe4uEEhkRtu7SoiAVPj8R3Hc/WP55MBTVFzaDP9QkdJhdVhP8A1Q==", + "requires": { + "deep-equal": "^1.0.1" + } + }, "babel-cli": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-cli/-/babel-cli-6.26.0.tgz", @@ -8483,6 +8480,26 @@ "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=", "dev": true }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, "deep-equal-ident": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz", @@ -10779,7 +10796,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -11268,7 +11284,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, - "optional": true, "requires": { "is-glob": "^2.0.0" } @@ -11716,7 +11731,7 @@ "dependencies": { "domelementtype": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", "dev": true }, @@ -12039,6 +12054,11 @@ "is-decimal": "^1.0.0" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -12194,8 +12214,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true, - "optional": true + "dev": true }, "is-finite": { "version": "1.0.2", @@ -12222,7 +12241,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, - "optional": true, "requires": { "is-extglob": "^1.0.0" } @@ -13575,8 +13593,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -13613,8 +13630,7 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", @@ -13623,8 +13639,7 @@ }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -13727,8 +13742,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -13738,7 +13752,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -13764,7 +13777,6 @@ "minipass": { "version": "2.3.5", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -13781,7 +13793,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -13854,8 +13865,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -13865,7 +13875,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -13941,8 +13950,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -13972,7 +13980,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -13990,7 +13997,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -14029,13 +14035,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.0.3", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -16008,7 +16012,6 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -16018,8 +16021,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } }, @@ -20282,6 +20284,91 @@ "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==", "dev": true }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + } + } + }, "regexpp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", diff --git a/package.json b/package.json index ae888e7d2..b64e2e4ea 100644 --- a/package.json +++ b/package.json @@ -121,9 +121,11 @@ "@redhat-cloud-services/frontend-components-remediations": "0.0.5", "@redhat-cloud-services/frontend-components-utilities": "0.0.15", "@redhat-cloud-services/keycloak-js": "0.0.3", + "@redhat-cloud-services/rbac-client": "^1.0.49", "@sentry/browser": "^5.4.3", "axios": "^0.19.0", "axios-cache-adapter": "^2.3.0", + "axios-mock-adapter": "^1.17.0", "babel-plugin-rewire": "^1.2.0", "babel-preset-es2015": "^6.24.1", "broadcast-channel": "^2.1.9", diff --git a/src/__mocks__/rbacApi.js b/src/__mocks__/rbacApi.js new file mode 100644 index 000000000..2e0d0f140 --- /dev/null +++ b/src/__mocks__/rbacApi.js @@ -0,0 +1,4 @@ +import accessRbac from '../js/rbac/rbac.js'; +import MockAdapter from 'axios-mock-adapter'; + +export const mock = new MockAdapter(accessRbac('uSeRtOkEn').axios); diff --git a/src/js/entry.js b/src/js/entry.js index 94b641698..d0744da9a 100644 --- a/src/js/entry.js +++ b/src/js/entry.js @@ -17,6 +17,7 @@ import NoAccess from './App/NoAccess'; const log = require('./jwt/logger')('entry.js'); const sourceOfTruth = require('./nav/sourceOfTruth'); +import { fetchPermissions } from './rbac/fetchPermissions'; // used for translating event names exposed publicly to internal event names const PUBLIC_EVENTS = { @@ -126,6 +127,9 @@ export function bootstrap(libjwt, initFunc) { isBeta: () => { return (window.location.pathname.split('/')[1] === 'beta' ? true : false); }, + getUserPermissions: () => { + return fetchPermissions(libjwt.jwt.getEncodedToken()); + }, init: initFunc }, loadInventory, diff --git a/src/js/rbac/fetchPermissions.js b/src/js/rbac/fetchPermissions.js new file mode 100644 index 000000000..24d524352 --- /dev/null +++ b/src/js/rbac/fetchPermissions.js @@ -0,0 +1,20 @@ +import createRbacAPI from './rbac.js'; +const log = require('../jwt/logger')('fetchPermissions.js'); + +const perPage = 25; + +export const fetchPermissions = (userToken) => { + const rbacApi = createRbacAPI(userToken); + return rbacApi.getPrincipalAccess('*', undefined, perPage).then(({ data, meta }) => { + if (meta.count > perPage) { + return Promise.all( + [...new Array(Math.ceil(meta.count / perPage))] + .map((_empty, key) => rbacApi.getPrincipalAccess('*', undefined, perPage, (key + 1) * perPage) + .then(({ data }) => data)) + ).then(allAccess => allAccess.reduce((acc, curr) => ([...acc, ...curr]), data)) + .catch(error => log(error)); + } else { + return data; + }}) + .catch(error => log(error)); +}; diff --git a/src/js/rbac/fetchPermissions.test.js b/src/js/rbac/fetchPermissions.test.js new file mode 100644 index 000000000..304fe6bcd --- /dev/null +++ b/src/js/rbac/fetchPermissions.test.js @@ -0,0 +1,15 @@ +import { fetchPermissions } from './fetchPermissions'; +import { mock } from '../../__mocks__/rbacApi'; +import mockedRbac from '../../../testdata/rbacAccess.json'; + +it('should send all the paginated data as array', async () => { + mock.onGet('/api/rbac/v1/access/?application=*&limit=25').reply(200, mockedRbac); + const data = fetchPermissions('uSeRtOkEn'); + data.then(permissions => expect(permissions).toEqual(mockedRbac.data)); +}); + +it('should send the data as array', async () => { + mock.onGet('/api/rbac/v1/access/?application=*&limit=50').reply(200, mockedRbac); + const data = fetchPermissions('uSeRtOkEn'); + data.then(permissions => expect(permissions).toEqual(mockedRbac.data)); +}); diff --git a/src/js/rbac/rbac.js b/src/js/rbac/rbac.js new file mode 100644 index 000000000..f9062f73b --- /dev/null +++ b/src/js/rbac/rbac.js @@ -0,0 +1,13 @@ +const axios = require('axios'); +const { AccessApi } = require('@redhat-cloud-services/rbac-client'); +const { bootstrapCache } = require('../utils'); +const BASE_PATH = '/api/rbac/v1'; + +module.exports = (cachePrefix) => { + const cache = bootstrapCache(BASE_PATH, `${cachePrefix}-rbac`); + + const instance = axios.create({ adapter: cache.adapter }); + instance.interceptors.response.use((response) => response.data || response); + + return new AccessApi(undefined, BASE_PATH, instance); +}; diff --git a/testdata/rbacAccess.json b/testdata/rbacAccess.json new file mode 100644 index 000000000..dba2da130 --- /dev/null +++ b/testdata/rbacAccess.json @@ -0,0 +1,109 @@ +{ + "meta": { + "count": 25, + "limit": 25, + "offset": 0 + }, + "data": [ + { + "permission": "ansible-hub:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "vulnerability:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "compliance:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "insights:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "remediations:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "ansible-hub:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "vulnerability:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "compliance:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "insights:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "remediations:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "insights:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + }, + { + "permission": "remediations:*:*", + "resourceDefinitions": [] + }, + { + "permission": "inventory:*:*", + "resourceDefinitions": [] + } + ] +} \ No newline at end of file